mirror of
https://github.com/JOYCEQL/magic-resume.git
synced 2026-07-03 14:07:11 +02:00
feat: update navigation to support parameter objects and add error-safe persistence to resume store
This commit is contained in:
@@ -93,7 +93,7 @@ export const ResumeCardItem = ({
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setActiveResume(id);
|
||||
router.push(`/app/workbench/${id}`);
|
||||
router.push({ to: "/app/workbench/$id", params: { id } });
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 pb-6 flex items-center justify-center pointer-events-none transition-transform duration-300 group-hover:scale-[1.02] overflow-hidden" ref={containerRef}>
|
||||
@@ -139,7 +139,7 @@ export const ResumeCardItem = ({
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setActiveResume(id);
|
||||
router.push(`/app/workbench/${id}`);
|
||||
router.push({ to: "/app/workbench/$id", params: { id } });
|
||||
}}
|
||||
className="flex-1 flex items-center justify-center gap-1.5 hover:bg-white dark:hover:bg-gray-800/80 transition-all duration-200 text-gray-700 dark:text-gray-200 hover:text-primary font-medium text-sm group"
|
||||
>
|
||||
|
||||
@@ -100,7 +100,7 @@ export const ResumeWorkbench = () => {
|
||||
|
||||
setIsCreateModalOpen(false);
|
||||
setActiveResume(newId);
|
||||
router.push(`/app/workbench/${newId}`);
|
||||
router.push({ to: "/app/workbench/$id", params: { id: newId } });
|
||||
};
|
||||
|
||||
const duplicateResume = async (resume: any) => {
|
||||
@@ -138,7 +138,7 @@ export const ResumeWorkbench = () => {
|
||||
setActiveResume(resumeId);
|
||||
setIsImportDialogOpen(false);
|
||||
toast.success(t("dashboard.resumes.importSuccess"));
|
||||
router.push(`/app/workbench/${resumeId}`);
|
||||
router.push({ to: "/app/workbench/$id", params: { id: resumeId } });
|
||||
};
|
||||
|
||||
const extractImagesFromPdf = async (file: File) => {
|
||||
@@ -234,7 +234,7 @@ export const ResumeWorkbench = () => {
|
||||
setActiveResume(resumeId);
|
||||
setIsImportDialogOpen(false);
|
||||
toast.success(t("dashboard.resumes.importDialog.pdfSuccess"));
|
||||
router.push(`/app/workbench/${resumeId}`);
|
||||
router.push({ to: "/app/workbench/$id", params: { id: resumeId } });
|
||||
};
|
||||
|
||||
const handleJsonFileChange = async (
|
||||
|
||||
@@ -264,7 +264,7 @@ const TemplatesPage = () => {
|
||||
});
|
||||
}
|
||||
|
||||
router.push(`/app/workbench/${resumeId}`);
|
||||
router.push({ to: "/app/workbench/$id", params: { id: resumeId } });
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -147,7 +147,7 @@ const PreviewDock = ({
|
||||
const targetPath = `/app/workbench/${newId}`;
|
||||
setActiveResume(newId);
|
||||
toast.success(t("copyResume.success"));
|
||||
router.push(targetPath);
|
||||
router.push({ to: "/app/workbench/$id", params: { id: newId } });
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (window.location.pathname !== targetPath) {
|
||||
|
||||
+13
-3
@@ -1,13 +1,23 @@
|
||||
import { useLocation, useNavigate } from "@tanstack/react-router";
|
||||
|
||||
type NavigateTarget = string;
|
||||
type NavigateTarget =
|
||||
| string
|
||||
| {
|
||||
to: string;
|
||||
params?: Record<string, string>;
|
||||
search?: Record<string, unknown>;
|
||||
hash?: string;
|
||||
};
|
||||
|
||||
export function useRouter() {
|
||||
const navigate = useNavigate();
|
||||
const toNavigateOptions = (target: NavigateTarget) =>
|
||||
typeof target === "string" ? { to: target } : target;
|
||||
|
||||
return {
|
||||
push: (to: NavigateTarget) => navigate({ to }),
|
||||
replace: (to: NavigateTarget) => navigate({ to, replace: true }),
|
||||
push: (target: NavigateTarget) => navigate(toNavigateOptions(target) as any),
|
||||
replace: (target: NavigateTarget) =>
|
||||
navigate({ ...toNavigateOptions(target), replace: true } as any),
|
||||
back: () => window.history.back(),
|
||||
forward: () => window.history.forward(),
|
||||
refresh: () => window.location.reload()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { createJSONStorage, persist } from "zustand/middleware";
|
||||
import type { StateStorage } from "zustand/middleware";
|
||||
import { getFileHandle, verifyPermission } from "@/utils/fileSystem";
|
||||
import {
|
||||
BasicInfo,
|
||||
@@ -75,6 +76,32 @@ interface ResumeStore {
|
||||
|
||||
type PersistedResumeStore = Pick<ResumeStore, "resumes" | "activeResumeId">;
|
||||
|
||||
const warnedPersistFailures = new Set<string>();
|
||||
|
||||
const warnPersistFailure = (name: string, error: unknown) => {
|
||||
if (warnedPersistFailures.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
warnedPersistFailures.add(name);
|
||||
console.warn(
|
||||
`[resume-store] Failed to persist "${name}" to localStorage. Changes remain available in memory for this session.`,
|
||||
error
|
||||
);
|
||||
};
|
||||
|
||||
const createSafeLocalStorage = (): StateStorage => ({
|
||||
getItem: (name) => localStorage.getItem(name),
|
||||
setItem: (name, value) => {
|
||||
try {
|
||||
localStorage.setItem(name, value);
|
||||
} catch (error) {
|
||||
warnPersistFailure(name, error);
|
||||
}
|
||||
},
|
||||
removeItem: (name) => localStorage.removeItem(name),
|
||||
});
|
||||
|
||||
const parseTimestamp = (value?: string): number | null => {
|
||||
if (!value) {
|
||||
return null;
|
||||
@@ -201,7 +228,7 @@ const debouncedSyncToFile = (
|
||||
};
|
||||
|
||||
export const useResumeStore = create(
|
||||
persist<ResumeStore>(
|
||||
persist<ResumeStore, [], [], PersistedResumeStore>(
|
||||
(set, get) => ({
|
||||
resumes: {},
|
||||
activeResumeId: null,
|
||||
@@ -380,8 +407,14 @@ export const useResumeStore = create(
|
||||
},
|
||||
|
||||
setActiveResume: (resumeId) => {
|
||||
const resume = get().resumes[resumeId];
|
||||
set({ activeResume: resume ?? null, activeResumeId: resumeId });
|
||||
const { resumes, activeResume, activeResumeId } = get();
|
||||
const nextResume = resumes[resumeId] ?? null;
|
||||
|
||||
if (activeResumeId === resumeId && activeResume === nextResume) {
|
||||
return;
|
||||
}
|
||||
|
||||
set({ activeResume: nextResume, activeResumeId: resumeId });
|
||||
},
|
||||
|
||||
updateBasicInfo: (data) => {
|
||||
@@ -795,6 +828,9 @@ export const useResumeStore = create(
|
||||
}),
|
||||
{
|
||||
name: "resume-storage",
|
||||
storage: createJSONStorage<PersistedResumeStore>(() =>
|
||||
createSafeLocalStorage()
|
||||
),
|
||||
partialize: (state): PersistedResumeStore => ({
|
||||
resumes: state.resumes,
|
||||
activeResumeId: state.activeResumeId,
|
||||
|
||||
Reference in New Issue
Block a user