diff --git a/app/globals.css b/app/globals.css
index 382ca14..d767ad6 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -123,4 +123,4 @@
body {
@apply bg-background text-foreground;
}
-}
\ No newline at end of file
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index f7fa87e..41c0ab4 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,34 +1,30 @@
-import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
-import "./globals.css";
+import type { Metadata } from "next"
+import { Geist, Geist_Mono } from "next/font/google"
+import "./globals.css"
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
-});
+})
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
-});
+})
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
-};
+}
export default function RootLayout({
children,
}: Readonly<{
- children: React.ReactNode;
+ children: React.ReactNode
}>) {
return (
-
- {children}
-
+ {children}
- );
+ )
}
diff --git a/app/page.tsx b/app/page.tsx
index 295f8fd..fcc63c4 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,4 +1,4 @@
-import Image from "next/image";
+import Image from "next/image"
export default function Home() {
return (
@@ -61,5 +61,5 @@ export default function Home() {
- );
+ )
}
diff --git a/biome.json b/biome.json
new file mode 100644
index 0000000..91f7e0d
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,58 @@
+{
+ "$schema": "https://biomejs.dev/schemas/2.4.3/schema.json",
+ "vcs": {
+ "enabled": true,
+ "clientKind": "git",
+ "useIgnoreFile": true
+ },
+ "files": {
+ "ignoreUnknown": true,
+ "includes": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.mjs", "**/*.json", "**/*.css"]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2,
+ "lineWidth": 100
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true,
+ "correctness": {
+ "noUnusedImports": "warn",
+ "useExhaustiveDependencies": "warn"
+ },
+ "style": {
+ "noNonNullAssertion": "warn",
+ "useImportType": "error"
+ },
+ "suspicious": {
+ "noExplicitAny": "warn"
+ }
+ }
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "double",
+ "semicolons": "asNeeded"
+ }
+ },
+ "css": {
+ "parser": {
+ "cssModules": true,
+ "tailwindDirectives": true
+ },
+ "formatter": {
+ "enabled": true
+ },
+ "linter": {
+ "enabled": true
+ }
+ },
+ "json": {
+ "formatter": {
+ "enabled": true
+ }
+ }
+}
diff --git a/lib/utils.ts b/lib/utils.ts
index bd0c391..d084cca 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,4 +1,4 @@
-import { clsx, type ClassValue } from "clsx"
+import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
diff --git a/next-env.d.ts b/next-env.d.ts
index 9edff1c..7a70f65 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -1,6 +1,6 @@
///
///
-import "./.next/types/routes.d.ts";
+import "./.next/types/routes.d.ts"
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/next.config.ts b/next.config.ts
index e9ffa30..3b84f68 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,7 +1,7 @@
-import type { NextConfig } from "next";
+import type { NextConfig } from "next"
const nextConfig: NextConfig = {
/* config options here */
-};
+}
-export default nextConfig;
+export default nextConfig
diff --git a/package.json b/package.json
index 7e38f0e..bc6f91d 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,18 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "next dev --turbopack",
"build": "next build",
- "start": "next start"
+ "start": "next start",
+ "lint": "biome check .",
+ "lint:fix": "biome check --write .",
+ "format": "biome format --write .",
+ "check": "biome ci .",
+ "typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@hookform/resolvers": "^5.2.2",
+ "@tanstack/react-query": "^5.90.21",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.575.0",
@@ -15,9 +22,13 @@
"radix-ui": "^1.4.3",
"react": "19.2.3",
"react-dom": "19.2.3",
- "tailwind-merge": "^3.5.0"
+ "react-hook-form": "^7.71.1",
+ "tailwind-merge": "^3.5.0",
+ "zod": "^4.3.6",
+ "zustand": "^5.0.11"
},
"devDependencies": {
+ "@biomejs/biome": "^2.4.3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3076255..7defc3a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,12 @@ importers:
.:
dependencies:
+ '@hookform/resolvers':
+ specifier: ^5.2.2
+ version: 5.2.2(react-hook-form@7.71.1(react@19.2.3))
+ '@tanstack/react-query':
+ specifier: ^5.90.21
+ version: 5.90.21(react@19.2.3)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -29,10 +35,22 @@ importers:
react-dom:
specifier: 19.2.3
version: 19.2.3(react@19.2.3)
+ react-hook-form:
+ specifier: ^7.71.1
+ version: 7.71.1(react@19.2.3)
tailwind-merge:
specifier: ^3.5.0
version: 3.5.0
+ zod:
+ specifier: ^4.3.6
+ version: 4.3.6
+ zustand:
+ specifier: ^5.0.11
+ version: 5.0.11(@types/react@19.2.14)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
devDependencies:
+ '@biomejs/biome':
+ specifier: ^2.4.3
+ version: 2.4.3
'@tailwindcss/postcss':
specifier: ^4
version: 4.2.0
@@ -197,6 +215,63 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
+ '@biomejs/biome@2.4.3':
+ resolution: {integrity: sha512-cBrjf6PNF6yfL8+kcNl85AjiK2YHNsbU0EvDOwiZjBPbMbQ5QcgVGFpjD0O52p8nec5O8NYw7PKw3xUR7fPAkQ==}
+ engines: {node: '>=14.21.3'}
+ hasBin: true
+
+ '@biomejs/cli-darwin-arm64@2.4.3':
+ resolution: {integrity: sha512-eOafSFlI/CF4id2tlwq9CVHgeEqvTL5SrhWff6ZORp6S3NL65zdsR3ugybItkgF8Pf4D9GSgtbB6sE3UNgOM9w==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@biomejs/cli-darwin-x64@2.4.3':
+ resolution: {integrity: sha512-V2+av4ilbWcBMNufTtMMXVW00nPwyIjI5qf7n9wSvUaZ+tt0EvMGk46g9sAFDJBEDOzSyoRXiSP6pCvKTOEbPA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@biomejs/cli-linux-arm64-musl@2.4.3':
+ resolution: {integrity: sha512-QuFzvsGo8BA4Xm7jGX5idkw6BqFblcCPySMTvq0AhGYnhUej5VJIDJbmTKfHqwjHepZiC4fA+T5i6wmiZolZNw==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@biomejs/cli-linux-arm64@2.4.3':
+ resolution: {integrity: sha512-0m+O0x9FgK99FAwDK+fiDtjs2wnqq7bvfj17KJVeCkTwT/liI+Q9njJG7lwXK0iSJVXeFNRIxukpVI3SifMYAA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@biomejs/cli-linux-x64-musl@2.4.3':
+ resolution: {integrity: sha512-qEc0OCpj/uytruQ4wLM0yWNJLZy0Up8H1Er5MW3SrstqM6J2d4XqdNA86xzCy8MQCHpoVZ3lFye3GBlIL4/ljw==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@biomejs/cli-linux-x64@2.4.3':
+ resolution: {integrity: sha512-NVqh0saIU0u5OfOp/0jFdlKRE59+XyMvWmtx0f6Nm/2OpdxBl04coRIftBbY9d1gfu+23JVv4CItAqPYrjYh5w==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@biomejs/cli-win32-arm64@2.4.3':
+ resolution: {integrity: sha512-gRO96vrIARilv/Cp2ZnmNNL5LSZg3RO75GPp13hsLO3N4YVpE7saaMDp2bcyV48y2N2Pbit1brkGVGta0yd6VQ==}
+ engines: {node: '>=14.21.3'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@biomejs/cli-win32-x64@2.4.3':
+ resolution: {integrity: sha512-vSm/vOJe06pf14aGHfHl3Ar91Nlx4YYmohElDJ+17UbRwe99n987S/MhAlQOkONqf1utJor04ChkCPmKb8SWdw==}
+ engines: {node: '>=14.21.3'}
+ cpu: [x64]
+ os: [win32]
+
'@dotenvx/dotenvx@1.52.0':
resolution: {integrity: sha512-CaQcc8JvtzQhUSm9877b6V4Tb7HCotkcyud9X2YwdqtQKwgljkMRwU96fVYKnzN3V0Hj74oP7Es+vZ0mS+Aa1w==}
hasBin: true
@@ -231,6 +306,11 @@ packages:
peerDependencies:
hono: ^4
+ '@hookform/resolvers@5.2.2':
+ resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==}
+ peerDependencies:
+ react-hook-form: ^7.55.0
+
'@img/colour@1.0.0':
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
engines: {node: '>=18'}
@@ -1234,6 +1314,9 @@ packages:
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'}
+ '@standard-schema/utils@0.3.0':
+ resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
+
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@@ -1329,6 +1412,14 @@ packages:
'@tailwindcss/postcss@4.2.0':
resolution: {integrity: sha512-u6YBacGpOm/ixPfKqfgrJEjMfrYmPD7gEFRoygS/hnQaRtV0VCBdpkx5Ouw9pnaLRwwlgGCuJw8xLpaR0hOrQg==}
+ '@tanstack/query-core@5.90.20':
+ resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==}
+
+ '@tanstack/react-query@5.90.21':
+ resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==}
+ peerDependencies:
+ react: ^18 || ^19
+
'@ts-morph/common@0.27.0':
resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==}
@@ -2334,6 +2425,12 @@ packages:
peerDependencies:
react: ^19.2.3
+ react-hook-form@7.71.1:
+ resolution: {integrity: sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19
+
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
@@ -2724,6 +2821,27 @@ packages:
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+ zod@4.3.6:
+ resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
+
+ zustand@5.0.11:
+ resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
snapshots:
'@alloc/quick-lru@5.2.0': {}
@@ -2921,6 +3039,41 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
+ '@biomejs/biome@2.4.3':
+ optionalDependencies:
+ '@biomejs/cli-darwin-arm64': 2.4.3
+ '@biomejs/cli-darwin-x64': 2.4.3
+ '@biomejs/cli-linux-arm64': 2.4.3
+ '@biomejs/cli-linux-arm64-musl': 2.4.3
+ '@biomejs/cli-linux-x64': 2.4.3
+ '@biomejs/cli-linux-x64-musl': 2.4.3
+ '@biomejs/cli-win32-arm64': 2.4.3
+ '@biomejs/cli-win32-x64': 2.4.3
+
+ '@biomejs/cli-darwin-arm64@2.4.3':
+ optional: true
+
+ '@biomejs/cli-darwin-x64@2.4.3':
+ optional: true
+
+ '@biomejs/cli-linux-arm64-musl@2.4.3':
+ optional: true
+
+ '@biomejs/cli-linux-arm64@2.4.3':
+ optional: true
+
+ '@biomejs/cli-linux-x64-musl@2.4.3':
+ optional: true
+
+ '@biomejs/cli-linux-x64@2.4.3':
+ optional: true
+
+ '@biomejs/cli-win32-arm64@2.4.3':
+ optional: true
+
+ '@biomejs/cli-win32-x64@2.4.3':
+ optional: true
+
'@dotenvx/dotenvx@1.52.0':
dependencies:
commander: 11.1.0
@@ -2963,6 +3116,11 @@ snapshots:
dependencies:
hono: 4.12.0
+ '@hookform/resolvers@5.2.2(react-hook-form@7.71.1(react@19.2.3))':
+ dependencies:
+ '@standard-schema/utils': 0.3.0
+ react-hook-form: 7.71.1(react@19.2.3)
+
'@img/colour@1.0.0':
optional: true
@@ -3944,6 +4102,8 @@ snapshots:
'@sindresorhus/merge-streams@4.0.0': {}
+ '@standard-schema/utils@0.3.0': {}
+
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
@@ -4017,6 +4177,13 @@ snapshots:
postcss: 8.5.6
tailwindcss: 4.2.0
+ '@tanstack/query-core@5.90.20': {}
+
+ '@tanstack/react-query@5.90.21(react@19.2.3)':
+ dependencies:
+ '@tanstack/query-core': 5.90.20
+ react: 19.2.3
+
'@ts-morph/common@0.27.0':
dependencies:
fast-glob: 3.3.3
@@ -4957,6 +5124,10 @@ snapshots:
react: 19.2.3
scheduler: 0.27.0
+ react-hook-form@7.71.1(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+
react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.3):
dependencies:
react: 19.2.3
@@ -5376,3 +5547,11 @@ snapshots:
zod: 3.25.76
zod@3.25.76: {}
+
+ zod@4.3.6: {}
+
+ zustand@5.0.11(@types/react@19.2.14)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)):
+ optionalDependencies:
+ '@types/react': 19.2.14
+ react: 19.2.3
+ use-sync-external-store: 1.6.0(react@19.2.3)
diff --git a/postcss.config.mjs b/postcss.config.mjs
index 61e3684..2f8795a 100644
--- a/postcss.config.mjs
+++ b/postcss.config.mjs
@@ -2,6 +2,6 @@ const config = {
plugins: {
"@tailwindcss/postcss": {},
},
-};
+}
-export default config;
+export default config