- fix improper re-ordering of chips
- update dependencies, translations
This commit is contained in:
Amruth Pillai
2026-02-23 21:02:35 +01:00
parent 4b78a7c9dd
commit b6c274eeb6
59 changed files with 3803 additions and 4062 deletions
-3
View File
@@ -1,7 +1,4 @@
import { defineConfig } from "drizzle-kit";
import invariant from "tiny-invariant";
invariant(process.env.DATABASE_URL, "DATABASE_URL is not set");
export default defineConfig({
out: "./migrations",
+1 -1
View File
@@ -5,5 +5,5 @@
"project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"],
"ignoreBinaries": ["mint"],
"ignoreDependencies": ["npm-check-updates", "node-addon-api", "node-gyp"],
"ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}", "src/routeTree.gen.ts"]
"ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}"]
}
BIN
View File
Binary file not shown.
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "አጉር"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ዙሉ"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "تصغير"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "الزولو"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Uzaqlaşdır"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Намаляване"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулуски"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "জুম আউট"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "জুলু"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Allunya"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulú"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Oddálit"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Zoom ud"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Herauszoomen"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Σμίκρυνση"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Ζουλού"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Alejar"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulú"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "کوچک‌نمایی"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "زولو"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Loitonna"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Zoom arrière"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zoulou"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "התרחקות"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "זולו"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "ज़ूम आउट"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ज़ुलु"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Kicsinyítés"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Perkecil"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Rimpicciolisci"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "ズームアウト"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ズールー語"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "បង្រួម"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "ಗಾತ್ರ ಕುಗ್ಗಿಸಿ"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ಜೂಲೂ"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "축소"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "줄루어"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Tolinti"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulų"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Tālināt"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "സൂം ഔട്ട് ചെയ്യുക"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "സൂളു"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "बाहेर झूम करा"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "झुलू"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Zum keluar"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "जूम आउट"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "जुलु"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Uitzoomen"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Zoom ut"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "ଜୁମ୍ ଆଉଟ୍ କରନ୍ତୁ"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ଜୁଲୁ"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Pomniejsz"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Diminuir zoom"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Afastar"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Micșorează"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Уменьшить"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулу"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Oddialiť"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Zvogëlo"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Умањи"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулу"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Zooma ut"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "சிறிதாக்கு"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ஜூலு"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "జూమ్ అవుట్ చేయండి"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "జులు"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "ซูมออก"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ซูลู"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Uzaklaştır"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Зменшити"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулу"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Kichraytirish"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "Thu nhỏ"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "缩小"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "祖鲁语"
-1
View File
@@ -2848,4 +2848,3 @@ msgstr "縮小"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "祖魯語"
File diff suppressed because it is too large Load Diff
+52 -53
View File
@@ -4,7 +4,7 @@
"version": "5.0.9",
"license": "MIT",
"type": "module",
"packageManager": "pnpm@10.29.2+sha512.bef43fa759d91fd2da4b319a5a0d13ef7a45bb985a3d7342058470f9d2051a3ba8674e629672654686ef9443ad13a82da2beb9eeb3e0221c87b8154fff9d74b8",
"packageManager": "pnpm@10.30.1+sha512.3590e550d5384caa39bd5c7c739f72270234b2f6059e13018f975c313b1eb9fefcc09714048765d4d9efe961382c312e624572c0420762bdc5d5940cdf9be73a",
"repository": {
"type": "git",
"url": "https://github.com/amruthpillai/reactive-resume.git"
@@ -31,45 +31,45 @@
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"@ai-sdk/anthropic": "^3.0.39",
"@ai-sdk/google": "^3.0.23",
"@ai-sdk/openai": "^3.0.26",
"@ai-sdk/react": "^3.0.80",
"@aws-sdk/client-s3": "^3.986.0",
"@better-auth/core": "1.5.0-beta.13",
"@ai-sdk/anthropic": "^3.0.46",
"@ai-sdk/google": "^3.0.30",
"@ai-sdk/openai": "^3.0.30",
"@ai-sdk/react": "^3.0.99",
"@aws-sdk/client-s3": "^3.995.0",
"@better-auth/core": "1.5.0-beta.18",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fontsource-variable/ibm-plex-sans": "^5.2.8",
"@hookform/resolvers": "^5.2.2",
"@lingui/core": "^5.9.0",
"@lingui/react": "^5.9.0",
"@lingui/core": "^5.9.2",
"@lingui/react": "^5.9.2",
"@monaco-editor/react": "4.8.0-rc.3",
"@orpc/client": "^1.13.4",
"@orpc/json-schema": "^1.13.4",
"@orpc/openapi": "^1.13.4",
"@orpc/server": "^1.13.4",
"@orpc/tanstack-query": "^1.13.4",
"@orpc/zod": "^1.13.4",
"@orpc/client": "^1.13.5",
"@orpc/json-schema": "^1.13.5",
"@orpc/openapi": "^1.13.5",
"@orpc/server": "^1.13.5",
"@orpc/tanstack-query": "^1.13.5",
"@orpc/zod": "^1.13.5",
"@phosphor-icons/react": "^2.1.10",
"@phosphor-icons/web": "^2.1.2",
"@sindresorhus/slugify": "^3.0.0",
"@t3-oss/env-core": "^0.13.10",
"@tanstack/react-query": "5.90.20",
"@tanstack/react-router": "^1.159.5",
"@tanstack/react-router-ssr-query": "^1.159.5",
"@tanstack/react-start": "^1.159.5",
"@tanstack/zod-adapter": "^1.159.5",
"@tiptap/extension-highlight": "^3.19.0",
"@tiptap/extension-table": "^3.19.0",
"@tiptap/extension-text-align": "^3.19.0",
"@tiptap/pm": "^3.19.0",
"@tiptap/react": "^3.19.0",
"@tiptap/starter-kit": "^3.19.0",
"ai": "^6.0.78",
"ai-sdk-ollama": "^3.5.0",
"@tanstack/react-query": "5.90.21",
"@tanstack/react-router": "^1.162.6",
"@tanstack/react-router-ssr-query": "^1.162.6",
"@tanstack/react-start": "^1.162.6",
"@tanstack/zod-adapter": "^1.162.6",
"@tiptap/extension-highlight": "^3.20.0",
"@tiptap/extension-table": "^3.20.0",
"@tiptap/extension-text-align": "^3.20.0",
"@tiptap/pm": "^3.20.0",
"@tiptap/react": "^3.20.0",
"@tiptap/starter-kit": "^3.20.0",
"ai": "^6.0.97",
"ai-sdk-ollama": "^3.7.1",
"bcrypt": "^6.0.0",
"better-auth": "1.5.0-beta.13",
"better-auth": "1.5.0-beta.18",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
@@ -81,29 +81,28 @@
"fast-deep-equal": "^3.1.3",
"fast-json-patch": "^3.1.1",
"fuse.js": "^7.1.0",
"immer": "^11.1.3",
"immer": "^11.1.4",
"input-otp": "^1.4.2",
"js-cookie": "^3.0.5",
"monaco-editor": "^0.55.1",
"motion": "^12.34.0",
"motion": "^12.34.3",
"nodemailer": "^8.0.1",
"pg": "^8.18.0",
"puppeteer-core": "^24.37.2",
"puppeteer-core": "^24.37.5",
"qrcode.react": "^4.2.0",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.71.1",
"react-hook-form": "^7.71.2",
"react-hotkeys-hook": "^5.2.4",
"react-resizable-panels": "^4.6.2",
"react-window": "^2.2.6",
"react-resizable-panels": "^4.6.5",
"react-window": "^2.2.7",
"react-zoom-pan-pinch": "^3.7.0",
"shadcn": "^3.8.4",
"shadcn": "^3.8.5",
"sharp": "^0.34.5",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
"tiny-invariant": "^1.3.3",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.1",
"ts-pattern": "^5.9.0",
"tw-animate-css": "^1.4.0",
"unique-names-generator": "^4.7.1",
@@ -115,34 +114,34 @@
"zustand": "^5.0.11"
},
"devDependencies": {
"@biomejs/biome": "^2.3.14",
"@lingui/babel-plugin-lingui-macro": "^5.9.0",
"@lingui/cli": "^5.9.0",
"@lingui/vite-plugin": "^5.9.0",
"@tailwindcss/vite": "^4.1.18",
"@biomejs/biome": "^2.4.4",
"@lingui/babel-plugin-lingui-macro": "^5.9.2",
"@lingui/cli": "^5.9.2",
"@lingui/vite-plugin": "^5.9.2",
"@tailwindcss/vite": "^4.2.1",
"@types/bcrypt": "^6.0.0",
"@types/js-cookie": "^3.0.6",
"@types/node": "^25.2.2",
"@types/nodemailer": "^7.0.9",
"@types/node": "^25.3.0",
"@types/nodemailer": "^7.0.11",
"@types/pg": "^8.16.0",
"@types/react": "^19.2.13",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript/native-preview": "7.0.0-dev.20260209.1",
"@vitejs/plugin-react": "^5.1.3",
"@typescript/native-preview": "7.0.0-dev.20260223.1",
"@vitejs/plugin-react": "^5.1.4",
"babel-plugin-macros": "^3.1.0",
"drizzle-kit": "^1.0.0-beta.15-859cf75",
"knip": "^5.83.1",
"knip": "^5.85.0",
"nitro": "npm:nitro-nightly@latest",
"node-addon-api": "^8.5.0",
"node-gyp": "^12.2.0",
"npm-check-updates": "^19.3.2",
"npm-check-updates": "^19.4.1",
"tsx": "^4.21.0",
"vite": "^8.0.0-beta.13",
"vite": "^8.0.0-beta.15",
"vite-plugin-pwa": "^1.2.0"
},
"pnpm": {
"overrides": {
"vite": "^8.0.0-beta.13"
"vite": "^8.0.0-beta.15"
},
"onlyBuiltDependencies": [
"@prisma/engines",
+1785 -1966
View File
File diff suppressed because it is too large Load Diff
+78 -39
View File
@@ -1,6 +1,22 @@
import {
closestCenter,
DndContext,
type DragEndEvent,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from "@dnd-kit/core";
import {
horizontalListSortingStrategy,
SortableContext,
sortableKeyboardCoordinates,
useSortable,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Trans } from "@lingui/react/macro";
import { PencilSimpleIcon, XIcon } from "@phosphor-icons/react";
import { motion, Reorder, useDragControls } from "motion/react";
import { motion } from "motion/react";
import * as React from "react";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
@@ -12,6 +28,7 @@ const RETURN_KEY = "Enter";
const COMMA_KEY = ",";
type ChipItemProps = {
id: string;
chip: string;
index: number;
isEditing: boolean;
@@ -19,35 +36,37 @@ type ChipItemProps = {
onRemove: (index: number) => void;
};
function ChipItem({ chip, index, isEditing, onEdit, onRemove }: ChipItemProps) {
const controls = useDragControls();
function ChipItem({ id, chip, index, isEditing, onEdit, onRemove }: ChipItemProps) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
const [isHovered, setIsHovered] = React.useState(false);
const style = {
transform: CSS.Transform.toString(transform),
transition,
zIndex: isDragging ? 10 : undefined,
opacity: isDragging ? 0.6 : 1,
};
return (
<Reorder.Item
value={chip}
dragListener={false}
dragControls={controls}
initial={{ opacity: 1, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="relative"
<div
style={style}
ref={setNodeRef}
className="relative touch-none"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
{...attributes}
{...listeners}
>
<Badge
variant="outline"
className={cn(
"flex h-7 cursor-grab select-none items-center gap-0 overflow-hidden px-3 active:cursor-grabbing",
isEditing && "border-primary ring-1 ring-primary",
isDragging && "opacity-80",
)}
onPointerDown={(e) => {
e.preventDefault();
controls.start(e);
}}
>
<span className="max-w-32 truncate">{chip}</span>
<motion.div
initial={false}
animate={{ width: isHovered ? 40 : 0, marginInlineStart: isHovered ? 8 : 0, opacity: isHovered ? 1 : 0 }}
@@ -80,7 +99,7 @@ function ChipItem({ chip, index, isEditing, onEdit, onRemove }: ChipItemProps) {
</button>
</motion.div>
</Badge>
</Reorder.Item>
</div>
);
}
@@ -116,7 +135,6 @@ export function ChipInput({ value, defaultValue = [], onChange, className, ...pr
const trimmed = newValue.trim();
if (!trimmed || index < 0 || index >= chips.length) return;
// Check if the new value already exists at a different index
const existingIndex = chips.findIndex((c, i) => c === trimmed && i !== index);
if (existingIndex !== -1) return;
@@ -133,12 +151,10 @@ export function ChipInput({ value, defaultValue = [], onChange, className, ...pr
const newChips = chips.slice(0, index).concat(chips.slice(index + 1));
setChips(newChips);
// If we were editing this chip, clear edit mode
if (editingIndex === index) {
setEditingIndex(null);
setInput("");
} else if (editingIndex !== null && editingIndex > index) {
// Adjust editing index if we removed a chip before it
setEditingIndex(editingIndex - 1);
}
},
@@ -156,7 +172,6 @@ export function ChipInput({ value, defaultValue = [], onChange, className, ...pr
const handleReorder = React.useCallback(
(newOrder: string[]) => {
// Update editingIndex to follow the chip being edited
if (editingIndex !== null) {
const editingChip = chips[editingIndex];
const newIndex = newOrder.indexOf(editingChip);
@@ -169,14 +184,37 @@ export function ChipInput({ value, defaultValue = [], onChange, className, ...pr
[chips, editingIndex, setChips],
);
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: { distance: 3 },
}),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
);
const handleDragEnd = React.useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (!over || active.id === over.id) return;
const oldIndex = chips.indexOf(active.id as string);
const newIndex = chips.indexOf(over.id as string);
if (oldIndex !== -1 && newIndex !== -1 && oldIndex !== newIndex) {
const newOrder = Array.from(chips);
const [removed] = newOrder.splice(oldIndex, 1);
newOrder.splice(newIndex, 0, removed);
handleReorder(newOrder);
}
},
[chips, handleReorder],
);
const handleInputChange = React.useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
// When editing, don't split on comma - allow full text editing
if (editingIndex !== null) {
if (newValue.includes(",")) {
// Save current edit
updateChip(editingIndex, newValue.replace(",", ""));
setEditingIndex(null);
setInput("");
@@ -186,7 +224,6 @@ export function ChipInput({ value, defaultValue = [], onChange, className, ...pr
return;
}
// When adding new chips, split on comma
if (newValue.includes(",")) {
const parts = newValue.split(",");
parts.slice(0, -1).forEach(addChip);
@@ -204,19 +241,16 @@ export function ChipInput({ value, defaultValue = [], onChange, className, ...pr
e.preventDefault();
if (editingIndex !== null) {
// Save edit
if (input.trim()) {
updateChip(editingIndex, input);
}
setEditingIndex(null);
setInput("");
} else if (input.trim()) {
// Add new chip
addChip(input);
setInput("");
}
} else if (e.key === "Escape" && editingIndex !== null) {
// Cancel edit
setEditingIndex(null);
setInput("");
}
@@ -227,18 +261,23 @@ export function ChipInput({ value, defaultValue = [], onChange, className, ...pr
return (
<div className={cn("flex flex-col gap-2", className)} {...props}>
{chips.length > 0 && (
<Reorder.Group axis="x" values={chips} onReorder={handleReorder} className="flex flex-wrap gap-1.5">
{chips.map((chip, idx) => (
<ChipItem
key={`${chip}-${idx}`}
chip={chip}
index={idx}
isEditing={editingIndex === idx}
onEdit={handleEdit}
onRemove={removeChip}
/>
))}
</Reorder.Group>
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
<SortableContext items={chips} strategy={horizontalListSortingStrategy}>
<div className="flex flex-wrap gap-1.5">
{chips.map((chip, idx) => (
<ChipItem
key={`${chip}-${idx}`}
id={chip}
chip={chip}
index={idx}
isEditing={editingIndex === idx}
onEdit={handleEdit}
onRemove={removeChip}
/>
))}
</div>
</SortableContext>
</DndContext>
)}
<div className="flex flex-col gap-2">
+28 -24
View File
@@ -1,10 +1,14 @@
import { zodResolver } from "@hookform/resolvers/zod";
import type { MessageDescriptor } from "@lingui/core";
import { msg } from "@lingui/core/macro";
import { useLingui } from "@lingui/react";
import { Trans } from "@lingui/react/macro";
import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
import { useForm, useFormContext } from "react-hook-form";
import type z from "zod";
import { useResumeStore } from "@/components/resume/store/resume";
import { Button } from "@/components/ui/button";
import { Combobox } from "@/components/ui/combobox";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
@@ -18,21 +22,21 @@ const formSchema = customSectionSchema;
type FormValues = z.infer<typeof formSchema>;
const SECTION_TYPE_OPTIONS: { value: CustomSectionType; label: string }[] = [
{ value: "summary", label: "Summary" },
{ value: "experience", label: "Experience" },
{ value: "education", label: "Education" },
{ value: "projects", label: "Projects" },
{ value: "profiles", label: "Profiles" },
{ value: "skills", label: "Skills" },
{ value: "languages", label: "Languages" },
{ value: "interests", label: "Interests" },
{ value: "awards", label: "Awards" },
{ value: "certifications", label: "Certifications" },
{ value: "publications", label: "Publications" },
{ value: "volunteer", label: "Volunteer" },
{ value: "references", label: "References" },
{ value: "cover-letter", label: "Cover Letter" },
const SECTION_TYPE_OPTIONS: { value: CustomSectionType; label: MessageDescriptor }[] = [
{ value: "summary", label: msg`Summary` },
{ value: "experience", label: msg`Experience` },
{ value: "education", label: msg`Education` },
{ value: "projects", label: msg`Projects` },
{ value: "profiles", label: msg`Profiles` },
{ value: "skills", label: msg`Skills` },
{ value: "languages", label: msg`Languages` },
{ value: "interests", label: msg`Interests` },
{ value: "awards", label: msg`Awards` },
{ value: "certifications", label: msg`Certifications` },
{ value: "publications", label: msg`Publications` },
{ value: "volunteer", label: msg`Volunteer` },
{ value: "references", label: msg`References` },
{ value: "cover-letter", label: msg`Cover Letter` },
];
export function CreateCustomSectionDialog({ data }: DialogProps<"resume.sections.custom.create">) {
@@ -150,6 +154,7 @@ export function UpdateCustomSectionDialog({ data }: DialogProps<"resume.sections
}
function CustomSectionForm({ isUpdate = false }: { isUpdate?: boolean }) {
const { i18n } = useLingui();
const form = useFormContext<FormValues>();
return (
@@ -179,17 +184,16 @@ function CustomSectionForm({ isUpdate = false }: { isUpdate?: boolean }) {
<Trans>Section Type</Trans>
</FormLabel>
<FormControl>
<select
<Combobox
{...field}
value={field.value}
disabled={isUpdate}
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-xs transition-colors file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
>
{SECTION_TYPE_OPTIONS.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
onValueChange={field.onChange}
options={SECTION_TYPE_OPTIONS.map((option) => ({
value: option.value,
label: i18n.t(option.label),
}))}
/>
</FormControl>
<FormMessage />
</FormItem>
+7 -11
View File
@@ -16,18 +16,14 @@ import { cn } from "@/utils/style";
export const Route = createFileRoute("/$username/$slug")({
component: RouteComponent,
loader: async ({ context, params: { username, slug } }) => {
try {
// Ignore .well-known requests
if (username === ".well-known") throw notFound();
// Ignore .well-known requests
if (username === ".well-known") throw notFound();
const resume = await context.queryClient.ensureQueryData(
orpc.resume.getBySlug.queryOptions({ input: { username, slug } }),
);
const resume = await context.queryClient.ensureQueryData(
orpc.resume.getBySlug.queryOptions({ input: { username, slug } }),
);
return { resume };
} catch {
throw notFound();
}
return { resume };
},
head: ({ loaderData }) => ({
meta: [{ title: loaderData ? `${loaderData.resume.name} - Reactive Resume` : "Reactive Resume" }],
@@ -86,7 +82,7 @@ function RouteComponent() {
size="lg"
variant="secondary"
disabled={isPrinting}
className="fixed end-4 bottom-4 z-50 hidden rounded-full px-4 md:inline-flex print:hidden"
className="fixed inset-e-4 bottom-4 z-50 hidden rounded-full px-4 md:inline-flex print:hidden"
onClick={handleDownload}
>
{isPrinting ? <Spinner /> : <DownloadSimpleIcon />}