pbnj

bsrcl.jsx

import { render } from "preact";
import { useMemo, useState } from "preact/hooks";
import "./index.css";

const CARD_VALUES = [
    { label: "J", pts: 2 },
    { label: "Q", pts: 3 },
    { label: "K", pts: 4 },
    { label: "3", pts: 10 },
    { label: "A", pts: 11 },
];

function TeamScore({
    id,
    score,
    total,
    counts,
    opponentCounts,
    onAdd,
    reverse,
}) {
    return (
        <div className="flex flex-1 flex-col justify-between p-2">
            <div
                className={`${reverse ? "order-1" : "order-2"} flex items-start justify-between`}
            >
                <span className="border border-current px-1 font-bold">
                    T{id}
                </span>
                <span className="font-black text-5xl">{score}</span>
                <div className="text-right">
                    <div className="text-xs opacity-50">TOTAL</div>
                    <div className="font-bold text-lg">{total}</div>
                </div>
            </div>

            <div
                className={`${reverse ? "order-2" : "order-1"} grid grid-cols-5 gap-1`}
            >
                {CARD_VALUES.map((v, i) => (
                    <button
                        key={i}
                        onClick={() => onAdd(id, i, v.pts)}
                        disabled={counts[i] + opponentCounts[i] >= 4}
                        className="border border-current p-2 disabled:opacity-20 hover:bg-zinc-100 active:bg-black active:text-white   transition-colors"
                    >
                        {v.label}
                        <br />
                        <span className="text-[9px] opacity-60">
                            L:{4 - (counts[i] + opponentCounts[i])}
                        </span>
                    </button>
                ))}
            </div>
        </div>
    );
}

function ScorerView({
    s1,
    s2,
    total1,
    total2,
    c1,
    c2,
    onAdd,
    onClear,
    onSave,
}) {
    return (
        <div className="flex h-full flex-col">
            <TeamScore
                id={1}
                score={s1}
                total={total1}
                counts={c1}
                opponentCounts={c2}
                onAdd={onAdd}
                reverse
            />

            <div className="flex gap-1 border-y border-current bg-zinc-50 p-1 dark:bg-zinc-900">
                <button
                    onClick={onClear}
                    className="flex-1 border border-current px-2 py-3"
                >
                    CLR
                </button>
                <button
                    onClick={onSave}
                    className="flex-3 border border-current bg-black px-4 py-3 font-black text-white italic dark:bg-zinc-100 dark:text-black"
                >
                    SAVE ROUND
                </button>
            </div>

            <TeamScore
                id={2}
                score={s2}
                total={total2}
                counts={c2}
                opponentCounts={c1}
                onAdd={onAdd}
            />
        </div>
    );
}

function HistoryView({ history, onPurge }) {
    return (
        <div className="flex h-full flex-col">
            <div className="flex-1 overflow-y-auto">
                <table className="w-full border-collapse">
                    <thead className="sticky top-0 bg-zinc-100 dark:bg-zinc-900">
                        <tr className="border-current border-b">
                            <th className="w-10 border-current border-r p-2 text-left">
                                #
                            </th>
                            <th className="border-current border-r p-2 text-center">
                                T1
                            </th>
                            <th className="border-current border-r p-2 text-center">
                                T2
                            </th>
                            <th className="p-2 text-right">CUMUL</th>
                        </tr>
                    </thead>
                    <tbody>
                        {history.map((r, i) => {
                            const h1 = history
                                .slice(0, i + 1)
                                .reduce((a, b) => a + b.t1, 0);
                            const h2 = history
                                .slice(0, i + 1)
                                .reduce((a, b) => a + b.t2, 0);
                            return (
                                <tr key={i} className="border-current border-b">
                                    <td className="border-current border-r p-2 opacity-50">
                                        {i + 1}
                                    </td>
                                    <td className="border-current border-r p-2 text-center font-bold">
                                        {r.t1}
                                    </td>
                                    <td className="border-current border-r p-2 text-center font-bold">
                                        {r.t2}
                                    </td>
                                    <td className="p-2 text-right italic opacity-70">
                                        {h1} / {h2}
                                    </td>
                                </tr>
                            );
                        })}
                    </tbody>
                </table>
            </div>
            <button
                onClick={onPurge}
                className="w-full border-current border-t p-4 font-bold text-red-600 dark:text-red-400"
            >
                PURGE HISTORY
            </button>
        </div>
    );
}

// --- Main App ---
export function App() {
    const [tab, setTab] = useState("score");
    const [s1, setS1] = useState(0);
    const [s2, setS2] = useState(0);
    const [c1, setC1] = useState([0, 0, 0, 0, 0]);
    const [c2, setC2] = useState([0, 0, 0, 0, 0]);
    const [history, setHistory] = useState([]);

    const histT1 = useMemo(
        () => history.reduce((a, b) => a + b.t1, 0),
        [history],
    );
    const histT2 = useMemo(
        () => history.reduce((a, b) => a + b.t2, 0),
        [history],
    );

    const add = (team, idx, pts) => {
        if (c1[idx] + c2[idx] >= 4) return;
        if (team === 1) {
            setS1((v) => v + pts);
            setC1((v) => {
                const n = [...v];
                n[idx]++;
                return n;
            });
        } else {
            setS2((v) => v + pts);
            setC2((v) => {
                const n = [...v];
                n[idx]++;
                return n;
            });
        }
    };

    const clearRound = () => {
        setS1(0);
        setS2(0);
        setC1([0, 0, 0, 0, 0]);
        setC2([0, 0, 0, 0, 0]);
    };

    const save = () => {
        if (s1 === 0 && s2 === 0) return;
        setHistory((prev) => [...prev, { t1: s1, t2: s2 }]);
        clearRound();
    };

    return (
        <div className="flex h-screen flex-col bg-white font-mono text-black text-xs uppercase dark:bg-zinc-950 dark:text-zinc-100">
            <div className="flex border-current border-b">
                <button
                    onClick={() => setTab("score")}
                    className={`flex-1 border-r border-current p-3 font-bold ${tab === "score" ? "bg-black text-white dark:bg-white dark:text-black" : ""}`}
                >
                    [0] SCORER
                </button>
                <button
                    onClick={() => setTab("hist")}
                    className={`flex-1 p-3 font-bold ${tab === "hist" ? "bg-black text-white dark:bg-white dark:text-black" : ""}`}
                >
                    [1] HISTORY ({history.length})
                </button>
            </div>

            <div className="flex-1 overflow-auto">
                {tab === "score" ? (
                    <ScorerView
                        s1={s1}
                        s2={s2}
                        total1={histT1 + s1}
                        total2={histT2 + s2}
                        c1={c1}
                        c2={c2}
                        onAdd={add}
                        onClear={clearRound}
                        onSave={save}
                    />
                ) : (
                    <HistoryView
                        history={history}
                        onPurge={() => confirm("purge?") && setHistory([])}
                    />
                )}
            </div>
        </div>
    );
}

render(<App />, document.getElementById("app"));

// todo: central buttons animations