feat: review single AI-generated events before saving

This commit is contained in:
2026-04-09 11:59:32 -04:00
parent aef22f704f
commit ecff8bebb1
2 changed files with 86 additions and 41 deletions

View File

@@ -39,6 +39,7 @@ export default function HomePage() {
const [events, setEvents] = useState<CalendarEvent[]>([]);
const [dialogOpen, setDialogOpen] = useState(false);
const [editingId, setEditingId] = useState<string | null>(null);
const [dialogSource, setDialogSource] = useState<"manual" | "ai">("manual");
const [isDragOver, setIsDragOver] = useState(false);
// Form fields
@@ -85,6 +86,7 @@ export default function HomePage() {
setEnd("");
setAllDay(false);
setEditingId(null);
setDialogSource("manual");
setRecurrenceRule(undefined);
};
@@ -257,10 +259,11 @@ export default function HomePage() {
if (data.length === 1) {
populateEventForm(data[0]);
setDialogSource("ai");
setAiPrompt("");
setDialogOpen(true);
handleImagesClear();
return { message: "Event has been created!" };
return { message: "Draft event is ready for review." };
}
await persistAiEvents(data);
@@ -318,6 +321,7 @@ export default function HomePage() {
setEnd(eventData.end || "");
setAllDay(eventData.allDay || false);
setEditingId(eventData.id);
setDialogSource("manual");
setRecurrenceRule(eventData.recurrenceRule);
setDialogOpen(true);
};
@@ -344,7 +348,11 @@ export default function HomePage() {
summary={summary}
summaryUpdated={summaryUpdated}
events={events}
onAddEvent={() => setDialogOpen(true)}
onAddEvent={() => {
resetForm();
setDialogSource("manual");
setDialogOpen(true);
}}
onImport={handleImport}
onExport={handleExport}
onClearAll={handleClearAll}
@@ -356,6 +364,7 @@ export default function HomePage() {
open={dialogOpen}
onOpenChange={setDialogOpen}
editingId={editingId}
dialogSource={dialogSource}
title={title}
setTitle={setTitle}
description={description}

View File

@@ -22,6 +22,7 @@ interface EventDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
editingId: string | null;
dialogSource: "manual" | "ai";
title: string;
setTitle: (title: string) => void;
description: string;
@@ -46,6 +47,7 @@ export const EventDialog = ({
open,
onOpenChange,
editingId,
dialogSource,
title,
setTitle,
description,
@@ -65,6 +67,19 @@ export const EventDialog = ({
onSave,
onReset,
}: EventDialogProps) => {
const isAiDraft = dialogSource === "ai" && !editingId;
const titleText = editingId
? "Edit Event"
: isAiDraft
? "Review AI Draft"
: "New Event";
const descriptionText = editingId
? "Update the event details below. Title and start date are required."
: isAiDraft
? "AI filled in this event from your prompt. Review each field, then save when it looks right."
: "Create an event manually. Title and start date are required.";
const saveLabel = editingId ? "Update Event" : "Save Event";
const handleOpenChange = (val: boolean) => {
if (!val) onReset();
onOpenChange(val);
@@ -94,15 +109,20 @@ export const EventDialog = ({
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className="glass-strong max-w-md">
<DialogHeader>
<DialogTitle className="text-base">
{editingId ? "Edit Event" : "New Event"}
</DialogTitle>
<DialogDescription className="sr-only">
Fill in the event details below. Title and start date are required.
</DialogDescription>
<DialogTitle className="text-base">{titleText}</DialogTitle>
<DialogDescription>{descriptionText}</DialogDescription>
</DialogHeader>
<div className="space-y-3">
{isAiDraft && (
<div className="rounded-md border border-primary/20 bg-primary/5 px-3 py-2 text-xs leading-relaxed text-primary">
This draft was generated from natural language. Double-check
dates, times, location, recurrence, and links before saving.
</div>
)}
<div className="space-y-1.5">
<Label htmlFor="event-title">Title</Label>
<Input
id="event-title"
name="title"
@@ -111,7 +131,10 @@ export const EventDialog = ({
onChange={(e) => setTitle(e.target.value)}
className="font-medium"
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="event-description">Description / notes</Label>
<Textarea
id="event-description"
name="description"
@@ -120,8 +143,11 @@ export const EventDialog = ({
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<Label htmlFor="event-location">Location</Label>
<div className="relative">
<LucideMapPin className="absolute left-3 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground/50" />
<Input
@@ -133,6 +159,9 @@ export const EventDialog = ({
className="pl-8"
/>
</div>
</div>
<div className="space-y-1.5">
<Label htmlFor="event-url">URL</Label>
<Input
id="event-url"
name="url"
@@ -141,6 +170,7 @@ export const EventDialog = ({
onChange={(e) => setUrl(e.target.value)}
/>
</div>
</div>
<RecurrencePicker
value={recurrenceRule}
@@ -195,10 +225,16 @@ export const EventDialog = ({
</div>
<DialogFooter className="gap-2 sm:gap-0">
<Button variant="ghost" onClick={() => handleOpenChange(false)}>
<Button
type="button"
variant="ghost"
onClick={() => handleOpenChange(false)}
>
Cancel
</Button>
<Button onClick={onSave}>{editingId ? "Update" : "Create"}</Button>
<Button type="button" onClick={onSave}>
{saveLabel}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>