diff --git a/apps/web/src/components/input/color-picker.tsx b/apps/web/src/components/input/color-picker.tsx index 511ffd34e..c01da41d8 100644 --- a/apps/web/src/components/input/color-picker.tsx +++ b/apps/web/src/components/input/color-picker.tsx @@ -33,6 +33,7 @@ type ColorPickerProps = { onChange?: (value: string) => void; open?: boolean; onOpenChange?: React.ComponentProps["onOpenChange"]; + onOpenChangeComplete?: React.ComponentProps["onOpenChangeComplete"]; trigger?: React.ReactNode; children?: React.ReactNode; }; @@ -43,6 +44,7 @@ export function ColorPicker({ onChange, open, onOpenChange, + onOpenChangeComplete, trigger, children, }: ColorPickerProps) { @@ -60,7 +62,7 @@ export function ColorPicker({ } return ( - + {trigger ?? (
+ (() => (application ? toForm(application) : emptyForm())); @@ -127,7 +130,6 @@ export function ApplicationFormSheet({ open, onOpenChange, application }: Props) onSuccess: () => { invalidate(); toast.add({ type: "success", description: t`Application added to your pipeline.` }); - setForm(emptyForm()); onOpenChange(false); }, onError: () => toast.add({ type: "error", description: t`Couldn't add the application. Please try again.` }), @@ -195,7 +197,18 @@ export function ApplicationFormSheet({ open, onOpenChange, application }: Props) }; return ( - + { + onApplicationOpenChangeComplete(next); + // After adding, the fields clear once the sheet has closed; closing without saving keeps the draft. + if (!next && create.isSuccess) { + setForm(emptyForm()); + create.reset(); + } + }} + > {isEditing ? Edit application : Add application} diff --git a/apps/web/src/features/applications/components/detail/activity.tsx b/apps/web/src/features/applications/components/detail/activity.tsx index b04c9a556..e9f7d7e90 100644 --- a/apps/web/src/features/applications/components/detail/activity.tsx +++ b/apps/web/src/features/applications/components/detail/activity.tsx @@ -24,6 +24,7 @@ import { Icon } from "@reactive-resume/ui/components/icon"; import { Input } from "@reactive-resume/ui/components/input"; import { Textarea } from "@reactive-resume/ui/components/textarea"; import { toast } from "@reactive-resume/ui/components/toast"; +import { useClosingValue } from "@/hooks/use-closing-value"; import { useConfirm } from "@/hooks/use-confirm"; import { orpc } from "@/libs/orpc/client"; import { interviewKindOf } from "../../interviews"; @@ -53,6 +54,8 @@ export function Activity({ application, onOpenInterview }: ActivityProps) { const confirm = useConfirm(); const [note, setNote] = useState(""); const [editing, setEditing] = useState<{ entry: ApplicationTimelineEntry; date: string; text: string } | null>(null); + // Closing keeps the entry's title and fields on screen until the dialog has faded out. + const [shownEdit, onEditOpenChangeComplete] = useClosingValue(editing); const onError = () => toast.add({ type: "error", description: t`Couldn't update the timeline. Try again.` }); const addNote = useMutation({ @@ -194,22 +197,27 @@ export function Activity({ application, onOpenInterview }: ActivityProps) { })} - !open && setEditing(null)}> + !open && setEditing(null)} + onOpenChangeComplete={onEditOpenChangeComplete} + > - {editing?.entry.type === "note" ? Edit note : Edit date} + {shownEdit?.entry.type === "note" ? Edit note : Edit date} Change this timeline entry. - {editing && ( + {shownEdit && (
{ event.preventDefault(); + if (!editing) return; updateEntry.mutate({ id: application.id, entryId: editing.entry.id, @@ -218,19 +226,25 @@ export function Activity({ application, onOpenInterview }: ActivityProps) { }); }} > - {editing.entry.type === "note" && ( + {shownEdit.entry.type === "note" && (