first commit
Deploy / lint (push) Failing after 7s
Deploy / test (push) Has been skipped
Deploy / deploy (push) Has been skipped

This commit is contained in:
Dennis Thiessen
2026-02-20 17:31:01 +01:00
commit 61ab24490d
160 changed files with 17034 additions and 0 deletions
@@ -0,0 +1,57 @@
import { Link } from 'react-router-dom';
import type { RankingEntry } from '../../lib/types';
interface RankingsTableProps {
rankings: RankingEntry[];
}
function scoreColor(score: number): string {
if (score > 70) return 'text-emerald-400';
if (score >= 40) return 'text-amber-400';
return 'text-red-400';
}
export function RankingsTable({ rankings }: RankingsTableProps) {
if (rankings.length === 0) {
return <p className="py-8 text-center text-sm text-gray-500">No rankings available.</p>;
}
const dimensionNames = rankings.length > 0 ? rankings[0].dimensions.map((d) => d.dimension) : [];
return (
<div className="glass overflow-x-auto">
<table className="w-full text-left text-sm">
<thead>
<tr className="border-b border-white/[0.06] text-xs uppercase tracking-wider text-gray-500">
<th className="px-4 py-3">Rank</th>
<th className="px-4 py-3">Symbol</th>
<th className="px-4 py-3">Composite</th>
{dimensionNames.map((dim) => (
<th key={dim} className="px-4 py-3">{dim}</th>
))}
</tr>
</thead>
<tbody>
{rankings.map((entry, index) => (
<tr key={entry.symbol} className="border-b border-white/[0.04] transition-all duration-200 hover:bg-white/[0.03]">
<td className="px-4 py-3.5 font-mono text-gray-500">{index + 1}</td>
<td className="px-4 py-3.5">
<Link to={`/ticker/${entry.symbol}`} className="font-medium text-blue-400 hover:text-blue-300 transition-colors duration-150">
{entry.symbol}
</Link>
</td>
<td className={`px-4 py-3.5 font-semibold ${scoreColor(entry.composite_score)}`}>
{Math.round(entry.composite_score)}
</td>
{entry.dimensions.map((dim) => (
<td key={dim.dimension} className={`px-4 py-3.5 font-mono ${scoreColor(dim.score)}`}>
{Math.round(dim.score)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
@@ -0,0 +1,50 @@
import { useState, type FormEvent } from 'react';
import { useUpdateWeights } from '../../hooks/useScores';
interface WeightsFormProps {
weights: Record<string, number>;
}
export function WeightsForm({ weights }: WeightsFormProps) {
const [localWeights, setLocalWeights] = useState<Record<string, number>>(weights);
const updateWeights = useUpdateWeights();
const handleChange = (key: string, value: string) => {
const num = parseFloat(value);
if (!isNaN(num)) setLocalWeights((prev) => ({ ...prev, [key]: num }));
};
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
updateWeights.mutate(localWeights);
};
return (
<form onSubmit={handleSubmit} className="glass p-5">
<h3 className="mb-4 text-xs font-semibold uppercase tracking-widest text-gray-500">
Scoring Weights
</h3>
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
{Object.keys(weights).map((key) => (
<label key={key} className="flex flex-col gap-1.5">
<span className="text-xs text-gray-400 capitalize">{key.replace(/_/g, ' ')}</span>
<input
type="number"
step="any"
value={localWeights[key] ?? 0}
onChange={(e) => handleChange(key, e.target.value)}
className="input-glass px-2.5 py-1.5 text-sm"
/>
</label>
))}
</div>
<button
type="submit"
disabled={updateWeights.isPending}
className="mt-4 btn-gradient px-4 py-2 text-sm disabled:opacity-50"
>
<span>{updateWeights.isPending ? 'Updating…' : 'Update Weights'}</span>
</button>
</form>
);
}