Files
2026-06-06 17:14:53 +02:00

145 lines
5.0 KiB
TypeScript

'use client';
import { useState } from 'react';
import { SortableContext, rectSortingStrategy } from '@dnd-kit/sortable';
import { useDroppable } from '@dnd-kit/core';
import SortableCounter from './SortableCounter';
import { Counter } from './CounterCard';
import { Group } from './CounterModal';
interface Props {
group: Group | null; // null = ungrouped section
counters: 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>;
onRenameGroup?: (group: Group) => void;
onDeleteGroup?: (id: number) => void;
}
export default function GroupSection({
group,
counters,
onIncrement,
onDecrement,
onEdit,
onHistory,
onPrefetch,
editMode,
dragHandleProps,
onRenameGroup,
onDeleteGroup,
}: Props) {
const droppableId = group ? `group-${group.id}` : 'ungrouped';
const { setNodeRef, isOver } = useDroppable({ id: droppableId, data: { groupId: group?.id ?? null } });
const [editingName, setEditingName] = useState(false);
const [newName, setNewName] = useState(group?.name ?? '');
const [confirmDelete, setConfirmDelete] = useState(false);
function commitRename() {
if (group && newName.trim() && newName.trim() !== group.name) {
onRenameGroup?.({ ...group, name: newName.trim() });
}
setEditingName(false);
}
const isEmpty = counters.length === 0;
return (
<div className="mb-8">
{/* Section header */}
<div className="flex items-center gap-3 mb-3">
{group && editMode && dragHandleProps && (
<div
{...dragHandleProps}
className="cursor-grab active:cursor-grabbing text-ctp-overlay0 hover:text-ctp-subtext1 transition-colors shrink-0"
title="Drag to reorder group"
>
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="8" y1="6" x2="21" y2="6" /><line x1="8" y1="12" x2="21" y2="12" /><line x1="8" y1="18" x2="21" y2="18" />
<line x1="3" y1="6" x2="3.01" y2="6" /><line x1="3" y1="12" x2="3.01" y2="12" /><line x1="3" y1="18" x2="3.01" y2="18" />
</svg>
</div>
)}
{group ? (
editingName ? (
<input
autoFocus
value={newName}
onChange={e => setNewName(e.target.value)}
onBlur={commitRename}
onKeyDown={e => { if (e.key === 'Enter') commitRename(); if (e.key === 'Escape') setEditingName(false); }}
className="text-lg font-bold bg-transparent border-b-2 border-ctp-mauve outline-none text-ctp-text"
/>
) : (
editMode ? (
<h2
className="text-lg font-bold text-ctp-text cursor-pointer hover:text-ctp-mauve transition-colors"
title="Click to rename"
onClick={() => { setNewName(group.name); setEditingName(true); }}
>
{group.name}
</h2>
) : (
<h2 className="text-lg font-bold text-ctp-text">{group.name}</h2>
)
)
) : (
<h2 className="text-lg font-bold text-ctp-overlay1">Ungrouped</h2>
)}
{group && editMode && (
<button
onClick={() => {
if (confirmDelete) { onDeleteGroup?.(group.id); }
else {
setConfirmDelete(true);
setTimeout(() => setConfirmDelete(false), 3000);
}
}}
className={`text-xs px-2 py-0.5 rounded-full transition-colors ${
confirmDelete
? 'bg-ctp-red text-ctp-base'
: 'bg-ctp-surface0 text-ctp-overlay1 hover:bg-ctp-red/10 hover:text-ctp-red'
}`}
>
{confirmDelete ? 'Sure?' : 'Delete group'}
</button>
)}
</div>
{/* Drop zone */}
<div
ref={setNodeRef}
className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 min-h-[80px] rounded-2xl transition-colors ${
isOver ? 'bg-ctp-mauve/10 ring-2 ring-ctp-mauve' : ''
} ${isEmpty ? 'border-2 border-dashed border-ctp-surface1 p-4' : ''}`}
>
<SortableContext items={counters.map(c => c.id)} strategy={rectSortingStrategy}>
{counters.map(counter => (
<SortableCounter
key={counter.id}
counter={counter}
onIncrement={onIncrement}
onDecrement={onDecrement}
onEdit={onEdit}
onHistory={onHistory}
onPrefetch={onPrefetch}
editMode={editMode}
/>
))}
</SortableContext>
{isEmpty && (
<p className="col-span-full text-center text-sm text-ctp-overlay0 self-center">
Drop counters here
</p>
)}
</div>
</div>
);
}