Files
pn-new-crm/src/components/reminders/reminder-form.tsx

268 lines
8.2 KiB
TypeScript
Raw Normal View History

'use client';
import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button';
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 { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter } from '@/components/ui/sheet';
import { apiFetch } from '@/lib/api/client';
import { usePermissions } from '@/hooks/use-permissions';
interface UserOption {
id: string;
displayName: string;
}
interface ReminderFormProps {
open: boolean;
onOpenChange: (open: boolean) => void;
reminder?: {
id: string;
title: string;
note: string | null;
dueAt: string;
priority: string;
assignedTo: string | null;
clientId: string | null;
interestId: string | null;
berthId: string | null;
} | null;
// Pre-fill entity link when creating from entity detail pages
defaultClientId?: string;
defaultInterestId?: string;
defaultBerthId?: string;
onSuccess: () => void;
}
export function ReminderForm({
open,
onOpenChange,
reminder,
defaultClientId,
defaultInterestId,
defaultBerthId,
onSuccess,
}: ReminderFormProps) {
const [title, setTitle] = useState('');
const [note, setNote] = useState('');
const [dueAt, setDueAt] = useState('');
const [priority, setPriority] = useState('medium');
const [assignedTo, setAssignedTo] = useState('');
const [clientId, setClientId] = useState('');
const [interestId, setInterestId] = useState('');
const [berthId, setBerthId] = useState('');
const [users, setUsers] = useState<UserOption[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const { can } = usePermissions();
const canAssignOthers = can('reminders', 'assign_others');
const isEdit = !!reminder;
useEffect(() => {
if (open && canAssignOthers) {
void apiFetch<{ data: UserOption[] }>('/api/v1/admin/users/options').then((res) =>
setUsers(res.data),
);
}
}, [open, canAssignOthers]);
useEffect(() => {
if (open) {
if (reminder) {
setTitle(reminder.title);
setNote(reminder.note ?? '');
setDueAt(reminder.dueAt.slice(0, 16)); // datetime-local format
setPriority(reminder.priority);
setAssignedTo(reminder.assignedTo ?? '');
setClientId(reminder.clientId ?? '');
setInterestId(reminder.interestId ?? '');
setBerthId(reminder.berthId ?? '');
} else {
setTitle('');
setNote('');
// Default to tomorrow 9 AM
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(9, 0, 0, 0);
setDueAt(tomorrow.toISOString().slice(0, 16));
setPriority('medium');
setAssignedTo('');
setClientId(defaultClientId ?? '');
setInterestId(defaultInterestId ?? '');
setBerthId(defaultBerthId ?? '');
}
setError(null);
}
}, [open, reminder, defaultClientId, defaultInterestId, defaultBerthId]);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError(null);
setLoading(true);
try {
const body = {
title,
note: note || undefined,
dueAt: new Date(dueAt).toISOString(),
priority,
assignedTo: assignedTo || undefined,
clientId: clientId || undefined,
interestId: interestId || undefined,
berthId: berthId || undefined,
};
if (isEdit) {
await apiFetch(`/api/v1/reminders/${reminder.id}`, {
method: 'PATCH',
body,
});
} else {
await apiFetch('/api/v1/reminders', {
method: 'POST',
body,
});
}
onSuccess();
onOpenChange(false);
} catch (err: unknown) {
const message = err instanceof Error ? err.message : 'Something went wrong';
setError(message);
} finally {
setLoading(false);
}
}
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className="overflow-y-auto">
<SheetHeader>
<SheetTitle>{isEdit ? 'Edit Reminder' : 'New Reminder'}</SheetTitle>
</SheetHeader>
<form onSubmit={handleSubmit} className="mt-6 space-y-4">
<div className="space-y-2">
<Label htmlFor="reminder-title">Title</Label>
<Input
id="reminder-title"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Follow up with client..."
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="reminder-note">Note</Label>
<Textarea
id="reminder-note"
value={note}
onChange={(e) => setNote(e.target.value)}
placeholder="Additional details..."
rows={3}
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="reminder-due">Due Date & Time</Label>
<Input
id="reminder-due"
type="datetime-local"
value={dueAt}
onChange={(e) => setDueAt(e.target.value)}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="reminder-priority">Priority</Label>
<Select value={priority} onValueChange={setPriority}>
<SelectTrigger id="reminder-priority">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="low">Low</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="high">High</SelectItem>
<SelectItem value="urgent">Urgent</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{canAssignOthers && (
<div className="space-y-2">
<Label htmlFor="reminder-assign">Assign To</Label>
<Select value={assignedTo} onValueChange={setAssignedTo}>
<SelectTrigger id="reminder-assign">
<SelectValue placeholder="Myself" />
</SelectTrigger>
<SelectContent>
<SelectItem value="">Myself</SelectItem>
{users.map((u) => (
<SelectItem key={u.id} value={u.id}>
{u.displayName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
<div className="space-y-2">
<Label className="text-muted-foreground text-xs">
Link to Entity (optional paste UUIDs, or leave blank)
</Label>
<div className="grid grid-cols-1 gap-2">
<Input
placeholder="Client ID"
value={clientId}
onChange={(e) => setClientId(e.target.value)}
className="text-xs"
/>
<Input
placeholder="Interest ID"
value={interestId}
onChange={(e) => setInterestId(e.target.value)}
className="text-xs"
/>
<Input
placeholder="Berth ID"
value={berthId}
onChange={(e) => setBerthId(e.target.value)}
className="text-xs"
/>
</div>
</div>
{error && <p className="text-sm text-destructive">{error}</p>}
<SheetFooter>
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
disabled={loading}
>
Cancel
</Button>
<Button type="submit" disabled={loading || !title.trim() || !dueAt}>
{loading ? 'Saving...' : isEdit ? 'Save Changes' : 'Create Reminder'}
</Button>
</SheetFooter>
</form>
</SheetContent>
</Sheet>
);
}