From 46a80f237d982e5b321d678ba60004bda33b88a7 Mon Sep 17 00:00:00 2001
From: JOYCEQL <1449239013@qq.com>
Date: Fri, 17 Jan 2025 02:10:25 +0800
Subject: [PATCH] feat: import resume json
---
.../src/app/app/dashboard/resumes/page.tsx | 151 +++++++++++++-----
apps/fronted/src/i18n/locales/en.json | 18 ++-
apps/fronted/src/i18n/locales/zh.json | 6 +-
apps/fronted/src/store/useResumeStore.ts | 13 ++
4 files changed, 145 insertions(+), 43 deletions(-)
diff --git a/apps/fronted/src/app/app/dashboard/resumes/page.tsx b/apps/fronted/src/app/app/dashboard/resumes/page.tsx
index a69d3dc..64e48e5 100644
--- a/apps/fronted/src/app/app/dashboard/resumes/page.tsx
+++ b/apps/fronted/src/app/app/dashboard/resumes/page.tsx
@@ -2,9 +2,15 @@
import React, { useEffect } from "react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
-import { Plus, FileText, Settings, AlertCircle } from "lucide-react";
+import { Plus, FileText, Settings, AlertCircle, Upload } from "lucide-react";
import { motion, AnimatePresence } from "framer-motion";
+import { toast } from "sonner";
import { Button } from "@/components/ui/button";
+import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
+import { getConfig, getFileHandle, verifyPermission } from "@/utils/fileSystem";
+import { useResumeStore } from "@/store/useResumeStore";
+import { cn } from "@/lib/utils";
+import { initialResumeState } from "@/config/initialResumeData";
import {
Card,
CardContent,
@@ -12,27 +18,24 @@ import {
CardFooter,
CardTitle,
} from "@/components/ui/card";
-import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { getConfig, getFileHandle, verifyPermission } from "@/utils/fileSystem";
-import { useResumeStore } from "@/store/useResumeStore";
-import { cn } from "@/lib/utils";
const ResumesList = () => {
return ;
};
const ResumeWorkbench = () => {
+ const t = useTranslations();
const {
resumes,
- createResume,
- deleteResume,
setActiveResume,
updateResume,
updateResumeFromFile,
+ addResume,
+ deleteResume,
+ createResume,
} = useResumeStore();
const router = useRouter();
const [hasConfiguredFolder, setHasConfiguredFolder] = React.useState(false);
- const t = useTranslations();
useEffect(() => {
const syncResumesFromFiles = async () => {
@@ -88,6 +91,39 @@ const ResumeWorkbench = () => {
setActiveResume(newId);
};
+ const handleImportJson = () => {
+ const input = document.createElement("input");
+ input.type = "file";
+ input.accept = ".json";
+
+ input.onchange = async (e) => {
+ const file = (e.target as HTMLInputElement).files?.[0];
+ if (!file) return;
+
+ try {
+ const content = await file.text();
+ const config = JSON.parse(content);
+
+ const newResume = {
+ ...initialResumeState,
+ ...config,
+ id: crypto.randomUUID(),
+ title: config.title || t("dashboard.resumes.untitled"),
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ };
+
+ addResume(newResume);
+ toast.success(t("dashboard.resumes.importSuccess"));
+ } catch (error) {
+ console.error("Import error:", error);
+ toast.error(t("dashboard.resumes.importError"));
+ }
+ };
+
+ input.click();
+ };
+
return (
{
transition={{ duration: 0.3, delay: 0.1 }}
>
{hasConfiguredFolder ? (
-
+
-
+
{t("dashboard.resumes.synced")}
) : (
-
+
{t("dashboard.resumes.notice.title")}
- {t("dashboard.resumes.notice.description")}
+
+ {t("dashboard.resumes.notice.description")}
+
+
+
+
+ {t("dashboard.resumes.create")}
+
+
+
{
>
-
+
-
+
{t("dashboard.resumes.newResume")}
-
+
{t("dashboard.resumes.newResumeDescription")}
@@ -215,22 +279,23 @@ const ResumeWorkbench = () => {
>
-
+
-
+
{resume.title || "未命名简历"}
-
+
{t("dashboard.resumes.created")}
{new Date(resume.createdAt).toLocaleDateString()}
@@ -242,10 +307,15 @@ const ResumeWorkbench = () => {
{
e.stopPropagation();
@@ -259,10 +329,15 @@ const ResumeWorkbench = () => {
{
e.stopPropagation();
diff --git a/apps/fronted/src/i18n/locales/en.json b/apps/fronted/src/i18n/locales/en.json
index 5d678e2..c842669 100644
--- a/apps/fronted/src/i18n/locales/en.json
+++ b/apps/fronted/src/i18n/locales/en.json
@@ -50,10 +50,14 @@
"created": "Created At",
"synced": "Synced Files",
"view": "View",
- "myResume": "Resumes",
+ "myResume": "My Resumes",
"create": "Create Resume",
"newResume": "New Resume",
"newResumeDescription": "Create a new resume to get started.",
+ "import": "Import JSON Config",
+ "untitled": "Untitled Resume",
+ "importSuccess": "Configuration imported successfully",
+ "importError": "Import failed, please check the file format",
"notice": {
"title": "Attention",
"description": "It is recommended to configure a resume backup folder in the settings to prevent your data from being lost when the browser cache is cleared",
@@ -96,12 +100,18 @@
},
"pdfExport": {
"button": {
+ "export": "Export",
+ "exportPdf": "Export PDF (Server)",
+ "exportJson": "Export JSON Config",
"exporting": "Exporting...",
- "default": "Export PDF"
+ "exportingJson": "Exporting...",
+ "print": "Browser Print"
},
"toast": {
- "success": "PDF exported successfully!",
- "error": "PDF export failed, please try again"
+ "success": "PDF exported successfully",
+ "error": "PDF export failed",
+ "jsonSuccess": "Configuration exported successfully",
+ "jsonError": "Configuration export failed"
}
},
"previewDock": {
diff --git a/apps/fronted/src/i18n/locales/zh.json b/apps/fronted/src/i18n/locales/zh.json
index 88f18d2..ed65b47 100644
--- a/apps/fronted/src/i18n/locales/zh.json
+++ b/apps/fronted/src/i18n/locales/zh.json
@@ -51,9 +51,13 @@
"synced": "已备份文件夹",
"view": "去查看",
"myResume": "我的简历",
- "create": "创建简历",
+ "create": "新建简历",
"newResume": "新建简历",
"newResumeDescription": "创建一个新简历以开始。",
+ "import": "导入 JSON 配置",
+ "untitled": "未命名简历",
+ "importSuccess": "配置导入成功",
+ "importError": "配置导入失败,请检查文件格式",
"notice": {
"title": "注意",
"description": "建议在设置里中配置简历备份文件夹,防止您的数据可能会在浏览器清除缓存后丢失",
diff --git a/apps/fronted/src/store/useResumeStore.ts b/apps/fronted/src/store/useResumeStore.ts
index ce96fe5..a1c76e1 100644
--- a/apps/fronted/src/store/useResumeStore.ts
+++ b/apps/fronted/src/store/useResumeStore.ts
@@ -57,6 +57,7 @@ interface ResumeStore {
updateGlobalSettings: (settings: Partial) => void;
setThemeColor: (color: string) => void;
setTemplate: (templateId: string) => void;
+ addResume: (resume: ResumeData) => string;
}
// 同步简历到文件系统
@@ -562,6 +563,18 @@ export const useResumeStore = create(
activeResume: updatedResume,
});
},
+ addResume: (resume: ResumeData) => {
+ set((state) => ({
+ resumes: {
+ ...state.resumes,
+ [resume.id]: resume,
+ },
+ activeResumeId: resume.id,
+ }));
+
+ syncResumeToFile(resume);
+ return resume.id;
+ },
}),
{
name: "resume-storage",