Files
tally-counter/app/components/CounterCard.tsx
T
2026-06-06 17:14:53 +02:00

98 lines
4.3 KiB
TypeScript

'use client';
export interface Counter {
id: number;
name: string;
value: number;
image_path: string | null;
group_id: number | null;
order_index: number;
}
interface Props {
counter: Counter;
onIncrement: (id: number) => void;
onDecrement: (id: number) => void;
onEdit: (counter: Counter) => void;
onHistory: (id: number) => void;
onPrefetch?: (id: number) => void;
editMode?: boolean;
dragHandleProps?: Record<string, unknown>;
}
export default function CounterCard({ counter, onIncrement, onDecrement, onEdit, onHistory, onPrefetch, editMode, dragHandleProps }: Props) {
return (
<div
{...dragHandleProps}
className={`bg-ctp-surface0 rounded-2xl shadow-sm ring-1 ring-ctp-surface1 overflow-hidden flex flex-col select-none transition-shadow hover:shadow-lg hover:shadow-ctp-crust${dragHandleProps ? ' cursor-grab active:cursor-grabbing' : ''}`}
>
{/* Title area — click opens edit modal */}
<button
onClick={() => !editMode && onEdit(counter)}
className={`w-full flex items-center justify-center px-4 pt-3 pb-2 h-16 transition-colors ${!editMode ? 'hover:bg-ctp-surface1' : ''}`}
title={editMode ? undefined : 'Edit counter'}
>
<h3 className="font-bold text-ctp-text text-lg leading-tight line-clamp-2 text-center">
{counter.name}
</h3>
</button>
{/* Middle: image — click opens history modal */}
<button
onClick={() => !editMode && onHistory(counter.id)}
onMouseEnter={() => !editMode && onPrefetch?.(counter.id)}
className={`w-full flex-1 flex items-center justify-center transition-colors ${!editMode ? 'hover:bg-ctp-surface1' : ''}`}
title={editMode ? undefined : 'View history'}
>
{counter.image_path ? (
<div className="w-full bg-ctp-surface0 flex items-center justify-center overflow-hidden" style={{ minHeight: '6rem', maxHeight: '9rem' }}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={`/api/uploads/${counter.image_path}`}
alt={counter.name}
className="max-w-full max-h-36 object-contain py-2"
/>
</div>
) : (
<div className="w-full flex items-center justify-center" style={{ minHeight: '4rem' }}>
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5 text-ctp-overlay0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
<line x1="18" y1="20" x2="18" y2="10" /><line x1="12" y1="20" x2="12" y2="4" /><line x1="6" y1="20" x2="6" y2="14" />
</svg>
</div>
)}
</button>
{/* Counter controls */}
<div className="flex items-center justify-center px-3 py-3 mt-auto">
<div className="flex items-center bg-ctp-surface1 rounded-2xl overflow-hidden">
<button
onClick={() => onDecrement(counter.id)}
disabled={counter.value <= 0}
aria-label="Decrement"
className="w-11 h-11 flex items-center justify-center text-ctp-red hover:bg-ctp-red/20 active:bg-ctp-red/30 active:scale-95 transition-all disabled:opacity-25 disabled:pointer-events-none"
>
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
</button>
<span className="text-2xl font-extrabold w-14 text-center tabular-nums text-ctp-text tracking-tight select-none">
{counter.value}
</span>
<button
onClick={() => onIncrement(counter.id)}
aria-label="Increment"
className="w-11 h-11 flex items-center justify-center text-ctp-green hover:bg-ctp-green/20 active:bg-ctp-green/30 active:scale-95 transition-all"
>
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
</button>
</div>
</div>
</div>
);
}