-
-
- Licensed under{" "}
-
- MIT
-
- .
-
-
-
-
- By the community, for the community.
-
-
-
-
- A passion project by{" "}
-
- Amruth Pillai
-
- .
-
-
-
-
-
- Reactive Resume v{__APP_VERSION__}
-
-
-
- );
-}
diff --git a/apps/web/src/dialogs/resume/index.tsx b/apps/web/src/dialogs/resume/index.tsx
index ec402dfa2..7b8785b16 100644
--- a/apps/web/src/dialogs/resume/index.tsx
+++ b/apps/web/src/dialogs/resume/index.tsx
@@ -134,7 +134,6 @@ export function DuplicateResumeDialog({ data }: DialogProps<"resume.duplicate">)
toast.add({ type: "success", description: t`Your resume has been duplicated.`, id: toastId });
closeDialog();
- if (!data.shouldRedirect) return;
void navigate({ to: "/builder/$resumeId", params: { resumeId: id } });
},
onError: (error) => {
diff --git a/apps/web/src/dialogs/schemas.ts b/apps/web/src/dialogs/schemas.ts
index 33e617b90..639192be6 100644
--- a/apps/web/src/dialogs/schemas.ts
+++ b/apps/web/src/dialogs/schemas.ts
@@ -15,7 +15,7 @@ export type DialogSchema =
}
| {
type: "resume.duplicate";
- data: { id: string; name: string; slug: string; tags: string[]; shouldRedirect?: boolean };
+ data: { id: string; name: string; slug: string; tags: string[] };
};
export type DialogType = DialogSchema["type"];
diff --git a/apps/web/src/features/applications/next-step.test.ts b/apps/web/src/features/applications/next-step.test.ts
index a4596676e..ab4b8cb28 100644
--- a/apps/web/src/features/applications/next-step.test.ts
+++ b/apps/web/src/features/applications/next-step.test.ts
@@ -1,6 +1,6 @@
import type { Application } from "./types";
import { describe, expect, it } from "vitest";
-import { getNextStep, isOverdue } from "./next-step";
+import { getNextStep } from "./next-step";
const NOW = new Date("2026-09-28T12:00:00Z");
const day = (offset: number) => new Date(NOW.getTime() + offset * 86_400_000);
@@ -47,7 +47,6 @@ describe("getNextStep", () => {
});
const late = getNextStep(application({ followUpAt: day(-1) }), NOW);
expect(late).toMatchObject({ kind: "follow-up", overdue: true });
- expect(isOverdue(late)).toBe(true);
});
it("suggests a follow-up after ten days without a reply, and counts the wait before that", () => {
diff --git a/apps/web/src/features/applications/next-step.ts b/apps/web/src/features/applications/next-step.ts
index e0830cc0b..feba131e4 100644
--- a/apps/web/src/features/applications/next-step.ts
+++ b/apps/web/src/features/applications/next-step.ts
@@ -56,9 +56,6 @@ export function getNextStep(application: NextStepSource, now = new Date()): Next
return { kind: "waiting", days };
}
-/** Whether a next step needs attention now: an overdue follow-up, or no reply for too long. */
-export const isOverdue = (step: NextStep) => step.kind === "no-reply" || (step.kind === "follow-up" && step.overdue);
-
export type NextStepText = { icon: IconName; title: string; sub: string; tone: "normal" | "warn" | "muted" };
const formatWhen = (date: Date, locale: string, withTime: boolean) =>
diff --git a/apps/web/src/features/homepage/languages-showcase.tsx b/apps/web/src/features/homepage/languages-showcase.tsx
index 3f5463cbf..7e45faea2 100644
--- a/apps/web/src/features/homepage/languages-showcase.tsx
+++ b/apps/web/src/features/homepage/languages-showcase.tsx
@@ -4,8 +4,9 @@ import { useLingui } from "@lingui/react";
import { Trans } from "@lingui/react/macro";
import { ArrowUpRightIcon, CheckIcon, DownloadSimpleIcon, PlusIcon } from "@phosphor-icons/react";
import { useState } from "react";
+import { isLocale, isRTL } from "@reactive-resume/utils/locale";
import { cn } from "@reactive-resume/utils/style";
-import { changeLocale, isLocale, isRTL, localeMap, resolveLocale } from "@/libs/locale";
+import { changeLocale, localeMap, resolveLocale } from "@/libs/locale";
import { section, sectionHeading, sectionText, sectionTitle, textLink, wrap } from "./classes";
import "./languages-showcase.css";
diff --git a/apps/web/src/features/resume/builder/draft.ts b/apps/web/src/features/resume/builder/draft.ts
index 08f30beb1..32b3c9625 100644
--- a/apps/web/src/features/resume/builder/draft.ts
+++ b/apps/web/src/features/resume/builder/draft.ts
@@ -181,14 +181,6 @@ function getResumeQueryKey(id: string): QueryKey {
return orpc.resume.getById.queryOptions({ input: { id } }).queryKey as QueryKey;
}
-function cloneResumeData(data: ResumeData): ResumeData {
- return structuredClone(data);
-}
-
-function cloneResume(resume: Resume): Resume {
- return { ...resume, data: cloneResumeData(resume.data) };
-}
-
export function isEditableElementFocused(): boolean {
if (typeof document === "undefined") return false;
const element = document.activeElement as HTMLElement | null;
@@ -261,7 +253,7 @@ async function flushResumeSave(id: string) {
if (!runtime || runtime.isSaving || !runtime.pendingResume) return;
const submitted = runtime.pendingResume;
- const submittedData = cloneResumeData(submitted.data);
+ const submittedData = structuredClone(submitted.data);
runtime.pendingResume = undefined;
runtime.isSaving = true;
@@ -292,7 +284,7 @@ async function flushResumeSave(id: string) {
if (!runtime.pendingResume && currentResume?.id === submitted.id && !isEqual(currentResume.data, submittedData)) {
runtime.syncResume.cancel();
- runtime.pendingResume = cloneResume(currentResume);
+ runtime.pendingResume = structuredClone(currentResume);
}
}
@@ -319,7 +311,7 @@ async function flushResumeSave(id: string) {
function queueResumeSave(resume: Resume) {
const runtime = getRuntime(resume.id);
- runtime.pendingResume = cloneResume(resume);
+ runtime.pendingResume = structuredClone(resume);
runtime.hasPendingLocalChanges = true;
runtime.saveFailed = false;
void flushResumeSave(resume.id);
@@ -804,7 +796,7 @@ export function savePendingChanges(id: string): boolean | Promise