refactor(web): finding 1 — extract SectionItemDialog shell from 14 section dialogs

Each of the 14 section-item dialog files (award→volunteer) had identical
~50-line Create/Update shells (DialogContent + header + form + footer).
Added section-item-dialog.tsx with a SectionItemDialog wrapper that takes
title, icon, onSubmit, onCancel, isSubmitting, submitLabel, singleColumn?.
cover-letter and summary-item use singleColumn=true for their one-column
layout. custom.tsx is untouched (it creates section definitions, not items).

Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
This commit is contained in:
Amruth Pillai
2026-07-04 21:50:09 +02:00
parent 70df113ee6
commit 79a69c5507
15 changed files with 374 additions and 951 deletions
+21 -68
View File
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { awardItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
import { Switch } from "@reactive-resume/ui/components/switch";
@@ -23,6 +15,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = awardItemSchema;
@@ -57,36 +50,16 @@ export function CreateAwardDialog({ data }: DialogProps<"resume.sections.awards.
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new award</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<AwardForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new award</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<AwardForm form={form} />
</SectionItemDialog>
);
}
@@ -109,36 +82,16 @@ export function UpdateAwardDialog({ data }: DialogProps<"resume.sections.awards.
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing award</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<AwardForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing award</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<AwardForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { certificationItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Switch } from "@reactive-resume/ui/components/switch";
import { RichInput } from "@/components/input/rich-input";
@@ -22,6 +14,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = certificationItemSchema;
@@ -56,36 +49,16 @@ export function CreateCertificationDialog({ data }: DialogProps<"resume.sections
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new certification</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<CertificationForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new certification</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<CertificationForm form={form} />
</SectionItemDialog>
);
}
@@ -108,36 +81,16 @@ export function UpdateCertificationDialog({ data }: DialogProps<"resume.sections
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing certification</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<CertificationForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing certification</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<CertificationForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { coverLetterItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { RichInput } from "@/components/input/rich-input";
import { useDialogStore } from "@/dialogs/store";
@@ -19,6 +11,7 @@ import { useUpdateResumeData } from "@/features/resume/builder/draft";
import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = coverLetterItemSchema;
@@ -53,36 +46,17 @@ export function CreateCoverLetterDialog({ data }: DialogProps<"resume.sections.c
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new cover letter</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<CoverLetterForm form={form} />
<DialogFooter>
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new cover letter</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
singleColumn
>
<CoverLetterForm form={form} />
</SectionItemDialog>
);
}
@@ -110,36 +84,17 @@ export function UpdateCoverLetterDialog({ data }: DialogProps<"resume.sections.c
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing cover letter</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<CoverLetterForm form={form} />
<DialogFooter>
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing cover letter</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
singleColumn
>
<CoverLetterForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { educationItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Switch } from "@reactive-resume/ui/components/switch";
import { RichInput } from "@/components/input/rich-input";
@@ -22,6 +14,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = educationItemSchema;
@@ -59,36 +52,16 @@ export function CreateEducationDialog({ data }: DialogProps<"resume.sections.edu
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new education</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<EducationForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new education</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<EducationForm form={form} />
</SectionItemDialog>
);
}
@@ -111,36 +84,16 @@ export function UpdateEducationDialog({ data }: DialogProps<"resume.sections.edu
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing education</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<EducationForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing education</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<EducationForm form={form} />
</SectionItemDialog>
);
}
@@ -7,13 +7,6 @@ import { useStore } from "@tanstack/react-form";
import { AnimatePresence, Reorder, useDragControls } from "motion/react";
import { experienceItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
import { Switch } from "@reactive-resume/ui/components/switch";
@@ -26,6 +19,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = experienceItemSchema;
@@ -62,36 +56,16 @@ export function CreateExperienceDialog({ data }: DialogProps<"resume.sections.ex
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new experience</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ExperienceForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new experience</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<ExperienceForm form={form} />
</SectionItemDialog>
);
}
@@ -114,36 +88,16 @@ export function UpdateExperienceDialog({ data }: DialogProps<"resume.sections.ex
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing experience</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ExperienceForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing experience</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<ExperienceForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { interestItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
import { PopoverTrigger } from "@reactive-resume/ui/components/popover";
@@ -25,6 +17,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = interestItemSchema;
@@ -58,36 +51,16 @@ export function CreateInterestDialog({ data }: DialogProps<"resume.sections.inte
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new interest</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<InterestForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new interest</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<InterestForm form={form} />
</SectionItemDialog>
);
}
@@ -110,36 +83,16 @@ export function UpdateInterestDialog({ data }: DialogProps<"resume.sections.inte
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing interest</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<InterestForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing interest</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<InterestForm form={form} />
</SectionItemDialog>
);
}
@@ -5,14 +5,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { languageItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormDescription, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Slider } from "@reactive-resume/ui/components/slider";
import { useDialogStore } from "@/dialogs/store";
@@ -21,6 +13,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = languageItemSchema;
@@ -53,36 +46,16 @@ export function CreateLanguageDialog({ data }: DialogProps<"resume.sections.lang
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new language</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<LanguageForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new language</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<LanguageForm form={form} />
</SectionItemDialog>
);
}
@@ -105,36 +78,16 @@ export function UpdateLanguageDialog({ data }: DialogProps<"resume.sections.lang
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing language</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<LanguageForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing language</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<LanguageForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { AtIcon, PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { profileItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
import {
@@ -32,6 +24,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = profileItemSchema;
@@ -66,36 +59,16 @@ export function CreateProfileDialog({ data }: DialogProps<"resume.sections.profi
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new profile</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ProfileForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new profile</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<ProfileForm form={form} />
</SectionItemDialog>
);
}
@@ -118,36 +91,16 @@ export function UpdateProfileDialog({ data }: DialogProps<"resume.sections.profi
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing profile</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ProfileForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing profile</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<ProfileForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { projectItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Switch } from "@reactive-resume/ui/components/switch";
import { RichInput } from "@/components/input/rich-input";
@@ -22,6 +14,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = projectItemSchema;
@@ -55,36 +48,16 @@ export function CreateProjectDialog({ data }: DialogProps<"resume.sections.proje
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new project</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ProjectForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new project</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<ProjectForm form={form} />
</SectionItemDialog>
);
}
@@ -107,36 +80,16 @@ export function UpdateProjectDialog({ data }: DialogProps<"resume.sections.proje
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing project</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ProjectForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing project</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<ProjectForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { publicationItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Switch } from "@reactive-resume/ui/components/switch";
import { RichInput } from "@/components/input/rich-input";
@@ -22,6 +14,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = publicationItemSchema;
@@ -56,36 +49,16 @@ export function CreatePublicationDialog({ data }: DialogProps<"resume.sections.p
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new publication</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<PublicationForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new publication</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<PublicationForm form={form} />
</SectionItemDialog>
);
}
@@ -108,36 +81,16 @@ export function UpdatePublicationDialog({ data }: DialogProps<"resume.sections.p
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing publication</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<PublicationForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing publication</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<PublicationForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { referenceItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Switch } from "@reactive-resume/ui/components/switch";
import { RichInput } from "@/components/input/rich-input";
@@ -22,6 +14,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = referenceItemSchema;
@@ -56,36 +49,16 @@ export function CreateReferenceDialog({ data }: DialogProps<"resume.sections.ref
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new reference</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ReferenceForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new reference</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<ReferenceForm form={form} />
</SectionItemDialog>
);
}
@@ -108,36 +81,16 @@ export function UpdateReferenceDialog({ data }: DialogProps<"resume.sections.ref
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing reference</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<ReferenceForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing reference</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<ReferenceForm form={form} />
</SectionItemDialog>
);
}
@@ -0,0 +1,76 @@
import type { ReactNode } from "react";
import { Trans } from "@lingui/react/macro";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
type SectionItemDialogProps = {
/** Full dialog title — pass a Trans node so i18n extraction works per-file */
title: ReactNode;
/** Leading icon — PlusIcon (create) or PencilSimpleLineIcon (update) */
icon: ReactNode;
/** Called via `void form.handleSubmit()` from the form's onSubmit handler */
onSubmit: () => void;
/** Called by useFormBlocker's requestClose */
onCancel: () => void;
isSubmitting: boolean;
/** Button label: <Trans>Create</Trans> or <Trans>Save Changes</Trans> */
submitLabel: ReactNode;
/** When true, uses a single-column form layout (cover-letter, summary-item) */
singleColumn?: boolean;
children: ReactNode;
};
/**
* Shared shell for section-item Create/Update dialogs.
* Each dialog still owns its schema, defaultValues, and form hooks.
* This wrapper only removes the duplicated DialogContent/Header/form/Footer boilerplate.
*/
export function SectionItemDialog({
title,
icon,
onSubmit,
onCancel,
isSubmitting,
submitLabel,
singleColumn = false,
children,
}: SectionItemDialogProps) {
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
{icon}
{title}
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className={singleColumn ? "grid gap-4" : "grid gap-4 sm:grid-cols-2"}
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
onSubmit();
}}
>
{children}
<DialogFooter className={singleColumn ? undefined : "sm:col-span-full"}>
<Button variant="ghost" onClick={onCancel}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
{submitLabel}
</Button>
</DialogFooter>
</form>
</DialogContent>
);
}
+21 -68
View File
@@ -5,14 +5,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { skillItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormDescription, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
import { PopoverTrigger } from "@reactive-resume/ui/components/popover";
@@ -27,6 +19,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = skillItemSchema;
@@ -62,36 +55,16 @@ export function CreateSkillDialog({ data }: DialogProps<"resume.sections.skills.
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new skill</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<SkillForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new skill</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<SkillForm form={form} />
</SectionItemDialog>
);
}
@@ -114,36 +87,16 @@ export function UpdateSkillDialog({ data }: DialogProps<"resume.sections.skills.
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing skill</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<SkillForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing skill</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<SkillForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { summaryItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { RichInput } from "@/components/input/rich-input";
import { useDialogStore } from "@/dialogs/store";
@@ -19,6 +11,7 @@ import { useUpdateResumeData } from "@/features/resume/builder/draft";
import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = summaryItemSchema;
@@ -52,36 +45,17 @@ export function CreateSummaryItemDialog({ data }: DialogProps<"resume.sections.s
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new summary item</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<SummaryItemForm form={form} />
<DialogFooter>
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new summary item</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
singleColumn
>
<SummaryItemForm form={form} />
</SectionItemDialog>
);
}
@@ -109,36 +83,17 @@ export function UpdateSummaryItemDialog({ data }: DialogProps<"resume.sections.s
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing summary item</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<SummaryItemForm form={form} />
<DialogFooter>
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing summary item</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
singleColumn
>
<SummaryItemForm form={form} />
</SectionItemDialog>
);
}
@@ -4,14 +4,6 @@ import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useStore } from "@tanstack/react-form";
import { volunteerItemSchema } from "@reactive-resume/schema/resume/data";
import { Button } from "@reactive-resume/ui/components/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@reactive-resume/ui/components/dialog";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Switch } from "@reactive-resume/ui/components/switch";
import { RichInput } from "@/components/input/rich-input";
@@ -22,6 +14,7 @@ import { useFormBlocker } from "@/hooks/use-form-blocker";
import { makeSectionItem } from "@/libs/resume/make-section-item";
import { createSectionItem, updateSectionItem } from "@/libs/resume/section-actions";
import { useAppForm, withForm } from "@/libs/tanstack-form";
import { SectionItemDialog } from "./section-item-dialog";
const formSchema = volunteerItemSchema;
@@ -56,36 +49,16 @@ export function CreateVolunteerDialog({ data }: DialogProps<"resume.sections.vol
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PlusIcon />
<Trans>Create a new volunteer experience</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<VolunteerForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Create</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Create a new volunteer experience</Trans>}
icon={<PlusIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Create</Trans>}
>
<VolunteerForm form={form} />
</SectionItemDialog>
);
}
@@ -108,36 +81,16 @@ export function UpdateVolunteerDialog({ data }: DialogProps<"resume.sections.vol
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
return (
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-x-2">
<PencilSimpleLineIcon />
<Trans>Update an existing volunteer experience</Trans>
</DialogTitle>
<DialogDescription />
</DialogHeader>
<form
className="grid gap-4 sm:grid-cols-2"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<VolunteerForm form={form} />
<DialogFooter className="sm:col-span-full">
<Button variant="ghost" onClick={requestClose}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" disabled={isSubmitting}>
<Trans>Save Changes</Trans>
</Button>
</DialogFooter>
</form>
</DialogContent>
<SectionItemDialog
title={<Trans>Update an existing volunteer experience</Trans>}
icon={<PencilSimpleLineIcon />}
onSubmit={() => void form.handleSubmit()}
onCancel={requestClose}
isSubmitting={isSubmitting}
submitLabel={<Trans>Save Changes</Trans>}
>
<VolunteerForm form={form} />
</SectionItemDialog>
);
}