From 51c9c4f126bbd64fa6c4734dde11b0ec57746644 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Sun, 23 Nov 2025 17:35:59 +0800 Subject: [PATCH] tambahan --- README.md | 150 +++++--- bin/env.generate.ts | 53 +++ bin/route.generate.ts | 260 ++++++++++++++ bun.lock | 196 ++++++----- generated/prisma/edge.js | 1 + generated/prisma/index.js | 1 + generated/prisma/wasm.js | 1 + package.json | 16 +- src/App.tsx | 28 +- src/AppRoutes.tsx | 43 +-- src/clientRoutes.ts | 6 +- src/components/ProtectedRoute.tsx | 26 +- src/index.tsx | 27 +- src/pages/Home.tsx | 52 ++- src/pages/Login.tsx | 124 +++---- src/pages/NotFound.tsx | 12 +- src/pages/dashboard/apikey/apikey_page.tsx | 384 +++++++++++---------- src/pages/dashboard/dashboard_layout.tsx | 343 +++++++++--------- src/pages/dashboard/dashboard_page.tsx | 211 ++++++----- 19 files changed, 1168 insertions(+), 766 deletions(-) create mode 100644 bin/env.generate.ts create mode 100644 bin/route.generate.ts diff --git a/README.md b/README.md index 7b33d55..e006e16 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,138 @@ -# Full-Stack Template: Bun, ElysiaJS, React, and Prisma +# Bun React Template Starter -This is a comprehensive full-stack application template built with a modern JavaScript toolchain. It provides a solid foundation for building fast, type-safe web applications. +This template is a starting point for building modern full-stack web applications using Bun, React, ElysiaJS, and Prisma. This project is designed to provide a fast, efficient, and structured development experience with a cutting-edge technology stack. -## โœจ Features & Tech Stack +## Key Features -- **Runtime & Bundler**: [Bun](https://bun.sh/) - An incredibly fast all-in-one JavaScript runtime. -- **Backend Framework**: [ElysiaJS](https://elysiajs.com/) - A fast, ergonomic, and type-safe backend framework for Bun. -- **Frontend Library**: [React](https://react.dev/) - A popular library for building user interfaces. -- **UI Components**: [Mantine](https://mantine.dev/) - A full-featured React component library. -- **Database ORM**: [Prisma](https://www.prisma.io/) - A next-generation Node.js and TypeScript ORM. -- **Type-Safe Client**: [Eden](https://elysiajs.com/plugins/eden.html) - Creates a type-safe client from your ElysiaJS API to be used in the frontend. -- **API Documentation**: [Swagger](https://elysiajs.com/plugins/swagger.html) - Automatically generated API documentation. -- **Authentication**: JWT-based authentication middleware is included. +- **Super-Fast Runtime**: Built on top of [Bun](https://bun.sh/), a high-performance JavaScript runtime. +- **End-to-End Typesafe Backend**: Utilizes [ElysiaJS](https://elysiajs.com/) for a type-safe API from the backend to the frontend. +- **Automatic API Documentation**: Comes with [Elysia Swagger](https://elysiajs.com/plugins/swagger) to automatically generate interactive API documentation. +- **Modern Frontend**: A feature-rich and customizable user interface using [React](https://react.dev/) and [Mantine UI](https://mantine.dev/). +- **Easy Database Access**: Integrated with [Prisma](https://www.prisma.io/) as an ORM for intuitive and secure database interactions. +- **Clear Project Structure**: Logical file and folder organization to facilitate easy navigation and development. -## ๐Ÿš€ Getting Started +## Tech Stack -### 1. Prerequisites +- **Runtime**: Bun +- **Backend**: + - **Framework**: ElysiaJS + - **ElysiaJS Modules**: + - `@elysiajs/cors`: Manages Cross-Origin Resource Sharing policies. + - `@elysiajs/jwt`: JSON Web Token-based authentication. + - `@elysiajs/swagger`: Creates API documentation (Swagger/OpenAPI). + - `@elysiajs/eden`: A typesafe RPC-like client to connect the frontend with the Elysia API. +- **Frontend**: + - **Library**: React + - **UI Framework**: Mantine + - **Routing**: React Router + - **Data Fetching**: SWR +- **Database**: + - **ORM**: Prisma + - **Supported Databases**: PostgreSQL (default), MySQL, SQLite, etc. +- **Language**: TypeScript -Ensure you have [Bun](https://bun.sh/docs/installation) installed on your system. +## Getting Started -### 2. Clone the Repository +### 1. Clone the Repository ```bash -git clone -cd bun-template +git clone https://github.com/your-username/bun-react-template-starter.git +cd bun-react-template-starter ``` -### 3. Install Dependencies +### 2. Install Dependencies + +Ensure you have [Bun](https://bun.sh/docs/installation) installed. Then, run the following command: ```bash bun install ``` -### 4. Set Up Environment Variables +### 3. Configure Environment Variables -Copy the example environment file and update it with your own configuration. +Copy the `.env.example` file to `.env` and customize the values. ```bash cp .env.example .env ``` -You will need to configure the following variables in the `.env` file: +Fill in your `.env` file similar to the example below: -- `DATABASE_URL`: Your PostgreSQL connection string. -- `JWT_SECRET`: A secret key for signing JWTs. -- `BUN_PUBLIC_BASE_URL`: The public base URL of your application (e.g., `http://localhost:3000`). -- `PORT`: The port the server will run on. +``` +DATABASE_URL="postgresql://user:password@host:port/database?schema=public" +JWT_SECRET=a_super_long_and_secure_secret +BUN_PUBLIC_BASE_URL=http://localhost:3000 +PORT=3000 +``` -### 5. Set Up the Database - -Run the Prisma migration to create the database schema. This will also apply any pending migrations. +After that, create TypeScript type declarations for your environment variables with the provided script: ```bash -bunx prisma migrate dev +bun run generate:env ``` -### 6. Seed the Database (Optional) +This command will generate a `types/env.d.ts` file based on your `.env`. -You can seed the database with initial data using the provided seed script. +### 4. Database Preparation + +Make sure your PostgreSQL database server is running. Then, apply the Prisma schema to your database: ```bash -bunx prisma db seed +bunx prisma db push ``` -## ๐Ÿ“œ Available Scripts +You can also seed the database with initial data using the following script: -- **`bun dev`**: Starts the development server for both the backend and frontend with hot-reloading. The server runs on the port specified in your `.env` file (defaults to 3000). +```bash +bun run seed +``` -- **`bun build`**: Builds the React frontend for production. The output is placed in the `dist/` directory. +### 5. Running the Development Server -- **`bun start`**: Starts the application in production mode. Make sure you have run `bun build` first. +```bash +bun run dev +``` -## ๐Ÿ“‚ Project Structure +The application will be running at `http://localhost:3000`. The server supports hot-reloading, so changes in the code will be reflected instantly without needing a manual restart. + +### 6. Accessing API Documentation (Swagger) + +Once the server is running, you can access the automatically generated API documentation at: + +`http://localhost:3000/swagger` + +## Available Scripts + +- `bun run dev`: Runs the development server with hot-reloading. +- `bun run build`: Builds the frontend application for production into the `dist` directory. +- `bun run start`: Runs the application in production mode. +- `bun run seed`: Executes the database seeding script located in `prisma/seed.ts`. +- `bun run generate:route`: A utility to create new route files in the backend. +- `bun run generate:env`: Generates a type definition file (`.d.ts`) from the variables in `.env`. + +## Project Structure ``` -. -โ”œโ”€โ”€ prisma/ # Prisma schema, migrations, and seed script -โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ components/ # Shared React components -โ”‚ โ”œโ”€โ”€ lib/ # Shared library functions (e.g., apiFetch) -โ”‚ โ”œโ”€โ”€ pages/ # React components for different pages/routes -โ”‚ โ”œโ”€โ”€ server/ # ElysiaJS backend code (routes, middlewares) -โ”‚ โ”œโ”€โ”€ App.tsx # Main React App component -โ”‚ โ”œโ”€โ”€ frontend.tsx # Frontend entry point (client-side) -โ”‚ โ”œโ”€โ”€ index.css # Global styles -โ”‚ โ”œโ”€โ”€ index.html # HTML template for the frontend -โ”‚ โ””โ”€โ”€ index.tsx # Main application entry point (server-side) -โ””โ”€โ”€ ... -``` \ No newline at end of file +/ +โ”œโ”€โ”€ bin/ # Utility scripts (generators) +โ”œโ”€โ”€ prisma/ # Database schema, migrations, and seed +โ”œโ”€โ”€ src/ # Main source code +โ”‚ โ”œโ”€โ”€ App.tsx # Root application component +โ”‚ โ”œโ”€โ”€ clientRoutes.ts # Route definitions for the frontend +โ”‚ โ”œโ”€โ”€ frontend.tsx # Entry point for client-side rendering (React) +โ”‚ โ”œโ”€โ”€ index.css # Global CSS file +โ”‚ โ”œโ”€โ”€ index.html # Main HTML template +โ”‚ โ”œโ”€โ”€ index.tsx # Main entry point for the app (server and client) +โ”‚ โ”œโ”€โ”€ components/ # Reusable React components +โ”‚ โ”œโ”€โ”€ lib/ # Shared libraries/helpers (e.g., apiFetch) +โ”‚ โ”œโ”€โ”€ pages/ # React page components +โ”‚ โ””โ”€โ”€ server/ # Backend code (ElysiaJS) +โ”‚ โ”œโ”€โ”€ lib/ # Server-specific libraries (e.g., prisma client) +โ”‚ โ”œโ”€โ”€ middlewares/ # Middleware for the API +โ”‚ โ””โ”€โ”€ routes/ # API route files +โ””โ”€โ”€ types/ # TypeScript type definitions +``` + +## Contributing + +Contributions are highly welcome! Please feel free to create a pull request to add features, fix bugs, or improve the documentation. diff --git a/bin/env.generate.ts b/bin/env.generate.ts new file mode 100644 index 0000000..f186f5b --- /dev/null +++ b/bin/env.generate.ts @@ -0,0 +1,53 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as dotenv from "dotenv"; + +interface GenerateEnvTypesOptions { + envFilePath?: string; + outputDir?: string; + outputFileName?: string; +} + +export function generateEnvTypes(options: GenerateEnvTypesOptions = {}) { + const { + envFilePath = path.resolve(process.cwd(), ".env"), + outputDir = path.resolve(process.cwd(), "types"), + outputFileName = "env.d.ts", + } = options; + + const outputFile = path.join(outputDir, outputFileName); + + // 1. Baca .env + if (!fs.existsSync(envFilePath)) { + console.warn(`โš ๏ธ .env file not found at: ${envFilePath}`); + return; + } + + const envContent = fs.readFileSync(envFilePath, "utf-8"); + const parsed = dotenv.parse(envContent); + + // 2. Generate TypeScript declare + const lines = Object.keys(parsed).map((key) => ` ${key}?: string;`); + + const fileContent = `declare namespace NodeJS { + interface ProcessEnv { +${lines.join("\n")} + } +} +`; + + // 3. Buat folder kalau belum ada + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + // 4. Tulis file + fs.writeFileSync(outputFile, fileContent, "utf-8"); + + console.log(`โœ… Env types generated at: ${outputFile}`); +} + +if (import.meta.main) { + generateEnvTypes(); +} + diff --git a/bin/route.generate.ts b/bin/route.generate.ts new file mode 100644 index 0000000..1391c98 --- /dev/null +++ b/bin/route.generate.ts @@ -0,0 +1,260 @@ +#!/usr/bin/env bun +import fs from "fs"; +import path from "path"; +import * as parser from "@babel/parser"; +import traverse from "@babel/traverse"; +import * as t from "@babel/types"; + +import { readdirSync, statSync, writeFileSync } from "fs"; +import _ from "lodash"; +import { basename, extname, join, relative } from "path"; + +const PAGES_DIR = join(process.cwd(), "src/pages"); +const OUTPUT_FILE = join(process.cwd(), "src/AppRoutes.tsx"); + +/** + * โœ… Ubah nama file menjadi PascalCase + * - Support: snake_case, kebab-case, camelCase, PascalCase + */ +const toComponentName = (fileName: string): string => { + return fileName + .replace(/\.[^/.]+$/, "") // hilangkan ekstensi file + .replace(/[_-]+/g, " ") // snake_case & kebab-case โ†’ spasi + .replace(/([a-z])([A-Z])/g, "$1 $2") // camelCase โ†’ spasi + .replace(/\b\w/g, (c) => c.toUpperCase()) // kapital tiap kata + .replace(/\s+/g, ""); // gabung semua โ†’ PascalCase +}; + +/** + * โœ… Normalisasi nama menjadi path route (kebab-case) + */ +function toRoutePath(name: string): string { + name = name.replace(/\.[^/.]+$/, ""); // hapus ekstensi + + if (name.toLowerCase() === "home") return "/"; + if (name.toLowerCase() === "login") return "/login"; + if (name.toLowerCase() === "notfound") return "/*"; + + // Hapus prefix/suffix umum + name = name.replace(/_page$/i, "").replace(/^form_/i, ""); + + // โœ… Normalisasi ke kebab-case + return _.kebabCase(name); +} + +// ๐Ÿงญ Scan folder pages secara rekursif +function scan(dir: string): any[] { + const items = readdirSync(dir); + const routes: any[] = []; + + for (const item of items) { + const full = join(dir, item); + const stat = statSync(full); + + if (stat.isDirectory()) { + routes.push({ + name: item, + path: _.kebabCase(item), + children: scan(full), + }); + } else if (extname(item) === ".tsx") { + routes.push({ + name: basename(item, ".tsx"), + filePath: relative(join(process.cwd(), "src"), full).replace(/\\/g, "/"), + }); + } + } + return routes; +} + +// ๐Ÿ—๏ธ Generate JSX dari struktur folder +function generateJSX(routes: any[], parentPath = ""): string { + let jsx = ""; + + for (const route of routes) { + if (route.children) { + const layout = route.children.find((r: any) => r.name.endsWith("_layout")); + + if (layout) { + const LayoutComponent = toComponentName(layout.name.replace("_layout", "Layout")); + const nested = route.children.filter((r: any) => r !== layout); + const nestedRoutes = generateJSX(nested, `${parentPath}/${route.path}`); + + const homeFile = route.children.find((r: any) => + r.name.toLowerCase().endsWith("_home") + ); + const indexRoute = homeFile + ? `} />\n` + : ""; + + jsx += ` + }> + ${indexRoute} + ${nestedRoutes} + + `; + } else { + jsx += generateJSX(route.children, `${parentPath}/${route.path}`); + } + } else { + const Component = toComponentName(route.name); + const routePath = toRoutePath(route.name); + + const fullPath = routePath.startsWith("/") + ? routePath + : `${parentPath}/${routePath}`.replace(/\/+/g, "/"); + + jsx += `} />\n`; + } + } + return jsx; +} + +// ๐Ÿงพ Generate import otomatis +function generateImports(routes: any[]): string { + const imports = new Set(); + + function collect(rs: any[]) { + for (const r of rs) { + if (r.children) collect(r.children); + else { + const Comp = toComponentName(r.name); + imports.add(`import ${Comp} from "./${r.filePath.replace(/\.tsx$/, "")}";`); + } + } + } + collect(routes); + return Array.from(imports).join("\n"); +} + +function generateRoutes() { + const allRoutes = scan(PAGES_DIR); + const imports = generateImports(allRoutes); + const jsxRoutes = generateJSX(allRoutes); + + const finalCode = ` +// โšก Auto-generated by generateRoutes.ts โ€” DO NOT EDIT MANUALLY +import { BrowserRouter, Routes, Route } from "react-router-dom"; +${imports} + +export default function AppRoutes() { + return ( + + + ${jsxRoutes} + + + ); +} +`; + + writeFileSync(OUTPUT_FILE, finalCode); + console.log(`โœ… Routes generated โ†’ ${OUTPUT_FILE}`); + Bun.spawnSync(["bunx", "prettier", "--write", "src/**/*.tsx"]); +} + +// --- Extract untuk clientRoutes.ts --- +const SRC_DIR = path.resolve(process.cwd(), "src"); +const APP_ROUTES_FILE = path.join(SRC_DIR, "AppRoutes.tsx"); + +interface RouteNode { + path: string; + children: RouteNode[]; +} + +function getAttributePath(attrs: (t.JSXAttribute | t.JSXSpreadAttribute)[]) { + const pathAttr = attrs.find( + (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: "path" }) + ) as t.JSXAttribute | undefined; + + if (pathAttr && t.isStringLiteral(pathAttr.value)) return pathAttr.value.value; + return ""; +} + +function extractRouteNodes(node: t.JSXElement): RouteNode | null { + const opening = node.openingElement; + if (!t.isJSXIdentifier(opening.name) || opening.name.name !== "Route") return null; + + const currentPath = getAttributePath(opening.attributes); + const children: RouteNode[] = []; + + for (const child of node.children) { + if (t.isJSXElement(child)) { + const childNode = extractRouteNodes(child); + if (childNode) children.push(childNode); + } + } + return { path: currentPath, children }; +} + +function flattenRoutes(node: RouteNode, parentPath = ""): Record { + const record: Record = {}; + let fullPath = node.path; + + if (fullPath) { + if (!fullPath.startsWith("/")) { + if (parentPath) { + if (fullPath === "/") fullPath = parentPath; + else fullPath = `${parentPath.replace(/\/$/, "")}/${fullPath}`; + } + if (!fullPath.startsWith("/")) fullPath = `/${fullPath}`; + } + fullPath = fullPath.replace(/\/+/g, "/"); + record[fullPath] = fullPath; + } + + for (const child of node.children) { + Object.assign(record, flattenRoutes(child, fullPath || parentPath)); + } + return record; +} + +function extractRoutes(code: string): Record { + const ast = parser.parse(code, { + sourceType: "module", + plugins: ["typescript", "jsx"], + }); + + const routes: Record = {}; + traverse(ast, { + JSXElement(path) { + const opening = path.node.openingElement; + if (t.isJSXIdentifier(opening.name) && opening.name.name === "Routes") { + for (const child of path.node.children) { + if (t.isJSXElement(child)) { + const node = extractRouteNodes(child); + if (node) Object.assign(routes, flattenRoutes(node)); + } + } + } + }, + }); + + return routes; +} + +export default function route() { + generateRoutes(); + + if (!fs.existsSync(APP_ROUTES_FILE)) { + console.error("โŒ AppRoutes.tsx not found in src/"); + process.exit(1); + } + + const code = fs.readFileSync(APP_ROUTES_FILE, "utf-8"); + const routes = extractRoutes(code); + + console.log("โœ… Generated Routes:"); + console.log(routes); + + const outPath = path.join(SRC_DIR, "clientRoutes.ts"); + fs.writeFileSync( + outPath, + `// AUTO-GENERATED FILE\nconst clientRoutes = ${JSON.stringify(routes, null, 2)} as const;\n\nexport default clientRoutes;` + ); + + console.log(`๐Ÿ“„ clientRoutes.ts saved โ†’ ${outPath}`); +} + +route() + diff --git a/bun.lock b/bun.lock index f15732a..7353428 100644 --- a/bun.lock +++ b/bun.lock @@ -14,18 +14,24 @@ "@mantine/notifications": "^8.3.8", "@prisma/client": "^6.19.0", "@tabler/icons-react": "^3.35.0", - "@types/jwt-decode": "^3.1.0", "add": "^2.0.6", "dotenv": "^17.2.3", "elysia": "^1.4.16", "jwt-decode": "^4.0.0", + "lodash": "^4.17.21", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router-dom": "^7.9.6", "swr": "^2.3.6", }, "devDependencies": { + "@babel/parser": "^7.28.5", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@types/babel__traverse": "^7.28.0", "@types/bun": "latest", + "@types/jwt-decode": "^3.1.0", + "@types/lodash": "^4.17.21", "@types/react": "^19.2.6", "@types/react-dom": "^19.2.3", "postcss": "^8.5.6", @@ -36,37 +42,63 @@ }, }, "packages": { - "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, ""], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], + + "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], - "@elysiajs/cors": ["@elysiajs/cors@1.4.0", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-pb0SCzBfFbFSYA/U40HHO7R+YrcXBJXOWgL20eSViK33ol1e20ru2/KUaZYo5IMUn63yaTJI/bQERuQ+77ND8g=="], + "@elysiajs/cors": ["@elysiajs/cors@1.4.0", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, ""], "@elysiajs/eden": ["@elysiajs/eden@1.4.5", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-hIOeH+S5NU/84A7+t8yB1JjxqjmzRkBF9fnLn6y+AH8EcF39KumOAnciMhIOkhhThVZvXZ3d+GsizRc+Fxoi8g=="], - "@elysiajs/jwt": ["@elysiajs/jwt@1.4.0", "", { "dependencies": { "jose": "^6.0.11" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-Z0PvZhQxdDeKZ8HslXzDoXXD83NKExNPmoiAPki3nI2Xvh5wtUrBH+zWOD17yP14IbRo8fxGj3L25MRCAPsgPA=="], + "@elysiajs/jwt": ["@elysiajs/jwt@1.4.0", "", { "dependencies": { "jose": "^6.0.11" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, ""], - "@elysiajs/swagger": ["@elysiajs/swagger@1.3.1", "", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-LcbLHa0zE6FJKWPWKsIC/f+62wbDv3aXydqcNPVPyqNcaUgwvCajIi+5kHEU6GO3oXUCpzKaMsb3gsjt8sLzFQ=="], + "@elysiajs/swagger": ["@elysiajs/swagger@1.3.1", "", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, ""], - "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, ""], - "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, ""], - "@floating-ui/react": ["@floating-ui/react@0.27.16", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.6", "@floating-ui/utils": "^0.2.10", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=17.0.0", "react-dom": ">=17.0.0" } }, "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g=="], + "@floating-ui/react": ["@floating-ui/react@0.27.16", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.6", "@floating-ui/utils": "^0.2.10", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=17.0.0", "react-dom": ">=17.0.0" } }, ""], - "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="], + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, ""], - "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, ""], - "@mantine/core": ["@mantine/core@8.3.8", "", { "dependencies": { "@floating-ui/react": "^0.27.16", "clsx": "^2.1.1", "react-number-format": "^5.4.4", "react-remove-scroll": "^2.7.1", "react-textarea-autosize": "8.5.9", "type-fest": "^4.41.0" }, "peerDependencies": { "@mantine/hooks": "8.3.8", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-UM3Za7Yl0FzbZ2zPgHwNyCpLgtSqkAi8ku13+gRS/6JB0FjwSkMwibERUqQIpwqAHdR5KNmIohjuqHu8guJowg=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], - "@mantine/hooks": ["@mantine/hooks@8.3.8", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-2YCUk5IWz+Ebi7VpbdscUz1MwulyaVPKr236ugMfpK0PFwsun4aBaLCAc8UeMGP0LtoSkuFvnsCPR4U6rhNfeQ=="], + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - "@mantine/modals": ["@mantine/modals@8.3.8", "", { "peerDependencies": { "@mantine/core": "8.3.8", "@mantine/hooks": "8.3.8", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-hcYXchS1Zrdwz5xRnEsFTPv6o/kNQbl/Ey0LBXvZCMn//2aq70IHTlEbtUUM2FMQNz3i/wzcpOqvhUU9mGZVJw=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@mantine/notifications": ["@mantine/notifications@8.3.8", "", { "dependencies": { "@mantine/store": "8.3.8", "react-transition-group": "4.4.5" }, "peerDependencies": { "@mantine/core": "8.3.8", "@mantine/hooks": "8.3.8", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-AS3UGnHO8UGzLpxe4cUIVpwpCoGKplWhMGm6E2hJoHnO4Wg0h3HlsR7drFEnDOZhaOMyD6MD9tAeWZ2/7rnvrw=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@mantine/store": ["@mantine/store@8.3.8", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-B6LEed839OR2t9pnC7Bl3zhMyYzUvJZ46YaOpH9zCqLiFX+u4FKC+UCNzqkz2a+I+olrNlONLnrCA0NDTCjz9A=="], + "@mantine/core": ["@mantine/core@8.3.9", "", { "dependencies": { "@floating-ui/react": "^0.27.16", "clsx": "^2.1.1", "react-number-format": "^5.4.4", "react-remove-scroll": "^2.7.1", "react-textarea-autosize": "8.5.9", "type-fest": "^4.41.0" }, "peerDependencies": { "@mantine/hooks": "8.3.9", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-ivj0Crn5N521cI2eWZBsBGckg0ZYRqfOJz5vbbvYmfj65bp0EdsyqZuOxXzIcn2aUScQhskfvzyhV5XIUv81PQ=="], + + "@mantine/hooks": ["@mantine/hooks@8.3.9", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-Dfz7W0+K1cq4Gb1WFQCZn8tsMXkLH6MV409wZR/ToqsxdNDUMJ/xxbfnwEXWEZjXNJd1wDETHgc+cZG2lTe3Xw=="], + + "@mantine/modals": ["@mantine/modals@8.3.9", "", { "peerDependencies": { "@mantine/core": "8.3.9", "@mantine/hooks": "8.3.9", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-0WOikHgECJeWA/1TNf+sxOnpNwQjmpyph3XEhzFkgneimW6Ry7R6qd/i345CDLSu6kP6FGGRI73SUROiTcu2Ng=="], + + "@mantine/notifications": ["@mantine/notifications@8.3.9", "", { "dependencies": { "@mantine/store": "8.3.9", "react-transition-group": "4.4.5" }, "peerDependencies": { "@mantine/core": "8.3.9", "@mantine/hooks": "8.3.9", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-emUdoCyaccf/NuNmJ4fQgloJ7hEod0Pde7XIoD9xUUztVchL143oWRU2gYm6cwqzSyjpjTaqPXfz5UvEBRYjZw=="], + + "@mantine/store": ["@mantine/store@8.3.9", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-Z4tYW597mD3NxHLlJ3OJ1aKucmwrD9nhqobz+142JNw01aHqzKjxVXlu3L5GGa7F3u3OjXJk/qb1QmUs4sU+Jw=="], "@prisma/client": ["@prisma/client@6.19.0", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g=="], @@ -82,13 +114,13 @@ "@prisma/get-platform": ["@prisma/get-platform@6.19.0", "", { "dependencies": { "@prisma/debug": "6.19.0" } }, "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA=="], - "@scalar/openapi-types": ["@scalar/openapi-types@0.1.1", "", {}, "sha512-NMy3QNk6ytcCoPUGJH0t4NNr36OWXgZhA3ormr3TvhX1NDgoF95wFyodGVH8xiHeUyn2/FxtETm8UBLbB5xEmg=="], + "@scalar/openapi-types": ["@scalar/openapi-types@0.1.1", "", {}, ""], - "@scalar/themes": ["@scalar/themes@0.9.86", "", { "dependencies": { "@scalar/types": "0.1.7" } }, "sha512-QUHo9g5oSWi+0Lm1vJY9TaMZRau8LHg+vte7q5BVTBnu6NuQfigCaN+ouQ73FqIVd96TwMO6Db+dilK1B+9row=="], + "@scalar/themes": ["@scalar/themes@0.9.86", "", { "dependencies": { "@scalar/types": "0.1.7" } }, ""], - "@scalar/types": ["@scalar/types@0.0.12", "", { "dependencies": { "@scalar/openapi-types": "0.1.1", "@unhead/schema": "^1.9.5" } }, "sha512-XYZ36lSEx87i4gDqopQlGCOkdIITHHEvgkuJFrXFATQs9zHARop0PN0g4RZYWj+ZpCUclOcaOjbCt8JGe22mnQ=="], + "@scalar/types": ["@scalar/types@0.0.12", "", { "dependencies": { "@scalar/openapi-types": "0.1.1", "@unhead/schema": "^1.9.5" } }, ""], - "@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="], + "@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, ""], "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], @@ -96,43 +128,47 @@ "@tabler/icons-react": ["@tabler/icons-react@3.35.0", "", { "dependencies": { "@tabler/icons": "3.35.0" }, "peerDependencies": { "react": ">= 16" } }, "sha512-XG7t2DYf3DyHT5jxFNp5xyLVbL4hMJYJhiSdHADzAjLRYfL7AnjlRfiHDHeXxkb2N103rEIvTsBRazxXtAUz2g=="], - "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="], + "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], - "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="], + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + + "@types/bun": ["@types/bun@1.2.23", "", { "dependencies": { "bun-types": "1.2.23" } }, "sha512-le8ueOY5b6VKYf19xT3McVbXqLqmxzPXHsQT/q9JHgikJ2X22wyTW3g3ohz2ZMnp7dod6aduIiq8A14Xyimm0A=="], "@types/jwt-decode": ["@types/jwt-decode@3.1.0", "", { "dependencies": { "jwt-decode": "*" } }, "sha512-tthwik7TKkou3mVnBnvVuHnHElbjtdbM63pdBCbZTirCt3WAdM73Y79mOri7+ljsS99ZVwUFZHLMxJuJnv/z1w=="], - "@types/node": ["@types/node@24.7.0", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw=="], + "@types/lodash": ["@types/lodash@4.17.21", "", {}, "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ=="], + + "@types/node": ["@types/node@24.7.0", "", { "dependencies": { "undici-types": "~7.14.0" } }, ""], "@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], - "@unhead/schema": ["@unhead/schema@1.11.20", "", { "dependencies": { "hookable": "^5.5.3", "zhead": "^2.2.4" } }, "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA=="], + "@unhead/schema": ["@unhead/schema@1.11.20", "", { "dependencies": { "hookable": "^5.5.3", "zhead": "^2.2.4" } }, ""], "add": ["add@2.0.6", "", {}, "sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q=="], - "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="], + "bun-types": ["bun-types@1.2.23", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, ""], "c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="], - "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], + "camelcase-css": ["camelcase-css@2.0.1", "", {}, ""], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "clsx": ["clsx@2.1.1", "", {}, ""], "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], - "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + "cookie": ["cookie@1.0.2", "", {}, ""], - "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + "cssesc": ["cssesc@3.0.0", "", { "bin": "bin/cssesc" }, ""], "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], @@ -146,7 +182,7 @@ "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], - "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + "detect-node-es": ["detect-node-es@1.1.0", "", {}, ""], "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], @@ -160,41 +196,43 @@ "exact-mirror": ["exact-mirror@0.2.3", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-aLdARfO0W0ntufjDyytUJQMbNXoB9g+BbA8KcgIq4XOOTYRw48yUGON/Pr64iDrYNZKcKvKbqE0MPW56FF2BXA=="], - "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], + "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], - "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], + "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, ""], - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, ""], - "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], + "file-type": ["file-type@21.1.1", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-ifJXo8zUqbQ/bLbl9sFoqHNTNWbnPY1COImFfM6CCy7z+E+jC1eY9YfOKkx0fckIg+VljAy2/87T61fp0+eEkg=="], - "file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="], - - "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + "get-nonce": ["get-nonce@1.0.1", "", {}, ""], "giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="], - "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], + "hookable": ["hookable@5.5.3", "", {}, ""], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - "jose": ["jose@6.1.0", "", {}, "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="], + "jose": ["jose@6.1.0", "", {}, ""], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="], + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, ""], "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], @@ -204,31 +242,31 @@ "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], - "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + "openapi-types": ["openapi-types@12.1.3", "", {}, ""], - "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + "pathe": ["pathe@1.1.2", "", {}, ""], "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "picocolors": ["picocolors@1.1.1", "", {}, ""], - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "picomatch": ["picomatch@4.0.3", "", {}, ""], "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, ""], - "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="], + "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, ""], - "postcss-mixins": ["postcss-mixins@12.1.2", "", { "dependencies": { "postcss-js": "^4.0.1", "postcss-simple-vars": "^7.0.1", "sugarss": "^5.0.0", "tinyglobby": "^0.2.14" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-90pSxmZVfbX9e5xCv7tI5RV1mnjdf16y89CJKbf/hD7GyOz1FCxcYMl8ZYA8Hc56dbApTKKmU9HfvgfWdCxlwg=="], + "postcss-mixins": ["postcss-mixins@12.1.2", "", { "dependencies": { "postcss-js": "^4.0.1", "postcss-simple-vars": "^7.0.1", "sugarss": "^5.0.0", "tinyglobby": "^0.2.14" }, "peerDependencies": { "postcss": "^8.2.14" } }, ""], - "postcss-nested": ["postcss-nested@7.0.2", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw=="], + "postcss-nested": ["postcss-nested@7.0.2", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "postcss": "^8.2.14" } }, ""], - "postcss-preset-mantine": ["postcss-preset-mantine@1.18.0", "", { "dependencies": { "postcss-mixins": "^12.0.0", "postcss-nested": "^7.0.2" }, "peerDependencies": { "postcss": ">=8.0.0" } }, "sha512-sP6/s1oC7cOtBdl4mw/IRKmKvYTuzpRrH/vT6v9enMU/EQEQ31eQnHcWtFghOXLH87AAthjL/Q75rLmin1oZoA=="], + "postcss-preset-mantine": ["postcss-preset-mantine@1.18.0", "", { "dependencies": { "postcss-mixins": "^12.0.0", "postcss-nested": "^7.0.2" }, "peerDependencies": { "postcss": ">=8.0.0" } }, ""], - "postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="], + "postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, ""], - "postcss-simple-vars": ["postcss-simple-vars@7.0.1", "", { "peerDependencies": { "postcss": "^8.2.1" } }, "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A=="], + "postcss-simple-vars": ["postcss-simple-vars@7.0.1", "", { "peerDependencies": { "postcss": "^8.2.1" } }, ""], "prisma": ["prisma@6.19.0", "", { "dependencies": { "@prisma/config": "6.19.0", "@prisma/engines": "6.19.0" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw=="], @@ -238,92 +276,90 @@ "rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], - "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], + "react": ["react@19.2.0", "", {}, ""], - "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], + "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, ""], "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "react-number-format": ["react-number-format@5.4.4", "", { "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA=="], + "react-number-format": ["react-number-format@5.4.4", "", { "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], - "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], - "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], "react-router": ["react-router@7.9.6", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA=="], "react-router-dom": ["react-router-dom@7.9.6", "", { "dependencies": { "react-router": "7.9.6" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA=="], - "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], - "react-textarea-autosize": ["react-textarea-autosize@8.5.9", "", { "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A=="], + "react-textarea-autosize": ["react-textarea-autosize@8.5.9", "", { "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "scheduler": ["scheduler@0.27.0", "", {}, ""], - "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, ""], - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, ""], "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], - "sugarss": ["sugarss@5.0.1", "", { "peerDependencies": { "postcss": "^8.3.3" } }, "sha512-ctS5RYCBVvPoZAnzIaX5QSShK8ZiZxD5HUqSxlusvEMC+QZQIPCPOIJg6aceFX+K2rf4+SH89eu++h1Zmsr2nw=="], + "sugarss": ["sugarss@5.0.1", "", { "peerDependencies": { "postcss": "^8.3.3" } }, ""], "swr": ["swr@2.3.6", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw=="], - "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], + "tabbable": ["tabbable@6.2.0", "", {}, ""], - "tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, ""], "token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tslib": ["tslib@2.8.1", "", {}, ""], - "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "type-fest": ["type-fest@4.41.0", "", {}, ""], "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], - "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "undici-types": ["undici-types@7.14.0", "", {}, ""], - "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], - "use-composed-ref": ["use-composed-ref@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w=="], + "use-composed-ref": ["use-composed-ref@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], - "use-isomorphic-layout-effect": ["use-isomorphic-layout-effect@1.2.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA=="], + "use-isomorphic-layout-effect": ["use-isomorphic-layout-effect@1.2.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], - "use-latest": ["use-latest@1.3.0", "", { "dependencies": { "use-isomorphic-layout-effect": "^1.1.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ=="], + "use-latest": ["use-latest@1.3.0", "", { "dependencies": { "use-isomorphic-layout-effect": "^1.1.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], - "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, ""], - "zhead": ["zhead@2.2.4", "", {}, "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="], + "zhead": ["zhead@2.2.4", "", {}, ""], - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod": ["zod@3.25.76", "", {}, ""], - "@scalar/themes/@scalar/types": ["@scalar/types@0.1.7", "", { "dependencies": { "@scalar/openapi-types": "0.2.0", "@unhead/schema": "^1.11.11", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "zod": "^3.23.8" } }, "sha512-irIDYzTQG2KLvFbuTI8k2Pz/R4JR+zUUSykVTbEMatkzMmVFnn1VzNSMlODbadycwZunbnL2tA27AXed9URVjw=="], + "@scalar/themes/@scalar/types": ["@scalar/types@0.1.7", "", { "dependencies": { "@scalar/openapi-types": "0.2.0", "@unhead/schema": "^1.11.11", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "zod": "^3.23.8" } }, ""], "c12/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "c12/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "dom-helpers/csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="], + "@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, ""], - "@scalar/themes/@scalar/types/nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], + "@scalar/themes/@scalar/types/nanoid": ["nanoid@5.1.6", "", { "bin": "bin/nanoid.js" }, ""], } } diff --git a/generated/prisma/edge.js b/generated/prisma/edge.js index b131095..b0f001a 100644 --- a/generated/prisma/edge.js +++ b/generated/prisma/edge.js @@ -172,6 +172,7 @@ const config = { "db" ], "activeProvider": "postgresql", + "postinstall": true, "inlineDatasources": { "db": { "url": { diff --git a/generated/prisma/index.js b/generated/prisma/index.js index 89a1ced..5441877 100644 --- a/generated/prisma/index.js +++ b/generated/prisma/index.js @@ -173,6 +173,7 @@ const config = { "db" ], "activeProvider": "postgresql", + "postinstall": true, "inlineDatasources": { "db": { "url": { diff --git a/generated/prisma/wasm.js b/generated/prisma/wasm.js index 22dea83..92f8394 100644 --- a/generated/prisma/wasm.js +++ b/generated/prisma/wasm.js @@ -172,6 +172,7 @@ const config = { "db" ], "activeProvider": "postgresql", + "postinstall": true, "inlineDatasources": { "db": { "url": { diff --git a/package.json b/package.json index f9a2350..e05dd78 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "bun-react-template", + "name": "bun-react-template-starter", "version": "0.1.0", "private": true, "type": "module", @@ -7,7 +7,9 @@ "dev": "bun --hot src/index.tsx", "build": "bun build ./src/index.html --outdir=dist --sourcemap --target=browser --minify --define:process.env.NODE_ENV='\"production\"' --env='BUN_PUBLIC_*'", "start": "NODE_ENV=production bun src/index.tsx", - "seed": "bun prisma/seed.ts" + "seed": "bun prisma/seed.ts", + "generate:route": "bun bin/route.generate.ts", + "generate:env": "bun bin/env.generate.ts" }, "dependencies": { "@elysiajs/cors": "^1.4.0", @@ -20,18 +22,24 @@ "@mantine/notifications": "^8.3.8", "@prisma/client": "^6.19.0", "@tabler/icons-react": "^3.35.0", - "@types/jwt-decode": "^3.1.0", "add": "^2.0.6", "dotenv": "^17.2.3", "elysia": "^1.4.16", "jwt-decode": "^4.0.0", + "lodash": "^4.17.21", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router-dom": "^7.9.6", "swr": "^2.3.6" }, "devDependencies": { + "@babel/parser": "^7.28.5", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@types/babel__traverse": "^7.28.0", "@types/bun": "latest", + "@types/jwt-decode": "^3.1.0", + "@types/lodash": "^4.17.21", "@types/react": "^19.2.6", "@types/react-dom": "^19.2.3", "postcss": "^8.5.6", @@ -39,4 +47,4 @@ "postcss-simple-vars": "^7.0.1", "prisma": "^6.19.0" } -} +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 93eacdb..9bf5309 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,17 +1,17 @@ - -import '@mantine/core/styles.css'; -import '@mantine/notifications/styles.css'; -import { Notifications } from '@mantine/notifications'; -import { ModalsProvider } from '@mantine/modals'; -import { MantineProvider } from '@mantine/core'; -import AppRoutes from './AppRoutes'; +import "@mantine/core/styles.css"; +import "@mantine/notifications/styles.css"; +import { Notifications } from "@mantine/notifications"; +import { ModalsProvider } from "@mantine/modals"; +import { MantineProvider } from "@mantine/core"; +import AppRoutes from "./AppRoutes"; export function App() { - return - - - - - ; + return ( + + + + + + + ); } - diff --git a/src/AppRoutes.tsx b/src/AppRoutes.tsx index 45bc1f4..10d75bc 100644 --- a/src/AppRoutes.tsx +++ b/src/AppRoutes.tsx @@ -1,32 +1,25 @@ - +// โšก Auto-generated by generateRoutes.ts โ€” DO NOT EDIT MANUALLY import { BrowserRouter, Routes, Route } from "react-router-dom"; -import Home from "./pages/Home"; -import NotFound from "./pages/NotFound"; import Login from "./pages/Login"; -import ProtectedRoute from "./components/ProtectedRoute"; -import Dashboard from "./pages/dashboard/dashboard_page"; +import Home from "./pages/Home"; +import ApikeyPage from "./pages/dashboard/apikey/apikey_page"; +import DashboardPage from "./pages/dashboard/dashboard_page"; import DashboardLayout from "./pages/dashboard/dashboard_layout"; -import ApiKeyPage from "./pages/dashboard/apikey/apikey_page"; +import NotFound from "./pages/NotFound"; export default function AppRoutes() { - return ( - - - } /> - } /> - }> - }> - } /> - } /> - } /> - - + return ( + + + } /> + } /> - } /> - - - ); + }> + } /> + } /> + + } /> + + + ); } - - - diff --git a/src/clientRoutes.ts b/src/clientRoutes.ts index cf19abe..b137a53 100644 --- a/src/clientRoutes.ts +++ b/src/clientRoutes.ts @@ -1,10 +1,10 @@ // AUTO-GENERATED FILE const clientRoutes = { - "/": "/", "/login": "/login", + "/": "/", "/dashboard": "/dashboard", - "/dashboard/landing": "/dashboard/landing", - "/dashboard/apikey": "/dashboard/apikey", + "/dashboard/apikey/apikey": "/dashboard/apikey/apikey", + "/dashboard/dashboard": "/dashboard/dashboard", "/*": "/*" } as const; diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx index 39ec40e..fcb5450 100644 --- a/src/components/ProtectedRoute.tsx +++ b/src/components/ProtectedRoute.tsx @@ -1,25 +1,25 @@ -import { useEffect, useState } from 'react' -import { Navigate, Outlet } from 'react-router-dom' -import clientRoutes from '@/clientRoutes' -import apiFetch from '@/lib/apiFetch' +import { useEffect, useState } from "react"; +import { Navigate, Outlet } from "react-router-dom"; +import clientRoutes from "@/clientRoutes"; +import apiFetch from "@/lib/apiFetch"; export default function ProtectedRoute() { - const [isAuthenticated, setIsAuthenticated] = useState(null) + const [isAuthenticated, setIsAuthenticated] = useState(null); useEffect(() => { async function checkSession() { try { // backend otomatis baca cookie JWT dari request - const res = await apiFetch.api.user.find.get() - setIsAuthenticated(res.status === 200) + const res = await apiFetch.api.user.find.get(); + setIsAuthenticated(res.status === 200); } catch { - setIsAuthenticated(false) + setIsAuthenticated(false); } } - checkSession() - }, []) + checkSession(); + }, []); - if (isAuthenticated === null) return null // or loading spinner - if (!isAuthenticated) return - return + if (isAuthenticated === null) return null; // or loading spinner + if (!isAuthenticated) return ; + return ; } diff --git a/src/index.tsx b/src/index.tsx index 78793a7..33be5b7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,27 +1,26 @@ - import Elysia, { t } from "elysia"; import Swagger from "@elysiajs/swagger"; -import html from "./index.html" +import html from "./index.html"; import Dashboard from "./server/routes/darmasaba"; import { apiAuth } from "./server/middlewares/apiAuth"; import Auth from "./server/routes/auth_route"; import ApiKeyRoute from "./server/routes/apikey_route"; import type { User } from "generated/prisma"; -const Docs = new Elysia() - .use(Swagger({ +const Docs = new Elysia().use( + Swagger({ path: "/docs", - })) + }), +); const ApiUser = new Elysia({ prefix: "/user", -}) - .get('/find', (ctx) => { - const { user } = ctx as any - return { - user: user as User - } - }) +}).get("/find", (ctx) => { + const { user } = ctx as any; + return { + user: user as User, + }; +}); const Api = new Elysia({ prefix: "/api", @@ -29,7 +28,7 @@ const Api = new Elysia({ .use(apiAuth) .use(ApiKeyRoute) .use(Dashboard) - .use(ApiUser) + .use(ApiUser); const app = new Elysia() .use(Api) @@ -40,6 +39,4 @@ const app = new Elysia() console.log("Server running at http://localhost:3000"); }); - export type ServerApp = typeof app; - diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 8f8f2f8..611301c 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -2,34 +2,30 @@ import clientRoutes from "@/clientRoutes"; import { Button, Card, Container, Group, Stack, Title } from "@mantine/core"; export default function Home() { - return ( - - - - - Home - + return ( + + + + + Home + - - + + - - - - - - ); + + + + + + ); } diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 1dfad59..6e4e502 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,71 +1,77 @@ -import { Button, Card, Container, Group, PasswordInput, Stack, Text, TextInput, Title } from "@mantine/core"; +import { + Button, + Card, + Container, + Group, + PasswordInput, + Stack, + Text, + TextInput, + Title, +} from "@mantine/core"; import { useState } from "react"; import apiFetch from "../lib/apiFetch"; export default function Login() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [loading, setLoading] = useState(false); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [loading, setLoading] = useState(false); - const handleSubmit = async () => { - setLoading(true); - try { - const response = await apiFetch.auth.login.post({ - email, - password, - }); + const handleSubmit = async () => { + setLoading(true); + try { + const response = await apiFetch.auth.login.post({ + email, + password, + }); - if (response.data?.token) { - localStorage.setItem('token', response.data.token); - window.location.href = '/dashboard'; - return; - } + if (response.data?.token) { + localStorage.setItem("token", response.data.token); + window.location.href = "/dashboard"; + return; + } - if (response.error) { - alert(JSON.stringify(response.error)); - } - } catch (error) { - console.error(error); - } finally { - setLoading(false); - } - }; + if (response.error) { + alert(JSON.stringify(response.error)); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false); + } + }; - return ( - - - - - Login - + return ( + + + + + Login + - setEmail(e.target.value)} - required - /> + setEmail(e.target.value)} + required + /> - setPassword(e.target.value)} - required - /> + setPassword(e.target.value)} + required + /> - - - - - - - ); + + + + + + + ); } diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx index 34feca8..e3ef82c 100644 --- a/src/pages/NotFound.tsx +++ b/src/pages/NotFound.tsx @@ -1,9 +1,7 @@ - export default function NotFound() { - return ( -
-

