feat: review single AI-generated events before saving
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user