From 20d173b276d47fbb88e56d7e89dcf98606dc82e5 Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 14:19:55 +0300
Subject: [PATCH 01/10] Init frontend
---
attendance.db => backend/attendance.db | Bin
frontend/.cta.json | 13 +
frontend/.gitignore | 9 +
frontend/README.md | 290 ++++++++
frontend/bun.lock | 660 ++++++++++++++++++
frontend/index.html | 20 +
frontend/package.json | 35 +
frontend/public/favicon.ico | Bin 0 -> 3870 bytes
frontend/public/logo192.png | Bin 0 -> 5347 bytes
frontend/public/logo512.png | Bin 0 -> 9664 bytes
frontend/public/robots.txt | 3 +
frontend/src/components/Header.tsx | 17 +
.../integrations/tanstack-query/layout.tsx | 5 +
.../tanstack-query/root-provider.tsx | 15 +
frontend/src/logo.svg | 44 ++
frontend/src/main.tsx | 48 ++
frontend/src/reportWebVitals.ts | 13 +
frontend/src/routes/__root.tsx | 25 +
frontend/src/routes/demo.tanstack-query.tsx | 26 +
frontend/src/routes/index.tsx | 39 ++
frontend/src/styles.css | 15 +
frontend/tsconfig.json | 28 +
frontend/vite.config.ts | 24 +
23 files changed, 1329 insertions(+)
rename attendance.db => backend/attendance.db (100%)
create mode 100644 frontend/.cta.json
create mode 100644 frontend/.gitignore
create mode 100644 frontend/README.md
create mode 100644 frontend/bun.lock
create mode 100644 frontend/index.html
create mode 100644 frontend/package.json
create mode 100644 frontend/public/favicon.ico
create mode 100644 frontend/public/logo192.png
create mode 100644 frontend/public/logo512.png
create mode 100644 frontend/public/robots.txt
create mode 100644 frontend/src/components/Header.tsx
create mode 100644 frontend/src/integrations/tanstack-query/layout.tsx
create mode 100644 frontend/src/integrations/tanstack-query/root-provider.tsx
create mode 100644 frontend/src/logo.svg
create mode 100644 frontend/src/main.tsx
create mode 100644 frontend/src/reportWebVitals.ts
create mode 100644 frontend/src/routes/__root.tsx
create mode 100644 frontend/src/routes/demo.tanstack-query.tsx
create mode 100644 frontend/src/routes/index.tsx
create mode 100644 frontend/src/styles.css
create mode 100644 frontend/tsconfig.json
create mode 100644 frontend/vite.config.ts
diff --git a/attendance.db b/backend/attendance.db
similarity index 100%
rename from attendance.db
rename to backend/attendance.db
diff --git a/frontend/.cta.json b/frontend/.cta.json
new file mode 100644
index 0000000..9916813
--- /dev/null
+++ b/frontend/.cta.json
@@ -0,0 +1,13 @@
+{
+ "projectName": ".",
+ "mode": "file-router",
+ "typescript": true,
+ "tailwind": true,
+ "packageManager": "bun",
+ "git": true,
+ "version": 1,
+ "framework": "react-cra",
+ "chosenAddOns": [
+ "tanstack-query"
+ ]
+}
\ No newline at end of file
diff --git a/frontend/.gitignore b/frontend/.gitignore
new file mode 100644
index 0000000..693a705
--- /dev/null
+++ b/frontend/.gitignore
@@ -0,0 +1,9 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+count.txt
+.env
+.nitro
+.tanstack
diff --git a/frontend/README.md b/frontend/README.md
new file mode 100644
index 0000000..fbee20c
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,290 @@
+Welcome to your new TanStack app!
+
+# Getting Started
+
+To run this application:
+
+```bash
+bun install
+bunx --bun run start
+```
+
+# Building For Production
+
+To build this application for production:
+
+```bash
+bunx --bun run build
+```
+
+## Testing
+
+This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
+
+```bash
+bunx --bun run test
+```
+
+## Styling
+
+This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
+
+
+
+
+## Routing
+This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
+
+### Adding A Route
+
+To add a new route to your application just add another a new file in the `./src/routes` directory.
+
+TanStack will automatically generate the content of the route file for you.
+
+Now that you have two routes you can use a `Link` component to navigate between them.
+
+### Adding Links
+
+To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
+
+```tsx
+import { Link } from "@tanstack/react-router";
+```
+
+Then anywhere in your JSX you can use it like so:
+
+```tsx
+ About
+```
+
+This will create a link that will navigate to the `/about` route.
+
+More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
+
+### Using A Layout
+
+In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the ` ` component.
+
+Here is an example layout that includes a header:
+
+```tsx
+import { Outlet, createRootRoute } from '@tanstack/react-router'
+import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
+
+import { Link } from "@tanstack/react-router";
+
+export const Route = createRootRoute({
+ component: () => (
+ <>
+
+
+
+ >
+ ),
+})
+```
+
+The ` ` component is not required so you can remove it if you don't want it in your layout.
+
+More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
+
+
+## Data Fetching
+
+There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
+
+For example:
+
+```tsx
+const peopleRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: "/people",
+ loader: async () => {
+ const response = await fetch("https://swapi.dev/api/people");
+ return response.json() as Promise<{
+ results: {
+ name: string;
+ }[];
+ }>;
+ },
+ component: () => {
+ const data = peopleRoute.useLoaderData();
+ return (
+
+ {data.results.map((person) => (
+ {person.name}
+ ))}
+
+ );
+ },
+});
+```
+
+Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
+
+### React-Query
+
+React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.
+
+First add your dependencies:
+
+```bash
+bun install @tanstack/react-query @tanstack/react-query-devtools
+```
+
+Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`.
+
+```tsx
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+// ...
+
+const queryClient = new QueryClient();
+
+// ...
+
+if (!rootElement.innerHTML) {
+ const root = ReactDOM.createRoot(rootElement);
+
+ root.render(
+
+
+
+ );
+}
+```
+
+You can also add TanStack Query Devtools to the root route (optional).
+
+```tsx
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+
+const rootRoute = createRootRoute({
+ component: () => (
+ <>
+
+
+
+ >
+ ),
+});
+```
+
+Now you can use `useQuery` to fetch your data.
+
+```tsx
+import { useQuery } from "@tanstack/react-query";
+
+import "./App.css";
+
+function App() {
+ const { data } = useQuery({
+ queryKey: ["people"],
+ queryFn: () =>
+ fetch("https://swapi.dev/api/people")
+ .then((res) => res.json())
+ .then((data) => data.results as { name: string }[]),
+ initialData: [],
+ });
+
+ return (
+
+
+ {data.map((person) => (
+ {person.name}
+ ))}
+
+
+ );
+}
+
+export default App;
+```
+
+You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).
+
+## State Management
+
+Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.
+
+First you need to add TanStack Store as a dependency:
+
+```bash
+bun install @tanstack/store
+```
+
+Now let's create a simple counter in the `src/App.tsx` file as a demonstration.
+
+```tsx
+import { useStore } from "@tanstack/react-store";
+import { Store } from "@tanstack/store";
+import "./App.css";
+
+const countStore = new Store(0);
+
+function App() {
+ const count = useStore(countStore);
+ return (
+
+ countStore.setState((n) => n + 1)}>
+ Increment - {count}
+
+
+ );
+}
+
+export default App;
+```
+
+One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.
+
+Let's check this out by doubling the count using derived state.
+
+```tsx
+import { useStore } from "@tanstack/react-store";
+import { Store, Derived } from "@tanstack/store";
+import "./App.css";
+
+const countStore = new Store(0);
+
+const doubledStore = new Derived({
+ fn: () => countStore.state * 2,
+ deps: [countStore],
+});
+doubledStore.mount();
+
+function App() {
+ const count = useStore(countStore);
+ const doubledCount = useStore(doubledStore);
+
+ return (
+
+
countStore.setState((n) => n + 1)}>
+ Increment - {count}
+
+
Doubled - {doubledCount}
+
+ );
+}
+
+export default App;
+```
+
+We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.
+
+Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.
+
+You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).
+
+# Demo files
+
+Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
+
+# Learn More
+
+You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
diff --git a/frontend/bun.lock b/frontend/bun.lock
new file mode 100644
index 0000000..10d742a
--- /dev/null
+++ b/frontend/bun.lock
@@ -0,0 +1,660 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": ".",
+ "dependencies": {
+ "@tailwindcss/vite": "^4.0.6",
+ "@tanstack/react-query": "^5.66.5",
+ "@tanstack/react-query-devtools": "^5.66.5",
+ "@tanstack/react-router": "^1.121.2",
+ "@tanstack/react-router-devtools": "^1.121.2",
+ "@tanstack/router-plugin": "^1.121.2",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tailwindcss": "^4.0.6",
+ },
+ "devDependencies": {
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/react": "^16.2.0",
+ "@types/react": "^19.0.8",
+ "@types/react-dom": "^19.0.3",
+ "@vitejs/plugin-react": "^4.3.4",
+ "jsdom": "^26.0.0",
+ "typescript": "^5.7.2",
+ "vite": "^6.1.0",
+ "vitest": "^3.0.5",
+ "web-vitals": "^4.2.4",
+ },
+ },
+ },
+ "packages": {
+ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
+
+ "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="],
+
+ "@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/compat-data": ["@babel/compat-data@7.28.0", "", {}, "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw=="],
+
+ "@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
+
+ "@babel/generator": ["@babel/generator@7.28.0", "", { "dependencies": { "@babel/parser": "^7.28.0", "@babel/types": "^7.28.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg=="],
+
+ "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
+
+ "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+
+ "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.27.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A=="],
+
+ "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
+
+ "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA=="],
+
+ "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+
+ "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.27.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.27.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg=="],
+
+ "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
+
+ "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
+
+ "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
+
+ "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
+
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
+
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
+
+ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
+
+ "@babel/helpers": ["@babel/helpers@7.27.6", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.6" } }, "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug=="],
+
+ "@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="],
+
+ "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w=="],
+
+ "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ=="],
+
+ "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
+
+ "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
+
+ "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
+
+ "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg=="],
+
+ "@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="],
+
+ "@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="],
+
+ "@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.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/types": "^7.28.0", "debug": "^4.3.1" } }, "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg=="],
+
+ "@babel/types": ["@babel/types@7.28.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg=="],
+
+ "@csstools/color-helpers": ["@csstools/color-helpers@5.0.2", "", {}, "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="],
+
+ "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="],
+
+ "@csstools/css-color-parser": ["@csstools/css-color-parser@3.0.10", "", { "dependencies": { "@csstools/color-helpers": "^5.0.2", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg=="],
+
+ "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="],
+
+ "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="],
+
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="],
+
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="],
+
+ "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.4", "", {}, "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="],
+
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.19", "", {}, "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.1", "", { "os": "android", "cpu": "arm" }, "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.1", "", { "os": "android", "cpu": "arm64" }, "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g=="],
+
+ "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew=="],
+
+ "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.1", "", { "os": "win32", "cpu": "x64" }, "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug=="],
+
+ "@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
+
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
+
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
+
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
+
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
+
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
+
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
+
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
+
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
+
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
+
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
+
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
+
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
+
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
+
+ "@tailwindcss/vite": ["@tailwindcss/vite@4.1.11", "", { "dependencies": { "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "tailwindcss": "4.1.11" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw=="],
+
+ "@tanstack/history": ["@tanstack/history@1.121.34", "", {}, "sha512-YL8dGi5ZU+xvtav2boRlw4zrRghkY6hvdcmHhA0RGSJ/CBgzv+cbADW9eYJLx74XMZvIQ1pp6VMbrpXnnM5gHA=="],
+
+ "@tanstack/query-core": ["@tanstack/query-core@5.81.5", "", {}, "sha512-ZJOgCy/z2qpZXWaj/oxvodDx07XcQa9BF92c0oINjHkoqUPsmm3uG08HpTaviviZ/N9eP1f9CM7mKSEkIo7O1Q=="],
+
+ "@tanstack/query-devtools": ["@tanstack/query-devtools@5.81.2", "", {}, "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg=="],
+
+ "@tanstack/react-query": ["@tanstack/react-query@5.81.5", "", { "dependencies": { "@tanstack/query-core": "5.81.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-lOf2KqRRiYWpQT86eeeftAGnjuTR35myTP8MXyvHa81VlomoAWNEd8x5vkcAfQefu0qtYCvyqLropFZqgI2EQw=="],
+
+ "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.81.5", "", { "dependencies": { "@tanstack/query-devtools": "5.81.2" }, "peerDependencies": { "@tanstack/react-query": "^5.81.5", "react": "^18 || ^19" } }, "sha512-lCGMu4RX0uGnlrlLeSckBfnW/UV+KMlTBVqa97cwK7Z2ED5JKnZRSjNXwoma6sQBTJrcULvzgx2K6jEPvNUpDw=="],
+
+ "@tanstack/react-router": ["@tanstack/react-router@1.124.0", "", { "dependencies": { "@tanstack/history": "1.121.34", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.124.0", "isbot": "^5.1.22", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-jJxuLbPP/Cxirnft3CoiGWyH0aj94VTmLNcYauvjTGRNbUitK4udvGaHXVEP8bcifYvpko7ptsqqBlisaosugA=="],
+
+ "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.124.0", "", { "dependencies": { "@tanstack/router-devtools-core": "^1.124.0" }, "peerDependencies": { "@tanstack/react-router": "^1.124.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-CpOUUvtOYfLQEQS/ikGL9FQgEgYzBOKq9/2LqqFDXhZZgCVW18rBvR3LZeejkYSHAWlRphG33sdXCYVRM02sZQ=="],
+
+ "@tanstack/react-store": ["@tanstack/react-store@0.7.1", "", { "dependencies": { "@tanstack/store": "0.7.1", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-qUTEKdId6QPWGiWyKAPf/gkN29scEsz6EUSJ0C3HgLMgaqTAyBsQ2sMCfGVcqb+kkhEXAdjleCgH6LAPD6f2sA=="],
+
+ "@tanstack/router-core": ["@tanstack/router-core@1.124.0", "", { "dependencies": { "@tanstack/history": "1.121.34", "@tanstack/store": "^0.7.0", "cookie-es": "^1.2.2", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-mU2KA2v+ZFWC3NIjY2y+pPCx1sZDXPsUkzPjPPZxRgonE11nIu9MB89WuukqYuPbxoSWeodKNXsLe4KksGFCKA=="],
+
+ "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.124.0", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/router-core": "^1.124.0", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-F4xejY63XrQmQZ8q6IJmLHJGeow/5CzdCAWChYEHIFy9SWYiFMMvdGFpB8SReJuwld+eoHvvtp2qhUqNloqzRA=="],
+
+ "@tanstack/router-generator": ["@tanstack/router-generator@1.124.0", "", { "dependencies": { "@tanstack/router-core": "^1.124.0", "@tanstack/router-utils": "1.121.21", "@tanstack/virtual-file-routes": "^1.121.21", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-fatjfBvgLh7i2xcLKO3QaM5egHAhMy57B7DfE44sYx1D7/xxLOubSEjSnVSLE3dWBrstZ3aqyuYYhw7NuoXB7g=="],
+
+ "@tanstack/router-plugin": ["@tanstack/router-plugin@1.124.0", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-core": "^1.124.0", "@tanstack/router-generator": "1.124.0", "@tanstack/router-utils": "1.121.21", "@tanstack/virtual-file-routes": "^1.121.21", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.124.0", "vite": ">=5.0.0 || >=6.0.0", "vite-plugin-solid": "^2.11.2", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-CqV3PCVoMrHw0HyTioIGHTTjaMRgfwbW4ax2Pule++smyetn+3KPLV6C3VWc0vdukZMQz13JvLORSSeM0B2cYQ=="],
+
+ "@tanstack/router-utils": ["@tanstack/router-utils@1.121.21", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2" } }, "sha512-u7ubq1xPBtNiU7Fm+EOWlVWdgFLzuKOa1thhqdscVn8R4dNMUd1VoOjZ6AKmLw201VaUhFtlX+u0pjzI6szX7A=="],
+
+ "@tanstack/store": ["@tanstack/store@0.7.1", "", {}, "sha512-PjUQKXEXhLYj2X5/6c1Xn/0/qKY0IVFxTJweopRfF26xfjVyb14yALydJrHupDh3/d+1WKmfEgZPBVCmDkzzwg=="],
+
+ "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.121.21", "", {}, "sha512-3nuYsTyaq6ZN7jRZ9z6Gj3GXZqBOqOT0yzd/WZ33ZFfv4yVNIvsa5Lw+M1j3sgyEAxKMqGu/FaNi7FCjr3yOdw=="],
+
+ "@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="],
+
+ "@testing-library/react": ["@testing-library/react@16.3.0", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw=="],
+
+ "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
+
+ "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
+
+ "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
+
+ "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
+
+ "@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="],
+
+ "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
+
+ "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
+
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
+
+ "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="],
+
+ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.6.0", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.19", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" } }, "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ=="],
+
+ "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
+
+ "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="],
+
+ "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
+
+ "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="],
+
+ "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="],
+
+ "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
+
+ "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
+
+ "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+
+ "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
+
+ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "ansis": ["ansis@4.1.0", "", {}, "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w=="],
+
+ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
+
+ "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
+
+ "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
+
+ "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
+
+ "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.10", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="],
+
+ "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
+
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
+ "browserslist": ["browserslist@4.25.1", "", { "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw=="],
+
+ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
+
+ "caniuse-lite": ["caniuse-lite@1.0.30001726", "", {}, "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw=="],
+
+ "chai": ["chai@5.2.0", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw=="],
+
+ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+
+ "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
+
+ "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
+
+ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
+
+ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
+
+ "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="],
+
+ "cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="],
+
+ "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
+
+ "decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="],
+
+ "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
+
+ "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
+
+ "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
+
+ "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
+
+ "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
+
+ "electron-to-chromium": ["electron-to-chromium@1.5.178", "", {}, "sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
+
+ "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
+
+ "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
+
+ "esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
+
+ "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
+
+ "expect-type": ["expect-type@1.2.1", "", {}, "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw=="],
+
+ "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
+
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
+
+ "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
+
+ "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "goober": ["goober@2.1.16", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+
+ "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="],
+
+ "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
+
+ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
+
+ "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
+
+ "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
+
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
+ "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="],
+
+ "isbot": ["isbot@5.1.28", "", {}, "sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw=="],
+
+ "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
+
+ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
+ "jsdom": ["jsdom@26.1.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg=="],
+
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
+ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+
+ "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
+
+ "loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="],
+
+ "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+
+ "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],
+
+ "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
+
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
+
+ "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
+
+ "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=="],
+
+ "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
+
+ "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
+
+ "nwsapi": ["nwsapi@2.2.20", "", {}, "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA=="],
+
+ "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
+
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
+
+ "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
+
+ "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=="],
+
+ "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
+
+ "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
+
+ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+
+ "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
+
+ "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
+
+ "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
+
+ "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
+
+ "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
+
+ "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="],
+
+ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
+
+ "rollup": ["rollup@4.44.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.1", "@rollup/rollup-android-arm64": "4.44.1", "@rollup/rollup-darwin-arm64": "4.44.1", "@rollup/rollup-darwin-x64": "4.44.1", "@rollup/rollup-freebsd-arm64": "4.44.1", "@rollup/rollup-freebsd-x64": "4.44.1", "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", "@rollup/rollup-linux-arm-musleabihf": "4.44.1", "@rollup/rollup-linux-arm64-gnu": "4.44.1", "@rollup/rollup-linux-arm64-musl": "4.44.1", "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-musl": "4.44.1", "@rollup/rollup-linux-s390x-gnu": "4.44.1", "@rollup/rollup-linux-x64-gnu": "4.44.1", "@rollup/rollup-linux-x64-musl": "4.44.1", "@rollup/rollup-win32-arm64-msvc": "4.44.1", "@rollup/rollup-win32-ia32-msvc": "4.44.1", "@rollup/rollup-win32-x64-msvc": "4.44.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg=="],
+
+ "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
+
+ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
+
+ "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
+
+ "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
+
+ "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
+ "seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="],
+
+ "seroval-plugins": ["seroval-plugins@1.3.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ=="],
+
+ "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
+
+ "solid-js": ["solid-js@1.9.7", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw=="],
+
+ "source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
+
+ "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
+
+ "strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
+
+ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+
+ "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
+
+ "tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
+
+ "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
+
+ "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
+
+ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
+
+ "tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="],
+
+ "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
+
+ "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
+
+ "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
+
+ "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="],
+
+ "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
+
+ "tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="],
+
+ "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="],
+
+ "tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="],
+
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
+ "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="],
+
+ "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="],
+
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "tsx": ["tsx@4.20.3", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ=="],
+
+ "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
+
+ "unplugin": ["unplugin@2.3.5", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw=="],
+
+ "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
+
+ "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
+
+ "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
+
+ "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
+
+ "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
+
+ "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
+
+ "web-vitals": ["web-vitals@4.2.4", "", {}, "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="],
+
+ "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
+
+ "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
+
+ "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
+
+ "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
+
+ "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="],
+
+ "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
+
+ "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
+
+ "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="],
+
+ "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
+
+ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
+
+ "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],
+
+ "@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
+
+ "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
+
+ "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
+
+ "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
+ }
+}
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..e913164
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+ Create TanStack App - .
+
+
+
+
+
+
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000..f28294e
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,35 @@
+{
+ "name": ".",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port 3000",
+ "start": "vite --port 3000",
+ "build": "vite build && tsc",
+ "serve": "vite preview",
+ "test": "vitest run"
+ },
+ "dependencies": {
+ "@tailwindcss/vite": "^4.0.6",
+ "@tanstack/react-query": "^5.66.5",
+ "@tanstack/react-query-devtools": "^5.66.5",
+ "@tanstack/react-router": "^1.121.2",
+ "@tanstack/react-router-devtools": "^1.121.2",
+ "@tanstack/router-plugin": "^1.121.2",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tailwindcss": "^4.0.6"
+ },
+ "devDependencies": {
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/react": "^16.2.0",
+ "@types/react": "^19.0.8",
+ "@types/react-dom": "^19.0.3",
+ "@vitejs/plugin-react": "^4.3.4",
+ "jsdom": "^26.0.0",
+ "typescript": "^5.7.2",
+ "vite": "^6.1.0",
+ "vitest": "^3.0.5",
+ "web-vitals": "^4.2.4"
+ }
+}
\ No newline at end of file
diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a
GIT binary patch
literal 3870
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
baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@
literal 0
HcmV?d00001
diff --git a/frontend/public/logo192.png b/frontend/public/logo192.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9
GIT binary patch
literal 5347
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
b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN
literal 0
HcmV?d00001
diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/frontend/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
new file mode 100644
index 0000000..323584b
--- /dev/null
+++ b/frontend/src/components/Header.tsx
@@ -0,0 +1,17 @@
+import { Link } from '@tanstack/react-router'
+
+export default function Header() {
+ return (
+
+
+
+ Home
+
+
+
+ TanStack Query
+
+
+
+ )
+}
diff --git a/frontend/src/integrations/tanstack-query/layout.tsx b/frontend/src/integrations/tanstack-query/layout.tsx
new file mode 100644
index 0000000..3fef7cb
--- /dev/null
+++ b/frontend/src/integrations/tanstack-query/layout.tsx
@@ -0,0 +1,5 @@
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
+
+export default function LayoutAddition() {
+ return
+}
diff --git a/frontend/src/integrations/tanstack-query/root-provider.tsx b/frontend/src/integrations/tanstack-query/root-provider.tsx
new file mode 100644
index 0000000..00e048a
--- /dev/null
+++ b/frontend/src/integrations/tanstack-query/root-provider.tsx
@@ -0,0 +1,15 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+
+const queryClient = new QueryClient()
+
+export function getContext() {
+ return {
+ queryClient,
+ }
+}
+
+export function Provider({ children }: { children: React.ReactNode }) {
+ return (
+ {children}
+ )
+}
diff --git a/frontend/src/logo.svg b/frontend/src/logo.svg
new file mode 100644
index 0000000..d6c2da2
--- /dev/null
+++ b/frontend/src/logo.svg
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
new file mode 100644
index 0000000..b2cab02
--- /dev/null
+++ b/frontend/src/main.tsx
@@ -0,0 +1,48 @@
+import { StrictMode } from 'react'
+import ReactDOM from 'react-dom/client'
+import { RouterProvider, createRouter } from '@tanstack/react-router'
+
+import * as TanStackQueryProvider from './integrations/tanstack-query/root-provider.tsx'
+
+// Import the generated route tree
+import { routeTree } from './routeTree.gen'
+
+import './styles.css'
+import reportWebVitals from './reportWebVitals.ts'
+
+// Create a new router instance
+const router = createRouter({
+ routeTree,
+ context: {
+ ...TanStackQueryProvider.getContext(),
+ },
+ defaultPreload: 'intent',
+ scrollRestoration: true,
+ defaultStructuralSharing: true,
+ defaultPreloadStaleTime: 0,
+})
+
+// Register the router instance for type safety
+declare module '@tanstack/react-router' {
+ interface Register {
+ router: typeof router
+ }
+}
+
+// Render the app
+const rootElement = document.getElementById('app')
+if (rootElement && !rootElement.innerHTML) {
+ const root = ReactDOM.createRoot(rootElement)
+ root.render(
+
+
+
+
+ ,
+ )
+}
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals()
diff --git a/frontend/src/reportWebVitals.ts b/frontend/src/reportWebVitals.ts
new file mode 100644
index 0000000..16b66b5
--- /dev/null
+++ b/frontend/src/reportWebVitals.ts
@@ -0,0 +1,13 @@
+const reportWebVitals = (onPerfEntry?: () => void) => {
+ if (onPerfEntry && onPerfEntry instanceof Function) {
+ import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
+ onCLS(onPerfEntry)
+ onINP(onPerfEntry)
+ onFCP(onPerfEntry)
+ onLCP(onPerfEntry)
+ onTTFB(onPerfEntry)
+ })
+ }
+}
+
+export default reportWebVitals
diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx
new file mode 100644
index 0000000..41b5770
--- /dev/null
+++ b/frontend/src/routes/__root.tsx
@@ -0,0 +1,25 @@
+import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'
+import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
+
+import Header from '../components/Header'
+
+import TanStackQueryLayout from '../integrations/tanstack-query/layout.tsx'
+
+import type { QueryClient } from '@tanstack/react-query'
+
+interface MyRouterContext {
+ queryClient: QueryClient
+}
+
+export const Route = createRootRouteWithContext()({
+ component: () => (
+ <>
+
+
+
+
+
+
+ >
+ ),
+})
diff --git a/frontend/src/routes/demo.tanstack-query.tsx b/frontend/src/routes/demo.tanstack-query.tsx
new file mode 100644
index 0000000..f4e38f7
--- /dev/null
+++ b/frontend/src/routes/demo.tanstack-query.tsx
@@ -0,0 +1,26 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { useQuery } from '@tanstack/react-query'
+
+export const Route = createFileRoute('/demo/tanstack-query')({
+ component: TanStackQueryDemo,
+})
+
+function TanStackQueryDemo() {
+ const { data } = useQuery({
+ queryKey: ['people'],
+ queryFn: () =>
+ Promise.resolve([{ name: 'John Doe' }, { name: 'Jane Doe' }]),
+ initialData: [],
+ })
+
+ return (
+
+
People list
+
+ {data.map((person) => (
+ {person.name}
+ ))}
+
+
+ )
+}
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
new file mode 100644
index 0000000..b285335
--- /dev/null
+++ b/frontend/src/routes/index.tsx
@@ -0,0 +1,39 @@
+import { createFileRoute } from '@tanstack/react-router'
+import logo from '../logo.svg'
+
+export const Route = createFileRoute('/')({
+ component: App,
+})
+
+function App() {
+ return (
+
+ )
+}
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
new file mode 100644
index 0000000..89be609
--- /dev/null
+++ b/frontend/src/styles.css
@@ -0,0 +1,15 @@
+@import "tailwindcss";
+
+body {
+ @apply m-0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
+ "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
+ monospace;
+}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000..7920df9
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "include": ["**/*.ts", "**/*.tsx"],
+ "compilerOptions": {
+ "target": "ES2022",
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "types": ["vite/client"],
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ /* Linting */
+ "skipLibCheck": true,
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"],
+ }
+ }
+}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
new file mode 100644
index 0000000..140672e
--- /dev/null
+++ b/frontend/vite.config.ts
@@ -0,0 +1,24 @@
+import { defineConfig } from 'vite'
+import viteReact from '@vitejs/plugin-react'
+import tailwindcss from '@tailwindcss/vite'
+
+import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
+import { resolve } from 'node:path'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [
+ TanStackRouterVite({ autoCodeSplitting: true }),
+ viteReact(),
+ tailwindcss(),
+ ],
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ },
+ resolve: {
+ alias: {
+ '@': resolve(__dirname, './src'),
+ },
+ },
+})
From 9803b5b35d1200525d18cb4f62e9c677fce4f79c Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 14:51:00 +0300
Subject: [PATCH 02/10] Prettier / main page
---
frontend/.cta.json | 22 +--
frontend/.prettierrc.mjs | 9 +
frontend/README.md | 175 +++++++++---------
frontend/bun.lock | 1 +
frontend/index.html | 33 ++--
frontend/package.json | 69 +++----
frontend/src/components/Header.tsx | 22 +--
frontend/src/components/MainContainer.tsx | 3 +
.../integrations/tanstack-query/layout.tsx | 4 +-
.../tanstack-query/root-provider.tsx | 14 +-
frontend/src/main.tsx | 58 +++---
frontend/src/reportWebVitals.ts | 22 +--
frontend/src/routeTree.gen.ts | 59 ++++++
frontend/src/routes/__root.tsx | 30 +--
frontend/src/routes/demo.tanstack-query.tsx | 26 ---
frontend/src/routes/index.tsx | 45 +----
frontend/src/styles.css | 15 +-
frontend/tsconfig.json | 46 ++---
frontend/vite.config.ts | 34 ++--
19 files changed, 341 insertions(+), 346 deletions(-)
create mode 100644 frontend/.prettierrc.mjs
create mode 100644 frontend/src/components/MainContainer.tsx
create mode 100644 frontend/src/routeTree.gen.ts
delete mode 100644 frontend/src/routes/demo.tanstack-query.tsx
diff --git a/frontend/.cta.json b/frontend/.cta.json
index 9916813..5ce3585 100644
--- a/frontend/.cta.json
+++ b/frontend/.cta.json
@@ -1,13 +1,11 @@
{
- "projectName": ".",
- "mode": "file-router",
- "typescript": true,
- "tailwind": true,
- "packageManager": "bun",
- "git": true,
- "version": 1,
- "framework": "react-cra",
- "chosenAddOns": [
- "tanstack-query"
- ]
-}
\ No newline at end of file
+ "projectName": ".",
+ "mode": "file-router",
+ "typescript": true,
+ "tailwind": true,
+ "packageManager": "bun",
+ "git": true,
+ "version": 1,
+ "framework": "react-cra",
+ "chosenAddOns": ["tanstack-query"]
+}
diff --git a/frontend/.prettierrc.mjs b/frontend/.prettierrc.mjs
new file mode 100644
index 0000000..b949921
--- /dev/null
+++ b/frontend/.prettierrc.mjs
@@ -0,0 +1,9 @@
+const config = {
+ trailingComma: "es5",
+ printWidth: 120,
+ tabWidth: 4,
+ semi: true,
+ singleQuote: false,
+};
+
+export default config;
diff --git a/frontend/README.md b/frontend/README.md
index fbee20c..a10b1aa 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,4 +1,4 @@
-Welcome to your new TanStack app!
+Welcome to your new TanStack app!
# Getting Started
@@ -6,7 +6,7 @@ To run this application:
```bash
bun install
-bunx --bun run start
+bunx --bun run start
```
# Building For Production
@@ -29,10 +29,8 @@ bunx --bun run test
This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
-
-
-
## Routing
+
This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
### Adding A Route
@@ -68,32 +66,31 @@ In the File Based Routing setup the layout is located in `src/routes/__root.tsx`
Here is an example layout that includes a header:
```tsx
-import { Outlet, createRootRoute } from '@tanstack/react-router'
-import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
+import { Outlet, createRootRoute } from "@tanstack/react-router";
+import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import { Link } from "@tanstack/react-router";
export const Route = createRootRoute({
- component: () => (
- <>
-
-
-
- >
- ),
-})
+ component: () => (
+ <>
+
+
+
+ >
+ ),
+});
```
The ` ` component is not required so you can remove it if you don't want it in your layout.
More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
-
## Data Fetching
There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
@@ -102,26 +99,26 @@ For example:
```tsx
const peopleRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: "/people",
- loader: async () => {
- const response = await fetch("https://swapi.dev/api/people");
- return response.json() as Promise<{
- results: {
- name: string;
- }[];
- }>;
- },
- component: () => {
- const data = peopleRoute.useLoaderData();
- return (
-
- {data.results.map((person) => (
- {person.name}
- ))}
-
- );
- },
+ getParentRoute: () => rootRoute,
+ path: "/people",
+ loader: async () => {
+ const response = await fetch("https://swapi.dev/api/people");
+ return response.json() as Promise<{
+ results: {
+ name: string;
+ }[];
+ }>;
+ },
+ component: () => {
+ const data = peopleRoute.useLoaderData();
+ return (
+
+ {data.results.map((person) => (
+ {person.name}
+ ))}
+
+ );
+ },
});
```
@@ -149,13 +146,13 @@ const queryClient = new QueryClient();
// ...
if (!rootElement.innerHTML) {
- const root = ReactDOM.createRoot(rootElement);
+ const root = ReactDOM.createRoot(rootElement);
- root.render(
-
-
-
- );
+ root.render(
+
+
+
+ );
}
```
@@ -165,13 +162,13 @@ You can also add TanStack Query Devtools to the root route (optional).
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const rootRoute = createRootRoute({
- component: () => (
- <>
-
-
-
- >
- ),
+ component: () => (
+ <>
+
+
+
+ >
+ ),
});
```
@@ -183,24 +180,24 @@ import { useQuery } from "@tanstack/react-query";
import "./App.css";
function App() {
- const { data } = useQuery({
- queryKey: ["people"],
- queryFn: () =>
- fetch("https://swapi.dev/api/people")
- .then((res) => res.json())
- .then((data) => data.results as { name: string }[]),
- initialData: [],
- });
+ const { data } = useQuery({
+ queryKey: ["people"],
+ queryFn: () =>
+ fetch("https://swapi.dev/api/people")
+ .then((res) => res.json())
+ .then((data) => data.results as { name: string }[]),
+ initialData: [],
+ });
- return (
-
-
- {data.map((person) => (
- {person.name}
- ))}
-
-
- );
+ return (
+
+
+ {data.map((person) => (
+ {person.name}
+ ))}
+
+
+ );
}
export default App;
@@ -228,14 +225,12 @@ import "./App.css";
const countStore = new Store(0);
function App() {
- const count = useStore(countStore);
- return (
-
- countStore.setState((n) => n + 1)}>
- Increment - {count}
-
-
- );
+ const count = useStore(countStore);
+ return (
+
+ countStore.setState((n) => n + 1)}>Increment - {count}
+
+ );
}
export default App;
@@ -253,23 +248,21 @@ import "./App.css";
const countStore = new Store(0);
const doubledStore = new Derived({
- fn: () => countStore.state * 2,
- deps: [countStore],
+ fn: () => countStore.state * 2,
+ deps: [countStore],
});
doubledStore.mount();
function App() {
- const count = useStore(countStore);
- const doubledCount = useStore(doubledStore);
+ const count = useStore(countStore);
+ const doubledCount = useStore(doubledStore);
- return (
-
-
countStore.setState((n) => n + 1)}>
- Increment - {count}
-
-
Doubled - {doubledCount}
-
- );
+ return (
+
+
countStore.setState((n) => n + 1)}>Increment - {count}
+
Doubled - {doubledCount}
+
+ );
}
export default App;
diff --git a/frontend/bun.lock b/frontend/bun.lock
index 10d742a..a9446f6 100644
--- a/frontend/bun.lock
+++ b/frontend/bun.lock
@@ -21,6 +21,7 @@
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"jsdom": "^26.0.0",
+ "prettier": "^3.6.2",
"typescript": "^5.7.2",
"vite": "^6.1.0",
"vitest": "^3.0.5",
diff --git a/frontend/index.html b/frontend/index.html
index e913164..fc74934 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1,20 +1,17 @@
-
+
-
-
-
-
-
-
-
-
- Create TanStack App - .
-
-
-
-
-
+
+
+
+
+
+
+
+
+ Bolderjara Serviss
+
+
+
+
+
diff --git a/frontend/package.json b/frontend/package.json
index f28294e..dfa3767 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,35 +1,36 @@
{
- "name": ".",
- "private": true,
- "type": "module",
- "scripts": {
- "dev": "vite --port 3000",
- "start": "vite --port 3000",
- "build": "vite build && tsc",
- "serve": "vite preview",
- "test": "vitest run"
- },
- "dependencies": {
- "@tailwindcss/vite": "^4.0.6",
- "@tanstack/react-query": "^5.66.5",
- "@tanstack/react-query-devtools": "^5.66.5",
- "@tanstack/react-router": "^1.121.2",
- "@tanstack/react-router-devtools": "^1.121.2",
- "@tanstack/router-plugin": "^1.121.2",
- "react": "^19.0.0",
- "react-dom": "^19.0.0",
- "tailwindcss": "^4.0.6"
- },
- "devDependencies": {
- "@testing-library/dom": "^10.4.0",
- "@testing-library/react": "^16.2.0",
- "@types/react": "^19.0.8",
- "@types/react-dom": "^19.0.3",
- "@vitejs/plugin-react": "^4.3.4",
- "jsdom": "^26.0.0",
- "typescript": "^5.7.2",
- "vite": "^6.1.0",
- "vitest": "^3.0.5",
- "web-vitals": "^4.2.4"
- }
-}
\ No newline at end of file
+ "name": ".",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port 3000",
+ "start": "vite --port 3000",
+ "build": "vite build && tsc",
+ "serve": "vite preview",
+ "test": "vitest run"
+ },
+ "dependencies": {
+ "@tailwindcss/vite": "^4.0.6",
+ "@tanstack/react-query": "^5.66.5",
+ "@tanstack/react-query-devtools": "^5.66.5",
+ "@tanstack/react-router": "^1.121.2",
+ "@tanstack/react-router-devtools": "^1.121.2",
+ "@tanstack/router-plugin": "^1.121.2",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tailwindcss": "^4.0.6"
+ },
+ "devDependencies": {
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/react": "^16.2.0",
+ "@types/react": "^19.0.8",
+ "@types/react-dom": "^19.0.3",
+ "@vitejs/plugin-react": "^4.3.4",
+ "jsdom": "^26.0.0",
+ "prettier": "^3.6.2",
+ "typescript": "^5.7.2",
+ "vite": "^6.1.0",
+ "vitest": "^3.0.5",
+ "web-vitals": "^4.2.4"
+ }
+}
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
index 323584b..d5bb706 100644
--- a/frontend/src/components/Header.tsx
+++ b/frontend/src/components/Header.tsx
@@ -1,17 +1,11 @@
-import { Link } from '@tanstack/react-router'
+import { Link } from "@tanstack/react-router";
export default function Header() {
- return (
-
-
-
- Home
-
-
-
- TanStack Query
-
-
-
- )
+ return (
+
+ );
}
diff --git a/frontend/src/components/MainContainer.tsx b/frontend/src/components/MainContainer.tsx
new file mode 100644
index 0000000..f1f710a
--- /dev/null
+++ b/frontend/src/components/MainContainer.tsx
@@ -0,0 +1,3 @@
+export default function MainContainer(props: { children: React.ReactNode; className?: string }) {
+ return {props.children} ;
+}
diff --git a/frontend/src/integrations/tanstack-query/layout.tsx b/frontend/src/integrations/tanstack-query/layout.tsx
index 3fef7cb..02b7ead 100644
--- a/frontend/src/integrations/tanstack-query/layout.tsx
+++ b/frontend/src/integrations/tanstack-query/layout.tsx
@@ -1,5 +1,5 @@
-import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
export default function LayoutAddition() {
- return
+ return ;
}
diff --git a/frontend/src/integrations/tanstack-query/root-provider.tsx b/frontend/src/integrations/tanstack-query/root-provider.tsx
index 00e048a..2c8e02d 100644
--- a/frontend/src/integrations/tanstack-query/root-provider.tsx
+++ b/frontend/src/integrations/tanstack-query/root-provider.tsx
@@ -1,15 +1,13 @@
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-const queryClient = new QueryClient()
+const queryClient = new QueryClient();
export function getContext() {
- return {
- queryClient,
- }
+ return {
+ queryClient,
+ };
}
export function Provider({ children }: { children: React.ReactNode }) {
- return (
- {children}
- )
+ return {children} ;
}
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index b2cab02..1425c1b 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -1,48 +1,46 @@
-import { StrictMode } from 'react'
-import ReactDOM from 'react-dom/client'
-import { RouterProvider, createRouter } from '@tanstack/react-router'
+import { StrictMode } from "react";
+import ReactDOM from "react-dom/client";
+import { RouterProvider, createRouter } from "@tanstack/react-router";
-import * as TanStackQueryProvider from './integrations/tanstack-query/root-provider.tsx'
+import * as TanStackQueryProvider from "./integrations/tanstack-query/root-provider.tsx";
// Import the generated route tree
-import { routeTree } from './routeTree.gen'
+import { routeTree } from "./routeTree.gen";
-import './styles.css'
-import reportWebVitals from './reportWebVitals.ts'
+import "./styles.css";
+import reportWebVitals from "./reportWebVitals.ts";
// Create a new router instance
const router = createRouter({
- routeTree,
- context: {
- ...TanStackQueryProvider.getContext(),
- },
- defaultPreload: 'intent',
- scrollRestoration: true,
- defaultStructuralSharing: true,
- defaultPreloadStaleTime: 0,
-})
+ routeTree,
+ context: {
+ ...TanStackQueryProvider.getContext(),
+ },
+ defaultPreload: "intent",
+ scrollRestoration: true,
+ defaultStructuralSharing: true,
+ defaultPreloadStaleTime: 0,
+});
// Register the router instance for type safety
-declare module '@tanstack/react-router' {
- interface Register {
- router: typeof router
- }
+declare module "@tanstack/react-router" {
+ interface Register {
+ router: typeof router;
+ }
}
// Render the app
-const rootElement = document.getElementById('app')
+const rootElement = document.getElementById("app");
if (rootElement && !rootElement.innerHTML) {
- const root = ReactDOM.createRoot(rootElement)
- root.render(
-
-
-
-
- ,
- )
+ const root = ReactDOM.createRoot(rootElement);
+ root.render(
+
+
+
+ );
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals()
+reportWebVitals();
diff --git a/frontend/src/reportWebVitals.ts b/frontend/src/reportWebVitals.ts
index 16b66b5..7117685 100644
--- a/frontend/src/reportWebVitals.ts
+++ b/frontend/src/reportWebVitals.ts
@@ -1,13 +1,13 @@
const reportWebVitals = (onPerfEntry?: () => void) => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
- onCLS(onPerfEntry)
- onINP(onPerfEntry)
- onFCP(onPerfEntry)
- onLCP(onPerfEntry)
- onTTFB(onPerfEntry)
- })
- }
-}
+ if (onPerfEntry && onPerfEntry instanceof Function) {
+ import("web-vitals").then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
+ onCLS(onPerfEntry);
+ onINP(onPerfEntry);
+ onFCP(onPerfEntry);
+ onLCP(onPerfEntry);
+ onTTFB(onPerfEntry);
+ });
+ }
+};
-export default reportWebVitals
+export default reportWebVitals;
diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts
new file mode 100644
index 0000000..d204c26
--- /dev/null
+++ b/frontend/src/routeTree.gen.ts
@@ -0,0 +1,59 @@
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file was automatically generated by TanStack Router.
+// You should NOT make any changes in this file as it will be overwritten.
+// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
+
+import { Route as rootRouteImport } from './routes/__root'
+import { Route as IndexRouteImport } from './routes/index'
+
+const IndexRoute = IndexRouteImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRouteImport,
+} as any)
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+}
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+}
+export interface FileRoutesById {
+ __root__: typeof rootRouteImport
+ '/': typeof IndexRoute
+}
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/'
+ id: '__root__' | '/'
+ fileRoutesById: FileRoutesById
+}
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+}
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ }
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+}
+export const routeTree = rootRouteImport
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx
index 41b5770..cf7371f 100644
--- a/frontend/src/routes/__root.tsx
+++ b/frontend/src/routes/__root.tsx
@@ -1,25 +1,25 @@
-import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'
-import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
+import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
+import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
-import Header from '../components/Header'
+import Header from "../components/Header";
-import TanStackQueryLayout from '../integrations/tanstack-query/layout.tsx'
+import TanStackQueryLayout from "../integrations/tanstack-query/layout.tsx";
-import type { QueryClient } from '@tanstack/react-query'
+import type { QueryClient } from "@tanstack/react-query";
interface MyRouterContext {
- queryClient: QueryClient
+ queryClient: QueryClient;
}
export const Route = createRootRouteWithContext()({
- component: () => (
- <>
-
+ component: () => (
+ <>
+
-
-
+
+
-
- >
- ),
-})
+
+ >
+ ),
+});
diff --git a/frontend/src/routes/demo.tanstack-query.tsx b/frontend/src/routes/demo.tanstack-query.tsx
deleted file mode 100644
index f4e38f7..0000000
--- a/frontend/src/routes/demo.tanstack-query.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { createFileRoute } from '@tanstack/react-router'
-import { useQuery } from '@tanstack/react-query'
-
-export const Route = createFileRoute('/demo/tanstack-query')({
- component: TanStackQueryDemo,
-})
-
-function TanStackQueryDemo() {
- const { data } = useQuery({
- queryKey: ['people'],
- queryFn: () =>
- Promise.resolve([{ name: 'John Doe' }, { name: 'Jane Doe' }]),
- initialData: [],
- })
-
- return (
-
-
People list
-
- {data.map((person) => (
- {person.name}
- ))}
-
-
- )
-}
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
index b285335..3ee7dea 100644
--- a/frontend/src/routes/index.tsx
+++ b/frontend/src/routes/index.tsx
@@ -1,39 +1,14 @@
-import { createFileRoute } from '@tanstack/react-router'
-import logo from '../logo.svg'
+import MainContainer from "@/components/MainContainer";
+import { createFileRoute } from "@tanstack/react-router";
-export const Route = createFileRoute('/')({
- component: App,
-})
+export const Route = createFileRoute("/")({
+ component: App,
+});
function App() {
- return (
-
- )
+ return (
+
+ Darbinieki
+
+ );
}
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index 89be609..f6a3d12 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -1,15 +1,14 @@
@import "tailwindcss";
body {
- @apply m-0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
- "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
- sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
+ @apply m-0;
+ font-family:
+ -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
+ "Droid Sans", "Helvetica Neue", sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
- monospace;
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 7920df9..f9fc45c 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -1,28 +1,28 @@
{
- "include": ["**/*.ts", "**/*.tsx"],
- "compilerOptions": {
- "target": "ES2022",
- "jsx": "react-jsx",
- "module": "ESNext",
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
- "types": ["vite/client"],
+ "include": ["**/*.ts", "**/*.tsx"],
+ "compilerOptions": {
+ "target": "ES2022",
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "types": ["vite/client"],
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true,
- "noEmit": true,
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
- /* Linting */
- "skipLibCheck": true,
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true,
- "baseUrl": ".",
- "paths": {
- "@/*": ["./src/*"],
+ /* Linting */
+ "skipLibCheck": true,
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
}
- }
}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 140672e..2951b96 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,24 +1,20 @@
-import { defineConfig } from 'vite'
-import viteReact from '@vitejs/plugin-react'
-import tailwindcss from '@tailwindcss/vite'
+import { defineConfig } from "vite";
+import viteReact from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
-import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
-import { resolve } from 'node:path'
+import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
+import { resolve } from "node:path";
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [
- TanStackRouterVite({ autoCodeSplitting: true }),
- viteReact(),
- tailwindcss(),
- ],
- test: {
- globals: true,
- environment: 'jsdom',
- },
- resolve: {
- alias: {
- '@': resolve(__dirname, './src'),
+ plugins: [TanStackRouterVite({ autoCodeSplitting: true }), viteReact(), tailwindcss()],
+ test: {
+ globals: true,
+ environment: "jsdom",
},
- },
-})
+ resolve: {
+ alias: {
+ "@": resolve(__dirname, "./src"),
+ },
+ },
+});
From d72735e416946b84ff76d8d9c95fd48cbdfa347d Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 15:32:59 +0300
Subject: [PATCH 03/10] Fetched users with python
---
backend/.gitignore | 3 +++
backend/db/__init__.py | 0
backend/{ => db}/attendance.db | Bin
backend/db/db.py | 11 +++++++++++
backend/db/users.py | 9 +++++++++
backend/main.py | 10 ++++++++++
backend/requirements.txt | 13 +++++++++++++
7 files changed, 46 insertions(+)
create mode 100644 backend/.gitignore
create mode 100644 backend/db/__init__.py
rename backend/{ => db}/attendance.db (100%)
create mode 100644 backend/db/db.py
create mode 100644 backend/db/users.py
create mode 100644 backend/main.py
create mode 100644 backend/requirements.txt
diff --git a/backend/.gitignore b/backend/.gitignore
new file mode 100644
index 0000000..062e3f6
--- /dev/null
+++ b/backend/.gitignore
@@ -0,0 +1,3 @@
+.venv
+__pychace__/
+*.pyc
diff --git a/backend/db/__init__.py b/backend/db/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/attendance.db b/backend/db/attendance.db
similarity index 100%
rename from backend/attendance.db
rename to backend/db/attendance.db
diff --git a/backend/db/db.py b/backend/db/db.py
new file mode 100644
index 0000000..6cbf20d
--- /dev/null
+++ b/backend/db/db.py
@@ -0,0 +1,11 @@
+# This file is for database connection
+import sqlite3
+
+# Create the connection once, reuse it
+# Check same thread is false, because we fast api runs on multiple threads
+conn = sqlite3.connect("db/attendance.db", check_same_thread=False)
+conn.row_factory = sqlite3.Row
+
+def get_db():
+ return conn
+
diff --git a/backend/db/users.py b/backend/db/users.py
new file mode 100644
index 0000000..ffdef6e
--- /dev/null
+++ b/backend/db/users.py
@@ -0,0 +1,9 @@
+# This file is for database functions
+from db.db import get_db
+
+def get_users():
+ conn = get_db()
+ cursor = conn.cursor()
+ cursor.execute("SELECT * FROM Employees")
+ users = cursor.fetchall()
+ return users
diff --git a/backend/main.py b/backend/main.py
new file mode 100644
index 0000000..aa30b7b
--- /dev/null
+++ b/backend/main.py
@@ -0,0 +1,10 @@
+from fastapi import FastAPI
+import sqlite3
+from db.users import get_users
+
+# Get fastapi app
+app = FastAPI()
+
+@app.get("/")
+def list_users():
+ return get_users()
diff --git a/backend/requirements.txt b/backend/requirements.txt
new file mode 100644
index 0000000..bf73abe
--- /dev/null
+++ b/backend/requirements.txt
@@ -0,0 +1,13 @@
+annotated-types==0.7.0
+anyio==4.9.0
+click==8.2.1
+fastapi==0.115.14
+h11==0.16.0
+idna==3.10
+pydantic==2.11.7
+pydantic-core==2.33.2
+sniffio==1.3.1
+starlette==0.46.2
+typing-extensions==4.14.0
+typing-inspection==0.4.1
+uvicorn==0.35.0
From ebc0665468eba7f4a4674a46d0d7d0161163d3e0 Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 19:52:42 +0300
Subject: [PATCH 04/10] Finished backend
---
.gitignore | 1 +
backend/.gitignore | 2 +-
backend/db/users.py | 34 +++++++++++++++++++++++++++++++++-
backend/dev.sh | 2 ++
backend/main.py | 6 +++++-
backend/run.sh | 4 ++++
6 files changed, 46 insertions(+), 3 deletions(-)
create mode 100644 .gitignore
create mode 100755 backend/dev.sh
create mode 100755 backend/run.sh
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/backend/.gitignore b/backend/.gitignore
index 062e3f6..bff427b 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -1,3 +1,3 @@
.venv
-__pychace__/
+__pychace__
*.pyc
diff --git a/backend/db/users.py b/backend/db/users.py
index ffdef6e..a8eb4d4 100644
--- a/backend/db/users.py
+++ b/backend/db/users.py
@@ -1,9 +1,41 @@
# This file is for database functions
from db.db import get_db
+conn = get_db()
+
def get_users():
- conn = get_db()
cursor = conn.cursor()
cursor.execute("SELECT * FROM Employees")
users = cursor.fetchall()
return users
+
+def get_attendance(id):
+ # Set up query for worked hours, group by and ordered by date
+ cursor = conn.cursor()
+ cursor.execute("""
+ SELECT date, sum(hours_worked) as hours_worked, e.username
+ FROM Attendance a
+ JOIN Employees e ON a.employee_id = e.id
+ WHERE a.employee_id = ?
+ GROUP BY date
+ ORDER BY date DESC
+ """, (id,))
+ data = cursor.fetchall()
+
+ # Check if there's any attendance
+ if not data:
+ return {"username": None, "attendance": []}
+
+ # Remove repetetive username, and return clean api response
+ username = data[0]["username"] # get username from first row
+ attendance = [
+ {"date": row["date"], "hours_worked": row["hours_worked"]}
+ for row in data
+ ]
+
+ # Return clean api response
+ return {
+ "username": username,
+ "attendance": attendance
+ }
+
diff --git a/backend/dev.sh b/backend/dev.sh
new file mode 100755
index 0000000..061ca9f
--- /dev/null
+++ b/backend/dev.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+uvicorn main:app --reload
diff --git a/backend/main.py b/backend/main.py
index aa30b7b..5666f10 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -1,6 +1,6 @@
from fastapi import FastAPI
import sqlite3
-from db.users import get_users
+from db.users import get_users, get_attendance
# Get fastapi app
app = FastAPI()
@@ -8,3 +8,7 @@ app = FastAPI()
@app.get("/")
def list_users():
return get_users()
+
+@app.get("/{eployee_id}")
+def list_attendance(eployee_id: int):
+ return get_attendance(eployee_id)
diff --git a/backend/run.sh b/backend/run.sh
new file mode 100755
index 0000000..11d9f8c
--- /dev/null
+++ b/backend/run.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# Run the server
+uvicorn main:app --port 8000
From 95afdcb36452ba0938a942df93297ecfccd8467b Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 21:18:08 +0300
Subject: [PATCH 05/10] Main page / CORS
---
backend/main.py | 10 +++++
frontend/index.html | 2 +-
frontend/src/components/Header.tsx | 4 +-
frontend/src/components/MainContainer.tsx | 2 +-
frontend/src/components/UsersTable.tsx | 53 +++++++++++++++++++++++
frontend/src/consts.ts | 1 +
frontend/src/routes/index.tsx | 4 +-
frontend/src/styles.css | 6 +++
frontend/src/types/api.ts | 3 ++
9 files changed, 80 insertions(+), 5 deletions(-)
create mode 100644 frontend/src/components/UsersTable.tsx
create mode 100644 frontend/src/consts.ts
create mode 100644 frontend/src/types/api.ts
diff --git a/backend/main.py b/backend/main.py
index 5666f10..bb0e077 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -1,10 +1,20 @@
from fastapi import FastAPI
import sqlite3
from db.users import get_users, get_attendance
+from fastapi.middleware.cors import CORSMiddleware
# Get fastapi app
app = FastAPI()
+# Add cors headers to the app
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"],
+ allow_credentials=True,
+ allow_methods=["GET"],
+ allow_headers=["*"],
+)
+
@app.get("/")
def list_users():
return get_users()
diff --git a/frontend/index.html b/frontend/index.html
index fc74934..fe26a89 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -10,7 +10,7 @@
Bolderjara Serviss
-
+
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
index d5bb706..65b1db9 100644
--- a/frontend/src/components/Header.tsx
+++ b/frontend/src/components/Header.tsx
@@ -2,8 +2,8 @@ import { Link } from "@tanstack/react-router";
export default function Header() {
return (
-
-
+
diff --git a/frontend/src/components/MainContainer.tsx b/frontend/src/components/MainContainer.tsx
index f1f710a..a00444f 100644
--- a/frontend/src/components/MainContainer.tsx
+++ b/frontend/src/components/MainContainer.tsx
@@ -1,3 +1,3 @@
export default function MainContainer(props: { children: React.ReactNode; className?: string }) {
- return {props.children} ;
+ return {props.children} ;
}
diff --git a/frontend/src/components/UsersTable.tsx b/frontend/src/components/UsersTable.tsx
new file mode 100644
index 0000000..2335e9c
--- /dev/null
+++ b/frontend/src/components/UsersTable.tsx
@@ -0,0 +1,53 @@
+import { API_BASE_URL } from "@/consts";
+import type { User } from "@/types/api";
+import { useQuery, type UseQueryResult } from "@tanstack/react-query";
+import { useEffect, useState } from "react";
+
+function RenderList({ list }: { list: UseQueryResult }) {
+ // Base classes for each row
+ const baseRowClass = "text-xl p-2 px-4 rounded capitalize bg-panel/75";
+
+ // Display loading and error states
+ if (list.isLoading) return Loading...
;
+ if (list.isError) return Error: {list.error.message}
;
+
+ // Render all users to the list
+ return list.data?.map((u) => {
+ return (
+ {u.username}
+ );
+ });
+}
+
+export default function UsersTable() {
+ // UseState for search input
+ const [search, setSearch] = useState(""); // For input control
+ const [debouncedSearch, setDebouncedSearch] = useState(search); // For when user stops typing, do refetching
+
+ // Build search query url
+ const fetchUsersURL = new URL(`${API_BASE_URL}/`);
+ if (search) fetchUsersURL.searchParams.set("s", search);
+
+ // Using tanstack query to fetch, and automatically cache the data
+ const fetchUsers = () => fetch(fetchUsersURL).then((res) => res.json());
+ const users = useQuery({ queryKey: ["users", debouncedSearch], queryFn: fetchUsers });
+
+ // Use effect is being used to make refetch after user has stopped typing in the search bar
+ useEffect(() => {
+ const timeout = setTimeout(() => setDebouncedSearch(search), 500);
+ return () => clearTimeout(timeout); // Clear timeout
+ }, [search]);
+
+ return (
+
+
setSearch(e.target.value)}
+ />
+
+
+ );
+}
diff --git a/frontend/src/consts.ts b/frontend/src/consts.ts
new file mode 100644
index 0000000..0ba142d
--- /dev/null
+++ b/frontend/src/consts.ts
@@ -0,0 +1 @@
+export const API_BASE_URL = "http://127.0.0.1:8000";
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
index 3ee7dea..c73e939 100644
--- a/frontend/src/routes/index.tsx
+++ b/frontend/src/routes/index.tsx
@@ -1,5 +1,6 @@
import MainContainer from "@/components/MainContainer";
import { createFileRoute } from "@tanstack/react-router";
+import UsersTable from "@/components/UsersTable";
export const Route = createFileRoute("/")({
component: App,
@@ -8,7 +9,8 @@ export const Route = createFileRoute("/")({
function App() {
return (
- Darbinieki
+ Darbinieki
+
);
}
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index f6a3d12..2deb03f 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -1,5 +1,11 @@
@import "tailwindcss";
+@theme {
+ --color-accent: #123ffb;
+ --color-body: #dfe1e5;
+ --color-panel: white;
+}
+
body {
@apply m-0;
font-family:
diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts
new file mode 100644
index 0000000..cc5b6fd
--- /dev/null
+++ b/frontend/src/types/api.ts
@@ -0,0 +1,3 @@
+export interface User {
+ username: string;
+}
From acb81d671d676429fee2412880d62bfdcb6c0a1d Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 21:30:53 +0300
Subject: [PATCH 06/10] Implemented search funcionality
---
backend/db/users.py | 16 ++++++++++++++--
backend/main.py | 4 ++--
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/backend/db/users.py b/backend/db/users.py
index a8eb4d4..ed6bad5 100644
--- a/backend/db/users.py
+++ b/backend/db/users.py
@@ -3,9 +3,21 @@ from db.db import get_db
conn = get_db()
-def get_users():
+def get_users(search):
+ # Get search param
+ search_param = f"%{search}%"
+ print(search_param)
+
+ # Generate cursor
cursor = conn.cursor()
- cursor.execute("SELECT * FROM Employees")
+
+ # Search if needed
+ if search:
+ cursor.execute("SELECT * FROM Employees WHERE username LIKE ?", (search_param,))
+ else:
+ cursor.execute("SELECT * FROM Employees")
+
+ # Fetch users from database
users = cursor.fetchall()
return users
diff --git a/backend/main.py b/backend/main.py
index bb0e077..f1aaa17 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -16,8 +16,8 @@ app.add_middleware(
)
@app.get("/")
-def list_users():
- return get_users()
+def list_users(s: str = None):
+ return get_users(s)
@app.get("/{eployee_id}")
def list_attendance(eployee_id: int):
From 46dd731eab29675aa3500c5a20d95bb28c9f5527 Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 22:25:03 +0300
Subject: [PATCH 07/10] I think finished?
---
backend/db/attendance.db | Bin 16384 -> 16384 bytes
backend/db/users.py | 28 ++++----
backend/main.py | 6 +-
frontend/src/components/UsersTable.tsx | 15 +++--
frontend/src/routeTree.gen.ts | 24 ++++++-
frontend/src/routes/$userId.tsx | 67 ++++++++++++++++++++
frontend/src/routes/index.tsx | 2 +-
frontend/src/types/api.ts | 11 ++++
frontend/src/utils/capitalizeFirstLetter.ts | 3 +
9 files changed, 129 insertions(+), 27 deletions(-)
create mode 100644 frontend/src/routes/$userId.tsx
create mode 100644 frontend/src/utils/capitalizeFirstLetter.ts
diff --git a/backend/db/attendance.db b/backend/db/attendance.db
index 80d95877cac10d46ec161eb10ee3a92aa9f254c7..bc8167ea9091567645756056af9ea4e43c3f676a 100644
GIT binary patch
delta 133
zcmV;00DAv`fB}Gj0gxL31(6&>1qA>ua-y+hpbriO56}P)_7Ck3;}6;o(6bQ`z7Glr
z1p@#VY-Mk5bFs5fBs)6A=mp00AKZ0x~c%G%YYPEio`flS4nfqvIj=
delta 98
zcmV-o0GE50gVb
Ey?@CZA^-pY
diff --git a/backend/db/users.py b/backend/db/users.py
index ed6bad5..2b364dc 100644
--- a/backend/db/users.py
+++ b/backend/db/users.py
@@ -6,7 +6,6 @@ conn = get_db()
def get_users(search):
# Get search param
search_param = f"%{search}%"
- print(search_param)
# Generate cursor
cursor = conn.cursor()
@@ -25,29 +24,26 @@ def get_attendance(id):
# Set up query for worked hours, group by and ordered by date
cursor = conn.cursor()
cursor.execute("""
- SELECT date, sum(hours_worked) as hours_worked, e.username
- FROM Attendance a
- JOIN Employees e ON a.employee_id = e.id
- WHERE a.employee_id = ?
+ SELECT date, sum(hours_worked) as hours_worked
+ FROM Attendance
+ WHERE employee_id = ?
GROUP BY date
ORDER BY date DESC
""", (id,))
- data = cursor.fetchall()
+ attendance = cursor.fetchall()
- # Check if there's any attendance
- if not data:
+ # Fetch username from database
+ # I am not using table JOIN because when theres no attendance, the query returns no rows (and shows user doesnt exist)
+ cursor.execute("SELECT username FROM Employees WHERE id = ?", (id,))
+ username = cursor.fetchone()
+
+ # Check if user exists
+ if not username:
return {"username": None, "attendance": []}
- # Remove repetetive username, and return clean api response
- username = data[0]["username"] # get username from first row
- attendance = [
- {"date": row["date"], "hours_worked": row["hours_worked"]}
- for row in data
- ]
-
# Return clean api response
return {
- "username": username,
+ "username": username[0],
"attendance": attendance
}
diff --git a/backend/main.py b/backend/main.py
index f1aaa17..74ba38d 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -19,6 +19,6 @@ app.add_middleware(
def list_users(s: str = None):
return get_users(s)
-@app.get("/{eployee_id}")
-def list_attendance(eployee_id: int):
- return get_attendance(eployee_id)
+@app.get("/{employee_id}")
+def list_attendance(employee_id: int):
+ return get_attendance(employee_id)
diff --git a/frontend/src/components/UsersTable.tsx b/frontend/src/components/UsersTable.tsx
index 2335e9c..4172ce0 100644
--- a/frontend/src/components/UsersTable.tsx
+++ b/frontend/src/components/UsersTable.tsx
@@ -1,11 +1,12 @@
import { API_BASE_URL } from "@/consts";
import type { User } from "@/types/api";
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
+import { Link } from "@tanstack/react-router";
import { useEffect, useState } from "react";
function RenderList({ list }: { list: UseQueryResult }) {
// Base classes for each row
- const baseRowClass = "text-xl p-2 px-4 rounded capitalize bg-panel/75";
+ const baseRowClass = "flex text-xl p-2 px-4 rounded capitalize bg-panel/75";
// Display loading and error states
if (list.isLoading) return Loading...
;
@@ -14,7 +15,13 @@ function RenderList({ list }: { list: UseQueryResult }) {
// Render all users to the list
return list.data?.map((u) => {
return (
- {u.username}
+
+ {u.username}
+
);
});
}
@@ -45,9 +52,9 @@ export default function UsersTable() {
className="bg-panel/75 transition focus:bg-panel border border-transparent outline-none focus:border-accent rounded px-4 py-2 mt-8 w-full "
onChange={(e) => setSearch(e.target.value)}
/>
-
+
-
+
);
}
diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts
index d204c26..0ca4b88 100644
--- a/frontend/src/routeTree.gen.ts
+++ b/frontend/src/routeTree.gen.ts
@@ -9,8 +9,14 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
+import { Route as UserIdRouteImport } from './routes/$userId'
import { Route as IndexRouteImport } from './routes/index'
+const UserIdRoute = UserIdRouteImport.update({
+ id: '/$userId',
+ path: '/$userId',
+ getParentRoute: () => rootRouteImport,
+} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
@@ -19,28 +25,39 @@ const IndexRoute = IndexRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
+ '/$userId': typeof UserIdRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
+ '/$userId': typeof UserIdRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
+ '/$userId': typeof UserIdRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
- fullPaths: '/'
+ fullPaths: '/' | '/$userId'
fileRoutesByTo: FileRoutesByTo
- to: '/'
- id: '__root__' | '/'
+ to: '/' | '/$userId'
+ id: '__root__' | '/' | '/$userId'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
+ UserIdRoute: typeof UserIdRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
+ '/$userId': {
+ id: '/$userId'
+ path: '/$userId'
+ fullPath: '/$userId'
+ preLoaderRoute: typeof UserIdRouteImport
+ parentRoute: typeof rootRouteImport
+ }
'/': {
id: '/'
path: '/'
@@ -53,6 +70,7 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
+ UserIdRoute: UserIdRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
diff --git a/frontend/src/routes/$userId.tsx b/frontend/src/routes/$userId.tsx
new file mode 100644
index 0000000..b027e62
--- /dev/null
+++ b/frontend/src/routes/$userId.tsx
@@ -0,0 +1,67 @@
+import MainContainer from "@/components/MainContainer";
+import { API_BASE_URL } from "@/consts";
+import type { UserInfo } from "@/types/api";
+import { capitalizeFirstLetter } from "@/utils/capitalizeFirstLetter";
+import { useQuery, type UseQueryResult } from "@tanstack/react-query";
+import { createFileRoute, useParams } from "@tanstack/react-router";
+
+export const Route = createFileRoute("/$userId")({
+ component: RouteComponent,
+});
+
+function DisplayUserName({ info }: { info: UseQueryResult }) {
+ // Base classes for headers
+ const baseClasses = "text-4xl font-bold text-center";
+
+ // Display loading and error states
+ if (info.isLoading) return Loading user info ... ;
+ if (info.isError) return Error: {info.error.message} ;
+
+ // Check if user is null
+ if (!info.data?.username) return User not found ;
+
+ return {capitalizeFirstLetter(info.data.username)} ;
+}
+
+function DisplayUserTable({ info }: { info: UseQueryResult }) {
+ // Base classes for headers
+ const baseClasses = "p-2 text-center";
+
+ if (!info.data) return; // return nothing when no data
+ if (!info.data.username) return; // Return nothing when user is null
+ if (info.data.attendance.length === 0) return No attendance
; // Empty attendance, return msg
+
+ // Display attendance in a table
+ return (
+
+
+ Date
+ Hours worked
+
+
+ {info.data.attendance.map((a) => (
+
+ {a.date}
+ {a.hours_worked}
+
+ ))}
+
+
+ );
+}
+
+function RouteComponent() {
+ const { userId } = Route.useParams();
+
+ // Define url for user info fetch
+ const userInfoURL = new URL(`${API_BASE_URL}/${userId}`);
+ const fetchUserInfo = () => fetch(userInfoURL).then((res) => res.json());
+ const userInfo = useQuery({ queryKey: ["user", userId], queryFn: fetchUserInfo });
+
+ return (
+
+
+
+
+ );
+}
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
index c73e939..21203a8 100644
--- a/frontend/src/routes/index.tsx
+++ b/frontend/src/routes/index.tsx
@@ -9,7 +9,7 @@ export const Route = createFileRoute("/")({
function App() {
return (
- Darbinieki
+ Employees
);
diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts
index cc5b6fd..365dfa7 100644
--- a/frontend/src/types/api.ts
+++ b/frontend/src/types/api.ts
@@ -1,3 +1,14 @@
export interface User {
+ id: number;
username: string;
}
+
+export interface Attendance {
+ date: string;
+ hours_worked: number;
+}
+
+export interface UserInfo {
+ username: string | null;
+ attendance: Attendance[];
+}
diff --git a/frontend/src/utils/capitalizeFirstLetter.ts b/frontend/src/utils/capitalizeFirstLetter.ts
new file mode 100644
index 0000000..fea7a6b
--- /dev/null
+++ b/frontend/src/utils/capitalizeFirstLetter.ts
@@ -0,0 +1,3 @@
+export function capitalizeFirstLetter(val: string) {
+ return String(val).charAt(0).toUpperCase() + String(val).slice(1);
+}
From d3274924f04f9170d4f14ac19aba29e105db8a96 Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 22:29:55 +0300
Subject: [PATCH 08/10] Cleaned up code
---
README.md | 55 --------------------------------
Uzdevums.md | 55 ++++++++++++++++++++++++++++++++
frontend/src/routes/$userId.tsx | 2 +-
frontend/src/routes/__root.tsx | 8 -----
preview 1.webp | Bin 0 -> 7836 bytes
preview 2.webp | Bin 0 -> 5314 bytes
6 files changed, 56 insertions(+), 64 deletions(-)
create mode 100644 Uzdevums.md
create mode 100644 preview 1.webp
create mode 100644 preview 2.webp
diff --git a/README.md b/README.md
index 985de08..e69de29 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +0,0 @@
-# Darbinieku darba laiku atskaite no SQLite datubāzes
-
-## Resursi
-
-Tev tiek dots **SQLite datubāzes fails**: `attendance.db`
-Šajā datubāzē ir divas tabulas:
-
-### Tabulu struktūra
-
-**`Employees`**
-
-| id | username |
-|:----|:-----------|
-| 1 | janis |
-| 2 | liga |
-| 3 | juris |
-| 4 | dace |
-
-**`Attendance`**
-
-| id | employee_id | date | hours_worked |
-|:----|:-------------|:-------------|:----------------|
-| 1 | 1 | 2024-06-29 | 4 |
-| 2 | 1 | 2024-06-29 | 3.5 |
-| 3 | 1 | 2024-06-28 | 8 |
-| 4 | 2 | 2024-06-29 | 5 |
-| 5 | 2 | 2024-06-29 | 2.5 |
-| 6 | 2 | 2024-06-28 | 6.75 |
-| 7 | 3 | 2024-06-30 | 6 |
-| 8 | 3 | 2024-06-27 | 2 |
-| 9 | 3 | 2024-06-27 | 7 |
-
----
-
-## 📌 Uzdevums
-
-Izveidot **WEB aplikāciju** (vēlams izmantot Python):
-
-### 📍 Sākuma lapa `/`
-- Parāda sarakstu ar visiem darbinieku `username`
-- Iespēja veikt **username meklēšanu**
-
----
-
-### 📍 Darbinieka skatījums
-- Klikšķinot uz kāda darbinieka username, atveras lapa/skats, kur:
- - Redzama **tabula ar divām kolonnām: `date`, `hours_worked`**
- - Tabulā katrai dienai **tiek summētas visas nostrādātās stundas šajā datumā**
- - 📌 *Piemērs*:
- Ja vienā dienā ir divi ieraksti ar 4h un 3.5h — kopsumma būs **7.5**
- - Tabula **sakārtota pēc `date` DESC**
-
----
-
-
diff --git a/Uzdevums.md b/Uzdevums.md
new file mode 100644
index 0000000..985de08
--- /dev/null
+++ b/Uzdevums.md
@@ -0,0 +1,55 @@
+# Darbinieku darba laiku atskaite no SQLite datubāzes
+
+## Resursi
+
+Tev tiek dots **SQLite datubāzes fails**: `attendance.db`
+Šajā datubāzē ir divas tabulas:
+
+### Tabulu struktūra
+
+**`Employees`**
+
+| id | username |
+|:----|:-----------|
+| 1 | janis |
+| 2 | liga |
+| 3 | juris |
+| 4 | dace |
+
+**`Attendance`**
+
+| id | employee_id | date | hours_worked |
+|:----|:-------------|:-------------|:----------------|
+| 1 | 1 | 2024-06-29 | 4 |
+| 2 | 1 | 2024-06-29 | 3.5 |
+| 3 | 1 | 2024-06-28 | 8 |
+| 4 | 2 | 2024-06-29 | 5 |
+| 5 | 2 | 2024-06-29 | 2.5 |
+| 6 | 2 | 2024-06-28 | 6.75 |
+| 7 | 3 | 2024-06-30 | 6 |
+| 8 | 3 | 2024-06-27 | 2 |
+| 9 | 3 | 2024-06-27 | 7 |
+
+---
+
+## 📌 Uzdevums
+
+Izveidot **WEB aplikāciju** (vēlams izmantot Python):
+
+### 📍 Sākuma lapa `/`
+- Parāda sarakstu ar visiem darbinieku `username`
+- Iespēja veikt **username meklēšanu**
+
+---
+
+### 📍 Darbinieka skatījums
+- Klikšķinot uz kāda darbinieka username, atveras lapa/skats, kur:
+ - Redzama **tabula ar divām kolonnām: `date`, `hours_worked`**
+ - Tabulā katrai dienai **tiek summētas visas nostrādātās stundas šajā datumā**
+ - 📌 *Piemērs*:
+ Ja vienā dienā ir divi ieraksti ar 4h un 3.5h — kopsumma būs **7.5**
+ - Tabula **sakārtota pēc `date` DESC**
+
+---
+
+
diff --git a/frontend/src/routes/$userId.tsx b/frontend/src/routes/$userId.tsx
index b027e62..53e6c07 100644
--- a/frontend/src/routes/$userId.tsx
+++ b/frontend/src/routes/$userId.tsx
@@ -3,7 +3,7 @@ import { API_BASE_URL } from "@/consts";
import type { UserInfo } from "@/types/api";
import { capitalizeFirstLetter } from "@/utils/capitalizeFirstLetter";
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
-import { createFileRoute, useParams } from "@tanstack/react-router";
+import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/$userId")({
component: RouteComponent,
diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx
index cf7371f..ccbdb9e 100644
--- a/frontend/src/routes/__root.tsx
+++ b/frontend/src/routes/__root.tsx
@@ -1,10 +1,6 @@
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
-import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
-
import Header from "../components/Header";
-import TanStackQueryLayout from "../integrations/tanstack-query/layout.tsx";
-
import type { QueryClient } from "@tanstack/react-query";
interface MyRouterContext {
@@ -15,11 +11,7 @@ export const Route = createRootRouteWithContext()({
component: () => (
<>
-
-
-
-
>
),
});
diff --git a/preview 1.webp b/preview 1.webp
new file mode 100644
index 0000000000000000000000000000000000000000..cadc291377acf0acaa9288b61ae2c4066f296e41
GIT binary patch
literal 7836
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
literal 0
HcmV?d00001
diff --git a/preview 2.webp b/preview 2.webp
new file mode 100644
index 0000000000000000000000000000000000000000..d0c6917cb8f8cf29de989eebcb069276f04a5e2a
GIT binary patch
literal 5314
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
Date: Wed, 2 Jul 2025 22:39:18 +0300
Subject: [PATCH 09/10] Update README.md
---
README.md | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/README.md b/README.md
index e69de29..ceb5810 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,17 @@
+
+# Task for work hour overview
+
+
+
+
+# Backend (_python_)
+
+1. Install requirements.txt (I used [uv](https://github.com/astral-sh/uv), but should work with regular pip too)
+2. run command inside of `dev.sh` file
+3. Backend will start on port 8000
+
+# Frontend (_React_)
+
+1. Install dependencies with [bun](https://bun.sh/) (*should work with npm too*)
+2. Run development stage with `bun run dev`
+3. Frontend will start on port 3000
\ No newline at end of file
From babcf519e6962167d7e88d4696da4bbfb8163746 Mon Sep 17 00:00:00 2001
From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 22:41:52 +0300
Subject: [PATCH 10/10] Updated images for readme file
---
README.md | 10 +++++-----
p1.png | Bin 0 -> 40040 bytes
p2.png | Bin 0 -> 25928 bytes
preview 1.webp | Bin 7836 -> 0 bytes
preview 2.webp | Bin 5314 -> 0 bytes
5 files changed, 5 insertions(+), 5 deletions(-)
create mode 100644 p1.png
create mode 100644 p2.png
delete mode 100644 preview 1.webp
delete mode 100644 preview 2.webp
diff --git a/README.md b/README.md
index ceb5810..d74a337 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
-
# Task for work hour overview
-
-
+
+
# Backend (_python_)
@@ -12,6 +11,7 @@
# Frontend (_React_)
-1. Install dependencies with [bun](https://bun.sh/) (*should work with npm too*)
+1. Install dependencies with [bun](https://bun.sh/) (_should work with npm too_)
2. Run development stage with `bun run dev`
-3. Frontend will start on port 3000
\ No newline at end of file
+3. Frontend will start on port 3000
+
diff --git a/p1.png b/p1.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f625620b59ad257f48036dfc2d6cd857a6674b6
GIT binary patch
literal 40040
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