130 lines
5.5 KiB
TypeScript
130 lines
5.5 KiB
TypeScript
"use client"
|
|
|
|
import { NumberBall } from "@/components/number-ball"
|
|
import { SettingsPanel } from "@/components/settings"
|
|
import { ResultPanel } from "@/components/result"
|
|
import { SingleModePanel } from "@/components/single-panel"
|
|
import { useRollCallLogic } from "@/components/logic"
|
|
|
|
export default function Page() {
|
|
const [state, actions] = useRollCallLogic()
|
|
const isSingleMode = state.mode === "single"
|
|
|
|
return (
|
|
<div className="h-screen w-full bg-[#ffffff] text-[#16191f] font-mono flex flex-col lg:flex-row overflow-hidden">
|
|
|
|
{/* Left sidebar — config (fixed width, full height) */}
|
|
<aside className="w-full lg:w-72 flex-shrink-0 border-b lg:border-b-0 lg:border-r border-[#e2e6ea] bg-[#f7f8fa] overflow-y-auto scrollbar-thin">
|
|
<div className="p-5">
|
|
<SettingsPanel
|
|
mode={state.mode}
|
|
totalTasks={state.totalTasks}
|
|
pickCount={state.pickCount}
|
|
rounds={state.rounds}
|
|
allowRepeat={state.allowRepeat}
|
|
singleAllowRepeat={state.singleAllowRepeat}
|
|
selectedRecords={state.selectedRecords}
|
|
onModeChange={actions.setMode}
|
|
onTotalTasksChange={actions.setTotalTasks}
|
|
onPickCountChange={actions.setPickCount}
|
|
onRoundsChange={actions.setRounds}
|
|
onAllowRepeatChange={actions.setAllowRepeat}
|
|
onSingleAllowRepeatChange={actions.setSingleAllowRepeat}
|
|
onRoll={isSingleMode ? actions.handleSingleRoll : actions.handleRoll}
|
|
onReset={actions.resetSingle}
|
|
isRolling={state.isRolling}
|
|
errorMsg={state.errorMsg}
|
|
/>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Right content — fills remaining space */}
|
|
<main className="flex-1 flex flex-col min-w-0 min-h-0">
|
|
|
|
{/* Single mode: Full height matrix with records */}
|
|
{isSingleMode ? (
|
|
<>
|
|
{/* Matrix area - full width, dynamic centering */}
|
|
<section className="flex flex-col flex-1 min-h-0">
|
|
<div className="flex items-center gap-3 px-5 py-2.5 border-b border-[#e2e6ea] flex-shrink-0">
|
|
<span className="text-[10px] tracking-[0.2em] uppercase text-[#8a95a1]">MATRIX</span>
|
|
<span className="flex-1 h-px bg-[#e2e6ea]" />
|
|
{state.selectedRecords.length > 0 && (
|
|
<span className="text-[10px] text-[#0f141a]">
|
|
{state.selectedRecords.length} SELECTED
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="flex-1 flex items-center justify-center overflow-auto scrollbar-thin p-8">
|
|
<div className="flex flex-wrap gap-2 justify-center max-w-2xl">
|
|
{Array.from({ length: state.totalTasks }, (_, i) => i + 1).map((n) => (
|
|
<NumberBall
|
|
key={n}
|
|
number={n}
|
|
state={actions.getBallState(n)}
|
|
colorIndex={actions.getBallColorIndex(n)}
|
|
size="large"
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Single mode records panel */}
|
|
<section className="flex flex-col border-t border-[#e2e6ea] max-h-[35vh]">
|
|
<SingleModePanel singleBatches={state.singleBatches} />
|
|
</section>
|
|
</>
|
|
) : (
|
|
<>
|
|
{/* Group mode: Matrix grid */}
|
|
<section className="flex flex-col flex-shrink-0 border-b border-[#e2e6ea]">
|
|
<div className="flex items-center gap-3 px-5 py-2.5 border-b border-[#e2e6ea]">
|
|
<span className="text-[10px] tracking-[0.2em] uppercase text-[#8a95a1]">MATRIX</span>
|
|
<span className="flex-1 h-px bg-[#e2e6ea]" />
|
|
{state.hasResult && (
|
|
<span className="text-[10px] text-[#0f141a]">
|
|
{state.highlighted.size} SELECTED
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="flex flex-wrap gap-2 p-4 max-h-[40vh] overflow-y-auto scrollbar-thin">
|
|
{Array.from({ length: state.totalTasks }, (_, i) => i + 1).map((n) => (
|
|
<NumberBall
|
|
key={n}
|
|
number={n}
|
|
state={actions.getBallState(n)}
|
|
colorIndex={actions.getBallColorIndex(n)}
|
|
size="large"
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
|
|
{/* Group mode result panel */}
|
|
<section className="flex flex-col flex-1 min-h-0">
|
|
<div className="flex items-center gap-3 px-5 py-2.5 border-b border-[#e2e6ea] flex-shrink-0">
|
|
<span className="text-[10px] tracking-[0.2em] uppercase text-[#8a95a1]">OUTPUT</span>
|
|
<span className="flex-1 h-px bg-[#e2e6ea]" />
|
|
{state.hasResult && state.groups.length > 0 && (
|
|
<button
|
|
onClick={actions.clearResult}
|
|
className="text-[10px] text-[#8a95a1] hover:text-[#16191f] transition-colors tracking-widest uppercase"
|
|
>
|
|
CLEAR
|
|
</button>
|
|
)}
|
|
</div>
|
|
<div className="flex-1 overflow-y-auto scrollbar-thin px-4 py-3">
|
|
<ResultPanel groups={state.groups} hasResult={state.hasResult} placeholder={state.placeholder} />
|
|
</div>
|
|
</section>
|
|
</>
|
|
)}
|
|
|
|
</main>
|
|
|
|
</div>
|
|
)
|
|
}
|