mirror of
https://github.com/JOYCEQL/magic-resume.git
synced 2026-06-01 23:38:48 +02:00
feat: add backup configuration status
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslations } from "@/i18n/compat/client";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { AlertCircle, ShieldCheck, ShieldAlert } from "lucide-react";
|
||||
import { motion } from "framer-motion";
|
||||
import { useRouter } from "@/lib/navigation";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -13,8 +14,15 @@ import {
|
||||
HoverCardTrigger,
|
||||
HoverCardContent
|
||||
} from "@/components/ui/hover-card";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { GrammarCheckDrawer } from "./grammar/GrammarCheckDrawer";
|
||||
import { getFileHandle, getConfig } from "@/utils/fileSystem";
|
||||
|
||||
interface EditorHeaderProps {
|
||||
isMobile?: boolean;
|
||||
@@ -32,6 +40,23 @@ export function EditorHeader({ isMobile }: EditorHeaderProps) {
|
||||
?.filter((section) => section.enabled)
|
||||
.sort((a, b) => a.order - b.order);
|
||||
|
||||
const [backupConfigured, setBackupConfigured] = useState<boolean | null>(null);
|
||||
const [backupPath, setBackupPath] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const checkBackup = async () => {
|
||||
try {
|
||||
const handle = await getFileHandle("syncDirectory");
|
||||
const path = await getConfig("syncDirectoryPath");
|
||||
setBackupConfigured(!!handle);
|
||||
setBackupPath(path || "");
|
||||
} catch {
|
||||
setBackupConfigured(false);
|
||||
}
|
||||
};
|
||||
checkBackup();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<motion.header
|
||||
className={`h-16 border-b sticky top-0 z-10`}
|
||||
@@ -39,7 +64,7 @@ export function EditorHeader({ isMobile }: EditorHeaderProps) {
|
||||
animate={{ y: 0 }}
|
||||
>
|
||||
<div className="flex items-center justify-between px-6 h-full pr-2">
|
||||
<div className="flex items-center space-x-6 scrollbar-hide">
|
||||
<div className="flex items-center space-x-4 scrollbar-hide">
|
||||
<motion.div
|
||||
className="flex items-center space-x-2 shrink-0 cursor-pointer"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
@@ -50,6 +75,61 @@ export function EditorHeader({ isMobile }: EditorHeaderProps) {
|
||||
>
|
||||
<span className="text-lg font-semibold">{t("common.title")}</span>
|
||||
</motion.div>
|
||||
|
||||
{/* Backup Status Badge */}
|
||||
{backupConfigured !== null && (
|
||||
<TooltipProvider delayDuration={100}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<motion.div
|
||||
className={`
|
||||
hidden md:flex items-center gap-1.5 px-2.5 py-1 rounded-full cursor-pointer
|
||||
text-[11px] font-medium tracking-wide
|
||||
border transition-all duration-300
|
||||
${backupConfigured
|
||||
? "border-emerald-200 bg-emerald-50/60 text-emerald-600 hover:bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/30 dark:text-emerald-400 dark:hover:bg-emerald-950/50"
|
||||
: "border-amber-200 bg-amber-50/60 text-amber-600 hover:bg-amber-50 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-400 dark:hover:bg-amber-950/50"
|
||||
}
|
||||
`}
|
||||
initial={{ opacity: 0, x: -8 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.3, duration: 0.3 }}
|
||||
onClick={() => router.push("/app/dashboard/settings")}
|
||||
>
|
||||
{backupConfigured ? (
|
||||
<>
|
||||
<ShieldCheck className="w-3 h-3" />
|
||||
<span>{t("previewDock.backup.configured")}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<motion.div
|
||||
animate={{ scale: [1, 1.15, 1] }}
|
||||
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
|
||||
>
|
||||
<ShieldAlert className="w-3 h-3" />
|
||||
</motion.div>
|
||||
<span>{t("previewDock.backup.notConfigured")}</span>
|
||||
</>
|
||||
)}
|
||||
</motion.div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={8} className="max-w-[240px]">
|
||||
{backupConfigured ? (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span className="text-xs font-medium">{t("previewDock.backup.configured")}</span>
|
||||
<span className="text-[10px] text-muted-foreground truncate">{backupPath}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span className="text-xs font-medium">{t("previewDock.backup.notConfigured")}</span>
|
||||
<span className="text-[10px] text-muted-foreground">{t("previewDock.backup.clickToConfigure")}</span>
|
||||
</div>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
@@ -84,3 +164,4 @@ export function EditorHeader({ isMobile }: EditorHeaderProps) {
|
||||
</motion.header>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -506,3 +506,4 @@ const PreviewDock = ({
|
||||
};
|
||||
|
||||
export default PreviewDock;
|
||||
|
||||
|
||||
@@ -308,6 +308,11 @@
|
||||
"enabled": "One page mode enabled",
|
||||
"disabled": "One page mode disabled",
|
||||
"cannotFit": "Too much content. Optimized as much as possible but cannot fit on one page. Try simplifying the content or adjusting margins and font size in the sidebar."
|
||||
},
|
||||
"backup": {
|
||||
"configured": "Backup Configured",
|
||||
"notConfigured": "Backup Not Configured",
|
||||
"clickToConfigure": "Click to configure"
|
||||
}
|
||||
},
|
||||
"workbench": {
|
||||
|
||||
@@ -715,6 +715,11 @@
|
||||
"enabled": "已开启一页纸模式",
|
||||
"disabled": "已关闭一页纸模式",
|
||||
"cannotFit": "内容较多,已尽量压缩但无法完美一页,建议精简内容, 也可在此基础上调节页边距、字体大小等左侧设置栏选项"
|
||||
},
|
||||
"backup": {
|
||||
"configured": "备份已配置",
|
||||
"notConfigured": "未配置备份目录",
|
||||
"clickToConfigure": "点击前往设置"
|
||||
}
|
||||
},
|
||||
"aiPolishDialog": {
|
||||
|
||||
Reference in New Issue
Block a user