Files
llink/js/desktop/src/features/compose/task-compose-step.tsx
T
Arjun PatelandGitHub 095b9876f9 feat: stream list view and tasks (#279)
* first attempt at stream sidebar, tasks, and events

* fix folder from root

* cleanup folders and events, and condense changes

* cleanup and add toggle for sidebar

* cleanup

* fix nits
2026-06-12 12:26:49 -07:00

126 lines
3.9 KiB
TypeScript

import { useCallback, useState } from 'react';
import type { TaskProperties } from '@/api/types';
import { useNetwork } from '@/hooks/use-networks';
import { metaKey } from '@/lib/platform';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { KeyHint } from '@/components/key-hint';
const UNASSIGNED = 'unassigned';
interface TaskComposeStepProps {
networkId: string;
onCancel: () => void;
onSubmit: (properties: TaskProperties) => void;
}
/**
* Minimal task creation form. Checklist items are added after creation in
* the always-editable task view, keeping this step a quick capture.
*/
export function TaskComposeStep({
networkId,
onCancel,
onSubmit,
}: TaskComposeStepProps) {
const network = useNetwork(networkId);
const [title, setTitle] = useState('');
const [notes, setNotes] = useState('');
const [assignedTo, setAssignedTo] = useState<string>(UNASSIGNED);
const handleSubmit = useCallback(() => {
const trimmed = title.trim();
if (!trimmed) return;
onSubmit({
title: trimmed,
...(notes.trim() && { notes: notes.trim() }),
...(assignedTo !== UNASSIGNED && { assigned_to: assignedTo }),
checklist: [],
done: false,
});
}, [title, notes, assignedTo, onSubmit]);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
e.preventDefault();
onCancel();
} else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
handleSubmit();
}
},
[onCancel, handleSubmit],
);
return (
<div
className="absolute inset-0 z-50 flex flex-col bg-black/90 pt-16"
onKeyDown={handleKeyDown}
tabIndex={-1}
>
<div className="mx-auto w-full max-w-sm space-y-4 px-6">
<div>
<Label className="mb-1 text-xs text-white/50">Task</Label>
<Input
type="text"
autoFocus
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="What needs to get done?"
className="border-white/10 bg-white/5 text-white placeholder-white/30 focus-visible:border-white/30 focus-visible:ring-0"
/>
</div>
<div>
<Label className="mb-1 text-xs text-white/50">Notes</Label>
<Textarea
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="Optional details…"
className="min-h-20 border-white/10 bg-white/5 text-white placeholder:text-white/30 focus-visible:border-white/30 focus-visible:ring-0 dark:bg-white/5"
/>
</div>
<div>
<Label className="mb-1 text-xs text-white/50">Assign to</Label>
<Select value={assignedTo} onValueChange={setAssignedTo}>
<SelectTrigger className="w-full border-white/10 bg-white/5 text-white">
<SelectValue placeholder="Unassigned" />
</SelectTrigger>
<SelectContent>
<SelectItem value={UNASSIGNED}>Unassigned</SelectItem>
{network?.humans?.map((human) => (
<SelectItem key={human.id} value={human.id}>
{human.email_prefix}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className="absolute bottom-8 left-0 right-0 flex items-center justify-center gap-4 text-sm text-white/50">
<KeyHint keys="Esc" onClick={onCancel} title="Cancel (or press Esc)">
cancel
</KeyHint>
<KeyHint
keys={`${metaKey}+Enter`}
onClick={handleSubmit}
title={`Create task (or press ${metaKey}+Enter)`}
>
create
</KeyHint>
</div>
</div>
);
}