Skip to content

Commit 16eda66

Browse files
committed
feat: encapsulate export logic into PdfExport component to simplify PreviewDock
1 parent da12188 commit 16eda66

2 files changed

Lines changed: 38 additions & 147 deletions

File tree

src/components/preview/PreviewDock.tsx

Lines changed: 18 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ import { toast } from "sonner";
1818
import { motion } from "framer-motion";
1919
import { useTranslations } from "@/i18n/compat/client";
2020
import { useRouter } from "@/lib/navigation";
21-
import { exportResumeToBrowserPrint } from "@/utils/print";
22-
import { exportResumeAsJson, exportResumeAsMarkdown, exportToPdf } from "@/utils/export";
2321
import { Dock, DockIcon } from "@/components/magicui/dock";
2422
import { Button } from "@/components/ui/button";
2523
import {
@@ -28,12 +26,6 @@ import {
2826
TooltipProvider,
2927
TooltipTrigger
3028
} from "@/components/ui/tooltip";
31-
import {
32-
DropdownMenu,
33-
DropdownMenuContent,
34-
DropdownMenuItem,
35-
DropdownMenuTrigger
36-
} from "@/components/ui/dropdown-menu";
3729
import TemplateSheet from "@/components/shared/TemplateSheet";
3830
import { GITHUB_REPO_URL, PDF_EXPORT_CONFIG } from "@/config";
3931
import { cn } from "@/lib/utils";
@@ -43,6 +35,7 @@ import { AI_MODEL_CONFIGS } from "@/config/ai";
4335
import { useResumeStore } from "@/store/useResumeStore";
4436
import { useAIConfiguration } from "@/hooks/useAIConfiguration";
4537
import { FAQDialog } from "./FAQDialog";
38+
import PdfExport from "@/components/shared/PdfExport";
4639

4740
export type IconProps = React.HTMLAttributes<SVGElement>;
4841

@@ -98,12 +91,7 @@ const PreviewDock = ({
9891
}: PreviewDockProps) => {
9992
const router = useRouter();
10093
const t = useTranslations("previewDock");
101-
const tPdf = useTranslations("pdfExport");
102-
const tBasicField = useTranslations("workbench.basicPanel.basicFields");
10394
const { checkGrammar, isChecking } = useGrammarCheck();
104-
const [isExporting, setIsExporting] = useState(false);
105-
const [isExportingJson, setIsExportingJson] = useState(false);
106-
const [isExportingMarkdown, setIsExportingMarkdown] = useState(false);
10795

10896
const {
10997
selectedModel,
@@ -117,67 +105,7 @@ const PreviewDock = ({
117105
} = useAIConfigStore();
118106

119107
const { duplicateResume, setActiveResume, activeResumeId, activeResume, updateGlobalSettings } = useResumeStore();
120-
const { globalSettings = {}, title } = activeResume || {};
121-
122-
const handleExportPdf = async () => {
123-
await exportToPdf({
124-
elementId: "resume-preview",
125-
title: title || "resume",
126-
pagePadding: globalSettings?.pagePadding || 0,
127-
fontFamily: globalSettings?.fontFamily,
128-
onStart: () => setIsExporting(true),
129-
onEnd: () => setIsExporting(false),
130-
successMessage: tPdf("toast.success"),
131-
errorMessage: tPdf("toast.error")
132-
});
133-
};
134-
135-
const handleExportJson = () => {
136-
exportResumeAsJson({
137-
resume: activeResume,
138-
title,
139-
onStart: () => setIsExportingJson(true),
140-
onEnd: () => setIsExportingJson(false),
141-
successMessage: tPdf("toast.jsonSuccess"),
142-
errorMessage: tPdf("toast.jsonError")
143-
});
144-
};
145-
146-
const handleExportMarkdown = () => {
147-
exportResumeAsMarkdown({
148-
resume: activeResume,
149-
title,
150-
onStart: () => setIsExportingMarkdown(true),
151-
onEnd: () => setIsExportingMarkdown(false),
152-
successMessage: tPdf("toast.markdownSuccess"),
153-
errorMessage: tPdf("toast.markdownError"),
154-
markdownOptions: {
155-
basicFieldLabels: {
156-
name: tBasicField("name"),
157-
title: tBasicField("title"),
158-
employementStatus: tBasicField("employementStatus"),
159-
birthDate: tBasicField("birthDate"),
160-
email: tBasicField("email"),
161-
phone: tBasicField("phone"),
162-
location: tBasicField("location")
163-
}
164-
}
165-
});
166-
};
167-
168-
const handlePrint = () => {
169-
const resumeContent = document.getElementById("resume-preview");
170-
if (!resumeContent) {
171-
console.error("Resume content not found");
172-
return;
173-
}
174-
const pagePadding = globalSettings?.pagePadding || 0;
175-
exportResumeToBrowserPrint(
176-
resumeContent,
177-
pagePadding,
178-
globalSettings?.fontFamily
179-
);
180-
};
108+
const { globalSettings = {} } = activeResume || {};
181109

182110
const { checkConfiguration } = useAIConfiguration();
183111

@@ -231,8 +159,6 @@ const PreviewDock = ({
231159
}
232160
}, [activeResumeId, duplicateResume, router, setActiveResume, t]);
233161

234-
const isLoading = isExporting || isExportingJson || isExportingMarkdown;
235-
236162
return (
237163
<>
238164
<div className="hidden md:flex flex-col items-center fixed top-1/2 right-3 transform -translate-y-1/2 z-[50]">
@@ -314,61 +240,24 @@ const PreviewDock = ({
314240
</Tooltip>
315241
</DockIcon>
316242
<DockIcon>
317-
<DropdownMenu>
318-
<Tooltip>
243+
<Tooltip>
244+
<PdfExport>
319245
<TooltipTrigger asChild>
320-
<DropdownMenuTrigger asChild>
321-
<div
322-
className={cn(
323-
"flex cursor-pointer h-7 w-7 items-center justify-center rounded-lg",
324-
"hover:bg-gray-100/50 dark:hover:bg-neutral-800/50",
325-
"transition-all duration-200",
326-
isLoading && "animate-pulse"
327-
)}
328-
>
329-
{isLoading ? (
330-
<Loader2 className="h-4 w-4 animate-spin" />
331-
) : (
332-
<Download className="h-4 w-4" />
333-
)}
334-
</div>
335-
</DropdownMenuTrigger>
246+
<button
247+
className={cn(
248+
"flex cursor-pointer h-7 w-7 items-center justify-center rounded-lg",
249+
"hover:bg-gray-100/50 dark:hover:bg-neutral-800/50",
250+
"transition-all duration-200"
251+
)}
252+
>
253+
<Download className="h-4 w-4" />
254+
</button>
336255
</TooltipTrigger>
337-
<TooltipContent side="left" sideOffset={10}>
338-
<p>{t("export.tooltip")}</p>
339-
</TooltipContent>
340-
</Tooltip>
341-
<DropdownMenuContent align="end" side="left">
342-
<DropdownMenuItem
343-
onClick={handleExportPdf}
344-
disabled={isLoading}
345-
>
346-
<Download className="w-4 h-4 mr-2" />
347-
{t("export.pdf")}
348-
</DropdownMenuItem>
349-
<DropdownMenuItem
350-
onClick={handlePrint}
351-
disabled={isLoading}
352-
>
353-
<Printer className="w-4 h-4 mr-2" />
354-
{t("export.print")}
355-
</DropdownMenuItem>
356-
<DropdownMenuItem
357-
onClick={handleExportJson}
358-
disabled={isLoading}
359-
>
360-
<FileJson className="w-4 h-4 mr-2" />
361-
{t("export.json")}
362-
</DropdownMenuItem>
363-
<DropdownMenuItem
364-
onClick={handleExportMarkdown}
365-
disabled={isLoading}
366-
>
367-
<RiMarkdownLine className="w-4 h-4 mr-2" />
368-
{t("export.markdown")}
369-
</DropdownMenuItem>
370-
</DropdownMenuContent>
371-
</DropdownMenu>
256+
</PdfExport>
257+
<TooltipContent side="left" sideOffset={10}>
258+
<p>{t("export.tooltip")}</p>
259+
</TooltipContent>
260+
</Tooltip>
372261
</DockIcon>
373262
<DockIcon>
374263
<Tooltip>

src/components/shared/PdfExport.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const ExportCard = ({
7575
);
7676
};
7777

78-
const PdfExport = () => {
78+
const PdfExport = ({ children }: { children?: React.ReactNode }) => {
7979
const [isOpen, setIsOpen] = useState(false);
8080
const [isExporting, setIsExporting] = useState(false);
8181
const [isPrinting, setIsPrinting] = useState(false);
@@ -169,23 +169,25 @@ const PdfExport = () => {
169169
setIsOpen(val);
170170
}}>
171171
<DialogTrigger asChild>
172-
<Button
173-
className="px-4 py-2 rounded-xl text-sm font-medium flex items-center gap-2 transition-all hover:shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
174-
disabled={isLoading}
175-
>
176-
{isLoading ? (
177-
<>
178-
<Loader2 className="w-4 h-4 animate-spin" />
179-
<span>{loadingText}</span>
180-
</>
181-
) : (
182-
<>
183-
<Download className="w-4 h-4" />
184-
<span>{t("button.export")}</span>
185-
<ChevronDown className="w-3.5 h-3.5 opacity-60 ml-0.5" />
186-
</>
187-
)}
188-
</Button>
172+
{children ? children : (
173+
<Button
174+
className="px-4 py-2 rounded-xl text-sm font-medium flex items-center gap-2 transition-all hover:shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
175+
disabled={isLoading}
176+
>
177+
{isLoading ? (
178+
<>
179+
<Loader2 className="w-4 h-4 animate-spin" />
180+
<span>{loadingText}</span>
181+
</>
182+
) : (
183+
<>
184+
<Download className="w-4 h-4" />
185+
<span>{t("button.export")}</span>
186+
<ChevronDown className="w-3.5 h-3.5 opacity-60 ml-0.5" />
187+
</>
188+
)}
189+
</Button>
190+
)}
189191
</DialogTrigger>
190192

191193
<DialogContent className="max-w-4xl gap-8 p-8 sm:rounded-2xl border-none shadow-2xl bg-gradient-to-b from-background to-muted/20">

0 commit comments

Comments
 (0)