TasksStep.tsx4.3 KBView on GitHub import { useState } from 'react';
import { Check, Loader2, Plus } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { taskGroupIcon } from '@/modules/userTasks/utils/task-group-icons';
import { cn } from '@/lib/utils';
import { SetupPreviewFrame } from '../components/SetupPreviewFrame';
import { useSetupTaskGroups } from '../hooks/use-setup-task-groups';
/**
* What a task is, then the lanes to file them into.
*
* The explainer is short on purpose — the useful half of this step is the group
* picker, because groups are the one thing about tasks a user must decide up
* front. Everything else about the surface is discoverable by opening it.
*/
export function TasksStepBody() {
const { starters, has, create } = useSetupTaskGroups();
const [customName, setCustomName] = useState('');
const submitCustom = () => {
const name = customName.trim();
if (!name) return;
void create({ name });
setCustomName('');
};
return (
<div className="flex flex-col gap-5">
<div className="space-y-2">
<p className="text-foreground text-base font-semibold">Groups</p>
<div className="grid grid-cols-2 gap-3">
{starters.map((group) => {
const Icon = taskGroupIcon(group.icon);
const added = has(group.name);
return (
<button
key=[redacted]
type="button"
disabled={added}
onClick={() => void create(group)}
className={cn(
'border-border flex items-center gap-2 rounded-md border p-3 text-left transition-colors',
added
? 'bg-sunken cursor-default'
: 'bg-background hover:border-foreground/30 cursor-pointer',
)}
>
<Icon className="h-4 w-4 shrink-0" style={{ color: group.color }} />
<span className="min-w-0 flex-1 text-sm font-medium">{group.name}</span>
{added && <Check className="h-3.5 w-3.5 shrink-0 text-emerald-600" />}
</button>
);
})}
</div>
</div>
<div className="flex items-center gap-2">
<Input
value={customName}
onChange={(e) => setCustomName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
submitCustom();
}
}}
placeholder="New group"
className="h-9"
/>
<Button
variant="outline"
className="h-9 shrink-0 cursor-pointer"
disabled={!customName.trim()}
onClick={submitCustom}
>
<Plus className="mr-1 h-4 w-4" />
Add
</Button>
</div>
</div>
);
}
/** The task-group rail as it will look on /tasks, live from `listGroups`. */
export function TasksStepPreview() {
const { groups, isLoading } = useSetupTaskGroups();
return (
<SetupPreviewFrame label="Your task groups">
<div className="flex h-full flex-col gap-0.5 p-3">
{isLoading ? (
<div className="flex flex-1 items-center justify-center">
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
</div>
) : (
groups.map((group) => {
const row = group as {
id: string | null;
name: string;
color: string | null;
icon: string | null;
dueTaskCount?: number;
};
const Icon = taskGroupIcon(row.icon);
return (
<div
key=[redacted] ?? 'misc'}
className="flex items-center gap-2 rounded-lg px-2 py-1.5 text-sm"
>
<Icon
className={cn('h-4 w-4 shrink-0', !row.color && 'text-muted-foreground')}
style={row.color ? { color: row.color } : undefined}
/>
<span className="min-w-0 flex-1 truncate">{row.name}</span>
{!!row.dueTaskCount && (
<span className="text-muted-foreground text-xs tabular-nums">
{row.dueTaskCount}
</span>
)}
</div>
);
})
)}
</div>
</SetupPreviewFrame>
);
}