chore: lint using react-doctor, update translations, dynamic imports

This commit is contained in:
Amruth Pillai
2026-05-21 09:56:26 +02:00
parent 3596102c63
commit 39e88dd365
208 changed files with 5876 additions and 4778 deletions
@@ -1,7 +1,7 @@
import type z from "zod";
import { Trans } from "@lingui/react/macro";
import { useStore } from "@tanstack/react-form";
import { AnimatePresence, motion } from "motion/react";
import { AnimatePresence, m } from "motion/react";
import { colorDesignSchema, levelDesignSchema } from "@reactive-resume/schema/resume/data";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
@@ -231,15 +231,15 @@ function QuickColorCircle({ color, active, onSelect, className, ...props }: Quic
<AnimatePresence>
{active && (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
<m.div
initial={{ scale: 0.95, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.95, opacity: 0 }}
transition={{ duration: 0.16, ease: "easeOut" }}
className="absolute inset-0 flex size-8 items-center justify-center will-change-transform"
>
<div className="size-4 rounded-md bg-foreground" />
</motion.div>
</m.div>
)}
</AnimatePresence>
</button>
@@ -1,5 +1,5 @@
import type { DragEndEvent, DragStartEvent } from "@dnd-kit/core";
import type { CSSProperties, HTMLAttributes } from "react";
import type { CSSProperties, HTMLAttributes, Ref } from "react";
import {
closestCorners,
DndContext,
@@ -14,7 +14,7 @@ import { CSS } from "@dnd-kit/utilities";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { DotsSixVerticalIcon, PlusIcon, TrashIcon } from "@phosphor-icons/react";
import { forwardRef, useCallback, useId, useState } from "react";
import { useCallback, useId, useState } from "react";
import { match } from "ts-pattern";
import { Button } from "@reactive-resume/ui/components/button";
import { Switch } from "@reactive-resume/ui/components/switch";
@@ -95,8 +95,10 @@ export function LayoutPages() {
// Search through all pages
for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex++) {
const page = layout.pages[pageIndex];
if (page.main.includes(id)) return { pageIndex, columnId: "main" };
if (page.sidebar.includes(id)) return { pageIndex, columnId: "sidebar" };
const mainSections = new Set(page.main);
const sidebarSections = new Set(page.sidebar);
if (mainSections.has(id)) return { pageIndex, columnId: "main" };
if (sidebarSections.has(id)) return { pageIndex, columnId: "sidebar" };
}
return null;
@@ -395,35 +397,32 @@ function SortableLayoutItem({ id }: SortableLayoutItemProps) {
type LayoutItemContentProps = HTMLAttributes<HTMLDivElement> & {
id: string;
ref?: Ref<HTMLDivElement>;
isDragging?: boolean;
isOverlay?: boolean;
};
const LayoutItemContent = forwardRef<HTMLDivElement, LayoutItemContentProps>(
({ id, isDragging, isOverlay, className, style, ...rest }, ref) => {
const resume = useCurrentResume();
const title = resume ? resolveLayoutSectionTitle(resume.data, id) : id;
function LayoutItemContent({ id, ref, isDragging, isOverlay, className, style, ...rest }: LayoutItemContentProps) {
const resume = useCurrentResume();
const title = resume ? resolveLayoutSectionTitle(resume.data, id) : id;
return (
<div
ref={ref}
style={style}
data-overlay={isOverlay ? "true" : undefined}
data-dragging={isDragging ? "true" : undefined}
className={cn(
"group/item flex cursor-grab touch-none select-none items-center gap-x-2 rounded-md border border-border bg-background px-2 py-1.5 font-medium text-sm transition-all duration-200 ease-out",
"hover:bg-secondary/40 active:cursor-grabbing active:border-primary/60 active:bg-secondary/40",
"data-[overlay=true]:cursor-grabbing data-[overlay=true]:border-primary/60 data-[overlay=true]:bg-background",
"data-[dragging=true]:cursor-grabbing data-[dragging=true]:border-primary/60 data-[dragging=true]:bg-background",
className,
)}
{...rest}
>
<DotsSixVerticalIcon className="opacity-40 transition-opacity group-hover/item:opacity-100" />
<span className="truncate">{title}</span>
</div>
);
},
);
LayoutItemContent.displayName = "LayoutItemContent";
return (
<div
ref={ref}
style={style}
data-overlay={isOverlay ? "true" : undefined}
data-dragging={isDragging ? "true" : undefined}
className={cn(
"group/item flex cursor-grab touch-none select-none items-center gap-x-2 rounded-md border border-border bg-background px-2 py-1.5 font-medium text-sm transition-all duration-200 ease-out",
"hover:bg-secondary/40 active:cursor-grabbing active:border-primary/60 active:bg-secondary/40",
"data-[overlay=true]:cursor-grabbing data-[overlay=true]:border-primary/60 data-[overlay=true]:bg-background",
"data-[dragging=true]:cursor-grabbing data-[dragging=true]:border-primary/60 data-[dragging=true]:bg-background",
className,
)}
{...rest}
>
<DotsSixVerticalIcon className="opacity-40 transition-opacity group-hover/item:opacity-100" />
<span className="truncate">{title}</span>
</div>
);
}
@@ -3,7 +3,7 @@ import { Trans } from "@lingui/react/macro";
import { ArrowRightIcon, InfoIcon, LightningIcon, SparkleIcon } from "@phosphor-icons/react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { match } from "ts-pattern";
import { Alert, AlertDescription } from "@reactive-resume/ui/components/alert";
@@ -90,7 +90,9 @@ export function ResumeAnalysisSectionBuilder() {
const analysis = analysisQuery.data;
const score = analysis?.overallScore ?? null;
const analyzeLabel = isPending ? t`Analyzing...` : t`Analyze Resume`;
const updatedAt = analysis?.updatedAt ?? null;
const [updatedAtLabel, setUpdatedAtLabel] = useState<string | null>(null);
const analyzeLabel = isPending ? t`Analyzing…` : t`Analyze Resume`;
const scoreTone = useMemo(() => {
if (score == null) return "bg-muted";
@@ -99,6 +101,10 @@ export function ResumeAnalysisSectionBuilder() {
return "bg-rose-600";
}, [score]);
useEffect(() => {
setUpdatedAtLabel(updatedAt ? new Date(updatedAt).toLocaleString() : null);
}, [updatedAt]);
const onAnalyze = () => {
if (!resume) return;
@@ -153,11 +159,11 @@ export function ResumeAnalysisSectionBuilder() {
);
})}
</div>
{analysis?.updatedAt && (
{updatedAtLabel ? (
<p className="text-muted-foreground text-xs leading-none">
<Trans>Last analyzed on {new Date(analysis.updatedAt).toLocaleString()}</Trans>
<Trans>Last analyzed on {updatedAtLabel}</Trans>
</p>
)}
) : null}
</div>
</div>
</div>
@@ -67,7 +67,7 @@ type StatisticsItemProps = {
function StatisticsItem({ label, value, timestamp }: StatisticsItemProps) {
return (
<div>
<h4 className="mb-1 font-bold font-mono text-4xl">{value}</h4>
<h4 className="mb-1 font-mono font-semibold text-4xl">{value}</h4>
<p className="font-medium text-muted-foreground leading-none">{label}</p>
{timestamp && <span className="text-muted-foreground text-xs">{timestamp}</span>}
</div>
@@ -43,7 +43,7 @@ function TemplateSectionForm() {
</div>
</Button>
<div className="flex flex-1 flex-col space-y-4 @md:pt-1 @md:pb-3">
<div className="flex flex-1 flex-col gap-y-4 @md:pt-1 @md:pb-3">
<div className="space-y-1">
<h3 className="font-semibold text-2xl capitalize tracking-tight">{metadata.name}</h3>
<p className="text-muted-foreground text-sm">{i18n.t(metadata.description)}</p>
@@ -1,3 +1,4 @@
import type { ReactNode } from "react";
import type z from "zod";
import { Trans } from "@lingui/react/macro";
import { useStore } from "@tanstack/react-form";
@@ -66,13 +67,7 @@ function TypographySectionForm() {
void form.handleSubmit();
}}
>
<div className="col-span-full flex items-center gap-x-2">
<Separator className="basis-[16px]" />
<div className="shrink-0 font-medium text-base leading-none">
<Trans context="Body Text (paragraphs, lists, etc.)">Body</Trans>
</div>
<Separator className="flex-1" />
</div>
<TypographyFieldGroup label={<Trans context="Body Text (paragraphs, lists, etc.)">Body</Trans>} />
<form.Field name="body.fontFamily">
{(field) => (
@@ -198,13 +193,7 @@ function TypographySectionForm() {
)}
</form.Field>
<div className="col-span-full flex items-center gap-x-2">
<Separator className="basis-[16px]" />
<div className="shrink-0 font-medium text-base leading-none">
<Trans context="Headings or Titles (H1, H2, H3, H4, H5, H6)">Heading</Trans>
</div>
<Separator className="flex-1" />
</div>
<TypographyFieldGroup label={<Trans context="Headings or Titles (H1, H2, H3, H4, H5, H6)">Heading</Trans>} />
<form.Field name="heading.fontFamily">
{(field) => (
@@ -332,3 +321,13 @@ function TypographySectionForm() {
</form>
);
}
function TypographyFieldGroup({ label }: { label: ReactNode }) {
return (
<div className="col-span-full flex items-center gap-x-2">
<Separator className="basis-[16px]" />
<div className="shrink-0 font-medium text-base leading-none">{label}</div>
<Separator className="flex-1" />
</div>
);
}
@@ -34,7 +34,7 @@ export function SectionBase({ type, className, ...props }: Props) {
<div className="flex flex-1 items-center gap-x-4">
{getSectionIcon(type)}
<h2 className="line-clamp-1 font-bold text-2xl tracking-tight">{getSectionTitle(type)}</h2>
<h2 className="line-clamp-1 font-semibold text-2xl tracking-tight">{getSectionTitle(type)}</h2>
</div>
</div>