404 Not Found

-
- ); + return ( +
+

404 Not Found

+
+ ); } - diff --git a/src/pages/dashboard/apikey/apikey_page.tsx b/src/pages/dashboard/apikey/apikey_page.tsx index 3af0860..bf23c6f 100644 --- a/src/pages/dashboard/apikey/apikey_page.tsx +++ b/src/pages/dashboard/apikey/apikey_page.tsx @@ -1,15 +1,15 @@ import { - Button, - Card, - Container, - Group, - Stack, - Table, - Text, - TextInput, - Title, - Divider, - Loader, + Button, + Card, + Container, + Group, + Stack, + Table, + Text, + TextInput, + Title, + Divider, + Loader, } from "@mantine/core"; import { useEffect, useState } from "react"; import apiFetch from "@/lib/apiFetch"; @@ -18,207 +18,213 @@ import useSwr from "swr"; import { modals } from "@mantine/modals"; export default function ApiKeyPage() { - return ( - - - API Key Management - - - - - ); + return ( + + + API Key Management + + + + + ); } function CreateApiKey() { - const [name, setName] = useState(""); - const [description, setDescription] = useState(""); - const [expiredAt, setExpiredAt] = useState(""); - const [loading, setLoading] = useState(false); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [expiredAt, setExpiredAt] = useState(""); + const [loading, setLoading] = useState(false); - const handleSubmit = async () => { - try { - setLoading(true); + const handleSubmit = async () => { + try { + setLoading(true); - if (!name || !description || !expiredAt) { - showNotification({ - title: "Error", - message: "All fields are required", - color: "red", - }); - return; - } + if (!name || !description || !expiredAt) { + showNotification({ + title: "Error", + message: "All fields are required", + color: "red", + }); + return; + } - const res = await apiFetch.api.apikey.create.post({ - name, - description, - expiredAt, - }); + const res = await apiFetch.api.apikey.create.post({ + name, + description, + expiredAt, + }); - if (res.status === 200) { - setName(""); - setDescription(""); - setExpiredAt(""); + if (res.status === 200) { + setName(""); + setDescription(""); + setExpiredAt(""); - showNotification({ - title: "Success", - message: "API key created successfully", - color: "green", - }); - } + showNotification({ + title: "Success", + message: "API key created successfully", + color: "green", + }); + } - setLoading(false); - } catch (error) { - showNotification({ - title: "Error", - message: "Failed to create API key " + JSON.stringify(error), - color: "red", - }); - setLoading(false); - } finally { - setLoading(false); - } - }; + setLoading(false); + } catch (error) { + showNotification({ + title: "Error", + message: "Failed to create API key " + JSON.stringify(error), + color: "red", + }); + setLoading(false); + } finally { + setLoading(false); + } + }; - return ( - - - Create API Key + return ( + + + Create API Key - setName(e.target.value)} - required - /> + setName(e.target.value)} + required + /> - setDescription(e.target.value)} - /> + setDescription(e.target.value)} + /> - setExpiredAt(e.target.value)} - /> + setExpiredAt(e.target.value)} + /> - - + + - - - - - ); + + + + + ); } function ListApiKey() { - const { data, error, isLoading, mutate } = useSwr("/", () => apiFetch.api.apikey.list.get(), { - revalidateOnFocus: false, - revalidateOnReconnect: false, - revalidateIfStale: false, - refreshInterval: 3000, - }) - const apiKeys = data?.data?.apiKeys || [] + const { data, error, isLoading, mutate } = useSwr( + "/", + () => apiFetch.api.apikey.list.get(), + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + refreshInterval: 3000, + }, + ); + const apiKeys = data?.data?.apiKeys || []; - useEffect(() => { - mutate() - }, []); + useEffect(() => { + mutate(); + }, []); - if (error) return Error fetching API keys - if (isLoading) return + if (error) return Error fetching API keys; + if (isLoading) return ; - return ( - - - API Key List + return ( + + + API Key List - + - - - - Name - Description - Expired At - Created At - Actions - - +
+ + + Name + Description + Expired At + Created At + Actions + + - - {apiKeys.map((apiKey: any, index: number) => ( - - {apiKey.name} - {apiKey.description} - - {apiKey.expiredAt?.toISOString().split("T")[0]} - - - {apiKey.createdAt?.toISOString().split("T")[0]} - - - - + + {apiKeys.map((apiKey: any, index: number) => ( + + {apiKey.name} + {apiKey.description} + + {apiKey.expiredAt?.toISOString().split("T")[0]} + + + {apiKey.createdAt?.toISOString().split("T")[0]} + + + + - - - - - ))} - -
-
-
- ); + + + + + ))} + + +
+
+ ); } diff --git a/src/pages/dashboard/dashboard_layout.tsx b/src/pages/dashboard/dashboard_layout.tsx index 562c2ec..c074053 100644 --- a/src/pages/dashboard/dashboard_layout.tsx +++ b/src/pages/dashboard/dashboard_layout.tsx @@ -1,205 +1,204 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState } from "react"; import { - ActionIcon, - AppShell, - Avatar, - Button, - Card, - Divider, - Flex, - Group, - NavLink, - Paper, - ScrollArea, - Stack, - Text, - Title, - Tooltip -} from '@mantine/core' -import { useLocalStorage } from '@mantine/hooks' + ActionIcon, + AppShell, + Avatar, + Button, + Card, + Divider, + Flex, + Group, + NavLink, + Paper, + ScrollArea, + Stack, + Text, + Title, + Tooltip, +} from "@mantine/core"; +import { useLocalStorage } from "@mantine/hooks"; import { - IconChevronLeft, - IconChevronRight, - IconDashboard -} from '@tabler/icons-react' -import type { User } from 'generated/prisma' -import { Outlet, useLocation, useNavigate } from 'react-router-dom' - -import { default as clientRoute, default as clientRoutes } from '@/clientRoutes' -import apiFetch from '@/lib/apiFetch' + IconChevronLeft, + IconChevronRight, + IconDashboard, +} from "@tabler/icons-react"; +import type { User } from "generated/prisma"; +import { Outlet, useLocation, useNavigate } from "react-router-dom"; +import { + default as clientRoute, + default as clientRoutes, +} from "@/clientRoutes"; +import apiFetch from "@/lib/apiFetch"; /* ----------------------- Logout ----------------------- */ function Logout() { - return ( - - - - ) + return ( + + + + ); } - /* ----------------------- Layout ----------------------- */ export default function DashboardLayout() { - const [opened, setOpened] = useLocalStorage({ - key: 'nav_open', - defaultValue: true, - }) + const [opened, setOpened] = useLocalStorage({ + key: "nav_open", + defaultValue: true, + }); - return ( - - {/* NAVBAR */} - - {/* Collapse toggle */} - - - - setOpened(v => !v)} - radius="xl" - > - {opened ? : } - - - - + return ( + + {/* NAVBAR */} + + {/* Collapse toggle */} + + + + setOpened((v) => !v)} + radius="xl" + > + {opened ? : } + + + + - {/* Navigation */} - - - + {/* Navigation */} + + + - {/* User info */} - - - - + {/* User info */} + + + + - {/* MAIN CONTENT */} - - - - - {!opened && ( - - setOpened(true)} - radius="xl" - > - - - - )} + {/* MAIN CONTENT */} + + + + + {!opened && ( + + setOpened(true)} + radius="xl" + > + + + + )} - - App Dashboard - - - + + App Dashboard + + + - - - - - ) + + + + + ); } - /* ----------------------- Host Info ----------------------- */ function HostView() { - const [host, setHost] = useState(null) + const [host, setHost] = useState(null); - useEffect(() => { - async function fetchHost() { - const { data } = await apiFetch.api.user.find.get() - setHost(data?.user ?? null) - } - fetchHost() - }, []) + useEffect(() => { + async function fetchHost() { + const { data } = await apiFetch.api.user.find.get(); + setHost(data?.user ?? null); + } + fetchHost(); + }, []); - return ( - - {host ? ( - - - - {host.name?.[0]} - + return ( + + {host ? ( + + + + {host.name?.[0]} + - - {host.name} - {host.email} - - + + + {host.name} + + + {host.email} + + + - - - - ) : ( - - No host information available - - )} - - ) + + + + ) : ( + + No host information available + + )} + + ); } - /* ----------------------- Navigation ----------------------- */ function NavigationDashboard() { - const navigate = useNavigate() - const location = useLocation() + const navigate = useNavigate(); + const location = useLocation(); - const isActive = (path: keyof typeof clientRoute) => - location.pathname.startsWith(clientRoute[path]) + const isActive = (path: keyof typeof clientRoute) => + location.pathname.startsWith(clientRoute[path]); - return ( - - } - label="Dashboard Overview" - description="Quick summary and activity highlights" - onClick={() => navigate(clientRoutes['/dashboard/landing'])} - /> + return ( + + } + label="Dashboard Overview" + description="Quick summary and activity highlights" + onClick={() => navigate(clientRoutes["/dashboard/dashboard"])} + /> - } - label="API Keys" - description="Manage your API credentials" - onClick={() => navigate(clientRoutes['/dashboard/apikey'])} - /> - - ) + } + label="API Keys" + description="Manage your API credentials" + onClick={() => navigate(clientRoutes["/dashboard/apikey/apikey"])} + /> + + ); } diff --git a/src/pages/dashboard/dashboard_page.tsx b/src/pages/dashboard/dashboard_page.tsx index c2fe40a..6d46d10 100644 --- a/src/pages/dashboard/dashboard_page.tsx +++ b/src/pages/dashboard/dashboard_page.tsx @@ -1,121 +1,120 @@ import { - AppShell, - Group, - Text, - Button, - Card, - SimpleGrid, - Table, - Stack, - Title, - Avatar, - Divider, - Container, + AppShell, + Group, + Text, + Button, + Card, + SimpleGrid, + Table, + Stack, + Title, + Avatar, + Divider, + Container, } from "@mantine/core"; export default function Dashboard() { - return ( - - - {/* -------- STATS SECTION -------- */} - - - - Total Users - - 1,234 - + return ( + + + {/* -------- STATS SECTION -------- */} + + + + Total Users + + 1,234 + - - - Active Sessions - - 87 - + + + Active Sessions + + 87 + - - - API Calls today - - 12,490 - + + + API Calls today + + 12,490 + - - - Errors - - 5 - - + + + Errors + + 5 + + - {/* -------- QUICK ACTIONS -------- */} - - - Quick Actions - + {/* -------- QUICK ACTIONS -------- */} + + + Quick Actions + - - - - - - + + + + + + - {/* -------- ACTIVITY TABLE -------- */} - - - Recent Activity - + {/* -------- ACTIVITY TABLE -------- */} + + + Recent Activity + - - - - User - Action - Date - Status - - +
+ + + User + Action + Date + Status + + - - - John Doe - Generated new API key - 2025-01-21 - - - - + + + John Doe + Generated new API key + 2025-01-21 + + + + - - Ana Smith - Deleted session - 2025-01-20 - - - - + + Ana Smith + Deleted session + 2025-01-20 + + + + - - Michael - Failed login attempt - 2025-01-19 - - - - - -
-
-
-
-
- ); + + Michael + Failed login attempt + 2025-01-19 + + + + + + + + + + + ); } -