From 35326c3fd8ab0ad9e2c396c1908a55c269ddc84f Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 11 Aug 2025 17:03:29 -0400 Subject: [PATCH 01/17] Added attendance system --- .env.example | 1 + apps/attendance/.gitignore | 46 + apps/attendance/drizzle.config.ts | 11 + apps/attendance/next.config.ts | 98 + apps/attendance/package.json | 52 + apps/attendance/postcss.config.mjs | 5 + apps/attendance/public/_headers | 3 + apps/attendance/public/manifest.json | 28 + apps/attendance/public/sw.js | 1 + apps/attendance/public/workbox-e9849328.js | 1 + .../src/app/_actions/server-actions.ts | 100 + .../src/app/_components/attendance-status.tsx | 63 + .../app/_components/attendance-tracker.tsx | 77 + .../src/app/_components/install-app-ios.tsx | 34 + .../src/app/_components/status-indicator.tsx | 22 + .../src/app/_components/time-display.tsx | 23 + .../src/app/_components/user-header.tsx | 37 + .../app/_components/view-admin-dashboard.tsx | 28 + .../dashboard/_actions/server-actions.ts | 146 + .../dashboard/_components/admin-header.tsx | 39 + .../_components/floating-settings.tsx | 50 + .../user-detail/attendance-overview-card.tsx | 58 + .../user-detail/attendance-record.tsx | 82 + .../editable-attendance-record.tsx | 347 +++ .../_components/user-detail/loading-state.tsx | 22 + .../_components/user-detail/types.ts | 38 + .../user-detail/user-detail-sheet.tsx | 242 ++ .../_components/user-detail/user-header.tsx | 128 + .../user-detail/user-info-card.tsx | 39 + .../_components/user-detail/utils.ts | 80 + .../user-detail/weekly-attendance-card.tsx | 56 + .../user-detail/weekly-attendance-item.tsx | 67 + .../dashboard/_components/user-table.tsx | 124 + .../src/app/admin/dashboard/page.tsx | 71 + .../admin/settings/_actions/server-actions.ts | 29 + .../_components/day-selection-handler.tsx | 21 + .../settings/_components/day-selector.tsx | 132 + .../src/app/admin/settings/page.tsx | 31 + apps/attendance/src/app/api/cron/route.ts | 106 + apps/attendance/src/app/favicon.ico | Bin 0 -> 4022 bytes apps/attendance/src/app/globals.css | 122 + apps/attendance/src/app/layout.tsx | 39 + apps/attendance/src/app/page.tsx | 30 + .../data-table/data-table-action-bar.tsx | 178 ++ .../data-table/data-table-column-header.tsx | 99 + .../data-table/data-table-date-filter.tsx | 220 ++ .../data-table/data-table-pagination.tsx | 112 + .../data-table/data-table-slider-filter.tsx | 239 ++ .../data-table/data-table-view-options.tsx | 85 + .../src/components/data-table/data-table.tsx | 101 + apps/attendance/src/config/data-table.ts | 82 + apps/attendance/src/hooks/use-callback-ref.ts | 27 + apps/attendance/src/hooks/use-data-table.ts | 296 ++ .../src/hooks/use-debounced-callback.ts | 28 + apps/attendance/src/lib/data-table.ts | 77 + apps/attendance/src/lib/date-helper.tsx | 8 + apps/attendance/src/lib/format.ts | 17 + apps/attendance/src/lib/parsers.ts | 99 + apps/attendance/src/lib/types/attendance.ts | 5 + apps/attendance/src/types/data-table.ts | 40 + apps/attendance/tsconfig.json | 30 + packages/auth/src/auth.ts | 7 + packages/db/drizzle.config.ts | 1 + packages/db/src/db/platforms/attendance.ts | 30 + packages/db/src/index.ts | 3 + packages/env/src/env.shared.ts | 2 + pnpm-lock.yaml | 2628 ++++++++++++++++- 67 files changed, 7128 insertions(+), 15 deletions(-) create mode 100644 apps/attendance/.gitignore create mode 100644 apps/attendance/drizzle.config.ts create mode 100644 apps/attendance/next.config.ts create mode 100644 apps/attendance/package.json create mode 100644 apps/attendance/postcss.config.mjs create mode 100644 apps/attendance/public/_headers create mode 100644 apps/attendance/public/manifest.json create mode 100644 apps/attendance/public/sw.js create mode 100644 apps/attendance/public/workbox-e9849328.js create mode 100644 apps/attendance/src/app/_actions/server-actions.ts create mode 100644 apps/attendance/src/app/_components/attendance-status.tsx create mode 100644 apps/attendance/src/app/_components/attendance-tracker.tsx create mode 100644 apps/attendance/src/app/_components/install-app-ios.tsx create mode 100644 apps/attendance/src/app/_components/status-indicator.tsx create mode 100644 apps/attendance/src/app/_components/time-display.tsx create mode 100644 apps/attendance/src/app/_components/user-header.tsx create mode 100644 apps/attendance/src/app/_components/view-admin-dashboard.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_actions/server-actions.ts create mode 100644 apps/attendance/src/app/admin/dashboard/_components/admin-header.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/floating-settings.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-record.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/loading-state.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/types.ts create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/user-header.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/user-info-card.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/utils.ts create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/_components/user-table.tsx create mode 100644 apps/attendance/src/app/admin/dashboard/page.tsx create mode 100644 apps/attendance/src/app/admin/settings/_actions/server-actions.ts create mode 100644 apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx create mode 100644 apps/attendance/src/app/admin/settings/_components/day-selector.tsx create mode 100644 apps/attendance/src/app/admin/settings/page.tsx create mode 100644 apps/attendance/src/app/api/cron/route.ts create mode 100644 apps/attendance/src/app/favicon.ico create mode 100644 apps/attendance/src/app/globals.css create mode 100644 apps/attendance/src/app/layout.tsx create mode 100644 apps/attendance/src/app/page.tsx create mode 100644 apps/attendance/src/components/data-table/data-table-action-bar.tsx create mode 100644 apps/attendance/src/components/data-table/data-table-column-header.tsx create mode 100644 apps/attendance/src/components/data-table/data-table-date-filter.tsx create mode 100644 apps/attendance/src/components/data-table/data-table-pagination.tsx create mode 100644 apps/attendance/src/components/data-table/data-table-slider-filter.tsx create mode 100644 apps/attendance/src/components/data-table/data-table-view-options.tsx create mode 100644 apps/attendance/src/components/data-table/data-table.tsx create mode 100644 apps/attendance/src/config/data-table.ts create mode 100644 apps/attendance/src/hooks/use-callback-ref.ts create mode 100644 apps/attendance/src/hooks/use-data-table.ts create mode 100644 apps/attendance/src/hooks/use-debounced-callback.ts create mode 100644 apps/attendance/src/lib/data-table.ts create mode 100644 apps/attendance/src/lib/date-helper.tsx create mode 100644 apps/attendance/src/lib/format.ts create mode 100644 apps/attendance/src/lib/parsers.ts create mode 100644 apps/attendance/src/lib/types/attendance.ts create mode 100644 apps/attendance/src/types/data-table.ts create mode 100644 apps/attendance/tsconfig.json create mode 100644 packages/db/src/db/platforms/attendance.ts diff --git a/.env.example b/.env.example index d8da9be..25620d6 100644 --- a/.env.example +++ b/.env.example @@ -13,5 +13,6 @@ NEXT_PUBLIC_DOCS_BASE_URL='http://localhost:3002' NEXT_PUBLIC_LANDING_BASE_URL='http://localhost:3000' NEXT_PUBLIC_LEARN_BASE_URL='http://localhost:3003' NEXT_PUBLIC_SCOUT_BASE_URL='http://localhost:3004' +NEXT_PUBLIC_ATTENDANCE_BASE_URL='http://localhost:3005' NODE_ENV='development' diff --git a/apps/attendance/.gitignore b/apps/attendance/.gitignore new file mode 100644 index 0000000..6fb47a0 --- /dev/null +++ b/apps/attendance/.gitignore @@ -0,0 +1,46 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# OpenNext +/.open-next + +# wrangler files +.wrangler +.dev.vars* +!.dev.vars.example +.env* +!.env.example diff --git a/apps/attendance/drizzle.config.ts b/apps/attendance/drizzle.config.ts new file mode 100644 index 0000000..6a7445c --- /dev/null +++ b/apps/attendance/drizzle.config.ts @@ -0,0 +1,11 @@ +import 'dotenv/config'; +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + out: './drizzle', + schema: './src/db/schema.ts', + dialect: 'postgresql', + dbCredentials: { + url: process.env.DATABASE_URL!, + }, +}); diff --git a/apps/attendance/next.config.ts b/apps/attendance/next.config.ts new file mode 100644 index 0000000..2b1bcc1 --- /dev/null +++ b/apps/attendance/next.config.ts @@ -0,0 +1,98 @@ +const withPWA = require("next-pwa")({ + dest: "public", +}); + +const IS_PRODUCTION = process.env.NODE_ENV === "production"; +const DB_URL = process.env.DATABASE_URL; +const ENABLE_REACT_COMPILER = process.env.ENABLE_REACT_COMPILER === "true"; + +const INTERNAL_PACKAGES = [ + "@spike/api", + "@spike/auth", + "@spike/client", + "@spike/db", + "@spike/env", + "@spike/next", + "@spike/ui", + "@spike/stripe", +]; + +/** @type {import('next').NextConfig} */ +const config = { + reactStrictMode: true, + /** Enables hot reloading for local packages without a build step */ + transpilePackages: INTERNAL_PACKAGES, + images: { + remotePatterns: getRemotePatterns(), + }, + logging: { + fetches: { + fullUrl: true, + }, + }, + serverExternalPackages: [], + redirects: getRedirects, + experimental: { + mdxRs: true, + reactCompiler: ENABLE_REACT_COMPILER, + turbo: { + resolveExtensions: [".ts", ".tsx", ".js", ".jsx"], + }, + optimizePackageImports: [ + "recharts", + "lucide-react", + "@radix-ui/react-icons", + "@radix-ui/react-avatar", + "@radix-ui/react-select", + "date-fns", + ...INTERNAL_PACKAGES, + ], + }, + modularizeImports: { + lodash: { + transform: "lodash/{{member}}", + }, + }, + /** We already do linting and typechecking as separate tasks in CI */ + eslint: { ignoreDuringBuilds: true }, + typescript: { ignoreBuildErrors: true }, +}; + +export default withPWA(config); + +function getRemotePatterns() { + /** @type {import('next').NextConfig['remotePatterns']} */ + const remotePatterns = []; + + if (DB_URL) { + const hostname = new URL(DB_URL).hostname; + + remotePatterns.push({ + protocol: "https", + hostname, + }); + } + + return IS_PRODUCTION + ? remotePatterns + : [ + { + protocol: "http", + hostname: "127.0.0.1", + }, + { + protocol: "http", + hostname: "localhost", + }, + ]; +} + +async function getRedirects() { + return [ + { + source: "/server-sitemap.xml", + destination: "/sitemap.xml", + permanent: true, + }, + ]; +} diff --git a/apps/attendance/package.json b/apps/attendance/package.json new file mode 100644 index 0000000..7b09a71 --- /dev/null +++ b/apps/attendance/package.json @@ -0,0 +1,52 @@ +{ + "name": "spike-attendance", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack --port 3005", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@spike/api": "workspace:*", + "@spike/auth": "workspace:*", + "@spike/client": "workspace:*", + "@spike/config": "workspace:*", + "@spike/db": "workspace:*", + "@spike/env": "workspace:*", + "@spike/next": "workspace:*", + "@spike/ui": "workspace:*", + "@tanstack/table-core": "^8.21.3", + "@types/luxon": "^3.7.1", + "better-auth": "^1.3.4", + "date-fns": "^4.1.0", + "dotenv": "^17.2.1", + "lucide-react": "^0.537.0", + "luxon": "^3.7.1", + "motion": "^12.23.12", + "next": "15.3.5", + "next-pwa": "^5.6.0", + "node-cron": "^4.2.1", + "nuqs": "^2.4.3", + "react": "^19.0.0", + "react-day-picker": "^9.8.1", + "react-dom": "^19.0.0", + "tailwind-merge": "^3.3.1", + "zod": "^4.0.15" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/pg": "^8.15.5", + "@types/react": "^19", + "@types/react-dom": "^19", + "drizzle-kit": "^0.31.4", + "tailwindcss": "^4", + "tsx": "^4.20.3", + "tw-animate-css": "^1.3.6", + "typescript": "5.9.2", + "wrangler": "^4.28.1" + }, + "packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912" +} diff --git a/apps/attendance/postcss.config.mjs b/apps/attendance/postcss.config.mjs new file mode 100644 index 0000000..c7bcb4b --- /dev/null +++ b/apps/attendance/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/apps/attendance/public/_headers b/apps/attendance/public/_headers new file mode 100644 index 0000000..fcf9539 --- /dev/null +++ b/apps/attendance/public/_headers @@ -0,0 +1,3 @@ +# https://developers.cloudflare.com/workers/static-assets/headers +/_next/static/* + Cache-Control: public,max-age=31536000,immutable diff --git a/apps/attendance/public/manifest.json b/apps/attendance/public/manifest.json new file mode 100644 index 0000000..617547a --- /dev/null +++ b/apps/attendance/public/manifest.json @@ -0,0 +1,28 @@ +{ + "name": "Spike Attendance", + "short_name": "Spike Attendance", + "icons": [ + { + "src": "/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/icons/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#FFFFFF", + "background_color": "#FFFFFF", + "start_url": "/", + "scope": "/", + "display": "standalone", + "orientation": "portrait" +} diff --git a/apps/attendance/public/sw.js b/apps/attendance/public/sw.js new file mode 100644 index 0000000..1e0ca4a --- /dev/null +++ b/apps/attendance/public/sw.js @@ -0,0 +1 @@ +if(!self.define){let e,s={};const a=(a,n)=>(a=new URL(a+".js",n).href,s[a]||new Promise((s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()})).then((()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didn’t register its module`);return e})));self.define=(n,t)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(s[i])return;let c={};const r=e=>a(e,i),o={module:{uri:i},exports:c,require:r};s[i]=Promise.all(n.map((e=>o[e]||r(e)))).then((e=>(t(...e),c)))}}define(["./workbox-e9849328"],(function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_headers",revision:"d77c17723be48cc0d54b256fe9fc37ab"},{url:"/_next/app-build-manifest.json",revision:"b5caf39e920f105fc3ecb21c97757f17"},{url:"/_next/static/_VNHU-NBzqSw2d_XX-6k5/_buildManifest.js",revision:"0b154aab08013aaa66e38c8eeed0ee18"},{url:"/_next/static/_VNHU-NBzqSw2d_XX-6k5/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/441-f5df486dd7f1af4d.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/462-fb7039d6d1b38e46.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/571-698bafaa6de9dc83.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/710-a8b21a5b91f04080.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/app/_not-found/page-a45fba5aa61ca63b.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/app/admin/dashboard/page-8e9b5fcf1835ce67.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/app/admin/settings/page-70c4ba7c9af5e0be.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/app/api/auth/%5B...all%5D/route-f0d3e7ddc6d9713e.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/app/api/cron/route-f1b1ff766dffe269.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/app/layout-24c7e3b7446fa945.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/app/page-fac6b190a63b6180.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/e5ae447d-524de10f7f73c5f7.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/framework-86fc9e0a07af7b7c.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/main-9b100880526afe9b.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/main-app-09c938dbf79375b1.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/pages/_app-29fc371b412ab543.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/pages/_error-422164231d583f75.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-3f231fbb215e33f0.js",revision:"_VNHU-NBzqSw2d_XX-6k5"},{url:"/_next/static/css/d3628d8eee61c4e0.css",revision:"d3628d8eee61c4e0"},{url:"/_next/static/media/569ce4b8f30dc480-s.p.woff2",revision:"ef6cefb32024deac234e82f932a95cbd"},{url:"/_next/static/media/747892c23ea88013-s.woff2",revision:"a0761690ccf4441ace5cec893b82d4ab"},{url:"/_next/static/media/8d697b304b401681-s.woff2",revision:"cc728f6c0adb04da0dfcb0fc436a8ae5"},{url:"/_next/static/media/93f479601ee12b01-s.p.woff2",revision:"da83d5f06d825c5ae65b7cca706cb312"},{url:"/_next/static/media/9610d9e46709d722-s.woff2",revision:"7b7c0ef93df188a852344fc272fc096b"},{url:"/_next/static/media/ba015fad6dcf6784-s.woff2",revision:"8ea4f719af3312a055caf09f34c89a77"},{url:"/manifest.json",revision:"f5b306e1a757b6f01558195a9650e596"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:a,state:n})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute((({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")}),new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute((({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")}),new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute((({url:e})=>!(self.origin===e.origin)),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")})); diff --git a/apps/attendance/public/workbox-e9849328.js b/apps/attendance/public/workbox-e9849328.js new file mode 100644 index 0000000..96b4c94 --- /dev/null +++ b/apps/attendance/public/workbox-e9849328.js @@ -0,0 +1 @@ +define(["exports"],(function(t){"use strict";try{self["workbox:core:6.5.4"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:6.5.4"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class i extends r{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class a{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let a=i&&i.handler;const o=t.method;if(!a&&this.i.has(o)&&(a=this.i.get(o)),!a)return;let c;try{c=a.handle({url:s,request:t,event:e,params:r})}catch(t){c=Promise.reject(t)}const h=i&&i.catchHandler;return c instanceof Promise&&(this.o||h)&&(c=c.catch((async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:r})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n}))),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const r=this.t.get(s.method)||[];for(const i of r){let r;const a=i.match({url:t,sameOrigin:e,request:s,event:n});if(a)return r=a,(Array.isArray(r)&&0===r.length||a.constructor===Object&&0===Object.keys(a).length||"boolean"==typeof a)&&(r=void 0),{route:i,params:r}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let o;const c=()=>(o||(o=new a,o.addFetchListener(),o.addCacheListener()),o);function h(t,e,n){let a;if("string"==typeof t){const s=new URL(t,location.href);a=new r((({url:t})=>t.href===s.href),e,n)}else if(t instanceof RegExp)a=new i(t,e,n);else if("function"==typeof t)a=new r(t,e,n);else{if(!(t instanceof r))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});a=t}return c().registerRoute(a),a}try{self["workbox:strategies:6.5.4"]&&_()}catch(t){}const u={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null},l={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},f=t=>[l.prefix,t,l.suffix].filter((t=>t&&t.length>0)).join("-"),w=t=>t||f(l.precache),d=t=>t||f(l.runtime);function p(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class y{constructor(){this.promise=new Promise(((t,e)=>{this.resolve=t,this.reject=e}))}}const g=new Set;function m(t){return"string"==typeof t?new Request(t):t}class R{constructor(t,e){this.h={},Object.assign(this,e),this.event=e.event,this.u=t,this.l=new y,this.p=[],this.g=[...t.plugins],this.m=new Map;for(const t of this.g)this.m.set(t,{});this.event.waitUntil(this.l.promise)}async fetch(t){const{event:e}=this;let n=m(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const r=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const i=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.u.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:i,response:t});return t}catch(t){throw r&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:r.clone(),request:i.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=m(t);let s;const{cacheName:n,matchOptions:r}=this.u,i=await this.getCacheKey(e,"read"),a=Object.assign(Object.assign({},r),{cacheName:n});s=await caches.match(i,a);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:r,cachedResponse:s,request:i,event:this.event})||void 0;return s}async cachePut(t,e){const n=m(t);var r;await(r=0,new Promise((t=>setTimeout(t,r))));const i=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(a=i.url,new URL(String(a),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var a;const o=await this.R(e);if(!o)return!1;const{cacheName:c,matchOptions:h}=this.u,u=await self.caches.open(c),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const r=p(e.url,s);if(e.url===r)return t.match(e,n);const i=Object.assign(Object.assign({},n),{ignoreSearch:!0}),a=await t.keys(e,i);for(const e of a)if(r===p(e.url,s))return t.match(e,n)}(u,i.clone(),["__WB_REVISION__"],h):null;try{await u.put(i,l?o.clone():o)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of g)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:c,oldResponse:f,newResponse:o.clone(),request:i,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.h[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=m(await t({mode:e,request:n,event:this.event,params:this.params}));this.h[s]=n}return this.h[s]}hasCallback(t){for(const e of this.u.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.u.plugins)if("function"==typeof e[t]){const s=this.m.get(e),n=n=>{const r=Object.assign(Object.assign({},n),{state:s});return e[t](r)};yield n}}waitUntil(t){return this.p.push(t),t}async doneWaiting(){let t;for(;t=this.p.shift();)await t}destroy(){this.l.resolve(null)}async R(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class v{constructor(t={}){this.cacheName=d(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,r=new R(this,{event:e,request:s,params:n}),i=this.v(r,s,e);return[i,this.q(i,r,s,e)]}async v(t,e,n){let r;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(r=await this.D(e,t),!r||"error"===r.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const i of t.iterateCallbacks("handlerDidError"))if(r=await i({error:s,event:n,request:e}),r)break;if(!r)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))r=await s({event:n,request:e,response:r});return r}async q(t,e,s,n){let r,i;try{r=await t}catch(i){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:r}),await e.doneWaiting()}catch(t){t instanceof Error&&(i=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:r,error:i}),e.destroy(),i)throw i}}function b(t){t.then((()=>{}))}function q(){return q=Object.assign?Object.assign.bind():function(t){for(var e=1;ee.some((e=>t instanceof e));let U,x;const L=new WeakMap,I=new WeakMap,C=new WeakMap,E=new WeakMap,N=new WeakMap;let O={get(t,e,s){if(t instanceof IDBTransaction){if("done"===e)return I.get(t);if("objectStoreNames"===e)return t.objectStoreNames||C.get(t);if("store"===e)return s.objectStoreNames[1]?void 0:s.objectStore(s.objectStoreNames[0])}return B(t[e])},set:(t,e,s)=>(t[e]=s,!0),has:(t,e)=>t instanceof IDBTransaction&&("done"===e||"store"===e)||e in t};function T(t){return t!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(x||(x=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(t)?function(...e){return t.apply(P(this),e),B(L.get(this))}:function(...e){return B(t.apply(P(this),e))}:function(e,...s){const n=t.call(P(this),e,...s);return C.set(n,e.sort?e.sort():[e]),B(n)}}function k(t){return"function"==typeof t?T(t):(t instanceof IDBTransaction&&function(t){if(I.has(t))return;const e=new Promise(((e,s)=>{const n=()=>{t.removeEventListener("complete",r),t.removeEventListener("error",i),t.removeEventListener("abort",i)},r=()=>{e(),n()},i=()=>{s(t.error||new DOMException("AbortError","AbortError")),n()};t.addEventListener("complete",r),t.addEventListener("error",i),t.addEventListener("abort",i)}));I.set(t,e)}(t),D(t,U||(U=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]))?new Proxy(t,O):t)}function B(t){if(t instanceof IDBRequest)return function(t){const e=new Promise(((e,s)=>{const n=()=>{t.removeEventListener("success",r),t.removeEventListener("error",i)},r=()=>{e(B(t.result)),n()},i=()=>{s(t.error),n()};t.addEventListener("success",r),t.addEventListener("error",i)}));return e.then((e=>{e instanceof IDBCursor&&L.set(e,t)})).catch((()=>{})),N.set(e,t),e}(t);if(E.has(t))return E.get(t);const e=k(t);return e!==t&&(E.set(t,e),N.set(e,t)),e}const P=t=>N.get(t);const M=["get","getKey","getAll","getAllKeys","count"],W=["put","add","delete","clear"],j=new Map;function S(t,e){if(!(t instanceof IDBDatabase)||e in t||"string"!=typeof e)return;if(j.get(e))return j.get(e);const s=e.replace(/FromIndex$/,""),n=e!==s,r=W.includes(s);if(!(s in(n?IDBIndex:IDBObjectStore).prototype)||!r&&!M.includes(s))return;const i=async function(t,...e){const i=this.transaction(t,r?"readwrite":"readonly");let a=i.store;return n&&(a=a.index(e.shift())),(await Promise.all([a[s](...e),r&&i.done]))[0]};return j.set(e,i),i}O=(t=>q({},t,{get:(e,s,n)=>S(e,s)||t.get(e,s,n),has:(e,s)=>!!S(e,s)||t.has(e,s)}))(O);try{self["workbox:expiration:6.5.4"]&&_()}catch(t){}const K="cache-entries",A=t=>{const e=new URL(t,location.href);return e.hash="",e.href};class F{constructor(t){this.U=null,this._=t}L(t){const e=t.createObjectStore(K,{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1})}I(t){this.L(t),this._&&function(t,{blocked:e}={}){const s=indexedDB.deleteDatabase(t);e&&s.addEventListener("blocked",(t=>e(t.oldVersion,t))),B(s).then((()=>{}))}(this._)}async setTimestamp(t,e){const s={url:t=A(t),timestamp:e,cacheName:this._,id:this.C(t)},n=(await this.getDb()).transaction(K,"readwrite",{durability:"relaxed"});await n.store.put(s),await n.done}async getTimestamp(t){const e=await this.getDb(),s=await e.get(K,this.C(t));return null==s?void 0:s.timestamp}async expireEntries(t,e){const s=await this.getDb();let n=await s.transaction(K).store.index("timestamp").openCursor(null,"prev");const r=[];let i=0;for(;n;){const s=n.value;s.cacheName===this._&&(t&&s.timestamp=e?r.push(n.value):i++),n=await n.continue()}const a=[];for(const t of r)await s.delete(K,t.id),a.push(t.url);return a}C(t){return this._+"|"+A(t)}async getDb(){return this.U||(this.U=await function(t,e,{blocked:s,upgrade:n,blocking:r,terminated:i}={}){const a=indexedDB.open(t,e),o=B(a);return n&&a.addEventListener("upgradeneeded",(t=>{n(B(a.result),t.oldVersion,t.newVersion,B(a.transaction),t)})),s&&a.addEventListener("blocked",(t=>s(t.oldVersion,t.newVersion,t))),o.then((t=>{i&&t.addEventListener("close",(()=>i())),r&&t.addEventListener("versionchange",(t=>r(t.oldVersion,t.newVersion,t)))})).catch((()=>{})),o}("workbox-expiration",1,{upgrade:this.I.bind(this)})),this.U}}class H{constructor(t,e={}){this.N=!1,this.O=!1,this.T=e.maxEntries,this.k=e.maxAgeSeconds,this.B=e.matchOptions,this._=t,this.P=new F(t)}async expireEntries(){if(this.N)return void(this.O=!0);this.N=!0;const t=this.k?Date.now()-1e3*this.k:0,e=await this.P.expireEntries(t,this.T),s=await self.caches.open(this._);for(const t of e)await s.delete(t,this.B);this.N=!1,this.O&&(this.O=!1,b(this.expireEntries()))}async updateTimestamp(t){await this.P.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.k){const e=await this.P.getTimestamp(t),s=Date.now()-1e3*this.k;return void 0===e||er||e&&e<0)throw new s("range-not-satisfiable",{size:r,end:n,start:e});let i,a;return void 0!==e&&void 0!==n?(i=e,a=n+1):void 0!==e&&void 0===n?(i=e,a=r):void 0!==n&&void 0===e&&(i=r-n,a=r),{start:i,end:a}}(i,r.start,r.end),o=i.slice(a.start,a.end),c=o.size,h=new Response(o,{status:206,statusText:"Partial Content",headers:e.headers});return h.headers.set("Content-Length",String(c)),h.headers.set("Content-Range",`bytes ${a.start}-${a.end-1}/${i.size}`),h}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}function z(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:6.5.4"]&&_()}catch(t){}function G(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const r=new URL(n,location.href),i=new URL(n,location.href);return r.searchParams.set("__WB_REVISION__",e),{cacheKey:r.href,url:i.href}}class V{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class J{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.M.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.M=t}}let Q,X;async function Y(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const r=t.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},a=e?e(i):i,o=function(){if(void 0===Q){const t=new Response("");if("body"in t)try{new Response(t.body),Q=!0}catch(t){Q=!1}Q=!1}return Q}()?r.body:await r.blob();return new Response(o,a)}class Z extends v{constructor(t={}){t.cacheName=w(t.cacheName),super(t),this.W=!1!==t.fallbackToNetwork,this.plugins.push(Z.copyRedirectedCacheableResponsesPlugin)}async D(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.j(t,e):await this.S(t,e))}async S(t,e){let n;const r=e.params||{};if(!this.W)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=r.integrity,i=t.integrity,a=!i||i===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?i||s:void 0})),s&&a&&"no-cors"!==t.mode&&(this.K(),await e.cachePut(t,n.clone()))}return n}async j(t,e){this.K();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}K(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==Z.copyRedirectedCacheableResponsesPlugin&&(n===Z.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(Z.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}Z.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},Z.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await Y(t):t};class tt{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.A=new Map,this.F=new Map,this.H=new Map,this.u=new Z({cacheName:w(t),plugins:[...e,new J({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.u}precache(t){this.addToCacheList(t),this.$||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.$=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:r}=G(n),i="string"!=typeof n&&n.revision?"reload":"default";if(this.A.has(r)&&this.A.get(r)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.A.get(r),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.H.has(t)&&this.H.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:r});this.H.set(t,n.integrity)}if(this.A.set(r,t),this.F.set(r,i),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return z(t,(async()=>{const e=new V;this.strategy.plugins.push(e);for(const[e,s]of this.A){const n=this.H.get(s),r=this.F.get(e),i=new Request(e,{integrity:n,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:i,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}}))}activate(t){return z(t,(async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.A.values()),n=[];for(const r of e)s.has(r.url)||(await t.delete(r),n.push(r.url));return{deletedURLs:n}}))}getURLsToCacheKeys(){return this.A}getCachedURLs(){return[...this.A.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.A.get(e.href)}getIntegrityForCacheKey(t){return this.H.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}const et=()=>(X||(X=new tt),X);class st extends r{constructor(t,e){super((({request:s})=>{const n=t.getURLsToCacheKeys();for(const r of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:r}={}){const i=new URL(t,location.href);i.hash="",yield i.href;const a=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some((t=>t.test(s)))&&t.searchParams.delete(s);return t}(i,e);if(yield a.href,s&&a.pathname.endsWith("/")){const t=new URL(a.href);t.pathname+=s,yield t.href}if(n){const t=new URL(a.href);t.pathname+=".html",yield t.href}if(r){const t=r({url:i});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(r);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}}),t.strategy)}}t.CacheFirst=class extends v{async D(t,e){let n,r=await e.cacheMatch(t);if(!r)try{r=await e.fetchAndCachePut(t)}catch(t){t instanceof Error&&(n=t)}if(!r)throw new s("no-response",{url:t.url,error:n});return r}},t.ExpirationPlugin=class{constructor(t={}){this.cachedResponseWillBeUsed=async({event:t,request:e,cacheName:s,cachedResponse:n})=>{if(!n)return null;const r=this.G(n),i=this.V(s);b(i.expireEntries());const a=i.updateTimestamp(e.url);if(t)try{t.waitUntil(a)}catch(t){}return r?n:null},this.cacheDidUpdate=async({cacheName:t,request:e})=>{const s=this.V(t);await s.updateTimestamp(e.url),await s.expireEntries()},this.J=t,this.k=t.maxAgeSeconds,this.X=new Map,t.purgeOnQuotaError&&function(t){g.add(t)}((()=>this.deleteCacheAndMetadata()))}V(t){if(t===d())throw new s("expire-custom-caches-only");let e=this.X.get(t);return e||(e=new H(t,this.J),this.X.set(t,e)),e}G(t){if(!this.k)return!0;const e=this.Y(t);if(null===e)return!0;return e>=Date.now()-1e3*this.k}Y(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async deleteCacheAndMetadata(){for(const[t,e]of this.X)await self.caches.delete(t),await e.delete();this.X=new Map}},t.NetworkFirst=class extends v{constructor(t={}){super(t),this.plugins.some((t=>"cacheWillUpdate"in t))||this.plugins.unshift(u),this.Z=t.networkTimeoutSeconds||0}async D(t,e){const n=[],r=[];let i;if(this.Z){const{id:s,promise:a}=this.tt({request:t,logs:n,handler:e});i=s,r.push(a)}const a=this.et({timeoutId:i,request:t,logs:n,handler:e});r.push(a);const o=await e.waitUntil((async()=>await e.waitUntil(Promise.race(r))||await a)());if(!o)throw new s("no-response",{url:t.url});return o}tt({request:t,logs:e,handler:s}){let n;return{promise:new Promise((e=>{n=setTimeout((async()=>{e(await s.cacheMatch(t))}),1e3*this.Z)})),id:n}}async et({timeoutId:t,request:e,logs:s,handler:n}){let r,i;try{i=await n.fetchAndCachePut(e)}catch(t){t instanceof Error&&(r=t)}return t&&clearTimeout(t),!r&&i||(i=await n.cacheMatch(e)),i}},t.RangeRequestsPlugin=class{constructor(){this.cachedResponseWillBeUsed=async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await $(t,e):e}},t.StaleWhileRevalidate=class extends v{constructor(t={}){super(t),this.plugins.some((t=>"cacheWillUpdate"in t))||this.plugins.unshift(u)}async D(t,e){const n=e.fetchAndCachePut(t).catch((()=>{}));e.waitUntil(n);let r,i=await e.cacheMatch(t);if(i);else try{i=await n}catch(t){t instanceof Error&&(r=t)}if(!i)throw new s("no-response",{url:t.url,error:r});return i}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",(t=>{const e=w();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter((s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t));return await Promise.all(s.map((t=>self.caches.delete(t)))),s})(e).then((t=>{})))}))},t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.precacheAndRoute=function(t,e){!function(t){et().precache(t)}(t),function(t){const e=et();h(new st(e,t))}(e)},t.registerRoute=h})); diff --git a/apps/attendance/src/app/_actions/server-actions.ts b/apps/attendance/src/app/_actions/server-actions.ts new file mode 100644 index 0000000..bb4e466 --- /dev/null +++ b/apps/attendance/src/app/_actions/server-actions.ts @@ -0,0 +1,100 @@ +"use server"; + +import { auth } from "@spike/auth"; +import { attendance, db } from "@spike/db"; +import { eq, and } from "drizzle-orm"; +import { headers } from "next/headers"; + +export async function logAction(action: string, userId: string) { + const today = new Date().toISOString().split("T")[0]; + + // Check if there's already an attendance record for today + const existingRecord = await db + .select() + .from(attendance) + .where(and(eq(attendance.userId, userId), eq(attendance.date, today))) + .limit(1); + + if (existingRecord.length > 0) { + // Update existing record + const updateData: any = { + updatedAt: new Date(), + }; + + if (action === "check-in") { + updateData.checkInTime = new Date(); + } else if (action === "check-out") { + updateData.checkOutTime = new Date(); + } + + await db + .update(attendance) + .set(updateData) + .where(eq(attendance.id, existingRecord[0].id)); + } else { + // Create new record + const recordData: any = { + userId: userId, + date: today, + status: "present", + }; + + if (action === "check-in") { + recordData.checkInTime = new Date(); + } else if (action === "check-out") { + recordData.checkOutTime = new Date(); + } + + await db.insert(attendance).values(recordData); + } +} + +export async function getStatus( + userId: string, +): Promise<"checked-in" | "checked-out"> { + const today = new Date().toISOString().split("T")[0]; + + // Get today's attendance record + const todayRecord = await db + .select() + .from(attendance) + .where(and(eq(attendance.userId, userId), eq(attendance.date, today))) + .limit(1); + + if (todayRecord.length === 0) { + return "checked-out"; + } + + const record = todayRecord[0]; + + // If user has checked in but not checked out, they're checked in + if (record.checkInTime && !record.checkOutTime) { + return "checked-in"; + } + + // Otherwise they're checked out + return "checked-out"; +} + +export async function getCheckedInTime(userId: string): Promise { + const today = new Date().toISOString().split("T")[0]; + + // Get today's attendance record + const todayRecord = await db + .select() + .from(attendance) + .where(and(eq(attendance.userId, userId), eq(attendance.date, today))) + .limit(1); + + if (todayRecord.length === 0 || !todayRecord[0].checkInTime) { + return null; + } + + return todayRecord[0].checkInTime; +} + +export async function signOut() { + await auth.api.signOut({ + headers: await headers(), + }); +} diff --git a/apps/attendance/src/app/_components/attendance-status.tsx b/apps/attendance/src/app/_components/attendance-status.tsx new file mode 100644 index 0000000..880e1fd --- /dev/null +++ b/apps/attendance/src/app/_components/attendance-status.tsx @@ -0,0 +1,63 @@ +import { Button } from "@spike/ui/button"; +import { Card, CardContent } from "@spike/ui/card"; +import { Clock } from "lucide-react"; +import { TimeDisplay } from "./time-display"; + +interface AttendanceStatusProps { + isCheckedIn: boolean; + checkInTime: string | null; + checkOutTime: string | null; + onCheckInOut: (status: string) => void; + loading?: boolean; +} + +export function AttendanceStatus({ + isCheckedIn, + checkInTime, + checkOutTime, + onCheckInOut, + loading = false, +}: AttendanceStatusProps) { + return ( + + +
+
+ +
+ +
+

+ {isCheckedIn ? "Checked In" : "Not Checked In"} +

+

+ {isCheckedIn + ? "You are currently in session" + : "Ready to start your session?"} +

+
+ + + + +
+
+
+ ); +} diff --git a/apps/attendance/src/app/_components/attendance-tracker.tsx b/apps/attendance/src/app/_components/attendance-tracker.tsx new file mode 100644 index 0000000..1299c9c --- /dev/null +++ b/apps/attendance/src/app/_components/attendance-tracker.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { useState } from "react"; +import { AttendanceStatus } from "./attendance-status"; +import { StatusIndicator } from "./status-indicator"; +import { getCurrentDate } from "@/lib/date-helper"; +import { UserHeader } from "./user-header"; +import { ViewAdminDashboard } from "@/app/_components/view-admin-dashboard"; +import { logAction } from "@/app/_actions/server-actions"; +import { asUrl } from "@spike/config/paths.config"; +import sharedEnv from "@spike/env/env.shared"; +import { redirect } from "next/navigation"; + +export default function AttendanceTracker({ + session, + isCheckedIn: initialCheckedIn, + checkInTime: initialCheckInTime, +}: { + session?: any; + isCheckedIn: boolean; + checkInTime?: Date; +}) { + if (!session) { + return redirect( + asUrl("auth", "login") + "?redirectUrl=" + sharedEnv.ATTENDANCE_BASE_URL, + ); + } + + const [isCheckedIn, setIsCheckedIn] = useState(initialCheckedIn); + const [checkInTime, setCheckInTime] = useState( + initialCheckInTime, + ); + const [checkOutTime, setCheckOutTime] = useState(null); + const [loading, setLoading] = useState(false); + + const formatTime = (date: Date) => { + return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); + }; + + const checkInOutHandler = async (status: string) => { + setLoading(true); + await logAction(status, session.user.id); + if (status === "check-in") { + setIsCheckedIn(true); + setCheckInTime(new Date()); + setCheckOutTime(null); + } else { + setIsCheckedIn(false); + setCheckOutTime(formatTime(new Date())); + setCheckInTime(undefined); + } + setLoading(false); + }; + + return ( +
+
+ + + + + + + +
+
+ ); +} diff --git a/apps/attendance/src/app/_components/install-app-ios.tsx b/apps/attendance/src/app/_components/install-app-ios.tsx new file mode 100644 index 0000000..41a8775 --- /dev/null +++ b/apps/attendance/src/app/_components/install-app-ios.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Card, CardContent } from "@spike/ui/card"; + +export default function IOSInstallPrompt() { + const [visible, setVisible] = useState(false); + + useEffect(() => { + const isIos = /iphone|ipad|ipod/.test( + window.navigator.userAgent.toLowerCase(), + ); + const isStandalone = + "standalone" in window.navigator && (window.navigator as any).standalone; + if (isIos && !isStandalone) { + setVisible(true); + } + }, []); + + if (!visible) return null; + + return ( +
+ + +

+ To install this app: tap Share →{" "} + Add to Home Screen +

+
+
+
+ ); +} diff --git a/apps/attendance/src/app/_components/status-indicator.tsx b/apps/attendance/src/app/_components/status-indicator.tsx new file mode 100644 index 0000000..c977c98 --- /dev/null +++ b/apps/attendance/src/app/_components/status-indicator.tsx @@ -0,0 +1,22 @@ +import { Card, CardContent } from "@spike/ui/card"; + +interface StatusIndicatorProps { + isCheckedIn: boolean; +} + +export function StatusIndicator({ isCheckedIn }: StatusIndicatorProps) { + return ( + + +
+
+ + Status: {isCheckedIn ? "Active" : "Inactive"} + +
+
+
+ ); +} diff --git a/apps/attendance/src/app/_components/time-display.tsx b/apps/attendance/src/app/_components/time-display.tsx new file mode 100644 index 0000000..fc7ff0e --- /dev/null +++ b/apps/attendance/src/app/_components/time-display.tsx @@ -0,0 +1,23 @@ +interface TimeDisplayProps { + checkInTime: string | null + checkOutTime: string | null +} + +export function TimeDisplay({ checkInTime, checkOutTime }: TimeDisplayProps) { + if (!checkInTime) return null + + return ( +
+
+ Check In: + {checkInTime} +
+ {checkOutTime && ( +
+ Check Out: + {checkOutTime} +
+ )} +
+ ) +} diff --git a/apps/attendance/src/app/_components/user-header.tsx b/apps/attendance/src/app/_components/user-header.tsx new file mode 100644 index 0000000..8cf5dbc --- /dev/null +++ b/apps/attendance/src/app/_components/user-header.tsx @@ -0,0 +1,37 @@ +import { Button } from "@spike/ui/button"; +import { Card, CardDescription, CardHeader, CardTitle } from "@spike/ui/card"; +import { LogOut } from "lucide-react"; +import { signOut } from "../_actions/server-actions"; + +interface UserHeaderProps { + userName: string; + currentDate: string; +} + +export function UserHeader({ userName, currentDate }: UserHeaderProps) { + const logout = async () => { + await signOut(); + window.location.reload(); + }; + + return ( + + +
+
+ Welcome, {userName} + {currentDate} +
+ +
+
+
+ ); +} diff --git a/apps/attendance/src/app/_components/view-admin-dashboard.tsx b/apps/attendance/src/app/_components/view-admin-dashboard.tsx new file mode 100644 index 0000000..e5d2072 --- /dev/null +++ b/apps/attendance/src/app/_components/view-admin-dashboard.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { Card, CardContent } from "@spike/ui/card"; +import { Button } from "@spike/ui/button"; +import { Shield } from "lucide-react"; +import { redirect } from "next/navigation"; + +export function ViewAdminDashboard({ hasAccess }: { hasAccess: boolean }) { + if (!hasAccess) { + return
; + } + + const redirectToAdminDashboard = async () => { + redirect("/admin/dashboard"); + }; + + return ( +
+ + + + + +
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_actions/server-actions.ts b/apps/attendance/src/app/admin/dashboard/_actions/server-actions.ts new file mode 100644 index 0000000..c6e9561 --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_actions/server-actions.ts @@ -0,0 +1,146 @@ +"use server"; + +import { desc, eq } from "drizzle-orm"; +import { UserDetail } from "@/app/admin/dashboard/_components/user-detail/types"; +import { attendance, db } from "@spike/db"; + +export async function calculateHours(userId: string): Promise { + const records = await db + .select() + .from(attendance) + .where(eq(attendance.userId, userId)); + + let totalMilliseconds = 0; + + for (const record of records) { + if (record.checkInTime && record.checkOutTime) { + totalMilliseconds += + record.checkOutTime.getTime() - record.checkInTime.getTime(); + } + } + + return totalMilliseconds / (1000 * 60 * 60); +} + +export async function getUserDetail(userId: string): Promise { + const dbUser = await db + .select() + .from(user) + .where(eq(user.id, userId)) + .execute(); + + if (dbUser.length === 0) { + throw new Error("User not found"); + } + + const hoursInShop = await calculateHours(userId); + const attendanceRecordsRaw = await db + .select() + .from(attendance) + .where(eq(attendance.userId, userId)) + .orderBy(desc(attendance.date)) + .execute(); + + const attendanceRecords = attendanceRecordsRaw.map((record) => ({ + id: record.id, + date: record.date, + checkInTime: record.checkInTime + ? record.checkInTime.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + hour12: true, + }) + : null, + checkOutTime: record.checkOutTime + ? record.checkOutTime.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + hour12: true, + }) + : null, + })); + + return { + id: dbUser[0].id, + userName: dbUser[0].name, + email: dbUser[0].email, + // @ts-ignore + role: dbUser[0].role, + hoursInShop, + attendanceRecords, + }; +} + +// CRUD operations for attendance editing +export async function updateAttendanceRecord( + recordId: number, + data: { + date: string; + checkInTime?: Date | null; + checkOutTime?: Date | null; + status: string; + }, +) { + console.log("updateAttendanceRecord called with:", { recordId, data }); + try { + const result = await db + .update(attendance) + .set({ + date: data.date, + checkInTime: data.checkInTime, + checkOutTime: data.checkOutTime, + status: data.status, + updatedAt: new Date(), + }) + .where(eq(attendance.id, recordId)); + + console.log("Update result:", result); + return { success: true }; + } catch (error) { + console.error("Error updating attendance record:", error); + return { success: false, error: "Failed to update attendance record" }; + } +} + +export async function createAttendanceRecord(data: { + userId: string; + date: string; + checkInTime?: Date | null; + checkOutTime?: Date | null; + status: string; +}) { + console.log("createAttendanceRecord called with:", data); + try { + const result = await db + .insert(attendance) + .values({ + userId: data.userId, + date: data.date, + checkInTime: data.checkInTime, + checkOutTime: data.checkOutTime, + status: data.status, + }) + .returning(); + + console.log("Create result:", result); + return { success: true, record: result[0] }; + } catch (error) { + console.error("Error creating attendance record:", error); + return { success: false, error: "Failed to create attendance record" }; + } +} + +export async function deleteAttendanceRecord(recordId: number) { + console.log("deleteAttendanceRecord called with recordId:", recordId); + try { + const result = await db + .delete(attendance) + .where(eq(attendance.id, recordId)); + + console.log("Delete result:", result); + return { success: true }; + } catch (error) { + console.error("Error deleting attendance record:", error); + return { success: false, error: "Failed to delete attendance record" }; + } +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/admin-header.tsx b/apps/attendance/src/app/admin/dashboard/_components/admin-header.tsx new file mode 100644 index 0000000..b037cca --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/admin-header.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { Button } from "@spike/ui/button"; +import { Card, CardDescription, CardHeader, CardTitle } from "@spike/ui/card"; +import { Undo2 } from "lucide-react"; + +interface UserHeaderProps { + userName: string; + backUrl: string; +} + +export function AdminHeader({ userName, backUrl }: UserHeaderProps) { + const back = async () => { + window.location.href = backUrl; + }; + + return ( + + +
+
+ Welcome, {userName} + + You are in the Admin Dashboard + +
+ +
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/floating-settings.tsx b/apps/attendance/src/app/admin/dashboard/_components/floating-settings.tsx new file mode 100644 index 0000000..cbd17ed --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/floating-settings.tsx @@ -0,0 +1,50 @@ +"use client"; + +import React from "react"; +import { Settings } from "lucide-react"; +import { Button } from "@spike/ui/button"; + +function FloatingSettings() { + const handleSettingsClick = () => { + window.location.href = "/admin/settings"; + }; + + return ( +
+ +
+ ); +} + +export default FloatingSettings; diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx new file mode 100644 index 0000000..6f77c1c --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@spike/ui/card"; +import { Calendar, CheckCircle, XCircle } from "lucide-react"; + +interface AttendanceStats { + present: number; + absent: number; + total: number; +} + +interface AttendanceOverviewCardProps { + stats: AttendanceStats; +} + +export function AttendanceOverviewCard({ stats }: AttendanceOverviewCardProps) { + return ( + + + + + Attendance Overview + + + +
+
+
+ + + Present + +
+

{stats.present}

+

+ {stats.total > 0 + ? Math.round((stats.present / stats.total) * 100) + : 0} + % +

+
+
+
+ + Absent +
+

{stats.absent}

+

+ {stats.total > 0 + ? Math.round((stats.absent / stats.total) * 100) + : 0} + % +

+
+
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-record.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-record.tsx new file mode 100644 index 0000000..86a9de0 --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-record.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import { Badge } from "@spike/ui/badge"; +import { CheckCircle, XCircle } from "lucide-react"; +import { formatDate, formatTime, getAttendanceStatus } from "./utils"; +import type { AttendanceRecord as AttendanceRecordType } from "./types"; + +interface AttendanceRecordProps { + record: AttendanceRecordType; +} + +export function AttendanceRecord({ record }: AttendanceRecordProps) { + const status = getAttendanceStatus(record.checkInTime, record.checkOutTime); + + const getStatusBadge = (status: string) => { + switch (status) { + case "complete": + return ( + + Complete + + ); + case "absent": + return ( + + Absent + + ); + default: + return Unknown; + } + }; + + const getStatusIcon = (status: string) => { + switch (status) { + case "complete": + return ; + case "absent": + return ; + default: + return null; + } + }; + + return ( +
+
+
+ {getStatusIcon(status)} + + {formatDate(record.date)} + +
+ {getStatusBadge(status)} +
+ +
+
+

+ Check In +

+

+ {formatTime(record.checkInTime)} +

+
+
+

+ Check Out +

+

+ {formatTime(record.checkOutTime)} +

+
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx new file mode 100644 index 0000000..91bab01 --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx @@ -0,0 +1,347 @@ +"use client"; + +import React, { useState } from "react"; +import { Badge } from "@spike/ui/badge"; +import { Button } from "@spike/ui/button"; +import { Input } from "@spike/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@spike/ui/select"; +import { CheckCircle, XCircle, Save, X, Trash2, Edit } from "lucide-react"; +import { formatDate, getAttendanceStatus } from "./utils"; +import { + updateAttendanceRecord, + deleteAttendanceRecord, +} from "../../_actions/server-actions"; +import type { AttendanceRecord as AttendanceRecordType } from "./types"; + +interface EditableAttendanceRecordProps { + record: AttendanceRecordType; + userId: string; + onUpdateAction: (updatedRecord: AttendanceRecordType) => void; + onDeleteAction: (recordId: number) => void; + isEditMode: boolean; +} + +export function EditableAttendanceRecord({ + record, + userId, + onUpdateAction, + onDeleteAction, + isEditMode, +}: EditableAttendanceRecordProps) { + // Helper function to convert 12-hour time to 24-hour format for input fields + const convertTo24Hour = (time12h: string): string => { + if (!time12h) return ""; + + // If it's already in 24-hour format (no AM/PM), return as is + if (!time12h.includes("AM") && !time12h.includes("PM")) { + return time12h; + } + + const [time, modifier] = time12h.split(" "); + let [hours, minutes] = time.split(":"); + + if (hours === "12") { + hours = "00"; + } + if (modifier === "PM") { + hours = (parseInt(hours, 10) + 12).toString(); + } + + return `${hours.padStart(2, "0")}:${minutes}`; + }; + + // Helper function to convert 24-hour time to 12-hour format for display + const convertTo12Hour = (time24h: string): string => { + if (!time24h) return ""; + + const [hours, minutes] = time24h.split(":"); + const hour = parseInt(hours, 10); + const ampm = hour >= 12 ? "PM" : "AM"; + const displayHour = hour % 12 || 12; + + return `${displayHour}:${minutes} ${ampm}`; + }; + + const [isEditing, setIsEditing] = useState(false); + const [editData, setEditData] = useState({ + date: record.date, + checkInTime: convertTo24Hour(record.checkInTime || ""), + checkOutTime: convertTo24Hour(record.checkOutTime || ""), + status: + getAttendanceStatus(record.checkInTime, record.checkOutTime) === + "complete" + ? "present" + : "absent", + }); + const [isLoading, setIsLoading] = useState(false); + + const status = getAttendanceStatus(record.checkInTime, record.checkOutTime); + + const handleSave = async () => { + setIsLoading(true); + try { + // Convert times to Date objects for database storage + const checkInTime = editData.checkInTime + ? new Date(`${editData.date}T${editData.checkInTime}:00`) + : null; + const checkOutTime = editData.checkOutTime + ? new Date(`${editData.date}T${editData.checkOutTime}:00`) + : null; + + const result = await updateAttendanceRecord(record.id, { + date: editData.date, + checkInTime, + checkOutTime, + status: editData.status, + }); + + if (!result.success) { + alert("Failed to save: " + result.error); + return; + } + + // Update the local state with converted times for proper display + onUpdateAction({ + id: record.id, + date: editData.date, + checkInTime: editData.checkInTime + ? convertTo12Hour(editData.checkInTime) + : null, + checkOutTime: editData.checkOutTime + ? convertTo12Hour(editData.checkOutTime) + : null, + }); + + setIsEditing(false); + } catch (error) { + console.error("Error saving attendance record:", error); + alert("Failed to save attendance record. Please try again."); + } finally { + setIsLoading(false); + } + }; + + const handleDelete = async () => { + if (!confirm("Are you sure you want to delete this attendance record?")) + return; + + setIsLoading(true); + try { + const result = await deleteAttendanceRecord(record.id); + + if (result.success) { + onDeleteAction(record.id); + } else { + alert("Failed to delete record: " + result.error); + } + } catch (error) { + console.error("Error deleting attendance record:", error); + alert("Failed to delete record. Please try again."); + } finally { + setIsLoading(false); + } + }; + + const handleCancel = () => { + setEditData({ + date: record.date, + checkInTime: convertTo24Hour(record.checkInTime || ""), + checkOutTime: convertTo24Hour(record.checkOutTime || ""), + status: + getAttendanceStatus(record.checkInTime, record.checkOutTime) === + "complete" + ? "present" + : "absent", + }); + setIsEditing(false); + }; + + const getStatusBadge = (status: string) => { + switch (status) { + case "complete": + return ( + + Complete + + ); + case "absent": + return ( + + Absent + + ); + default: + return Unknown; + } + }; + + const getStatusIcon = (status: string) => { + switch (status) { + case "complete": + return ; + case "absent": + return ; + default: + return null; + } + }; + + if (isEditMode && isEditing) { + return ( +
+
+
+
+ + + setEditData({ ...editData, date: e.target.value }) + } + className="mt-1" + /> +
+
+ + + setEditData({ ...editData, checkInTime: e.target.value }) + } + className="mt-1" + /> +
+
+ + + setEditData({ ...editData, checkOutTime: e.target.value }) + } + className="mt-1" + /> +
+
+ +
+ + +
+ +
+ + +
+
+
+ ); + } + + return ( +
+
+
+ {getStatusIcon(status)} + + {formatDate(record.date)} + +
+
+ {getStatusBadge(status)} + {isEditMode && ( +
+ + +
+ )} +
+
+ +
+
+

+ Check In +

+

+ {record.checkInTime || "--:--"} +

+
+
+

+ Check Out +

+

+ {record.checkOutTime || "--:--"} +

+
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/loading-state.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/loading-state.tsx new file mode 100644 index 0000000..50ec26d --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/loading-state.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { Sheet, SheetContent } from "@spike/ui/sheet"; + +interface LoadingStateProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function LoadingState({ open, onOpenChange }: LoadingStateProps) { + return ( + + +
+
+
+

Loading user details...

+
+
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/types.ts b/apps/attendance/src/app/admin/dashboard/_components/user-detail/types.ts new file mode 100644 index 0000000..b232a25 --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/types.ts @@ -0,0 +1,38 @@ +export interface UserDetail { + id: string; + userName: string; + email: string; + role?: string; + hoursInShop: number; + attendanceRecords: Array<{ + id: number; + date: string; + checkInTime: string | null; + checkOutTime: string | null; + }>; +} + +export interface AttendanceRecord { + id: number; + date: string; + checkInTime: string | null; + checkOutTime: string | null; +} + +export interface WeeklyAttendance { + weekStart: Date; + weekEnd: Date; + weekLabel: string; + records: AttendanceRecord[]; + summary: { + present: number; + absent: number; + total: number; + }; +} + +export interface UserDetailSheetProps { + userId: string; + open: boolean; + onOpenChange: (open: boolean) => void; +} \ No newline at end of file diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx new file mode 100644 index 0000000..e9df217 --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx @@ -0,0 +1,242 @@ +import React, { useEffect, useState } from "react"; +import { Sheet, SheetContent } from "@spike/ui/sheet"; +import { Button } from "@spike/ui/button"; +import { Plus } from "lucide-react"; +import { + getUserDetail, + createAttendanceRecord, +} from "@/app/admin/dashboard/_actions/server-actions"; +import { LoadingState } from "./loading-state"; +import { UserHeader } from "./user-header"; +import { UserInfoCard } from "./user-info-card"; +import { AttendanceOverviewCard } from "./attendance-overview-card"; +import { WeeklyAttendanceCard } from "./weekly-attendance-card"; +import { EditableAttendanceRecord } from "./editable-attendance-record"; +import { groupAttendanceByWeek, getAttendanceStatus } from "./utils"; +import type { + UserDetail, + UserDetailSheetProps, + AttendanceRecord, +} from "./types"; + +function UserDetailSheet({ userId, open, onOpenChange }: UserDetailSheetProps) { + const [expandedWeeks, setExpandedWeeks] = useState>(new Set()); + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(false); + const [isEditMode, setIsEditMode] = useState(false); + + useEffect(() => { + if (userId && open) { + setLoading(true); + getUserDetail(userId) + .then(setUser) + .catch(console.error) + .finally(() => setLoading(false)); + } + }, [userId, open]); + + // Reset edit mode when sheet closes + useEffect(() => { + if (!open) { + setIsEditMode(false); + } + }, [open]); + + const toggleWeek = (weekKey: string) => { + const newExpanded = new Set(expandedWeeks); + if (newExpanded.has(weekKey)) { + newExpanded.delete(weekKey); + } else { + newExpanded.add(weekKey); + } + setExpandedWeeks(newExpanded); + }; + + const handleToggleEditMode = () => { + setIsEditMode(!isEditMode); + }; + + const handleRoleUpdated = () => { + // Refresh user data to get updated role + if (userId) { + setLoading(true); + getUserDetail(userId) + .then(setUser) + .catch(console.error) + .finally(() => setLoading(false)); + } + }; + + const handleUpdateRecord = (updatedRecord: AttendanceRecord) => { + if (!user) return; + + setUser({ + ...user, + attendanceRecords: user.attendanceRecords.map((record) => + record.id === updatedRecord.id ? updatedRecord : record, + ), + }); + }; + + const handleDeleteRecord = (recordId: number) => { + if (!user) return; + + setUser({ + ...user, + attendanceRecords: user.attendanceRecords.filter( + (record) => record.id !== recordId, + ), + }); + }; + + const handleAddNewRecord = async () => { + if (!user) return; + + const today = new Date().toISOString().split("T")[0]; + const now = new Date(); + const currentTime = now.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + hour12: true, + }); + + try { + const result = await createAttendanceRecord({ + userId: user.id, + date: today, + checkInTime: now, + status: "present", + }); + + if (result.success && result.record) { + const newRecord: AttendanceRecord = { + id: result.record.id, + date: today, + checkInTime: currentTime, + checkOutTime: null, + }; + + setUser({ + ...user, + attendanceRecords: [newRecord, ...user.attendanceRecords], + }); + } else { + console.error("Failed to create record:", result.error); + alert( + "Failed to create new record: " + (result.error || "Unknown error"), + ); + } + } catch (error) { + console.error("Error adding new record:", error); + alert("Failed to create new record. Please try again."); + } + }; + + if (loading || !user) { + return ; + } + + const weeklyData = groupAttendanceByWeek(user); + const totalStats = user.attendanceRecords.reduce( + (acc, record) => { + const status = getAttendanceStatus( + record.checkInTime, + record.checkOutTime, + ); + acc.total++; + if (status === "complete") acc.present++; + else acc.absent++; + return acc; + }, + { present: 0, absent: 0, total: 0 }, + ); + + return ( + + + + +
+
+ + + + + {isEditMode && ( +
+
+

+ Edit Mode Active +

+

+ Click on attendance records to edit or delete them +

+
+ +
+ )} + + {isEditMode ? ( + // Edit mode: show individual records with edit capabilities +
+

+ All Attendance Records +

+ {user.attendanceRecords.map((record) => ( + + ))} +
+ ) : ( + // View mode: show weekly grouped records + + )} + +
+
+
+
+
+ ); +} + +export default UserDetailSheet; diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-header.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-header.tsx new file mode 100644 index 0000000..fb28dfd --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-header.tsx @@ -0,0 +1,128 @@ +import React, { useState } from "react"; +import { Avatar, AvatarFallback } from "@spike/ui/avatar"; +import { Button } from "@spike/ui/button"; +import { SheetDescription, SheetTitle } from "@spike/ui/sheet"; +import { Edit, X, UserCheck } from "lucide-react"; +import { authClient } from "@spike/auth/client"; + +function getInitials(name: string): string { + return name + .split(" ") + .map((word) => word.charAt(0)) + .join("") + .toUpperCase() + .slice(0, 2); +} + +interface UserHeaderProps { + userName: string; + userId: string; + userRole?: string; + isEditMode?: boolean; + onToggleEditMode?: () => void; + onRoleUpdated?: () => void; +} + +export function UserHeader({ + userName, + userId, + userRole, + isEditMode = false, + onToggleEditMode, + onRoleUpdated, +}: UserHeaderProps) { + const [isUpdatingRole, setIsUpdatingRole] = useState(false); + + const handleMakeAdmin = async () => { + if (!confirm(`Are you sure you want to make ${userName} an administrator?`)) + return; + + setIsUpdatingRole(true); + try { + const { data, error } = await authClient.admin.setRole({ + userId: userId, + role: "admin", + }); + + if (error) { + console.error("Error setting admin role:", error); + alert("Failed to update user role. Please try again."); + } else { + console.log("Successfully updated user role:", data); + alert(`${userName} is now an administrator!`); + onRoleUpdated?.(); + } + } catch (error) { + console.error("Error making user admin:", error); + alert("Failed to update user role. Please try again."); + } finally { + setIsUpdatingRole(false); + } + }; + + const isAlreadyAdmin = userRole === "admin"; + + return ( +
+
+
+ + + {getInitials(userName)} + + +
+ + {userName} + {isAlreadyAdmin && ( + + Admin + + )} + + + {isEditMode + ? "Editing Attendance Records" + : "Member Attendance Details"} + +
+
+ +
+ {!isAlreadyAdmin && ( + + )} + + {onToggleEditMode && ( + + )} +
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-info-card.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-info-card.tsx new file mode 100644 index 0000000..ff0ebfc --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-info-card.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@spike/ui/card"; +import { Mail, Clock, User } from "lucide-react"; + +interface UserInfoCardProps { + email: string; + hoursInShop: number; +} + +export function UserInfoCard({ email, hoursInShop }: UserInfoCardProps) { + return ( + + + + + User Information + + + +
+ +
+

Email

+

{email}

+
+
+
+ +
+

Total Hours in Shop

+

+ {hoursInShop.toFixed(1)} hours +

+
+
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/utils.ts b/apps/attendance/src/app/admin/dashboard/_components/user-detail/utils.ts new file mode 100644 index 0000000..ea5802d --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/utils.ts @@ -0,0 +1,80 @@ +import type { UserDetail, WeeklyAttendance } from './types'; + +export const getInitials = (name: string) => { + return name + .split(' ') + .map(word => word.charAt(0)) + .join('') + .toUpperCase() + .slice(0, 2); +}; + +export const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString('en-US', { + weekday: 'short', + month: 'short', + day: 'numeric', + year: 'numeric' + }); +}; + +export const formatTime = (timeString: string | null) => { + if (!timeString) return '--:--'; + return timeString; +}; + +export const getAttendanceStatus = (checkIn: string | null, checkOut: string | null) => { + if (checkIn && checkOut) return 'complete'; + return 'absent'; +}; + +export const getWeekStart = (date: Date) => { + const d = new Date(date); + const day = d.getDay(); + const diff = d.getDate() - day; + return new Date(d.setDate(diff)); +}; + +export const getWeekEnd = (weekStart: Date) => { + const weekEnd = new Date(weekStart); + weekEnd.setDate(weekStart.getDate() + 6); + return weekEnd; +}; + +export const formatWeekRange = (start: Date, end: Date) => { + const startStr = start.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); + const endStr = end.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + return `${startStr} - ${endStr}`; +}; + +export const groupAttendanceByWeek = (user: UserDetail): WeeklyAttendance[] => { + const weekMap = new Map(); + + user.attendanceRecords.forEach(record => { + const recordDate = new Date(record.date); + const weekStart = getWeekStart(recordDate); + const weekEnd = getWeekEnd(weekStart); + const weekKey = weekStart.toISOString().split('T')[0]; + + if (!weekMap.has(weekKey)) { + weekMap.set(weekKey, { + weekStart, + weekEnd, + weekLabel: formatWeekRange(weekStart, weekEnd), + records: [], + summary: { present: 0, absent: 0, total: 0 } + }); + } + + const week = weekMap.get(weekKey)!; + week.records.push(record); + + const status = getAttendanceStatus(record.checkInTime, record.checkOutTime); + week.summary.total++; + if (status === 'complete') week.summary.present++; + else week.summary.absent++; + }); + + return Array.from(weekMap.values()).sort((a, b) => b.weekStart.getTime() - a.weekStart.getTime()); +}; \ No newline at end of file diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx new file mode 100644 index 0000000..2079b92 --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@spike/ui/card"; +import { Badge } from "@spike/ui/badge"; +import { Calendar } from "lucide-react"; +import { WeeklyAttendanceItem } from "./weekly-attendance-item"; +import type { WeeklyAttendance } from "./types"; + +interface WeeklyAttendanceCardProps { + weeklyData: WeeklyAttendance[]; + expandedWeeks: Set; + onToggleWeek: (weekKey: string) => void; +} + +export function WeeklyAttendanceCard({ + weeklyData, + expandedWeeks, + onToggleWeek, +}: WeeklyAttendanceCardProps) { + return ( + + + + + Weekly Attendance + + {weeklyData.length} weeks + + + + + {weeklyData.length === 0 ? ( +
+ +

No attendance records found

+
+ ) : ( +
+ {weeklyData.map((week) => { + const weekKey = week.weekStart.toISOString().split("T")[0]; + const isExpanded = expandedWeeks.has(weekKey); + + return ( + onToggleWeek(weekKey)} + /> + ); + })} +
+ )} +
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx new file mode 100644 index 0000000..cc2e9ae --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { Badge } from "@spike/ui/badge"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@spike/ui/collapsible"; +import { ChevronDown, ChevronRight } from "lucide-react"; +import { AttendanceRecord } from "./attendance-record"; +import type { WeeklyAttendance } from "./types"; + +interface WeeklyAttendanceItemProps { + week: WeeklyAttendance; + isExpanded: boolean; + onToggle: () => void; +} + +export function WeeklyAttendanceItem({ + week, + isExpanded, + onToggle, +}: WeeklyAttendanceItemProps) { + return ( + + +
+
+
+ {isExpanded ? ( + + ) : ( + + )} +
+

{week.weekLabel}

+

+ {week.summary.present} present, {week.summary.absent} absent +

+
+
+
+ + {week.summary.present} + + + {week.summary.absent} + +
+
+
+
+ +
+ {week.records.map((record, recordIndex) => ( + + ))} +
+
+
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-table.tsx b/apps/attendance/src/app/admin/dashboard/_components/user-table.tsx new file mode 100644 index 0000000..80cd94c --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/_components/user-table.tsx @@ -0,0 +1,124 @@ +"use client"; + +import { ColumnDef } from "@tanstack/table-core"; +import React, { useState } from "react"; +import { Eye, Download } from "lucide-react"; +import { Button } from "@spike/ui/button"; +import { useDataTable } from "@/hooks/use-data-table"; +import { DataTable } from "@/components/data-table/data-table"; +import UserDetailSheet from "@/app/admin/dashboard/_components/user-detail/user-detail-sheet"; + +export interface TableUser { + id: string; + userName: string; + hours: number; +} + +export function UserTable({ users }: { users: TableUser[] }) { + const [open, setOpen] = useState(false); + const [selectedUser, setSelectedUser] = useState(null); + + const exportToCSV = () => { + // Create CSV content + const headers = ["ID", "Name", "Hours"]; + const csvContent = [ + headers.join(","), + ...users.map((user) => + [ + user.id, + `"${user.userName}"`, // Wrap name in quotes to handle commas + user.hours, + ].join(","), + ), + ].join("\n"); + + // Create and download file + const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); + const link = document.createElement("a"); + + if (link.download !== undefined) { + const url = URL.createObjectURL(blob); + link.setAttribute("href", url); + link.setAttribute( + "download", + `user-attendance-${new Date().toISOString().split("T")[0]}.csv`, + ); + link.style.visibility = "hidden"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + }; + + const columns = React.useMemo[]>( + () => [ + { + id: "name", + accessorKey: "userName", + header: "User Name", + cell: (info) => info.getValue(), + }, + { + accessorKey: "hours", + header: "Hours In Shop", + cell: (info) => info.getValue(), + }, + { + id: "actions", + cell: function Cell({ row }) { + return ( + + ); + }, + size: 32, + }, + ], + [], + ); + + const { table } = useDataTable({ + data: users, + columns, + pageCount: 1, + initialState: { + sorting: [{ id: "hours", desc: true }], + columnPinning: { right: ["actions"] }, + }, + getRowId: (row) => row.id, + }); + + return ( +
+
+

Users

+ +
+ + + + +
+ ); +} diff --git a/apps/attendance/src/app/admin/dashboard/page.tsx b/apps/attendance/src/app/admin/dashboard/page.tsx new file mode 100644 index 0000000..097fb3e --- /dev/null +++ b/apps/attendance/src/app/admin/dashboard/page.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; +import { AdminHeader } from "@/app/admin/dashboard/_components/admin-header"; +import { + TableUser, + UserTable, +} from "@/app/admin/dashboard/_components/user-table"; +import { Card, CardContent, CardHeader } from "@spike/ui/card"; +import FloatingSettings from "@/app/admin/dashboard/_components/floating-settings"; +import { calculateHours } from "@/app/admin/dashboard/_actions/server-actions"; +import { auth } from "@spike/auth"; + +async function AdminPage(props: any) { + const session = await auth.api.getSession({ + headers: await headers(), + }); + + if (!session || !session.user || session.user.role !== "admin") { + redirect("/"); + } + + const users: TableUser[] = []; + + const rawUsers = await auth.api.listUsers({ + query: {}, + headers: await headers(), + }); + + for (const user of rawUsers.users) { + let hoursWorked = await calculateHours(user.id); + + if (hoursWorked === null || isNaN(hoursWorked)) { + hoursWorked = 0; + } + + if (hoursWorked < 0) { + hoursWorked = 0; + } + + hoursWorked = Math.round((hoursWorked + Number.EPSILON) * 100) / 100; + + users.push({ + id: user.id, + userName: user.name || user.email || "Unknown", + hours: hoursWorked, + }); + } + + return ( +
+
+ + + + + +

+ User Attendance Overview +

+
+ + + +
+
+
+ ); +} + +export default AdminPage; diff --git a/apps/attendance/src/app/admin/settings/_actions/server-actions.ts b/apps/attendance/src/app/admin/settings/_actions/server-actions.ts new file mode 100644 index 0000000..059d165 --- /dev/null +++ b/apps/attendance/src/app/admin/settings/_actions/server-actions.ts @@ -0,0 +1,29 @@ +"use server"; + +import { db, shop_days } from "@spike/db"; +import { eq } from "drizzle-orm"; + +export async function updateShopDays(days: string[]) { + // get current shop days + const currentDays = await db.select().from(shop_days); + const currentDayNames = currentDays.map((d) => d.day); + + // delete days that are no longer selected + for (const day of currentDayNames) { + if (!days.includes(day)) { + await db.delete(shop_days).where(eq(shop_days.day, day)); + } + } + + // insert new days + for (const day of days) { + if (!currentDayNames.includes(day)) { + await db.insert(shop_days).values({ day }); + } + } +} + +export async function getShopDays() { + const days = await db.select().from(shop_days); + return days.map((d) => d.day); +} diff --git a/apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx b/apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx new file mode 100644 index 0000000..e583ba4 --- /dev/null +++ b/apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx @@ -0,0 +1,21 @@ +"use client"; + +import React from 'react'; +import DaySelector from "@/app/admin/settings/_components/day-selector"; +import {updateShopDays} from "@/app/admin/settings/_actions/server-actions"; + +function DaySelectionHandler({ selectedDays }: { selectedDays: string[] }) { + const handleDaySelection = (days: string[]) => { + updateShopDays(days).catch((error) => { + console.error("Failed to update shop days:", error); + }); + }; + + return ( +
+ +
+ ); +} + +export default DaySelectionHandler; \ No newline at end of file diff --git a/apps/attendance/src/app/admin/settings/_components/day-selector.tsx b/apps/attendance/src/app/admin/settings/_components/day-selector.tsx new file mode 100644 index 0000000..dc82b1a --- /dev/null +++ b/apps/attendance/src/app/admin/settings/_components/day-selector.tsx @@ -0,0 +1,132 @@ +"use client"; + +import React, { useState } from "react"; +import { Button } from "@spike/ui/button"; +import { Badge } from "@spike/ui/badge"; +import { Card, CardContent, CardHeader, CardTitle } from "@spike/ui/card"; +import { cn } from "@spike/ui/utils"; + +interface DaySelectorProps { + onSelectionChange: (selectedDays: string[]) => void; + initialSelection?: string[]; + className?: string; +} + +const DaySelector: React.FC = ({ + onSelectionChange, + initialSelection = [], + className = "", +}) => { + const daysOfWeek = [ + { full: "Sunday", short: "Sun" }, + { full: "Monday", short: "Mon" }, + { full: "Tuesday", short: "Tue" }, + { full: "Wednesday", short: "Wed" }, + { full: "Thursday", short: "Thu" }, + { full: "Friday", short: "Fri" }, + { full: "Saturday", short: "Sat" }, + ]; + + const [selectedDays, setSelectedDays] = useState(initialSelection); + + const toggleDay = (day: string) => { + const updatedSelection = selectedDays.includes(day) + ? selectedDays.filter((d) => d !== day) + : [...selectedDays, day]; + + setSelectedDays(updatedSelection); + onSelectionChange(updatedSelection); + }; + + const clearAll = () => { + setSelectedDays([]); + onSelectionChange([]); + }; + + const selectAll = () => { + const allDays = daysOfWeek.map((day) => day.full); + setSelectedDays(allDays); + onSelectionChange(allDays); + }; + + return ( + + + + Select Shop Days + + + +
+ {daysOfWeek.map((day) => ( + + ))} +
+ + {/* Control Buttons */} +
+ + +
+ + {/* Selected Days Display */} + {selectedDays.length > 0 && ( +
+

+ Selected Days ({selectedDays.length}): +

+
+ {selectedDays.map((day) => ( + toggleDay(day)} + > + {day} + × + + ))} +
+
+ )} + + {selectedDays.length === 0 && ( +

+ No days selected +

+ )} +
+
+ ); +}; + +export default DaySelector; diff --git a/apps/attendance/src/app/admin/settings/page.tsx b/apps/attendance/src/app/admin/settings/page.tsx new file mode 100644 index 0000000..e000ad0 --- /dev/null +++ b/apps/attendance/src/app/admin/settings/page.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; +import { AdminHeader } from "@/app/admin/dashboard/_components/admin-header"; +import DaySelectionHandler from "@/app/admin/settings/_components/day-selection-handler"; +import { getShopDays } from "@/app/admin/settings/_actions/server-actions"; +import { auth } from "@spike/auth"; + +async function SettingsPage(props: any) { + const session = await auth.api.getSession({ + headers: await headers(), + }); + + if (!session || !session.user || session.user.role !== "admin") { + redirect("/"); + } + + const selectedDays = await getShopDays(); + + return ( +
+
+ + + +
+
+ ); +} + +export default SettingsPage; diff --git a/apps/attendance/src/app/api/cron/route.ts b/apps/attendance/src/app/api/cron/route.ts new file mode 100644 index 0000000..25e9b5b --- /dev/null +++ b/apps/attendance/src/app/api/cron/route.ts @@ -0,0 +1,106 @@ +import {attendance, db, shop_days, user} from "@/index"; +import { and, eq, isNull, isNotNull } from 'drizzle-orm'; +import { DateTime } from 'luxon'; + +const SECURITY_KEY = process.env.CRON_SECURITY_KEY || 'default_secret_key'; + +export async function POST(request: Request) { + const headerKey = request.headers.get('x-cron-key'); + + if (headerKey !== SECURITY_KEY) { + console.log("[Cron] Unauthorized access attempt"); + return new Response("Unauthorized", { status: 401 }); + } + + console.log("[Cron] Job started"); + + const estNow = DateTime.now().setZone('America/New_York'); + const today = estNow.toFormat('yyyy-MM-dd'); // Format as YYYY-MM-DD string + + const checkoutTime = estNow + .set({ hour: 17, minute: 0, second: 0, millisecond: 0 }) // 5:00 PM EST + .toUTC() // convert to UTC + .toJSDate(); // convert to JS Date object + + if (await isShopDay(new Date())) { + console.log("[Cron] Today is a shop day"); + + const users = await db.select().from(user).execute(); + console.log(`[Cron] Found ${users.length} users`); + + for (const u of users) { + // Check if user has any attendance record for today + const todayAttendance = await db + .select() + .from(attendance) + .where( + and( + eq(attendance.userId, u.id), + eq(attendance.date, today) + ) + ) + .limit(1) + .execute(); + + if (todayAttendance.length === 0) { + // User has no attendance record for today - mark as absent + console.log(`[Cron] User ${u.id} did not check in. Marking absent.`); + await db.insert(attendance).values({ + userId: u.id, + date: today, + checkInTime: null, + checkOutTime: null, + status: 'absent', + }); + } + } + } else { + console.log("[Cron] Today is not a shop day"); + } + + console.log("[Cron] Checking for users who checked in but not out"); + + // Find users who have checked in today but haven't checked out + const checkedInNotOutUsers = await db + .select() + .from(attendance) + .where( + and( + eq(attendance.date, today), + isNotNull(attendance.checkInTime), // Has checked in + isNull(attendance.checkOutTime) // But hasn't checked out + ) + ) + .execute(); + + console.log(`[Cron] Found ${checkedInNotOutUsers.length} users to auto check-out`); + + for (const record of checkedInNotOutUsers) { + console.log(`[Cron] Auto check-out for user ${record.userId}`); + + // Update the existing record to add check-out time + await db + .update(attendance) + .set({ + checkOutTime: checkoutTime, + updatedAt: new Date(), + }) + .where(eq(attendance.id, record.id)); + } + + console.log("[Cron] Job completed successfully"); + return new Response("OK", { status: 200 }); +} + +async function isShopDay(date: Date): Promise { + const dayName = date.toLocaleDateString('en-US', { weekday: 'long' }).toLowerCase(); + + const shopDay = await db + .select() + .from(shop_days) + .where(eq(shop_days.day, dayName)) + .limit(1) + .execute(); + + return shopDay.length > 0; +} \ No newline at end of file diff --git a/apps/attendance/src/app/favicon.ico b/apps/attendance/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..76a084763024e43e80dca0423d3a74672526c903 GIT binary patch literal 4022 zcmbVP32>C<8U7IvMjf>}TJ1O(w6wN3YFkuVDM*DDIV8tsH@o}q{`bGx91xOl1Y$@) zIV4nZ8VryF!$R(mO#(vhfQArJ6v`a}Qk~kFGNs2Tw!qWpTUe@NXFBPhdGa6M@x0&r zz3=mWD@nun7c)i@|D~;WNz(n2Bt1sN5R(Y;J#w*aD3>hDl4V&DM8_G1@rYrXuj!`Q zNOTeH#4k+KjC4^oT{3Jd0-bl8_T}b(8;+7BDxH`-hHH_fY61@{b_bT+1|%b3PcBRcu<;ay}p7{Y%|8p%KCc(f&1j z$@LHN{j$b3(MPrQqnc&>Y$yhhe-imWCqKCbk9k$xO_OsP78FB)Vyf_%I(*_?7hpfr zpe^hd8Dqibcs}>gj)#ZHf1ha>74BsFYJ6HcT#5>nbIYa;XU1YA<-ZQsx>C5xnvhW1 z499C*kXW!Dv6&0u)omEM0gqz98Ju^`Wv3SDs(W<6lDU5(j{bWrFoq=oE07|k8NQK; zy1v#~RCZO~c>uF>*I`!1BFtH_36Am(Bv$N)ulXp{edpmPyv;`t8|vZry@*}05pz

asU8_wVXEzlXo; zJe<|NNNnhZFMJqE(-HVO&Ojlr-aW|nLCE{g!P9XXa`R!R;Sb@eIf#VnccJ%OfWGS( z5;B(k)zGyZ!!&BSk42hk-1>C`bjG`87}Lq$Ppp!4^JLQ6El|5YAqRO{Parb#JzTv)Is5Rv+Eqpo zdOqRY18}vSi1531a&For=2yDNNhmk=!O{3W95n|Z?>zyza{xZ}@o^4sSqtoxbmoSA zjl6$me!WfHd6VBXe?*_`^r^}ptv~=P*Q~<2ZFR7!dtfxZ4`+ERTos*A>igl^SO=rw zJ^J}9V{ec?AB4Mo5N6#0xQoJ&!|y|>=z_bv4O;jR^r~(wc)bYOi*t~dUvPzMolovN zh+$EFB|TX(0@f(n@P?}E@T&@TR8^z4aTiMJ8?m{%4rMisDBV$yP(uUW4AmmJs0P~G zyWrot2gw_&u&J^R+iM$9QeBVIx_XpXH=wjOj8J_OvR4!!&h0_=!o^s+bZL&*d-6x= zCGtwk@?O5huc;xSGdVdqcrB}Ze+i*3VL!1^7B_Bb3x`?s-oSjYl2%~erAT? zZ?Te-F>mD>sO9z0I+$DB>zB6ghsOPXk^Y_aRx^w?`n&xk#!h+><6fA-csR~HJdHV< z!ie420Hy6D5;#WhW^A;dfPZ5x(w476dip$Y-L2H;2}BGrjNAg#iG{=|Ro4)ow-#|b zm~U;2FUG#BF*P+Gv)7g5zHyJ^d*h$P?7}UWoK}D-8HMcA4=3Zq z+eEGC9$?!ji_7QPGqa*ckEfZFL@xgLJzyszVIB(`O) z{iU+KaBb^^Z$}TFR?;x$>F06VJrClJdmq5VPfx)_Z2>&Bop4m_!R)uf@K6){`>YbmBCq+yh_hNyzlSpZ@c8Q7@LRX5IP^ zKkHJCI9FJPEm0Q}s0~*&BN<*zha)Wu8Z|`a-aDze_S^5EqOKW_JvSM@3@$`s;Z`_G zTHvHV9HB1E+!BWSw`G`?z6?(=Cbl)UBDu1Idi*gAuEE2cbf#uQQH-zX?>_qfH1~d1 zGy!PKSo)+&R_#Jp-iA--%*#O>^IYhRPx!937pdWcPTvg2sMHowANT*d{n;8?igRJ<$c5!<#OCjaeGek%|hW(R|#o-~3_ z$PK-p$8T$gvzGBk+Z8cq_aFFtfqO|FVH0)Vz2`XN`y4Sy{b!wOr=5KEao_x69JTxU z*z*xhnoAp$y57&8Rno_6!tXq%MK9A1h{s{SSyT3^ukFM&p{cP|@4_qW7aX)ftkuEV zm%v&WSM?qe8$V#2e*iD_IH~>+68J5y`T(3^Z@Db@JNGwWYa& zE6N?c|3u4r%v;%e&d-{s(Pv3bA2SDg$vb#sowR}Tsa$7L_#^sg! zA+rYismqaiz;VL&Njw`k>!`_m7FwNHwQt}FZ}9t3-spXTx#TI=j_%JQV?~`M5<>4G zY1dKeJbe%uPoJ`$DfETL`X8xHtWV;u(N8M-iMa67e{$Dp)~w_7)dAXk23}zg+x?YY z7oJt-kB{<3@3-GOcKF2kOI}LY9Nss#wDn6b>#fT5#8&kozOEl~RVy5YrEpU3{q=o} z#UoJmFvl4OO3P7r8v2pMHustecq{iKfn(-Yz6+IpSExk^6>a}WEZg1rLiXyJqep-D zmMCxZe)l6km5lbo(t|&K?mN%R8P<#?g?&o+gTMJ(kHZ<-bJ?A_7(R{KWCY;0Q<0Ru zh-bkiNLa8Kj(M3#2&TcqHkEbWn^gdBsFQX07}VyEFV0x;yP{`|lxYu5n08lk^8xAZ zF^@-Szi}TvYP2-w=d&cBTKe^D`JNXSteG+M)r}cbf{SJus@*~1ExR%&t03ieY1D|R3-hG0(;U)>+y3{Du>9PW(o9dP zB(JZQvJZVKO<(+~$QPBGc}1N0#o3;k`32ENG!o@R9??&THet_2p7}PLiu}-1bNt^} z=TKj$lf%eApY`I5`28V%cadX&h#^K2qJ~I3S7y;Cr}$hy`ranbuFNxJI_=SH`*9QiQ-_4MD4V<~X} literal 0 HcmV?d00001 diff --git a/apps/attendance/src/app/globals.css b/apps/attendance/src/app/globals.css new file mode 100644 index 0000000..dc98be7 --- /dev/null +++ b/apps/attendance/src/app/globals.css @@ -0,0 +1,122 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/attendance/src/app/layout.tsx b/apps/attendance/src/app/layout.tsx new file mode 100644 index 0000000..28c8a66 --- /dev/null +++ b/apps/attendance/src/app/layout.tsx @@ -0,0 +1,39 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; +import IOSInstallPrompt from "@/app/_components/install-app-ios"; +import { NuqsAdapter } from 'nuqs/adapters/next/app' + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Spike Attendance", + description: "Manage and track attendance with ease.", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + + {children} + + + ); +} diff --git a/apps/attendance/src/app/page.tsx b/apps/attendance/src/app/page.tsx new file mode 100644 index 0000000..1e2bcc6 --- /dev/null +++ b/apps/attendance/src/app/page.tsx @@ -0,0 +1,30 @@ +import AttendanceTracker from "@/app/_components/attendance-tracker"; +import { headers } from "next/headers"; +import { getCheckedInTime, getStatus } from "@/app/_actions/server-actions"; +import { auth } from "@spike/auth"; + +export default async function Home() { + const session = await auth.api.getSession({ + headers: await headers(), + }); + + let isCheckedIn = false; + let checkInTime: Date | undefined = undefined; + + if (session && session.user) { + isCheckedIn = (await getStatus(session.user.id)) === "checked-in"; + if (isCheckedIn) { + checkInTime = (await getCheckedInTime(session.user.id)) || undefined; + } + } + + return ( +

+ +
+ ); +} diff --git a/apps/attendance/src/components/data-table/data-table-action-bar.tsx b/apps/attendance/src/components/data-table/data-table-action-bar.tsx new file mode 100644 index 0000000..aa1f107 --- /dev/null +++ b/apps/attendance/src/components/data-table/data-table-action-bar.tsx @@ -0,0 +1,178 @@ +"use client"; + +import type { Table } from "@tanstack/react-table"; +import { Loader, X } from "lucide-react"; +import { AnimatePresence, motion } from "motion/react"; +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import { Button } from "@/components/ui/button"; +import { Separator } from "@/components/ui/separator"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; + +interface DataTableActionBarProps + extends React.ComponentProps { + table: Table; + visible?: boolean; + container?: Element | DocumentFragment | null; +} + +function DataTableActionBar({ + table, + visible: visibleProp, + container: containerProp, + children, + className, + ...props +}: DataTableActionBarProps) { + const [mounted, setMounted] = React.useState(false); + + React.useLayoutEffect(() => { + setMounted(true); + }, []); + + React.useEffect(() => { + function onKeyDown(event: KeyboardEvent) { + if (event.key === "Escape") { + table.toggleAllRowsSelected(false); + } + } + + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [table]); + + const container = + containerProp ?? (mounted ? globalThis.document?.body : null); + + if (!container) return null; + + const visible = + visibleProp ?? table.getFilteredSelectedRowModel().rows.length > 0; + + return ReactDOM.createPortal( + + {visible && ( + + {children} + + )} + , + container, + ); +} + +interface DataTableActionBarActionProps + extends React.ComponentProps { + tooltip?: string; + isPending?: boolean; +} + +function DataTableActionBarAction({ + size = "sm", + tooltip, + isPending, + disabled, + className, + children, + ...props +}: DataTableActionBarActionProps) { + const trigger = ( + + ); + + if (!tooltip) return trigger; + + return ( + + {trigger} + +

{tooltip}

+
+
+ ); +} + +interface DataTableActionBarSelectionProps { + table: Table; +} + +function DataTableActionBarSelection({ + table, +}: DataTableActionBarSelectionProps) { + const onClearSelection = React.useCallback(() => { + table.toggleAllRowsSelected(false); + }, [table]); + + return ( +
+ + {table.getFilteredSelectedRowModel().rows.length} selected + + + + + + + +

Clear selection

+ + + Esc + + +
+
+
+ ); +} + +export { + DataTableActionBar, + DataTableActionBarAction, + DataTableActionBarSelection, +}; diff --git a/apps/attendance/src/components/data-table/data-table-column-header.tsx b/apps/attendance/src/components/data-table/data-table-column-header.tsx new file mode 100644 index 0000000..1cfe107 --- /dev/null +++ b/apps/attendance/src/components/data-table/data-table-column-header.tsx @@ -0,0 +1,99 @@ +"use client"; + +import type { Column } from "@tanstack/react-table"; +import { + ChevronDown, + ChevronsUpDown, + ChevronUp, + EyeOff, + X, +} from "lucide-react"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { cn } from "@/lib/utils"; + +interface DataTableColumnHeaderProps + extends React.ComponentProps { + column: Column; + title: string; +} + +export function DataTableColumnHeader({ + column, + title, + className, + ...props +}: DataTableColumnHeaderProps) { + if (!column.getCanSort() && !column.getCanHide()) { + return
{title}
; + } + + return ( + + + {title} + {column.getCanSort() && + (column.getIsSorted() === "desc" ? ( + + ) : column.getIsSorted() === "asc" ? ( + + ) : ( + + ))} + + + {column.getCanSort() && ( + <> + column.toggleSorting(false)} + > + + Asc + + column.toggleSorting(true)} + > + + Desc + + {column.getIsSorted() && ( + column.clearSorting()} + > + + Reset + + )} + + )} + {column.getCanHide() && ( + column.toggleVisibility(false)} + > + + Hide + + )} + + + ); +} diff --git a/apps/attendance/src/components/data-table/data-table-date-filter.tsx b/apps/attendance/src/components/data-table/data-table-date-filter.tsx new file mode 100644 index 0000000..9291fa3 --- /dev/null +++ b/apps/attendance/src/components/data-table/data-table-date-filter.tsx @@ -0,0 +1,220 @@ +"use client"; + +import type { Column } from "@tanstack/react-table"; +import { CalendarIcon, XCircle } from "lucide-react"; +import * as React from "react"; +import type { DateRange } from "react-day-picker"; + +import { Button } from "@/components/ui/button"; +import { Calendar } from "@/components/ui/calendar"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Separator } from "@/components/ui/separator"; +import { formatDate } from "@/lib/format"; + +type DateSelection = Date[] | DateRange; + +function getIsDateRange(value: DateSelection): value is DateRange { + return value && typeof value === "object" && !Array.isArray(value); +} + +function parseAsDate(timestamp: number | string | undefined): Date | undefined { + if (!timestamp) return undefined; + const numericTimestamp = + typeof timestamp === "string" ? Number(timestamp) : timestamp; + const date = new Date(numericTimestamp); + return !Number.isNaN(date.getTime()) ? date : undefined; +} + +function parseColumnFilterValue(value: unknown) { + if (value === null || value === undefined) { + return []; + } + + if (Array.isArray(value)) { + return value.map((item) => { + if (typeof item === "number" || typeof item === "string") { + return item; + } + return undefined; + }); + } + + if (typeof value === "string" || typeof value === "number") { + return [value]; + } + + return []; +} + +interface DataTableDateFilterProps { + column: Column; + title?: string; + multiple?: boolean; +} + +export function DataTableDateFilter({ + column, + title, + multiple, +}: DataTableDateFilterProps) { + const columnFilterValue = column.getFilterValue(); + + const selectedDates = React.useMemo(() => { + if (!columnFilterValue) { + return multiple ? { from: undefined, to: undefined } : []; + } + + if (multiple) { + const timestamps = parseColumnFilterValue(columnFilterValue); + return { + from: parseAsDate(timestamps[0]), + to: parseAsDate(timestamps[1]), + }; + } + + const timestamps = parseColumnFilterValue(columnFilterValue); + const date = parseAsDate(timestamps[0]); + return date ? [date] : []; + }, [columnFilterValue, multiple]); + + const onSelect = React.useCallback( + (date: Date | DateRange | undefined) => { + if (!date) { + column.setFilterValue(undefined); + return; + } + + if (multiple && !("getTime" in date)) { + const from = date.from?.getTime(); + const to = date.to?.getTime(); + column.setFilterValue(from || to ? [from, to] : undefined); + } else if (!multiple && "getTime" in date) { + column.setFilterValue(date.getTime()); + } + }, + [column, multiple], + ); + + const onReset = React.useCallback( + (event: React.MouseEvent) => { + event.stopPropagation(); + column.setFilterValue(undefined); + }, + [column], + ); + + const hasValue = React.useMemo(() => { + if (multiple) { + if (!getIsDateRange(selectedDates)) return false; + return selectedDates.from || selectedDates.to; + } + if (!Array.isArray(selectedDates)) return false; + return selectedDates.length > 0; + }, [multiple, selectedDates]); + + const formatDateRange = React.useCallback((range: DateRange) => { + if (!range.from && !range.to) return ""; + if (range.from && range.to) { + return `${formatDate(range.from)} - ${formatDate(range.to)}`; + } + return formatDate(range.from ?? range.to); + }, []); + + const label = React.useMemo(() => { + if (multiple) { + if (!getIsDateRange(selectedDates)) return null; + + const hasSelectedDates = selectedDates.from || selectedDates.to; + const dateText = hasSelectedDates + ? formatDateRange(selectedDates) + : "Select date range"; + + return ( + + {title} + {hasSelectedDates && ( + <> + + {dateText} + + )} + + ); + } + + if (getIsDateRange(selectedDates)) return null; + + const hasSelectedDate = selectedDates.length > 0; + const dateText = hasSelectedDate + ? formatDate(selectedDates[0]) + : "Select date"; + + return ( + + {title} + {hasSelectedDate && ( + <> + + {dateText} + + )} + + ); + }, [selectedDates, multiple, formatDateRange, title]); + + return ( + + + + + + {multiple ? ( + + ) : ( + + )} + + + ); +} diff --git a/apps/attendance/src/components/data-table/data-table-pagination.tsx b/apps/attendance/src/components/data-table/data-table-pagination.tsx new file mode 100644 index 0000000..46d680f --- /dev/null +++ b/apps/attendance/src/components/data-table/data-table-pagination.tsx @@ -0,0 +1,112 @@ +import type { Table } from "@tanstack/react-table"; +import { + ChevronLeft, + ChevronRight, + ChevronsLeft, + ChevronsRight, +} from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { cn } from "@/lib/utils"; + +interface DataTablePaginationProps extends React.ComponentProps<"div"> { + table: Table; + pageSizeOptions?: number[]; +} + +export function DataTablePagination({ + table, + pageSizeOptions = [10, 20, 30, 40, 50], + className, + ...props +}: DataTablePaginationProps) { + return ( +
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+
+

Rows per page

+ +
+
+ Page {table.getState().pagination.pageIndex + 1} of{" "} + {table.getPageCount()} +
+
+ + + + +
+
+
+ ); +} diff --git a/apps/attendance/src/components/data-table/data-table-slider-filter.tsx b/apps/attendance/src/components/data-table/data-table-slider-filter.tsx new file mode 100644 index 0000000..505d934 --- /dev/null +++ b/apps/attendance/src/components/data-table/data-table-slider-filter.tsx @@ -0,0 +1,239 @@ +"use client"; + +import type { Column } from "@tanstack/react-table"; +import { PlusCircle, XCircle } from "lucide-react"; +import * as React from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Separator } from "@/components/ui/separator"; +import { Slider } from "@/components/ui/slider"; +import { cn } from "@/lib/utils"; + +interface Range { + min: number; + max: number; +} + +type RangeValue = [number, number]; + +function getIsValidRange(value: unknown): value is RangeValue { + return ( + Array.isArray(value) && + value.length === 2 && + typeof value[0] === "number" && + typeof value[1] === "number" + ); +} + +interface DataTableSliderFilterProps { + column: Column; + title?: string; +} + +export function DataTableSliderFilter({ + column, + title, +}: DataTableSliderFilterProps) { + const id = React.useId(); + + const columnFilterValue = getIsValidRange(column.getFilterValue()) + ? (column.getFilterValue() as RangeValue) + : undefined; + + const defaultRange = column.columnDef.meta?.range; + const unit = column.columnDef.meta?.unit; + + const { min, max, step } = React.useMemo(() => { + let minValue = 0; + let maxValue = 100; + + if (defaultRange && getIsValidRange(defaultRange)) { + [minValue, maxValue] = defaultRange; + } else { + const values = column.getFacetedMinMaxValues(); + if (values && Array.isArray(values) && values.length === 2) { + const [facetMinValue, facetMaxValue] = values; + if ( + typeof facetMinValue === "number" && + typeof facetMaxValue === "number" + ) { + minValue = facetMinValue; + maxValue = facetMaxValue; + } + } + } + + const rangeSize = maxValue - minValue; + const step = + rangeSize <= 20 + ? 1 + : rangeSize <= 100 + ? Math.ceil(rangeSize / 20) + : Math.ceil(rangeSize / 50); + + return { min: minValue, max: maxValue, step }; + }, [column, defaultRange]); + + const range = React.useMemo((): RangeValue => { + return columnFilterValue ?? [min, max]; + }, [columnFilterValue, min, max]); + + const formatValue = React.useCallback((value: number) => { + return value.toLocaleString(undefined, { maximumFractionDigits: 0 }); + }, []); + + const onFromInputChange = React.useCallback( + (event: React.ChangeEvent) => { + const numValue = Number(event.target.value); + if (!Number.isNaN(numValue) && numValue >= min && numValue <= range[1]) { + column.setFilterValue([numValue, range[1]]); + } + }, + [column, min, range], + ); + + const onToInputChange = React.useCallback( + (event: React.ChangeEvent) => { + const numValue = Number(event.target.value); + if (!Number.isNaN(numValue) && numValue <= max && numValue >= range[0]) { + column.setFilterValue([range[0], numValue]); + } + }, + [column, max, range], + ); + + const onSliderValueChange = React.useCallback( + (value: RangeValue) => { + if (Array.isArray(value) && value.length === 2) { + column.setFilterValue(value); + } + }, + [column], + ); + + const onReset = React.useCallback( + (event: React.MouseEvent) => { + if (event.target instanceof HTMLDivElement) { + event.stopPropagation(); + } + column.setFilterValue(undefined); + }, + [column], + ); + + return ( + + + + + +
+

+ {title} +

+
+ +
+ + {unit && ( + + {unit} + + )} +
+ +
+ + {unit && ( + + {unit} + + )} +
+
+ + +
+ +
+
+ ); +} diff --git a/apps/attendance/src/components/data-table/data-table-view-options.tsx b/apps/attendance/src/components/data-table/data-table-view-options.tsx new file mode 100644 index 0000000..69b90eb --- /dev/null +++ b/apps/attendance/src/components/data-table/data-table-view-options.tsx @@ -0,0 +1,85 @@ +"use client"; + +import type { Table } from "@tanstack/react-table"; +import { Check, ChevronsUpDown, Settings2 } from "lucide-react"; +import * as React from "react"; +import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { cn } from "@/lib/utils"; + +interface DataTableViewOptionsProps { + table: Table; +} + +export function DataTableViewOptions({ + table, +}: DataTableViewOptionsProps) { + const columns = React.useMemo( + () => + table + .getAllColumns() + .filter( + (column) => + typeof column.accessorFn !== "undefined" && column.getCanHide(), + ), + [table], + ); + + return ( + + + + + + + + + No columns found. + + {columns.map((column) => ( + + column.toggleVisibility(!column.getIsVisible()) + } + > + + {column.columnDef.meta?.label ?? column.id} + + + + ))} + + + + + + ); +} diff --git a/apps/attendance/src/components/data-table/data-table.tsx b/apps/attendance/src/components/data-table/data-table.tsx new file mode 100644 index 0000000..a3a9030 --- /dev/null +++ b/apps/attendance/src/components/data-table/data-table.tsx @@ -0,0 +1,101 @@ +import { flexRender, type Table as TanstackTable } from "@tanstack/react-table"; +import type * as React from "react"; + +import { DataTablePagination } from "@/components/data-table/data-table-pagination"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { cn } from "@/lib/utils"; +import {getCommonPinningStyles} from "@/lib/data-table"; + +interface DataTableProps extends React.ComponentProps<"div"> { + table: TanstackTable; + actionBar?: React.ReactNode; +} + +export function DataTable({ + table, + actionBar, + children, + className, + ...props +}: DataTableProps) { + return ( +
+ {children} +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ))} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+ + {actionBar && + table.getFilteredSelectedRowModel().rows.length > 0 && + actionBar} +
+
+ ); +} diff --git a/apps/attendance/src/config/data-table.ts b/apps/attendance/src/config/data-table.ts new file mode 100644 index 0000000..8220f33 --- /dev/null +++ b/apps/attendance/src/config/data-table.ts @@ -0,0 +1,82 @@ +export type DataTableConfig = typeof dataTableConfig; + +export const dataTableConfig = { + textOperators: [ + { label: "Contains", value: "iLike" as const }, + { label: "Does not contain", value: "notILike" as const }, + { label: "Is", value: "eq" as const }, + { label: "Is not", value: "ne" as const }, + { label: "Is empty", value: "isEmpty" as const }, + { label: "Is not empty", value: "isNotEmpty" as const }, + ], + numericOperators: [ + { label: "Is", value: "eq" as const }, + { label: "Is not", value: "ne" as const }, + { label: "Is less than", value: "lt" as const }, + { label: "Is less than or equal to", value: "lte" as const }, + { label: "Is greater than", value: "gt" as const }, + { label: "Is greater than or equal to", value: "gte" as const }, + { label: "Is between", value: "isBetween" as const }, + { label: "Is empty", value: "isEmpty" as const }, + { label: "Is not empty", value: "isNotEmpty" as const }, + ], + dateOperators: [ + { label: "Is", value: "eq" as const }, + { label: "Is not", value: "ne" as const }, + { label: "Is before", value: "lt" as const }, + { label: "Is after", value: "gt" as const }, + { label: "Is on or before", value: "lte" as const }, + { label: "Is on or after", value: "gte" as const }, + { label: "Is between", value: "isBetween" as const }, + { label: "Is relative to today", value: "isRelativeToToday" as const }, + { label: "Is empty", value: "isEmpty" as const }, + { label: "Is not empty", value: "isNotEmpty" as const }, + ], + selectOperators: [ + { label: "Is", value: "eq" as const }, + { label: "Is not", value: "ne" as const }, + { label: "Is empty", value: "isEmpty" as const }, + { label: "Is not empty", value: "isNotEmpty" as const }, + ], + multiSelectOperators: [ + { label: "Has any of", value: "inArray" as const }, + { label: "Has none of", value: "notInArray" as const }, + { label: "Is empty", value: "isEmpty" as const }, + { label: "Is not empty", value: "isNotEmpty" as const }, + ], + booleanOperators: [ + { label: "Is", value: "eq" as const }, + { label: "Is not", value: "ne" as const }, + ], + sortOrders: [ + { label: "Asc", value: "asc" as const }, + { label: "Desc", value: "desc" as const }, + ], + filterVariants: [ + "text", + "number", + "range", + "date", + "dateRange", + "boolean", + "select", + "multiSelect", + ] as const, + operators: [ + "iLike", + "notILike", + "eq", + "ne", + "inArray", + "notInArray", + "isEmpty", + "isNotEmpty", + "lt", + "lte", + "gt", + "gte", + "isBetween", + "isRelativeToToday", + ] as const, + joinOperators: ["and", "or"] as const, +}; diff --git a/apps/attendance/src/hooks/use-callback-ref.ts b/apps/attendance/src/hooks/use-callback-ref.ts new file mode 100644 index 0000000..b47cbfc --- /dev/null +++ b/apps/attendance/src/hooks/use-callback-ref.ts @@ -0,0 +1,27 @@ +import * as React from "react"; + +/** + * @see https://github.com/radix-ui/primitives/blob/main/packages/react/use-callback-ref/src/useCallbackRef.tsx + */ + +/** + * A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a + * prop or avoid re-executing effects when passed as a dependency + */ +function useCallbackRef unknown>( + callback: T | undefined, +): T { + const callbackRef = React.useRef(callback); + + React.useEffect(() => { + callbackRef.current = callback; + }); + + // https://github.com/facebook/react/issues/19240 + return React.useMemo( + () => ((...args) => callbackRef.current?.(...args)) as T, + [], + ); +} + +export { useCallbackRef }; diff --git a/apps/attendance/src/hooks/use-data-table.ts b/apps/attendance/src/hooks/use-data-table.ts new file mode 100644 index 0000000..b5022bb --- /dev/null +++ b/apps/attendance/src/hooks/use-data-table.ts @@ -0,0 +1,296 @@ +"use client"; + +import { + type ColumnFiltersState, + getCoreRowModel, + getFacetedMinMaxValues, + getFacetedRowModel, + getFacetedUniqueValues, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + type PaginationState, + type RowSelectionState, + type SortingState, + type TableOptions, + type TableState, + type Updater, + useReactTable, + type VisibilityState, +} from "@tanstack/react-table"; +import { + type Parser, + parseAsArrayOf, + parseAsInteger, + parseAsString, + type UseQueryStateOptions, + useQueryState, + useQueryStates, +} from "nuqs"; +import * as React from "react"; + +import { useDebouncedCallback } from "@/hooks/use-debounced-callback"; +import { getSortingStateParser } from "@/lib/parsers"; +import {ExtendedColumnSort} from "@/types/data-table"; + +const PAGE_KEY = "page"; +const PER_PAGE_KEY = "perPage"; +const SORT_KEY = "sort"; +const ARRAY_SEPARATOR = ","; +const DEBOUNCE_MS = 300; +const THROTTLE_MS = 50; + +interface UseDataTableProps + extends Omit< + TableOptions, + | "state" + | "pageCount" + | "getCoreRowModel" + | "manualFiltering" + | "manualPagination" + | "manualSorting" + >, + Required, "pageCount">> { + initialState?: Omit, "sorting"> & { + sorting?: ExtendedColumnSort[]; + }; + history?: "push" | "replace"; + debounceMs?: number; + throttleMs?: number; + clearOnDefault?: boolean; + enableAdvancedFilter?: boolean; + scroll?: boolean; + shallow?: boolean; + startTransition?: React.TransitionStartFunction; +} + +export function useDataTable(props: UseDataTableProps) { + const { + columns, + pageCount = -1, + initialState, + history = "replace", + debounceMs = DEBOUNCE_MS, + throttleMs = THROTTLE_MS, + clearOnDefault = false, + enableAdvancedFilter = false, + scroll = false, + shallow = true, + startTransition, + ...tableProps + } = props; + + const queryStateOptions = React.useMemo< + Omit, "parse"> + >( + () => ({ + history, + scroll, + shallow, + throttleMs, + debounceMs, + clearOnDefault, + startTransition, + }), + [ + history, + scroll, + shallow, + throttleMs, + debounceMs, + clearOnDefault, + startTransition, + ], + ); + + const [rowSelection, setRowSelection] = React.useState( + initialState?.rowSelection ?? {}, + ); + const [columnVisibility, setColumnVisibility] = + React.useState(initialState?.columnVisibility ?? {}); + + const [page, setPage] = useQueryState( + PAGE_KEY, + parseAsInteger.withOptions(queryStateOptions).withDefault(1), + ); + const [perPage, setPerPage] = useQueryState( + PER_PAGE_KEY, + parseAsInteger + .withOptions(queryStateOptions) + .withDefault(initialState?.pagination?.pageSize ?? 10), + ); + + const pagination: PaginationState = React.useMemo(() => { + return { + pageIndex: page - 1, // zero-based index -> one-based index + pageSize: perPage, + }; + }, [page, perPage]); + + const onPaginationChange = React.useCallback( + (updaterOrValue: Updater) => { + if (typeof updaterOrValue === "function") { + const newPagination = updaterOrValue(pagination); + void setPage(newPagination.pageIndex + 1); + void setPerPage(newPagination.pageSize); + } else { + void setPage(updaterOrValue.pageIndex + 1); + void setPerPage(updaterOrValue.pageSize); + } + }, + [pagination, setPage, setPerPage], + ); + + const columnIds = React.useMemo(() => { + return new Set( + columns.map((column) => column.id).filter(Boolean) as string[], + ); + }, [columns]); + + const [sorting, setSorting] = useQueryState( + SORT_KEY, + getSortingStateParser(columnIds) + .withOptions(queryStateOptions) + .withDefault(initialState?.sorting ?? []), + ); + + const onSortingChange = React.useCallback( + (updaterOrValue: Updater) => { + if (typeof updaterOrValue === "function") { + const newSorting = updaterOrValue(sorting); + setSorting(newSorting as ExtendedColumnSort[]); + } else { + setSorting(updaterOrValue as ExtendedColumnSort[]); + } + }, + [sorting, setSorting], + ); + + const filterableColumns = React.useMemo(() => { + if (enableAdvancedFilter) return []; + + return columns.filter((column) => column.enableColumnFilter); + }, [columns, enableAdvancedFilter]); + + const filterParsers = React.useMemo(() => { + if (enableAdvancedFilter) return {}; + + return filterableColumns.reduce< + Record | Parser> + >((acc, column) => { + if (column.meta?.options) { + acc[column.id ?? ""] = parseAsArrayOf( + parseAsString, + ARRAY_SEPARATOR, + ).withOptions(queryStateOptions); + } else { + acc[column.id ?? ""] = parseAsString.withOptions(queryStateOptions); + } + return acc; + }, {}); + }, [filterableColumns, queryStateOptions, enableAdvancedFilter]); + + const [filterValues, setFilterValues] = useQueryStates(filterParsers); + + const debouncedSetFilterValues = useDebouncedCallback( + (values: typeof filterValues) => { + void setPage(1); + void setFilterValues(values); + }, + debounceMs, + ); + + const initialColumnFilters: ColumnFiltersState = React.useMemo(() => { + if (enableAdvancedFilter) return []; + + return Object.entries(filterValues).reduce( + (filters, [key, value]) => { + if (value !== null) { + const processedValue = Array.isArray(value) + ? value + : typeof value === "string" && /[^a-zA-Z0-9]/.test(value) + ? value.split(/[^a-zA-Z0-9]+/).filter(Boolean) + : [value]; + + filters.push({ + id: key, + value: processedValue, + }); + } + return filters; + }, + [], + ); + }, [filterValues, enableAdvancedFilter]); + + const [columnFilters, setColumnFilters] = + React.useState(initialColumnFilters); + + const onColumnFiltersChange = React.useCallback( + (updaterOrValue: Updater) => { + if (enableAdvancedFilter) return; + + setColumnFilters((prev) => { + const next = + typeof updaterOrValue === "function" + ? updaterOrValue(prev) + : updaterOrValue; + + const filterUpdates = next.reduce< + Record + >((acc, filter) => { + if (filterableColumns.find((column) => column.id === filter.id)) { + acc[filter.id] = filter.value as string | string[]; + } + return acc; + }, {}); + + for (const prevFilter of prev) { + if (!next.some((filter) => filter.id === prevFilter.id)) { + filterUpdates[prevFilter.id] = null; + } + } + + debouncedSetFilterValues(filterUpdates); + return next; + }); + }, + [debouncedSetFilterValues, filterableColumns, enableAdvancedFilter], + ); + + const table = useReactTable({ + ...tableProps, + columns, + initialState, + pageCount, + state: { + pagination, + sorting, + columnVisibility, + rowSelection, + columnFilters, + }, + defaultColumn: { + ...tableProps.defaultColumn, + enableColumnFilter: false, + }, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + onPaginationChange, + onSortingChange, + onColumnFiltersChange, + onColumnVisibilityChange: setColumnVisibility, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFacetedRowModel: getFacetedRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + getFacetedMinMaxValues: getFacetedMinMaxValues(), + manualPagination: true, + manualSorting: true, + manualFiltering: true, + }); + + return { table, shallow, debounceMs, throttleMs }; +} diff --git a/apps/attendance/src/hooks/use-debounced-callback.ts b/apps/attendance/src/hooks/use-debounced-callback.ts new file mode 100644 index 0000000..784524b --- /dev/null +++ b/apps/attendance/src/hooks/use-debounced-callback.ts @@ -0,0 +1,28 @@ +import * as React from "react"; + +import { useCallbackRef } from "@/hooks/use-callback-ref"; + +export function useDebouncedCallback unknown>( + callback: T, + delay: number, +) { + const handleCallback = useCallbackRef(callback); + const debounceTimerRef = React.useRef(0); + React.useEffect( + () => () => window.clearTimeout(debounceTimerRef.current), + [], + ); + + const setValue = React.useCallback( + (...args: Parameters) => { + window.clearTimeout(debounceTimerRef.current); + debounceTimerRef.current = window.setTimeout( + () => handleCallback(...args), + delay, + ); + }, + [handleCallback, delay], + ); + + return setValue; +} diff --git a/apps/attendance/src/lib/data-table.ts b/apps/attendance/src/lib/data-table.ts new file mode 100644 index 0000000..0fc1d1b --- /dev/null +++ b/apps/attendance/src/lib/data-table.ts @@ -0,0 +1,77 @@ +import type { Column } from "@tanstack/react-table"; +import { dataTableConfig } from "@/config/data-table"; +import type { + ExtendedColumnFilter, + FilterOperator, + FilterVariant, +} from "@/types/data-table"; + +export function getCommonPinningStyles({ + column, + withBorder = false, +}: { + column: Column; + withBorder?: boolean; +}): React.CSSProperties { + const isPinned = column.getIsPinned(); + const isLastLeftPinnedColumn = + isPinned === "left" && column.getIsLastColumn("left"); + const isFirstRightPinnedColumn = + isPinned === "right" && column.getIsFirstColumn("right"); + + return { + boxShadow: withBorder + ? isLastLeftPinnedColumn + ? "-4px 0 4px -4px hsl(var(--border)) inset" + : isFirstRightPinnedColumn + ? "4px 0 4px -4px hsl(var(--border)) inset" + : undefined + : undefined, + left: isPinned === "left" ? `${column.getStart("left")}px` : undefined, + right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined, + opacity: isPinned ? 0.97 : 1, + position: isPinned ? "sticky" : "relative", + background: isPinned ? "hsl(var(--background))" : "hsl(var(--background))", + width: column.getSize(), + zIndex: isPinned ? 1 : 0, + }; +} + +export function getFilterOperators(filterVariant: FilterVariant) { + const operatorMap: Record< + FilterVariant, + { label: string; value: FilterOperator }[] + > = { + text: dataTableConfig.textOperators, + number: dataTableConfig.numericOperators, + range: dataTableConfig.numericOperators, + date: dataTableConfig.dateOperators, + dateRange: dataTableConfig.dateOperators, + boolean: dataTableConfig.booleanOperators, + select: dataTableConfig.selectOperators, + multiSelect: dataTableConfig.multiSelectOperators, + }; + + return operatorMap[filterVariant] ?? dataTableConfig.textOperators; +} + +export function getDefaultFilterOperator(filterVariant: FilterVariant) { + const operators = getFilterOperators(filterVariant); + + return operators[0]?.value ?? (filterVariant === "text" ? "iLike" : "eq"); +} + +export function getValidFilters( + filters: ExtendedColumnFilter[], +): ExtendedColumnFilter[] { + return filters.filter( + (filter) => + filter.operator === "isEmpty" || + filter.operator === "isNotEmpty" || + (Array.isArray(filter.value) + ? filter.value.length > 0 + : filter.value !== "" && + filter.value !== null && + filter.value !== undefined), + ); +} diff --git a/apps/attendance/src/lib/date-helper.tsx b/apps/attendance/src/lib/date-helper.tsx new file mode 100644 index 0000000..439729d --- /dev/null +++ b/apps/attendance/src/lib/date-helper.tsx @@ -0,0 +1,8 @@ +export function getCurrentDate(): string { + return new Date().toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric' + }) +} diff --git a/apps/attendance/src/lib/format.ts b/apps/attendance/src/lib/format.ts new file mode 100644 index 0000000..0e6b885 --- /dev/null +++ b/apps/attendance/src/lib/format.ts @@ -0,0 +1,17 @@ +export function formatDate( + date: Date | string | number | undefined, + opts: Intl.DateTimeFormatOptions = {}, +) { + if (!date) return ""; + + try { + return new Intl.DateTimeFormat("en-US", { + month: opts.month ?? "long", + day: opts.day ?? "numeric", + year: opts.year ?? "numeric", + ...opts, + }).format(new Date(date)); + } catch (_err) { + return ""; + } +} diff --git a/apps/attendance/src/lib/parsers.ts b/apps/attendance/src/lib/parsers.ts new file mode 100644 index 0000000..ea4f0dd --- /dev/null +++ b/apps/attendance/src/lib/parsers.ts @@ -0,0 +1,99 @@ +import { createParser } from "nuqs/server"; +import { z } from "zod"; + +import { dataTableConfig } from "@/config/data-table"; + +import type { + ExtendedColumnFilter, + ExtendedColumnSort, +} from "@/types/data-table"; + +const sortingItemSchema = z.object({ + id: z.string(), + desc: z.boolean(), +}); + +export const getSortingStateParser = ( + columnIds?: string[] | Set, +) => { + const validKeys = columnIds + ? columnIds instanceof Set + ? columnIds + : new Set(columnIds) + : null; + + return createParser({ + parse: (value) => { + try { + const parsed = JSON.parse(value); + const result = z.array(sortingItemSchema).safeParse(parsed); + + if (!result.success) return null; + + if (validKeys && result.data.some((item) => !validKeys.has(item.id))) { + return null; + } + + return result.data as ExtendedColumnSort[]; + } catch { + return null; + } + }, + serialize: (value) => JSON.stringify(value), + eq: (a, b) => + a.length === b.length && + a.every( + (item, index) => + item.id === b[index]?.id && item.desc === b[index]?.desc, + ), + }); +}; + +const filterItemSchema = z.object({ + id: z.string(), + value: z.union([z.string(), z.array(z.string())]), + variant: z.enum(dataTableConfig.filterVariants), + operator: z.enum(dataTableConfig.operators), + filterId: z.string(), +}); + +export type FilterItemSchema = z.infer; + +export const getFiltersStateParser = ( + columnIds?: string[] | Set, +) => { + const validKeys = columnIds + ? columnIds instanceof Set + ? columnIds + : new Set(columnIds) + : null; + + return createParser({ + parse: (value) => { + try { + const parsed = JSON.parse(value); + const result = z.array(filterItemSchema).safeParse(parsed); + + if (!result.success) return null; + + if (validKeys && result.data.some((item) => !validKeys.has(item.id))) { + return null; + } + + return result.data as ExtendedColumnFilter[]; + } catch { + return null; + } + }, + serialize: (value) => JSON.stringify(value), + eq: (a, b) => + a.length === b.length && + a.every( + (filter, index) => + filter.id === b[index]?.id && + filter.value === b[index]?.value && + filter.variant === b[index]?.variant && + filter.operator === b[index]?.operator, + ), + }); +}; diff --git a/apps/attendance/src/lib/types/attendance.ts b/apps/attendance/src/lib/types/attendance.ts new file mode 100644 index 0000000..7152bde --- /dev/null +++ b/apps/attendance/src/lib/types/attendance.ts @@ -0,0 +1,5 @@ +export interface AttendanceState { + isCheckedIn: boolean + checkInTime: string | null + checkOutTime: string | null +} diff --git a/apps/attendance/src/types/data-table.ts b/apps/attendance/src/types/data-table.ts new file mode 100644 index 0000000..3669c24 --- /dev/null +++ b/apps/attendance/src/types/data-table.ts @@ -0,0 +1,40 @@ +import type { ColumnSort, Row, RowData } from "@tanstack/react-table"; +import type { FilterItemSchema } from "@/lib/parsers"; +import {DataTableConfig} from "@/config/data-table"; + +declare module "@tanstack/react-table" { + // biome-ignore lint/correctness/noUnusedVariables: TValue is used in the ColumnMeta interface + interface ColumnMeta { + label?: string; + placeholder?: string; + variant?: FilterVariant; + options?: Option[]; + range?: [number, number]; + unit?: string; + icon?: React.FC>; + } +} + +export interface Option { + label: string; + value: string; + count?: number; + icon?: React.FC>; +} + +export type FilterOperator = DataTableConfig["operators"][number]; +export type FilterVariant = DataTableConfig["filterVariants"][number]; +export type JoinOperator = DataTableConfig["joinOperators"][number]; + +export interface ExtendedColumnSort extends Omit { + id: Extract; +} + +export interface ExtendedColumnFilter extends FilterItemSchema { + id: Extract; +} + +export interface DataTableRowAction { + row: Row; + variant: "update" | "delete"; +} diff --git a/apps/attendance/tsconfig.json b/apps/attendance/tsconfig.json new file mode 100644 index 0000000..03c211f --- /dev/null +++ b/apps/attendance/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + }, + "types": [ + "node" + ] + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts index 8a148e3..6121822 100644 --- a/packages/auth/src/auth.ts +++ b/packages/auth/src/auth.ts @@ -7,6 +7,7 @@ import { nextCookies } from 'better-auth/next-js'; import { admin, bearer, magicLink, organization } from 'better-auth/plugins'; import { baseAvatarPlugin } from './plugins/base-avatar-plugin'; +import sharedEnv from '@spike/env/env.shared'; const auth = betterAuth({ database: drizzleAdapter(db, { @@ -52,6 +53,12 @@ const auth = betterAuth({ 'https://auth.staging.spike.center', 'https://auth.spike.center', 'http://localhost:3001', + sharedEnv.ATTENDANCE_BASE_URL, + sharedEnv.AUTH_BASE_URL, + sharedEnv.LANDING_BASE_URL, + sharedEnv.DOCS_BASE_URL, + sharedEnv.LEARN_BASE_URL, + sharedEnv.SCOUT_BASE_URL ], emailVerification: { sendOnSignUp: false, diff --git a/packages/db/drizzle.config.ts b/packages/db/drizzle.config.ts index fdae44a..58f5847 100644 --- a/packages/db/drizzle.config.ts +++ b/packages/db/drizzle.config.ts @@ -9,6 +9,7 @@ export default defineConfig({ './src/db/platforms/lms.ts', './src/db/platforms/scout.ts', './src/db/platforms/landing.ts', + './src/db/platforms/attendance.ts', ], dialect: 'postgresql', dbCredentials: { diff --git a/packages/db/src/db/platforms/attendance.ts b/packages/db/src/db/platforms/attendance.ts new file mode 100644 index 0000000..fdfa168 --- /dev/null +++ b/packages/db/src/db/platforms/attendance.ts @@ -0,0 +1,30 @@ +import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { user } from "../auth"; + +export const attendance = pgTable("spikeattendance_attendance", { + id: integer("id").primaryKey().generatedByDefaultAsIdentity(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + date: text("date").notNull(), // Store as YYYY-MM-DD string + checkInTime: timestamp("check_in_time"), // Nullable - user might not have checked in yet + checkOutTime: timestamp("check_out_time"), // Nullable - user might not have checked out yet + status: text("status").notNull().default('present'), // 'present', 'absent' + createdAt: timestamp("created_at") + .$defaultFn(() => /* @__PURE__ */ new Date()) + .notNull(), + updatedAt: timestamp("updated_at") + .$defaultFn(() => /* @__PURE__ */ new Date()) + .notNull(), +}); + +export const shop_days = pgTable("spikeattendance_session_day", { + id: integer("id").primaryKey().generatedByDefaultAsIdentity(), + day: text("day").notNull(), + createdAt: timestamp("created_at") + .$defaultFn(() => /* @__PURE__ */ new Date()) + .notNull(), + updatedAt: timestamp("updated_at") + .$defaultFn(() => /* @__PURE__ */ new Date()) + .notNull(), +}); diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index 0885ed3..b723c98 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -6,6 +6,7 @@ import * as authSchema from './db/auth'; import * as notificationsSchema from './db/features/notifications'; import * as landingSchema from './db/platforms/landing'; import * as lmsSchema from './db/platforms/lms'; +import * as attendanceSchema from './db/platforms/attendance'; const pool = new Pool({ connectionString: serverEnv.DATABASE_URL, @@ -16,6 +17,7 @@ const fullSchema = { ...notificationsSchema, ...lmsSchema, ...landingSchema, + ...attendanceSchema, }; export const db = drizzle({ client: pool, schema: fullSchema }); @@ -25,4 +27,5 @@ export * from './db/auth'; export * from './db/features/notifications'; export * from './db/platforms/lms'; export * from './db/platforms/landing'; +export * from './db/platforms/attendance'; export { eq, lt, gte, ne, and, desc } from 'drizzle-orm'; diff --git a/packages/env/src/env.shared.ts b/packages/env/src/env.shared.ts index 9b7e3dc..5cbbdb0 100644 --- a/packages/env/src/env.shared.ts +++ b/packages/env/src/env.shared.ts @@ -8,6 +8,7 @@ const sharedEnvSchema = z.object({ LANDING_BASE_URL: z.string(), LEARN_BASE_URL: z.string(), SCOUT_BASE_URL: z.string(), + ATTENDANCE_BASE_URL: z.string(), }); const sharedEnv = sharedEnvSchema.parse({ @@ -17,6 +18,7 @@ const sharedEnv = sharedEnvSchema.parse({ LANDING_BASE_URL: process.env.NEXT_PUBLIC_LANDING_BASE_URL, LEARN_BASE_URL: process.env.NEXT_PUBLIC_LEARN_BASE_URL, SCOUT_BASE_URL: process.env.NEXT_PUBLIC_SCOUT_BASE_URL, + ATTENDANCE_BASE_URL: process.env.NEXT_PUBLIC_ATTENDANCE_BASE_URL, } satisfies Record, unknown>); export default sharedEnv; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f1c7ee..756c3f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,6 +76,118 @@ importers: specifier: 'catalog:' version: 5.8.3 + apps/attendance: + dependencies: + '@spike/api': + specifier: workspace:* + version: link:../../packages/api + '@spike/auth': + specifier: workspace:* + version: link:../../packages/auth + '@spike/client': + specifier: workspace:* + version: link:../../packages/client + '@spike/config': + specifier: workspace:* + version: link:../../packages/config + '@spike/db': + specifier: workspace:* + version: link:../../packages/db + '@spike/env': + specifier: workspace:* + version: link:../../packages/env + '@spike/next': + specifier: workspace:* + version: link:../../packages/next + '@spike/ui': + specifier: workspace:* + version: link:../../packages/ui + '@tanstack/table-core': + specifier: ^8.21.3 + version: 8.21.3 + '@types/luxon': + specifier: ^3.7.1 + version: 3.7.1 + better-auth: + specifier: ^1.3.4 + version: 1.3.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + dotenv: + specifier: ^17.2.1 + version: 17.2.1 + lucide-react: + specifier: ^0.537.0 + version: 0.537.0(react@19.1.1) + luxon: + specifier: ^3.7.1 + version: 3.7.1 + motion: + specifier: ^12.23.12 + version: 12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: + specifier: 15.3.5 + version: 15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next-pwa: + specifier: ^5.6.0 + version: 5.6.0(@babel/core@7.28.0)(@types/babel__core@7.20.5)(esbuild@0.25.8)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(webpack@5.101.0(esbuild@0.25.8)) + node-cron: + specifier: ^4.2.1 + version: 4.2.1 + nuqs: + specifier: ^2.4.3 + version: 2.4.3(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) + react: + specifier: ^19.0.0 + version: 19.1.1 + react-day-picker: + specifier: ^9.8.1 + version: 9.8.1(react@19.1.1) + react-dom: + specifier: ^19.0.0 + version: 19.1.1(react@19.1.1) + tailwind-merge: + specifier: ^3.3.1 + version: 3.3.1 + zod: + specifier: ^4.0.15 + version: 4.0.16 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4 + version: 4.1.11 + '@types/node': + specifier: ^20 + version: 20.19.10 + '@types/pg': + specifier: ^8.15.5 + version: 8.15.5 + '@types/react': + specifier: ^19 + version: 19.1.9 + '@types/react-dom': + specifier: ^19 + version: 19.1.7(@types/react@19.1.9) + drizzle-kit: + specifier: ^0.31.4 + version: 0.31.4 + tailwindcss: + specifier: ^4 + version: 4.1.11 + tsx: + specifier: ^4.20.3 + version: 4.20.3 + tw-animate-css: + specifier: ^1.3.6 + version: 1.3.6 + typescript: + specifier: 5.9.2 + version: 5.9.2 + wrangler: + specifier: ^4.28.1 + version: 4.28.1 + apps/auth: dependencies: '@spike/api': @@ -505,7 +617,7 @@ importers: version: 10.1.8(eslint@9.33.0(jiti@2.5.1)) eslint-plugin-prettier: specifier: ^5.5.4 - version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(prettier@3.6.2) + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(prettier@3.6.2) prettier: specifier: ^3.2.5 version: 3.6.2 @@ -1202,6 +1314,12 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@apideck/better-ajv-errors@0.3.6': + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + '@babel/code-frame@7.10.4': resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} @@ -1317,6 +1435,36 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': + resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1': + resolution: {integrity: sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/plugin-proposal-decorators@7.28.0': resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} engines: {node: '>=6.9.0'} @@ -1329,6 +1477,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -1373,6 +1527,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-attributes@7.27.1': resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} @@ -1443,6 +1603,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/plugin-transform-arrow-functions@7.27.1': resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} engines: {node: '>=6.9.0'} @@ -1461,6 +1627,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-block-scoping@7.28.0': resolution: {integrity: sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==} engines: {node: '>=6.9.0'} @@ -1473,6 +1645,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-class-static-block@7.27.1': + resolution: {integrity: sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + '@babel/plugin-transform-classes@7.28.0': resolution: {integrity: sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==} engines: {node: '>=6.9.0'} @@ -1491,6 +1669,42 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.0': + resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.27.1': + resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-export-namespace-from@7.27.1': resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} engines: {node: '>=6.9.0'} @@ -1515,6 +1729,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-json-strings@7.27.1': + resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-literals@7.27.1': resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} engines: {node: '>=6.9.0'} @@ -1527,18 +1747,48 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-modules-commonjs@7.27.1': resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-modules-systemjs@7.27.1': + resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} engines: {node: '>=6.9.0'} @@ -1557,6 +1807,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-optional-catch-binding@7.27.1': resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} engines: {node: '>=6.9.0'} @@ -1587,6 +1843,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-display-name@7.28.0': resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} engines: {node: '>=6.9.0'} @@ -1629,6 +1891,18 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-runtime@7.28.0': resolution: {integrity: sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==} engines: {node: '>=6.9.0'} @@ -1659,18 +1933,53 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typescript@7.28.0': resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-unicode-regex@7.27.1': resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.28.0': + resolution: {integrity: sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + '@babel/preset-react@7.27.1': resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==} engines: {node: '>=6.9.0'} @@ -1721,13 +2030,59 @@ packages: resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==} engines: {node: '>=18.0.0'} + '@cloudflare/unenv-preset@2.6.0': + resolution: {integrity: sha512-h7Txw0WbDuUbrvZwky6+x7ft+U/Gppfn/rWx6IdR+e9gjygozRJnV26Y2TOr3yrIFa6OsZqqR2lN+jWTrakHXg==} + peerDependencies: + unenv: 2.0.0-rc.19 + workerd: ^1.20250802.0 + peerDependenciesMeta: + workerd: + optional: true + + '@cloudflare/workerd-darwin-64@1.20250803.0': + resolution: {integrity: sha512-6QciMnJp1p3F1qUiN0LaLfmw7SuZA/gfUBOe8Ft81pw16JYZ3CyiqIKPJvc1SV8jgDx8r+gz/PRi1NwOMt329A==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20250803.0': + resolution: {integrity: sha512-DoIgghDowtqoNhL6OoN/F92SKtrk7mRQKc4YSs/Dst8IwFZq+pCShOlWfB0MXqHKPSoiz5xLSrUKR9H6gQMPvw==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20250803.0': + resolution: {integrity: sha512-mYdz4vNWX3+PoqRjssepVQqgh42IBiSrl+wb7vbh7VVWUVzBnQKtW3G+UFiBF62hohCLexGIEi7L0cFfRlcKSQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20250803.0': + resolution: {integrity: sha512-RmrtUYLRUg6djKU7Z6yebS6YGJVnaDVY6bbXca+2s26vw4ibJDOTPLuBHFQF62Grw3fAfsNbjQh5i14vG2mqUg==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20250803.0': + resolution: {integrity: sha512-uLV8gdudz36o9sUaAKbBxxTwZwLFz1KyW7QpBvOo4+r3Ib8yVKXGiySIMWGD7A0urSMrjf3e5LlLcJKgZUOjMA==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@date-fns/tz@1.3.1': + resolution: {integrity: sha512-LnBOyuj+piItX/D5BWBSckBsuZyOt7Jg2obGNiObq7qjl1A2/8F+i4RS8/MmkSdnw6hOe6afrJLCWrUWZw5Mlw==} + '@dependents/detective-less@5.0.1': resolution: {integrity: sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ==} engines: {node: '>=18'} @@ -1771,6 +2126,12 @@ packages: resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} deprecated: 'Merged into tsx: https://tsx.is' + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.25.5': resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} @@ -1789,6 +2150,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.25.5': resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} engines: {node: '>=18'} @@ -1807,6 +2174,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.25.5': resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} engines: {node: '>=18'} @@ -1825,6 +2198,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.25.5': resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} engines: {node: '>=18'} @@ -1843,6 +2222,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.25.5': resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} engines: {node: '>=18'} @@ -1861,6 +2246,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.25.5': resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} engines: {node: '>=18'} @@ -1879,6 +2270,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.25.5': resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} engines: {node: '>=18'} @@ -1897,6 +2294,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.5': resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} engines: {node: '>=18'} @@ -1915,7 +2318,13 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.5': + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.5': resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} engines: {node: '>=18'} cpu: [arm64] @@ -1933,6 +2342,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.25.5': resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} engines: {node: '>=18'} @@ -1951,6 +2366,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.25.5': resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} engines: {node: '>=18'} @@ -1969,6 +2390,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.25.5': resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} engines: {node: '>=18'} @@ -1987,6 +2414,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.25.5': resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} engines: {node: '>=18'} @@ -2005,6 +2438,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.25.5': resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} engines: {node: '>=18'} @@ -2023,6 +2462,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.25.5': resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} engines: {node: '>=18'} @@ -2041,6 +2486,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.25.5': resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} engines: {node: '>=18'} @@ -2059,6 +2510,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.25.5': resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} engines: {node: '>=18'} @@ -2071,6 +2528,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.25.5': resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} engines: {node: '>=18'} @@ -2089,6 +2552,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.5': resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} engines: {node: '>=18'} @@ -2101,6 +2570,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.25.5': resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} engines: {node: '>=18'} @@ -2119,6 +2594,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.5': resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} engines: {node: '>=18'} @@ -2143,6 +2624,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.25.5': resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} engines: {node: '>=18'} @@ -2161,6 +2648,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.25.5': resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} engines: {node: '>=18'} @@ -2179,6 +2672,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.25.5': resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} engines: {node: '>=18'} @@ -2197,6 +2696,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.25.5': resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} engines: {node: '>=18'} @@ -2389,33 +2894,65 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-arm64@0.34.3': resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + '@img/sharp-darwin-x64@0.34.3': resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.2.0': resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.0': resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} cpu: [x64] os: [darwin] + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linux-arm64@1.2.0': resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + '@img/sharp-libvips-linux-arm@1.2.0': resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} cpu: [arm] @@ -2426,32 +2963,64 @@ packages: cpu: [ppc64] os: [linux] + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + '@img/sharp-libvips-linux-s390x@1.2.0': resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} cpu: [s390x] os: [linux] + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + '@img/sharp-libvips-linux-x64@1.2.0': resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} cpu: [x64] os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.2.0': resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} cpu: [x64] os: [linux] + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linux-arm64@0.34.3': resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + '@img/sharp-linux-arm@0.34.3': resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2464,30 +3033,59 @@ packages: cpu: [ppc64] os: [linux] + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + '@img/sharp-linux-s390x@0.34.3': resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-linux-x64@0.34.3': resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linuxmusl-arm64@0.34.3': resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-linuxmusl-x64@0.34.3': resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + '@img/sharp-wasm32@0.34.3': resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2499,12 +3097,24 @@ packages: cpu: [arm64] os: [win32] + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-ia32@0.34.3': resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@img/sharp-win32-x64@0.34.3': resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2574,6 +3184,9 @@ packages: '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@levischuck/tiny-cbor@0.2.11': resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} @@ -2632,6 +3245,9 @@ packages: engines: {node: '>=18.14.0'} hasBin: true + '@next/env@15.3.5': + resolution: {integrity: sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g==} + '@next/env@15.4.5': resolution: {integrity: sha512-ruM+q2SCOVCepUiERoxOmZY9ZVoecR3gcXNwCYZRvQQWRjhOiPJGmQ2fAiLR6YKWXcSAh7G79KEFxN3rwhs4LQ==} @@ -2644,6 +3260,12 @@ packages: '@next/eslint-plugin-next@15.4.6': resolution: {integrity: sha512-2NOu3ln+BTcpnbIDuxx6MNq+pRrCyey4WSXGaJIyt0D2TYicHeO9QrUENNjcf673n3B1s7hsiV5xBYRCK1Q8kA==} + '@next/swc-darwin-arm64@15.3.5': + resolution: {integrity: sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-arm64@15.4.5': resolution: {integrity: sha512-84dAN4fkfdC7nX6udDLz9GzQlMUwEMKD7zsseXrl7FTeIItF8vpk1lhLEnsotiiDt+QFu3O1FVWnqwcRD2U3KA==} engines: {node: '>= 10'} @@ -2656,6 +3278,12 @@ packages: cpu: [arm64] os: [darwin] + '@next/swc-darwin-x64@15.3.5': + resolution: {integrity: sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-darwin-x64@15.4.5': resolution: {integrity: sha512-CL6mfGsKuFSyQjx36p2ftwMNSb8PQog8y0HO/ONLdQqDql7x3aJb/wB+LA651r4we2pp/Ck+qoRVUeZZEvSurA==} engines: {node: '>= 10'} @@ -2668,6 +3296,12 @@ packages: cpu: [x64] os: [darwin] + '@next/swc-linux-arm64-gnu@15.3.5': + resolution: {integrity: sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-gnu@15.4.5': resolution: {integrity: sha512-1hTVd9n6jpM/thnDc5kYHD1OjjWYpUJrJxY4DlEacT7L5SEOXIifIdTye6SQNNn8JDZrcN+n8AWOmeJ8u3KlvQ==} engines: {node: '>= 10'} @@ -2680,6 +3314,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-musl@15.3.5': + resolution: {integrity: sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-musl@15.4.5': resolution: {integrity: sha512-4W+D/nw3RpIwGrqpFi7greZ0hjrCaioGErI7XHgkcTeWdZd146NNu1s4HnaHonLeNTguKnL2Urqvj28UJj6Gqw==} engines: {node: '>= 10'} @@ -2692,6 +3332,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-x64-gnu@15.3.5': + resolution: {integrity: sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-gnu@15.4.5': resolution: {integrity: sha512-N6Mgdxe/Cn2K1yMHge6pclffkxzbSGOydXVKYOjYqQXZYjLCfN/CuFkaYDeDHY2VBwSHyM2fUjYBiQCIlxIKDA==} engines: {node: '>= 10'} @@ -2704,6 +3350,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-musl@15.3.5': + resolution: {integrity: sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-musl@15.4.5': resolution: {integrity: sha512-YZ3bNDrS8v5KiqgWE0xZQgtXgCTUacgFtnEgI4ccotAASwSvcMPDLua7BWLuTfucoRv6mPidXkITJLd8IdJplQ==} engines: {node: '>= 10'} @@ -2716,6 +3368,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-win32-arm64-msvc@15.3.5': + resolution: {integrity: sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-arm64-msvc@15.4.5': resolution: {integrity: sha512-9Wr4t9GkZmMNcTVvSloFtjzbH4vtT4a8+UHqDoVnxA5QyfWe6c5flTH1BIWPGNWSUlofc8dVJAE7j84FQgskvQ==} engines: {node: '>= 10'} @@ -2728,6 +3386,12 @@ packages: cpu: [arm64] os: [win32] + '@next/swc-win32-x64-msvc@15.3.5': + resolution: {integrity: sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@next/swc-win32-x64-msvc@15.4.5': resolution: {integrity: sha512-voWk7XtGvlsP+w8VBz7lqp8Y+dYw/MTI4KeS0gTVtfdhdJ5QwhXLmNrndFOin/MDoCvUaLWMkYKATaCoUkt2/A==} engines: {node: '>= 10'} @@ -3750,6 +4414,17 @@ packages: rollup: optional: true + '@rollup/plugin-babel@5.3.1': + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + '@rollup/plugin-commonjs@28.0.6': resolution: {integrity: sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -3777,6 +4452,12 @@ packages: rollup: optional: true + '@rollup/plugin-node-resolve@11.2.1': + resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + '@rollup/plugin-node-resolve@16.0.1': resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} engines: {node: '>=14.0.0'} @@ -3786,6 +4467,11 @@ packages: rollup: optional: true + '@rollup/plugin-replace@2.4.2': + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + '@rollup/plugin-replace@6.0.2': resolution: {integrity: sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==} engines: {node: '>=14.0.0'} @@ -3804,6 +4490,12 @@ packages: rollup: optional: true + '@rollup/pluginutils@3.1.0': + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + '@rollup/pluginutils@5.2.0': resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} engines: {node: '>=14.0.0'} @@ -3976,6 +4668,12 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@surma/rollup-plugin-off-main-thread@2.2.3': + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -4177,12 +4875,24 @@ packages: '@types/draco3d@1.4.10': resolution: {integrity: sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==} + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@0.0.39': + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/glob@7.2.0': + resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -4204,15 +4914,25 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/luxon@3.7.1': + resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + '@types/minimatch@6.0.0': + resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} + deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@20.19.10': + resolution: {integrity: sha512-iAFpG6DokED3roLSP0K+ybeDdIX6Bc0Vd3mLW5uDqThPWtNos3E+EqOM11mPQHKzfWHqEBuLjIlsBQQ8CsISmQ==} + '@types/node@22.17.1': resolution: {integrity: sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==} @@ -4257,6 +4977,9 @@ packages: '@types/react@19.1.9': resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==} + '@types/resolve@1.17.1': + resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -4272,6 +4995,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -4483,6 +5209,51 @@ packages: '@vue/shared@3.5.18': resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==} + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + '@webgpu/types@0.1.64': resolution: {integrity: sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==} @@ -4510,6 +5281,12 @@ packages: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + abbrev@3.0.1: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} engines: {node: ^18.17.0 || >=20.5.0} @@ -4527,11 +5304,26 @@ packages: peerDependencies: acorn: ^8 + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -4541,9 +5333,30 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + anser@1.4.10: resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} @@ -4622,6 +5435,18 @@ packages: array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + array-union@1.0.2: + resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} + engines: {node: '>=0.10.0'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array-uniq@1.0.3: + resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} + engines: {node: '>=0.10.0'} + array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -4677,6 +5502,10 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -4698,6 +5527,13 @@ packages: peerDependencies: '@babel/core': ^7.8.0 + babel-loader@8.4.1: + resolution: {integrity: sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==} + engines: {node: '>= 8.9'} + peerDependencies: + '@babel/core': ^7.0.0 + webpack: '>=2' + babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} @@ -4789,9 +5625,15 @@ packages: resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} engines: {node: '>=0.6'} + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + bplist-creator@0.1.0: resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} @@ -4841,6 +5683,10 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -4937,6 +5783,10 @@ packages: engines: {node: '>=12.13.0'} hasBin: true + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + chromium-edge-launcher@0.2.0: resolution: {integrity: sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==} @@ -4953,6 +5803,12 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + clean-webpack-plugin@4.0.0: + resolution: {integrity: sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==} + engines: {node: '>=10.0.0'} + peerDependencies: + webpack: '>=4.0.0 <6.0.0' + cli-cursor@2.1.0: resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} engines: {node: '>=4'} @@ -5052,6 +5908,10 @@ packages: common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -5225,6 +6085,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + date-fns-jalali@4.1.0-0: + resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} + date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} @@ -5314,6 +6177,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + del@4.1.1: + resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==} + engines: {node: '>=6'} + denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} @@ -5394,6 +6261,10 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -5545,8 +6416,13 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.199: - resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.199: + resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==} embla-carousel-react@8.6.0: resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} @@ -5567,6 +6443,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} @@ -5657,6 +6537,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.25.5: resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} @@ -5810,6 +6695,10 @@ packages: eslint: '>6.6.0' turbo: '>2.0.0' + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5859,6 +6748,10 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -5884,6 +6777,9 @@ packages: estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + estree-walker@1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -5920,6 +6816,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + exit-hook@2.2.1: + resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} + engines: {node: '>=6'} + expo-asset@11.1.7: resolution: {integrity: sha512-b5P8GpjUh08fRCf6m5XPVAh7ra42cQrHBIMgH2UXP+xsj4Wufl6pLy6jRF5w6U7DranUMbsXm8TOyq4EHy7ADg==} peerDependencies: @@ -6049,6 +6949,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -6086,6 +6989,9 @@ packages: file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -6098,6 +7004,10 @@ packages: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + find-up-simple@1.0.1: resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} engines: {node: '>=18'} @@ -6168,6 +7078,10 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -6275,6 +7189,9 @@ packages: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} @@ -6324,6 +7241,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -6344,10 +7264,18 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} + globby@6.1.0: + resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} + engines: {node: '>=0.10.0'} + glsl-noise@0.0.0: resolution: {integrity: sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==} @@ -6494,6 +7422,9 @@ packages: typescript: optional: true + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -6686,6 +7617,22 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + + is-path-cwd@2.2.0: + resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} + engines: {node: '>=6'} + + is-path-in-cwd@2.1.0: + resolution: {integrity: sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==} + engines: {node: '>=6'} + + is-path-inside@2.1.0: + resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==} + engines: {node: '>=6'} + is-path-inside@4.0.0: resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} engines: {node: '>=12'} @@ -6708,6 +7655,10 @@ packages: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} @@ -6804,6 +7755,11 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + javascript-natural-sort@0.7.1: resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} @@ -6839,6 +7795,14 @@ packages: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-worker@26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + jest-worker@29.7.0: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6886,9 +7850,18 @@ packages: json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -6901,6 +7874,13 @@ packages: engines: {node: '>=6'} hasBin: true + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -7107,6 +8087,14 @@ packages: resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==} hasBin: true + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + local-pkg@1.1.1: resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} engines: {node: '>=14'} @@ -7138,6 +8126,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash.throttle@4.1.1: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} @@ -7174,6 +8165,11 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lucide-react@0.537.0: + resolution: {integrity: sha512-VxWsdxBGeFnlC+HwMg/l08HptN4YRU9o/lRog156jOmRxI1ERKqN+rJiNY/mPcKAdWdM0UbyO8ft1o0jq69SSQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + luxon@3.7.1: resolution: {integrity: sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==} engines: {node: '>=12'} @@ -7190,12 +8186,19 @@ packages: '@types/three': '>=0.144.0' three: '>=0.144.0' + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} @@ -7496,6 +8499,11 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + miniflare@4.20250803.0: + resolution: {integrity: sha512-1tmCLfmMw0SqRBF9PPII9CVLQRzOrO7uIBmSng8BMSmtgs2kos7OeoM0sg6KbR9FrvP/zAniLyZuCAMAjuu4fQ==} + engines: {node: '>=18.0.0'} + hasBin: true + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -7609,6 +8617,9 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + nested-error-stacks@2.0.1: resolution: {integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==} @@ -7619,6 +8630,11 @@ packages: next-i18n-router@5.5.3: resolution: {integrity: sha512-btKVR/Mup05scxJp3EdPNALlufnZ/BO/zkIZc8a5ESdu+CNnmLW1AOkziTfRawXiZcznACCQtyfKNobC/WBTEA==} + next-pwa@5.6.0: + resolution: {integrity: sha512-XV8g8C6B7UmViXU8askMEYhWwQ4qc/XqJGnexbLV68hzKaGHZDMtHsm2TNxFcbR7+ypVuth/wwpiIlMwpRJJ5A==} + peerDependencies: + next: '>=9.0.0' + next-themes@0.4.4: resolution: {integrity: sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==} peerDependencies: @@ -7631,6 +8647,27 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + next@15.3.5: + resolution: {integrity: sha512-RkazLBMMDJSJ4XZQ81kolSpwiCt907l0xcgcpF4xC2Vml6QVcPNXW0NQRwQ80FFtSn7UM52XN0anaw8TEJXaiw==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + next@15.4.5: resolution: {integrity: sha512-nJ4v+IO9CPmbmcvsPebIoX3Q+S7f6Fu08/dEWu0Ttfa+wVwQRh9epcmsyCPjmL2b8MxC+CkBR97jgDhUUztI3g==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} @@ -7689,6 +8726,10 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-cron@4.2.1: + resolution: {integrity: sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==} + engines: {node: '>=6.0.0'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -7924,6 +8965,10 @@ packages: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + p-map@7.0.3: resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} engines: {node: '>=18'} @@ -7982,6 +9027,9 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + path-is-inside@1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -7997,6 +9045,13 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + path-type@6.0.0: resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} engines: {node: '>=18'} @@ -8062,10 +9117,30 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pinkie-promise@2.0.1: + resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} + engines: {node: '>=0.10.0'} + + pinkie@2.0.4: + resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} + engines: {node: '>=0.10.0'} + pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -8378,6 +9453,12 @@ packages: date-fns: ^2.28.0 || ^3.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-day-picker@9.8.1: + resolution: {integrity: sha512-kMcLrp3PfN/asVJayVv82IjF3iLOOxuH5TNFWezX6lS/T8iVRFPTETpHl3TUSTH99IDMZLubdNPJr++rQctkEw==} + engines: {node: '>=18'} + peerDependencies: + react: '>=16.8.0' + react-devtools-core@6.1.5: resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==} @@ -8753,11 +9834,22 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rollup-plugin-terser@7.0.2: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser + peerDependencies: + rollup: ^2.0.0 + rollup-plugin-visualizer@6.0.3: resolution: {integrity: sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==} engines: {node: '>=18'} @@ -8771,6 +9863,11 @@ packages: rollup: optional: true + rollup@2.79.2: + resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + rollup@4.46.2: resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -8813,6 +9910,14 @@ packages: scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + schema-utils@2.7.1: + resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} + engines: {node: '>= 8.9.0'} + + schema-utils@4.3.2: + resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + engines: {node: '>= 10.13.0'} + scroll-into-view-if-needed@3.1.0: resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} @@ -8844,6 +9949,9 @@ packages: resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} engines: {node: '>=0.10.0'} + serialize-javascript@4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -8876,6 +9984,10 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.34.3: resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -8948,6 +10060,9 @@ packages: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + source-list-map@2.0.1: + resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -8967,6 +10082,15 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions + + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -9037,10 +10161,18 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stoppable@1.1.0: + resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} + engines: {node: '>=4', npm: '>=6'} + stream-buffers@2.2.0: resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} engines: {node: '>= 0.10.0'} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + streamx@2.22.1: resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} @@ -9084,6 +10216,10 @@ packages: stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + strip-ansi@5.2.0: resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} engines: {node: '>=6'} @@ -9100,6 +10236,10 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -9215,10 +10355,30 @@ packages: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} + tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + terminal-link@2.1.1: resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} engines: {node: '>=8'} + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + terser@5.43.1: resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} engines: {node: '>=10'} @@ -9291,6 +10451,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -9371,6 +10534,9 @@ packages: resolution: {integrity: sha512-eZ7wI6KjtT1eBqCnh2JPXWNUAxtoxxfi6VdBdZFvil0ychCOTxbm7YLRBi1JSt7U3c+u3CLxpoPxLdvr/Npr3A==} hasBin: true + tw-animate-css@1.3.6: + resolution: {integrity: sha512-9dy0R9UsYEGmgf26L8UcHiLmSFTHa9+D7+dAt/G/sF5dCnPePZbfgDYinc7/UzAM7g/baVrmS6m9yEpU46d+LA==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -9379,6 +10545,10 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -9453,6 +10623,10 @@ packages: resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} engines: {node: '>=18.17'} + undici@7.13.0: + resolution: {integrity: sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==} + engines: {node: '>=20.18.1'} + unenv@2.0.0-rc.19: resolution: {integrity: sha512-t/OMHBNAkknVCI7bVB9OWjUUAwhVv9vsPIAGnNUxnu3FxPQN11rjh0sksLMzc3g7IlTgvHmOTl4JM7JHpcv5wA==} @@ -9509,6 +10683,10 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + unixify@1.0.0: resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} engines: {node: '>=0.10.0'} @@ -9602,6 +10780,10 @@ packages: unwasm@0.3.9: resolution: {integrity: sha512-LDxTx/2DkFURUd+BU1vUsF/moj0JsoTvl+2tcg2AUOiEzVturhGGx17/IMgGvKUYdZwr33EJHtChCJuhu9Ouvg==} + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -9710,6 +10892,10 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + engines: {node: '>=10.13.0'} + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -9726,13 +10912,33 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + webidl-conversions@5.0.0: resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} engines: {node: '>=8'} + webpack-sources@1.4.3: + resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} + + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + webpack@5.101.0: + resolution: {integrity: sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} @@ -9743,6 +10949,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -9784,6 +10993,78 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + workbox-background-sync@6.6.0: + resolution: {integrity: sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==} + + workbox-broadcast-update@6.6.0: + resolution: {integrity: sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==} + + workbox-build@6.6.0: + resolution: {integrity: sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==} + engines: {node: '>=10.0.0'} + + workbox-cacheable-response@6.6.0: + resolution: {integrity: sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==} + deprecated: workbox-background-sync@6.6.0 + + workbox-core@6.6.0: + resolution: {integrity: sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==} + + workbox-expiration@6.6.0: + resolution: {integrity: sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==} + + workbox-google-analytics@6.6.0: + resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained + + workbox-navigation-preload@6.6.0: + resolution: {integrity: sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==} + + workbox-precaching@6.6.0: + resolution: {integrity: sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==} + + workbox-range-requests@6.6.0: + resolution: {integrity: sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==} + + workbox-recipes@6.6.0: + resolution: {integrity: sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==} + + workbox-routing@6.6.0: + resolution: {integrity: sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==} + + workbox-strategies@6.6.0: + resolution: {integrity: sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==} + + workbox-streams@6.6.0: + resolution: {integrity: sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==} + + workbox-sw@6.6.0: + resolution: {integrity: sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==} + + workbox-webpack-plugin@6.6.0: + resolution: {integrity: sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==} + engines: {node: '>=10.0.0'} + peerDependencies: + webpack: ^4.4.0 || ^5.9.0 + + workbox-window@6.6.0: + resolution: {integrity: sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==} + + workerd@1.20250803.0: + resolution: {integrity: sha512-oYH29mE/wNolPc32NHHQbySaNorj6+KASUtOvQHySxB5mO1NWdGuNv49woxNCF5971UYceGQndY+OLT+24C3wQ==} + engines: {node: '>=16'} + hasBin: true + + wrangler@4.28.1: + resolution: {integrity: sha512-B1w6XS3o1q1Icyx1CyirY5GNyYhucd63Jqml/EYSbB5dgv0VT8ir7L8IkCdbICEa4yYTETIgvTTZqffM6tBulA==} + engines: {node: '>=18.0.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20250803.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -9826,6 +11107,18 @@ packages: utf-8-validate: optional: true + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -9891,6 +11184,9 @@ packages: youch-core@0.3.3: resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + youch@4.1.0-beta.10: + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} + youch@4.1.0-beta.8: resolution: {integrity: sha512-rY2A2lSF7zC+l7HH9Mq+83D1dLlsPnEvy8jTouzaptDZM6geqZ3aJe/b7ULCwRURPtWV3vbDjA2DDMdoBol0HQ==} engines: {node: '>=18'} @@ -9899,6 +11195,9 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} + zod@3.22.3: + resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -9952,6 +11251,13 @@ snapshots: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 + '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': + dependencies: + ajv: 8.17.1 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + '@babel/code-frame@7.10.4': dependencies: '@babel/highlight': 7.25.9 @@ -10121,6 +11427,41 @@ snapshots: dependencies: '@babel/types': 7.28.2 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10135,6 +11476,10 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10175,6 +11520,11 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10240,6 +11590,12 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10263,6 +11619,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-block-scoping@7.28.0(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10276,6 +11637,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-classes@7.28.0(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10302,6 +11671,41 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10330,6 +11734,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10340,6 +11749,19 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10348,12 +11770,35 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10375,6 +11820,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10410,6 +11863,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10454,6 +11912,17 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-runtime@7.28.0(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10489,6 +11958,11 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10500,12 +11974,112 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.28.0(@babel/core@7.28.0)': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/core': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.0) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-block-scoping': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-classes': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-object-rest-spread': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.0) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-regenerator': 7.28.1(@babel/core@7.28.0) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.0) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.0) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.0) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.0) + core-js-compat: 3.45.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.2 + esutils: 2.0.3 + '@babel/preset-react@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -10574,25 +12148,52 @@ snapshots: - react - react-dom - '@better-auth/utils@0.2.5': - dependencies: - typescript: 5.9.2 - uncrypto: 0.1.3 + '@better-auth/utils@0.2.5': + dependencies: + typescript: 5.9.2 + uncrypto: 0.1.3 + + '@better-fetch/fetch@1.1.18': {} + + '@cloudflare/kv-asset-handler@0.4.0': + dependencies: + mime: 3.0.0 + + '@cloudflare/unenv-preset@2.6.0(unenv@2.0.0-rc.19)(workerd@1.20250803.0)': + dependencies: + unenv: 2.0.0-rc.19 + optionalDependencies: + workerd: 1.20250803.0 + + '@cloudflare/workerd-darwin-64@1.20250803.0': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20250803.0': + optional: true - '@better-fetch/fetch@1.1.18': {} + '@cloudflare/workerd-linux-64@1.20250803.0': + optional: true - '@cloudflare/kv-asset-handler@0.4.0': - dependencies: - mime: 3.0.0 + '@cloudflare/workerd-linux-arm64@1.20250803.0': + optional: true + + '@cloudflare/workerd-windows-64@1.20250803.0': + optional: true '@colors/colors@1.6.0': {} + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 enabled: 2.0.0 kuler: 2.0.0 + '@date-fns/tz@1.3.1': {} + '@dependents/detective-less@5.0.1': dependencies: gonzales-pe: 4.3.0 @@ -10650,6 +12251,9 @@ snapshots: '@esbuild-kit/core-utils': 3.3.2 get-tsconfig: 4.10.1 + '@esbuild/aix-ppc64@0.25.4': + optional: true + '@esbuild/aix-ppc64@0.25.5': optional: true @@ -10659,6 +12263,9 @@ snapshots: '@esbuild/android-arm64@0.18.20': optional: true + '@esbuild/android-arm64@0.25.4': + optional: true + '@esbuild/android-arm64@0.25.5': optional: true @@ -10668,6 +12275,9 @@ snapshots: '@esbuild/android-arm@0.18.20': optional: true + '@esbuild/android-arm@0.25.4': + optional: true + '@esbuild/android-arm@0.25.5': optional: true @@ -10677,6 +12287,9 @@ snapshots: '@esbuild/android-x64@0.18.20': optional: true + '@esbuild/android-x64@0.25.4': + optional: true + '@esbuild/android-x64@0.25.5': optional: true @@ -10686,6 +12299,9 @@ snapshots: '@esbuild/darwin-arm64@0.18.20': optional: true + '@esbuild/darwin-arm64@0.25.4': + optional: true + '@esbuild/darwin-arm64@0.25.5': optional: true @@ -10695,6 +12311,9 @@ snapshots: '@esbuild/darwin-x64@0.18.20': optional: true + '@esbuild/darwin-x64@0.25.4': + optional: true + '@esbuild/darwin-x64@0.25.5': optional: true @@ -10704,6 +12323,9 @@ snapshots: '@esbuild/freebsd-arm64@0.18.20': optional: true + '@esbuild/freebsd-arm64@0.25.4': + optional: true + '@esbuild/freebsd-arm64@0.25.5': optional: true @@ -10713,6 +12335,9 @@ snapshots: '@esbuild/freebsd-x64@0.18.20': optional: true + '@esbuild/freebsd-x64@0.25.4': + optional: true + '@esbuild/freebsd-x64@0.25.5': optional: true @@ -10722,6 +12347,9 @@ snapshots: '@esbuild/linux-arm64@0.18.20': optional: true + '@esbuild/linux-arm64@0.25.4': + optional: true + '@esbuild/linux-arm64@0.25.5': optional: true @@ -10731,6 +12359,9 @@ snapshots: '@esbuild/linux-arm@0.18.20': optional: true + '@esbuild/linux-arm@0.25.4': + optional: true + '@esbuild/linux-arm@0.25.5': optional: true @@ -10740,6 +12371,9 @@ snapshots: '@esbuild/linux-ia32@0.18.20': optional: true + '@esbuild/linux-ia32@0.25.4': + optional: true + '@esbuild/linux-ia32@0.25.5': optional: true @@ -10749,6 +12383,9 @@ snapshots: '@esbuild/linux-loong64@0.18.20': optional: true + '@esbuild/linux-loong64@0.25.4': + optional: true + '@esbuild/linux-loong64@0.25.5': optional: true @@ -10758,6 +12395,9 @@ snapshots: '@esbuild/linux-mips64el@0.18.20': optional: true + '@esbuild/linux-mips64el@0.25.4': + optional: true + '@esbuild/linux-mips64el@0.25.5': optional: true @@ -10767,6 +12407,9 @@ snapshots: '@esbuild/linux-ppc64@0.18.20': optional: true + '@esbuild/linux-ppc64@0.25.4': + optional: true + '@esbuild/linux-ppc64@0.25.5': optional: true @@ -10776,6 +12419,9 @@ snapshots: '@esbuild/linux-riscv64@0.18.20': optional: true + '@esbuild/linux-riscv64@0.25.4': + optional: true + '@esbuild/linux-riscv64@0.25.5': optional: true @@ -10785,6 +12431,9 @@ snapshots: '@esbuild/linux-s390x@0.18.20': optional: true + '@esbuild/linux-s390x@0.25.4': + optional: true + '@esbuild/linux-s390x@0.25.5': optional: true @@ -10794,12 +12443,18 @@ snapshots: '@esbuild/linux-x64@0.18.20': optional: true + '@esbuild/linux-x64@0.25.4': + optional: true + '@esbuild/linux-x64@0.25.5': optional: true '@esbuild/linux-x64@0.25.8': optional: true + '@esbuild/netbsd-arm64@0.25.4': + optional: true + '@esbuild/netbsd-arm64@0.25.5': optional: true @@ -10809,12 +12464,18 @@ snapshots: '@esbuild/netbsd-x64@0.18.20': optional: true + '@esbuild/netbsd-x64@0.25.4': + optional: true + '@esbuild/netbsd-x64@0.25.5': optional: true '@esbuild/netbsd-x64@0.25.8': optional: true + '@esbuild/openbsd-arm64@0.25.4': + optional: true + '@esbuild/openbsd-arm64@0.25.5': optional: true @@ -10824,6 +12485,9 @@ snapshots: '@esbuild/openbsd-x64@0.18.20': optional: true + '@esbuild/openbsd-x64@0.25.4': + optional: true + '@esbuild/openbsd-x64@0.25.5': optional: true @@ -10836,6 +12500,9 @@ snapshots: '@esbuild/sunos-x64@0.18.20': optional: true + '@esbuild/sunos-x64@0.25.4': + optional: true + '@esbuild/sunos-x64@0.25.5': optional: true @@ -10845,6 +12512,9 @@ snapshots: '@esbuild/win32-arm64@0.18.20': optional: true + '@esbuild/win32-arm64@0.25.4': + optional: true + '@esbuild/win32-arm64@0.25.5': optional: true @@ -10854,6 +12524,9 @@ snapshots: '@esbuild/win32-ia32@0.18.20': optional: true + '@esbuild/win32-ia32@0.25.4': + optional: true + '@esbuild/win32-ia32@0.25.5': optional: true @@ -10863,6 +12536,9 @@ snapshots: '@esbuild/win32-x64@0.18.20': optional: true + '@esbuild/win32-x64@0.25.4': + optional: true + '@esbuild/win32-x64@0.25.5': optional: true @@ -11231,48 +12907,92 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + '@img/sharp-darwin-arm64@0.34.3': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.2.0 optional: true + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + '@img/sharp-darwin-x64@0.34.3': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.2.0 optional: true + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + '@img/sharp-libvips-darwin-arm64@1.2.0': optional: true + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + '@img/sharp-libvips-darwin-x64@1.2.0': optional: true + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + '@img/sharp-libvips-linux-arm64@1.2.0': optional: true + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + '@img/sharp-libvips-linux-arm@1.2.0': optional: true '@img/sharp-libvips-linux-ppc64@1.2.0': optional: true + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + '@img/sharp-libvips-linux-s390x@1.2.0': optional: true + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + '@img/sharp-libvips-linux-x64@1.2.0': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.0': optional: true + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + '@img/sharp-linux-arm64@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.2.0 optional: true + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + '@img/sharp-linux-arm@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.2.0 @@ -11283,26 +13003,51 @@ snapshots: '@img/sharp-libvips-linux-ppc64': 1.2.0 optional: true + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + '@img/sharp-linux-s390x@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.2.0 optional: true + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + '@img/sharp-linux-x64@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.2.0 optional: true + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + '@img/sharp-linuxmusl-arm64@0.34.3': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 optional: true + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + '@img/sharp-linuxmusl-x64@0.34.3': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.2.0 optional: true + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.4.5 + optional: true + '@img/sharp-wasm32@0.34.3': dependencies: '@emnapi/runtime': 1.4.5 @@ -11311,9 +13056,15 @@ snapshots: '@img/sharp-win32-arm64@0.34.3': optional: true + '@img/sharp-win32-ia32@0.33.5': + optional: true + '@img/sharp-win32-ia32@0.34.3': optional: true + '@img/sharp-win32-x64@0.33.5': + optional: true + '@img/sharp-win32-x64@0.34.3': optional: true @@ -11416,6 +13167,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + '@levischuck/tiny-cbor@0.2.11': {} '@mapbox/node-pre-gyp@2.0.0': @@ -11563,6 +13319,8 @@ snapshots: - rollup - supports-color + '@next/env@15.3.5': {} + '@next/env@15.4.5': {} '@next/env@15.4.6': {} @@ -11575,48 +13333,72 @@ snapshots: dependencies: fast-glob: 3.3.1 + '@next/swc-darwin-arm64@15.3.5': + optional: true + '@next/swc-darwin-arm64@15.4.5': optional: true '@next/swc-darwin-arm64@15.4.6': optional: true + '@next/swc-darwin-x64@15.3.5': + optional: true + '@next/swc-darwin-x64@15.4.5': optional: true '@next/swc-darwin-x64@15.4.6': optional: true + '@next/swc-linux-arm64-gnu@15.3.5': + optional: true + '@next/swc-linux-arm64-gnu@15.4.5': optional: true '@next/swc-linux-arm64-gnu@15.4.6': optional: true + '@next/swc-linux-arm64-musl@15.3.5': + optional: true + '@next/swc-linux-arm64-musl@15.4.5': optional: true '@next/swc-linux-arm64-musl@15.4.6': optional: true + '@next/swc-linux-x64-gnu@15.3.5': + optional: true + '@next/swc-linux-x64-gnu@15.4.5': optional: true '@next/swc-linux-x64-gnu@15.4.6': optional: true + '@next/swc-linux-x64-musl@15.3.5': + optional: true + '@next/swc-linux-x64-musl@15.4.5': optional: true '@next/swc-linux-x64-musl@15.4.6': optional: true + '@next/swc-win32-arm64-msvc@15.3.5': + optional: true + '@next/swc-win32-arm64-msvc@15.4.5': optional: true '@next/swc-win32-arm64-msvc@15.4.6': optional: true + '@next/swc-win32-x64-msvc@15.3.5': + optional: true + '@next/swc-win32-x64-msvc@15.4.5': optional: true @@ -13112,6 +14894,17 @@ snapshots: optionalDependencies: rollup: 4.46.2 + '@rollup/plugin-babel@5.3.1(@babel/core@7.28.0)(@types/babel__core@7.20.5)(rollup@2.79.2)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-imports': 7.27.1 + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + rollup: 2.79.2 + optionalDependencies: + '@types/babel__core': 7.20.5 + transitivePeerDependencies: + - supports-color + '@rollup/plugin-commonjs@28.0.6(rollup@4.46.2)': dependencies: '@rollup/pluginutils': 5.2.0(rollup@4.46.2) @@ -13138,6 +14931,16 @@ snapshots: optionalDependencies: rollup: 4.46.2 + '@rollup/plugin-node-resolve@11.2.1(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + '@types/resolve': 1.17.1 + builtin-modules: 3.3.0 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.10 + rollup: 2.79.2 + '@rollup/plugin-node-resolve@16.0.1(rollup@4.46.2)': dependencies: '@rollup/pluginutils': 5.2.0(rollup@4.46.2) @@ -13148,6 +14951,12 @@ snapshots: optionalDependencies: rollup: 4.46.2 + '@rollup/plugin-replace@2.4.2(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + magic-string: 0.25.9 + rollup: 2.79.2 + '@rollup/plugin-replace@6.0.2(rollup@4.46.2)': dependencies: '@rollup/pluginutils': 5.2.0(rollup@4.46.2) @@ -13163,6 +14972,13 @@ snapshots: optionalDependencies: rollup: 4.46.2 + '@rollup/pluginutils@3.1.0(rollup@2.79.2)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.79.2 + '@rollup/pluginutils@5.2.0(rollup@4.46.2)': dependencies: '@types/estree': 1.0.8 @@ -13312,6 +15128,15 @@ snapshots: '@standard-schema/spec@1.0.0': {} + '@surma/rollup-plugin-off-main-thread@2.2.3': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.12 + + '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -13497,12 +15322,29 @@ snapshots: '@types/draco3d@1.4.10': {} + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 + '@types/estree@0.0.39': {} + '@types/estree@1.0.8': {} + '@types/glob@7.2.0': + dependencies: + '@types/minimatch': 6.0.0 + '@types/node': 22.17.1 + '@types/graceful-fs@4.1.9': dependencies: '@types/node': 22.17.1 @@ -13525,14 +15367,24 @@ snapshots: '@types/json5@0.0.29': {} + '@types/luxon@3.7.1': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 '@types/mdx@2.0.13': {} + '@types/minimatch@6.0.0': + dependencies: + minimatch: 9.0.5 + '@types/ms@2.1.0': {} + '@types/node@20.19.10': + dependencies: + undici-types: 6.21.0 + '@types/node@22.17.1': dependencies: undici-types: 6.21.0 @@ -13579,6 +15431,10 @@ snapshots: dependencies: csstype: 3.1.3 + '@types/resolve@1.17.1': + dependencies: + '@types/node': 22.17.1 + '@types/resolve@1.20.2': {} '@types/stack-utils@2.0.3': {} @@ -13597,6 +15453,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/trusted-types@2.0.7': {} + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -13971,6 +15829,82 @@ snapshots: '@vue/shared@3.5.18': {} + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + '@webgpu/types@0.1.64': {} '@whatwg-node/disposablestack@0.0.6': @@ -14003,6 +15937,10 @@ snapshots: '@xmldom/xmldom@0.8.10': {} + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + abbrev@3.0.1: {} abort-controller@3.0.0: @@ -14018,14 +15956,35 @@ snapshots: dependencies: acorn: 8.15.0 + acorn-import-phases@1.0.4(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 + acorn-walk@8.3.2: {} + + acorn@8.14.0: {} + acorn@8.15.0: {} agent-base@7.1.4: {} + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -14033,6 +15992,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + anser@1.4.10: {} ansi-escapes@4.3.2: @@ -14116,6 +16082,14 @@ snapshots: array-timsort@1.0.3: {} + array-union@1.0.2: + dependencies: + array-uniq: 1.0.3 + + array-union@2.1.0: {} + + array-uniq@1.0.3: {} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.8 @@ -14189,6 +16163,8 @@ snapshots: async@3.2.6: {} + at-least-node@1.0.0: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -14212,6 +16188,15 @@ snapshots: transitivePeerDependencies: - supports-color + babel-loader@8.4.1(@babel/core@7.28.0)(webpack@5.101.0(esbuild@0.25.8)): + dependencies: + '@babel/core': 7.28.0 + find-cache-dir: 3.3.2 + loader-utils: 2.0.4 + make-dir: 3.1.0 + schema-utils: 2.7.1 + webpack: 5.101.0(esbuild@0.25.8) + babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.27.1 @@ -14365,10 +16350,14 @@ snapshots: big-integer@1.6.52: {} + big.js@5.2.2: {} + bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 + blake3-wasm@2.1.5: {} + bplist-creator@0.1.0: dependencies: stream-buffers: 2.2.0 @@ -14423,6 +16412,10 @@ snapshots: builtin-modules@3.3.0: {} + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + bytes@3.1.2: {} c12@3.2.0(magicast@0.3.5): @@ -14519,6 +16512,8 @@ snapshots: transitivePeerDependencies: - supports-color + chrome-trace-event@1.0.4: {} + chromium-edge-launcher@0.2.0: dependencies: '@types/node': 22.17.1 @@ -14542,6 +16537,11 @@ snapshots: dependencies: clsx: 2.1.1 + clean-webpack-plugin@4.0.0(webpack@5.101.0(esbuild@0.25.8)): + dependencies: + del: 4.1.1 + webpack: 5.101.0(esbuild@0.25.8) + cli-cursor@2.1.0: dependencies: restore-cursor: 2.0.0 @@ -14608,7 +16608,6 @@ snapshots: dependencies: color-convert: 2.0.1 color-string: 1.9.1 - optional: true colorspace@1.1.4: dependencies: @@ -14639,6 +16638,8 @@ snapshots: common-path-prefix@3.0.0: {} + common-tags@1.8.2: {} + commondir@1.0.1: {} compatx@0.2.0: {} @@ -14810,6 +16811,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + date-fns-jalali@4.1.0-0: {} + date-fns@4.1.0: {} db0@0.3.2(drizzle-orm@0.44.4(@types/pg@8.15.5)(kysely@0.28.4)(pg@8.16.3)): @@ -14864,6 +16867,16 @@ snapshots: defu@6.1.4: {} + del@4.1.1: + dependencies: + '@types/glob': 7.2.0 + globby: 6.1.0 + is-path-cwd: 2.2.0 + is-path-in-cwd: 2.1.0 + p-map: 2.1.0 + pify: 4.0.1 + rimraf: 2.7.1 + denque@2.1.0: {} depd@2.0.0: {} @@ -14944,6 +16957,10 @@ snapshots: dependencies: dequal: 2.0.3 + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -14959,7 +16976,7 @@ snapshots: dotenv-expand@11.0.7: dependencies: - dotenv: 16.4.7 + dotenv: 16.6.1 dotenv-flow@4.1.0: dependencies: @@ -15009,6 +17026,10 @@ snapshots: ee-first@1.1.1: {} + ejs@3.1.10: + dependencies: + jake: 10.9.4 + electron-to-chromium@1.5.199: {} embla-carousel-react@8.6.0(react@19.1.1): @@ -15027,6 +17048,8 @@ snapshots: emoji-regex@9.2.2: {} + emojis-list@3.0.0: {} + enabled@2.0.0: {} encodeurl@1.0.2: {} @@ -15207,6 +17230,34 @@ snapshots: '@esbuild/win32-ia32': 0.18.20 '@esbuild/win32-x64': 0.18.20 + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + esbuild@0.25.5: optionalDependencies: '@esbuild/aix-ppc64': 0.25.5 @@ -15473,13 +17524,14 @@ snapshots: eslint-plugin-only-warn@1.1.0: {} - eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(prettier@3.6.2): + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(prettier@3.6.2): dependencies: eslint: 9.33.0(jiti@2.5.1) prettier: 3.6.2 prettier-linter-helpers: 1.0.0 synckit: 0.11.11 optionalDependencies: + '@types/eslint': 9.6.1 eslint-config-prettier: 10.1.8(eslint@9.33.0(jiti@2.5.1)) eslint-plugin-react-hooks@5.2.0(eslint@9.31.0(jiti@2.5.1)): @@ -15540,6 +17592,11 @@ snapshots: eslint: 9.33.0(jiti@2.5.1) turbo: 2.5.5 + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -15649,6 +17706,8 @@ snapshots: dependencies: estraverse: 5.3.0 + estraverse@4.3.0: {} + estraverse@5.3.0: {} estree-util-attach-comments@3.0.0: @@ -15684,6 +17743,8 @@ snapshots: '@types/estree-jsx': 1.0.5 '@types/unist': 3.0.3 + estree-walker@1.0.1: {} + estree-walker@2.0.2: {} estree-walker@3.0.3: @@ -15726,6 +17787,8 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + exit-hook@2.2.1: {} + expo-asset@11.1.7(expo@53.0.20(@babel/core@7.28.0)(react-native@0.79.5(@babel/core@7.28.0)(@types/react@19.1.1)(react@19.1.1))(react@19.1.1))(react-native@0.79.5(@babel/core@7.28.0)(@types/react@19.1.1)(react@19.1.1))(react@19.1.1): dependencies: '@expo/image-utils': 0.7.6 @@ -15953,6 +18016,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.0.6: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -15986,6 +18051,10 @@ snapshots: file-uri-to-path@1.0.0: {} + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -16004,6 +18073,12 @@ snapshots: transitivePeerDependencies: - supports-color + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + find-up-simple@1.0.1: {} find-up@4.1.0: @@ -16064,6 +18139,13 @@ snapshots: fresh@2.0.0: {} + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -16193,6 +18275,8 @@ snapshots: get-nonce@1.0.1: {} + get-own-enumerable-property-symbols@3.0.2: {} + get-package-type@0.1.0: {} get-port-please@3.2.0: {} @@ -16241,6 +18325,8 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + glob@10.4.5: dependencies: foreground-child: 3.3.1 @@ -16268,6 +18354,15 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -16277,6 +18372,14 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.3.0 + globby@6.1.0: + dependencies: + array-union: 1.0.2 + glob: 7.2.3 + object-assign: 4.1.1 + pify: 2.3.0 + pinkie-promise: 2.0.1 + glsl-noise@0.0.0: {} gonzales-pe@4.3.0: @@ -16461,6 +18564,8 @@ snapshots: optionalDependencies: typescript: 5.8.3 + idb@7.1.1: {} + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -16638,6 +18743,18 @@ snapshots: is-number@7.0.0: {} + is-obj@1.0.1: {} + + is-path-cwd@2.2.0: {} + + is-path-in-cwd@2.1.0: + dependencies: + is-path-inside: 2.1.0 + + is-path-inside@2.1.0: + dependencies: + path-is-inside: 1.0.2 + is-path-inside@4.0.0: {} is-plain-obj@2.1.0: {} @@ -16657,6 +18774,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + is-regexp@1.0.0: {} + is-set@2.0.3: {} is-shared-array-buffer@1.0.4: @@ -16753,6 +18872,12 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.4 + picocolors: 1.1.1 + javascript-natural-sort@0.7.1: {} jest-environment-node@29.7.0: @@ -16820,6 +18945,18 @@ snapshots: leven: 3.1.0 pretty-format: 29.7.0 + jest-worker@26.6.2: + dependencies: + '@types/node': 22.17.1 + merge-stream: 2.0.0 + supports-color: 7.2.0 + + jest-worker@27.5.1: + dependencies: + '@types/node': 22.17.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + jest-worker@29.7.0: dependencies: '@types/node': 22.17.1 @@ -16856,8 +18993,14 @@ snapshots: json-parse-better-errors@1.0.2: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: @@ -16866,6 +19009,14 @@ snapshots: json5@2.2.3: {} + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpointer@5.0.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -17042,6 +19193,14 @@ snapshots: untun: 0.1.3 uqr: 0.1.2 + loader-runner@4.3.0: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + local-pkg@1.1.1: dependencies: mlly: 1.7.4 @@ -17070,6 +19229,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash.sortby@4.7.0: {} + lodash.throttle@4.1.1: {} lodash@4.17.21: {} @@ -17105,6 +19266,10 @@ snapshots: dependencies: react: 19.1.1 + lucide-react@0.537.0(react@19.1.1): + dependencies: + react: 19.1.1 + luxon@3.7.1: {} maath@0.10.8(@types/three@0.179.0)(three@0.179.1): @@ -17117,6 +19282,10 @@ snapshots: '@types/three': 0.179.0 three: 0.179.1 + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.4 @@ -17127,6 +19296,10 @@ snapshots: '@babel/types': 7.28.2 source-map-js: 1.2.1 + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + makeerror@1.0.12: dependencies: tmpl: 1.0.5 @@ -17788,6 +19961,24 @@ snapshots: mimic-fn@4.0.0: {} + miniflare@4.20250803.0: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + acorn: 8.14.0 + acorn-walk: 8.3.2 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + sharp: 0.33.5 + stoppable: 1.1.0 + undici: 7.13.0 + workerd: 1.20250803.0 + ws: 8.18.0 + youch: 4.1.0-beta.10 + zod: 3.22.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -17884,6 +20075,8 @@ snapshots: negotiator@1.0.0: {} + neo-async@2.6.2: {} + nested-error-stacks@2.0.1: {} netlify@13.3.5: @@ -17900,6 +20093,24 @@ snapshots: '@formatjs/intl-localematcher': 0.5.10 negotiator: 0.6.4 + next-pwa@5.6.0(@babel/core@7.28.0)(@types/babel__core@7.20.5)(esbuild@0.25.8)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(webpack@5.101.0(esbuild@0.25.8)): + dependencies: + babel-loader: 8.4.1(@babel/core@7.28.0)(webpack@5.101.0(esbuild@0.25.8)) + clean-webpack-plugin: 4.0.0(webpack@5.101.0(esbuild@0.25.8)) + globby: 11.1.0 + next: 15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + terser-webpack-plugin: 5.3.14(esbuild@0.25.8)(webpack@5.101.0(esbuild@0.25.8)) + workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.101.0(esbuild@0.25.8)) + workbox-window: 6.6.0 + transitivePeerDependencies: + - '@babel/core' + - '@swc/core' + - '@types/babel__core' + - esbuild + - supports-color + - uglify-js + - webpack + next-themes@0.4.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: react: 19.1.1 @@ -17910,6 +20121,31 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) + next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@next/env': 15.3.5 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001733 + postcss: 8.4.31 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + styled-jsx: 5.1.6(@babel/core@7.28.0)(react@19.1.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.3.5 + '@next/swc-darwin-x64': 15.3.5 + '@next/swc-linux-arm64-gnu': 15.3.5 + '@next/swc-linux-arm64-musl': 15.3.5 + '@next/swc-linux-x64-gnu': 15.3.5 + '@next/swc-linux-x64-musl': 15.3.5 + '@next/swc-win32-arm64-msvc': 15.3.5 + '@next/swc-win32-x64-msvc': 15.3.5 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@next/env': 15.4.5 @@ -18063,6 +20299,8 @@ snapshots: node-addon-api@7.1.1: {} + node-cron@4.2.1: {} + node-domexception@1.0.0: {} node-fetch-native@1.6.7: {} @@ -18128,6 +20366,13 @@ snapshots: nullthrows@1.1.1: {} + nuqs@2.4.3(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): + dependencies: + mitt: 3.0.1 + react: 19.1.1 + optionalDependencies: + next: 15.3.5(@babel/core@7.28.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + nuqs@2.4.3(next@15.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): dependencies: mitt: 3.0.1 @@ -18300,6 +20545,8 @@ snapshots: dependencies: p-limit: 4.0.0 + p-map@2.1.0: {} + p-map@7.0.3: {} p-timeout@6.1.4: {} @@ -18351,6 +20598,8 @@ snapshots: path-is-absolute@1.0.1: {} + path-is-inside@1.0.2: {} + path-key@3.1.1: {} path-key@4.0.0: {} @@ -18362,6 +20611,10 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@6.3.0: {} + + path-type@4.0.0: {} + path-type@6.0.0: {} pathe@1.1.2: {} @@ -18415,8 +20668,22 @@ snapshots: picomatch@4.0.3: {} + pify@2.3.0: {} + + pify@4.0.1: {} + + pinkie-promise@2.0.1: + dependencies: + pinkie: 2.0.4 + + pinkie@2.0.4: {} + pirates@4.0.7: {} + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -18688,6 +20955,13 @@ snapshots: date-fns: 4.1.0 react: 19.1.1 + react-day-picker@9.8.1(react@19.1.1): + dependencies: + '@date-fns/tz': 1.3.1 + date-fns: 4.1.0 + date-fns-jalali: 4.1.0-0 + react: 19.1.1 + react-devtools-core@6.1.5: dependencies: shell-quote: 1.8.3 @@ -19240,10 +21514,22 @@ snapshots: reusify@1.1.0: {} + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rimraf@3.0.2: dependencies: glob: 7.2.3 + rollup-plugin-terser@7.0.2(rollup@2.79.2): + dependencies: + '@babel/code-frame': 7.27.1 + jest-worker: 26.6.2 + rollup: 2.79.2 + serialize-javascript: 4.0.0 + terser: 5.43.1 + rollup-plugin-visualizer@6.0.3(rollup@4.46.2): dependencies: open: 8.4.2 @@ -19253,6 +21539,10 @@ snapshots: optionalDependencies: rollup: 4.46.2 + rollup@2.79.2: + optionalDependencies: + fsevents: 2.3.3 + rollup@4.46.2: dependencies: '@types/estree': 1.0.8 @@ -19316,6 +21606,19 @@ snapshots: scheduler@0.26.0: {} + schema-utils@2.7.1: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@4.3.2: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + scroll-into-view-if-needed@3.1.0: dependencies: compute-scroll-into-view: 3.1.1 @@ -19380,6 +21683,10 @@ snapshots: serialize-error@2.1.0: {} + serialize-javascript@4.0.0: + dependencies: + randombytes: 2.1.0 + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -19432,6 +21739,32 @@ snapshots: setprototypeof@1.2.0: {} + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + sharp@0.34.3: dependencies: color: 4.2.3 @@ -19538,6 +21871,8 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) + source-list-map@2.0.1: {} + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -19551,6 +21886,12 @@ snapshots: source-map@0.7.6: {} + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + sourcemap-codec@1.4.8: {} + space-separated-tokens@2.0.2: {} spdx-correct@3.2.0: @@ -19607,8 +21948,12 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + stoppable@1.1.0: {} + stream-buffers@2.2.0: {} + streamsearch@1.1.0: {} + streamx@2.22.1: dependencies: fast-fifo: 1.3.2 @@ -19691,6 +22036,12 @@ snapshots: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + strip-ansi@5.2.0: dependencies: ansi-regex: 4.1.1 @@ -19705,6 +22056,8 @@ snapshots: strip-bom@3.0.0: {} + strip-comments@2.0.1: {} + strip-final-newline@2.0.0: {} strip-final-newline@3.0.0: {} @@ -19806,11 +22159,29 @@ snapshots: temp-dir@2.0.0: {} + tempy@0.6.0: + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + terminal-link@2.1.1: dependencies: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 + terser-webpack-plugin@5.3.14(esbuild@0.25.8)(webpack@5.101.0(esbuild@0.25.8)): + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.43.1 + webpack: 5.101.0(esbuild@0.25.8) + optionalDependencies: + esbuild: 0.25.8 + terser@5.43.1: dependencies: '@jridgewell/source-map': 0.3.10 @@ -19883,6 +22254,10 @@ snapshots: tr46@0.0.3: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + trim-lines@3.0.1: {} triple-beam@1.4.1: {} @@ -19964,12 +22339,16 @@ snapshots: turbo-windows-64: 2.5.5 turbo-windows-arm64: 2.5.5 + tw-animate-css@1.3.6: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 type-detect@4.0.8: {} + type-fest@0.16.0: {} + type-fest@0.21.3: {} type-fest@0.7.1: {} @@ -20052,6 +22431,8 @@ snapshots: undici@6.21.3: {} + undici@7.13.0: {} + unenv@2.0.0-rc.19: dependencies: defu: 6.1.4 @@ -20133,6 +22514,8 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 + universalify@2.0.1: {} + unixify@1.0.0: dependencies: normalize-path: 2.1.1 @@ -20217,6 +22600,8 @@ snapshots: pkg-types: 1.3.1 unplugin: 1.16.1 + upath@1.2.0: {} + update-browserslist-db@1.1.3(browserslist@4.25.2): dependencies: browserslist: 4.25.2 @@ -20341,6 +22726,11 @@ snapshots: dependencies: makeerror: 1.0.12 + watchpack@2.4.4: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + wcwidth@1.0.1: dependencies: defaults: 1.0.4 @@ -20353,10 +22743,51 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + webidl-conversions@5.0.0: {} + webpack-sources@1.4.3: + dependencies: + source-list-map: 2.0.1 + source-map: 0.6.1 + + webpack-sources@3.3.3: {} + webpack-virtual-modules@0.6.2: {} + webpack@5.101.0(esbuild@0.25.8): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.25.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.2 + tapable: 2.2.2 + terser-webpack-plugin: 5.3.14(esbuild@0.25.8)(webpack@5.101.0(esbuild@0.25.8)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + whatwg-fetch@3.6.20: {} whatwg-url-without-unicode@8.0.0-3: @@ -20370,6 +22801,12 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -20443,6 +22880,155 @@ snapshots: word-wrap@1.2.5: {} + workbox-background-sync@6.6.0: + dependencies: + idb: 7.1.1 + workbox-core: 6.6.0 + + workbox-broadcast-update@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-build@6.6.0(@types/babel__core@7.20.5): + dependencies: + '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) + '@babel/core': 7.28.0 + '@babel/preset-env': 7.28.0(@babel/core@7.28.0) + '@babel/runtime': 7.28.2 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.28.0)(@types/babel__core@7.20.5)(rollup@2.79.2) + '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.2) + '@rollup/plugin-replace': 2.4.2(rollup@2.79.2) + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.17.1 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 7.2.3 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: 2.79.2 + rollup-plugin-terser: 7.0.2(rollup@2.79.2) + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 6.6.0 + workbox-broadcast-update: 6.6.0 + workbox-cacheable-response: 6.6.0 + workbox-core: 6.6.0 + workbox-expiration: 6.6.0 + workbox-google-analytics: 6.6.0 + workbox-navigation-preload: 6.6.0 + workbox-precaching: 6.6.0 + workbox-range-requests: 6.6.0 + workbox-recipes: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + workbox-streams: 6.6.0 + workbox-sw: 6.6.0 + workbox-window: 6.6.0 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-core@6.6.0: {} + + workbox-expiration@6.6.0: + dependencies: + idb: 7.1.1 + workbox-core: 6.6.0 + + workbox-google-analytics@6.6.0: + dependencies: + workbox-background-sync: 6.6.0 + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + + workbox-navigation-preload@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-precaching@6.6.0: + dependencies: + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + + workbox-range-requests@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-recipes@6.6.0: + dependencies: + workbox-cacheable-response: 6.6.0 + workbox-core: 6.6.0 + workbox-expiration: 6.6.0 + workbox-precaching: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + + workbox-routing@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-strategies@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-streams@6.6.0: + dependencies: + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + + workbox-sw@6.6.0: {} + + workbox-webpack-plugin@6.6.0(@types/babel__core@7.20.5)(webpack@5.101.0(esbuild@0.25.8)): + dependencies: + fast-json-stable-stringify: 2.1.0 + pretty-bytes: 5.6.0 + upath: 1.2.0 + webpack: 5.101.0(esbuild@0.25.8) + webpack-sources: 1.4.3 + workbox-build: 6.6.0(@types/babel__core@7.20.5) + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-window@6.6.0: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 6.6.0 + + workerd@1.20250803.0: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20250803.0 + '@cloudflare/workerd-darwin-arm64': 1.20250803.0 + '@cloudflare/workerd-linux-64': 1.20250803.0 + '@cloudflare/workerd-linux-arm64': 1.20250803.0 + '@cloudflare/workerd-windows-64': 1.20250803.0 + + wrangler@4.28.1: + dependencies: + '@cloudflare/kv-asset-handler': 0.4.0 + '@cloudflare/unenv-preset': 2.6.0(unenv@2.0.0-rc.19)(workerd@1.20250803.0) + blake3-wasm: 2.1.5 + esbuild: 0.25.4 + miniflare: 4.20250803.0 + path-to-regexp: 6.3.0 + unenv: 2.0.0-rc.19 + workerd: 1.20250803.0 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -20473,6 +23059,8 @@ snapshots: ws@7.5.10: {} + ws@8.18.0: {} + ws@8.18.3: {} xcode@3.0.1: @@ -20523,6 +23111,14 @@ snapshots: '@poppinss/exception': 1.2.2 error-stack-parser-es: 1.0.5 + youch@4.1.0-beta.10: + dependencies: + '@poppinss/colors': 4.1.5 + '@poppinss/dumper': 0.6.4 + '@speed-highlight/core': 1.2.7 + cookie: 1.0.2 + youch-core: 0.3.3 + youch@4.1.0-beta.8: dependencies: '@poppinss/colors': 4.1.5 @@ -20537,6 +23133,8 @@ snapshots: compress-commons: 6.0.2 readable-stream: 4.7.0 + zod@3.22.3: {} + zod@3.25.76: {} zod@4.0.16: {} From ad111ed4f450f150bbd07f817c3c6e9fa3af4fda Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 11 Aug 2025 17:19:44 -0400 Subject: [PATCH 02/17] refractor for better readability + pass lint checks --- .../{src => }/app/_actions/server-actions.ts | 0 .../app/_components/attendance-status.tsx | 0 .../app/_components/attendance-tracker.tsx | 19 +++++---- .../app/_components/install-app-ios.tsx | 0 .../app/_components/status-indicator.tsx | 0 .../app/_components/time-display.tsx | 0 .../{src => }/app/_components/user-header.tsx | 0 .../app/_components/view-admin-dashboard.tsx | 0 .../dashboard/_actions/server-actions.ts | 6 +-- .../dashboard/_components/admin-header.tsx | 0 .../_components/floating-settings.tsx | 0 .../user-detail/attendance-overview-card.tsx | 0 .../user-detail/attendance-record.tsx | 0 .../editable-attendance-record.tsx | 2 +- .../_components/user-detail/loading-state.tsx | 0 .../_components/user-detail/types.ts | 0 .../user-detail/user-detail-sheet.tsx | 8 ++-- .../_components/user-detail/user-header.tsx | 0 .../user-detail/user-info-card.tsx | 0 .../_components/user-detail/utils.ts | 0 .../user-detail/weekly-attendance-card.tsx | 0 .../user-detail/weekly-attendance-item.tsx | 0 .../dashboard/_components/user-table.tsx | 9 ++-- .../{src => }/app/admin/dashboard/page.tsx | 11 ++--- .../admin/settings/_actions/server-actions.ts | 0 .../_components/day-selection-handler.tsx | 24 +++++++++++ .../settings/_components/day-selector.tsx | 0 .../{src => }/app/admin/settings/page.tsx | 6 +-- .../{src => }/app/api/cron/route.ts | 0 apps/attendance/{src => }/app/favicon.ico | Bin apps/attendance/{src => }/app/globals.css | 0 apps/attendance/{src => }/app/layout.tsx | 10 ++--- apps/attendance/{src => }/app/page.tsx | 4 +- .../data-table/data-table-action-bar.tsx | 0 .../data-table/data-table-column-header.tsx | 0 .../data-table/data-table-date-filter.tsx | 0 .../data-table/data-table-pagination.tsx | 0 .../data-table/data-table-slider-filter.tsx | 0 .../data-table/data-table-view-options.tsx | 0 .../components/data-table/data-table.tsx | 0 .../attendance/{src => }/config/data-table.ts | 0 apps/attendance/drizzle.config.ts | 11 ----- apps/attendance/eslint.config.js | 4 ++ .../{src => }/hooks/use-callback-ref.ts | 0 .../{src => }/hooks/use-data-table.ts | 0 .../{src => }/hooks/use-debounced-callback.ts | 0 apps/attendance/{src => }/lib/data-table.ts | 0 apps/attendance/{src => }/lib/date-helper.tsx | 0 apps/attendance/{src => }/lib/format.ts | 0 apps/attendance/{src => }/lib/parsers.ts | 0 .../{src => }/lib/types/attendance.ts | 0 apps/attendance/package.json | 8 ++-- .../_components/day-selection-handler.tsx | 21 ---------- apps/attendance/tsconfig.json | 39 ++++++++++-------- apps/attendance/{src => }/types/data-table.ts | 0 pnpm-lock.yaml | 6 +++ 56 files changed, 97 insertions(+), 91 deletions(-) rename apps/attendance/{src => }/app/_actions/server-actions.ts (100%) rename apps/attendance/{src => }/app/_components/attendance-status.tsx (100%) rename apps/attendance/{src => }/app/_components/attendance-tracker.tsx (91%) rename apps/attendance/{src => }/app/_components/install-app-ios.tsx (100%) rename apps/attendance/{src => }/app/_components/status-indicator.tsx (100%) rename apps/attendance/{src => }/app/_components/time-display.tsx (100%) rename apps/attendance/{src => }/app/_components/user-header.tsx (100%) rename apps/attendance/{src => }/app/_components/view-admin-dashboard.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_actions/server-actions.ts (94%) rename apps/attendance/{src => }/app/admin/dashboard/_components/admin-header.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/floating-settings.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/attendance-record.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx (99%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/loading-state.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/types.ts (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx (99%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/user-header.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/user-info-card.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/utils.ts (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx (100%) rename apps/attendance/{src => }/app/admin/dashboard/_components/user-table.tsx (90%) rename apps/attendance/{src => }/app/admin/dashboard/page.tsx (83%) rename apps/attendance/{src => }/app/admin/settings/_actions/server-actions.ts (100%) create mode 100644 apps/attendance/app/admin/settings/_components/day-selection-handler.tsx rename apps/attendance/{src => }/app/admin/settings/_components/day-selector.tsx (100%) rename apps/attendance/{src => }/app/admin/settings/page.tsx (75%) rename apps/attendance/{src => }/app/api/cron/route.ts (100%) rename apps/attendance/{src => }/app/favicon.ico (100%) rename apps/attendance/{src => }/app/globals.css (100%) rename apps/attendance/{src => }/app/layout.tsx (73%) rename apps/attendance/{src => }/app/page.tsx (81%) rename apps/attendance/{src => }/components/data-table/data-table-action-bar.tsx (100%) rename apps/attendance/{src => }/components/data-table/data-table-column-header.tsx (100%) rename apps/attendance/{src => }/components/data-table/data-table-date-filter.tsx (100%) rename apps/attendance/{src => }/components/data-table/data-table-pagination.tsx (100%) rename apps/attendance/{src => }/components/data-table/data-table-slider-filter.tsx (100%) rename apps/attendance/{src => }/components/data-table/data-table-view-options.tsx (100%) rename apps/attendance/{src => }/components/data-table/data-table.tsx (100%) rename apps/attendance/{src => }/config/data-table.ts (100%) delete mode 100644 apps/attendance/drizzle.config.ts create mode 100644 apps/attendance/eslint.config.js rename apps/attendance/{src => }/hooks/use-callback-ref.ts (100%) rename apps/attendance/{src => }/hooks/use-data-table.ts (100%) rename apps/attendance/{src => }/hooks/use-debounced-callback.ts (100%) rename apps/attendance/{src => }/lib/data-table.ts (100%) rename apps/attendance/{src => }/lib/date-helper.tsx (100%) rename apps/attendance/{src => }/lib/format.ts (100%) rename apps/attendance/{src => }/lib/parsers.ts (100%) rename apps/attendance/{src => }/lib/types/attendance.ts (100%) delete mode 100644 apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx rename apps/attendance/{src => }/types/data-table.ts (100%) diff --git a/apps/attendance/src/app/_actions/server-actions.ts b/apps/attendance/app/_actions/server-actions.ts similarity index 100% rename from apps/attendance/src/app/_actions/server-actions.ts rename to apps/attendance/app/_actions/server-actions.ts diff --git a/apps/attendance/src/app/_components/attendance-status.tsx b/apps/attendance/app/_components/attendance-status.tsx similarity index 100% rename from apps/attendance/src/app/_components/attendance-status.tsx rename to apps/attendance/app/_components/attendance-status.tsx diff --git a/apps/attendance/src/app/_components/attendance-tracker.tsx b/apps/attendance/app/_components/attendance-tracker.tsx similarity index 91% rename from apps/attendance/src/app/_components/attendance-tracker.tsx rename to apps/attendance/app/_components/attendance-tracker.tsx index 1299c9c..51b4759 100644 --- a/apps/attendance/src/app/_components/attendance-tracker.tsx +++ b/apps/attendance/app/_components/attendance-tracker.tsx @@ -3,13 +3,13 @@ import { useState } from "react"; import { AttendanceStatus } from "./attendance-status"; import { StatusIndicator } from "./status-indicator"; -import { getCurrentDate } from "@/lib/date-helper"; +import { getCurrentDate } from "~/lib/date-helper"; import { UserHeader } from "./user-header"; -import { ViewAdminDashboard } from "@/app/_components/view-admin-dashboard"; -import { logAction } from "@/app/_actions/server-actions"; import { asUrl } from "@spike/config/paths.config"; import sharedEnv from "@spike/env/env.shared"; import { redirect } from "next/navigation"; +import { logAction } from "~/_actions/server-actions"; +import { ViewAdminDashboard } from "./view-admin-dashboard"; export default function AttendanceTracker({ session, @@ -20,12 +20,6 @@ export default function AttendanceTracker({ isCheckedIn: boolean; checkInTime?: Date; }) { - if (!session) { - return redirect( - asUrl("auth", "login") + "?redirectUrl=" + sharedEnv.ATTENDANCE_BASE_URL, - ); - } - const [isCheckedIn, setIsCheckedIn] = useState(initialCheckedIn); const [checkInTime, setCheckInTime] = useState( initialCheckInTime, @@ -33,11 +27,18 @@ export default function AttendanceTracker({ const [checkOutTime, setCheckOutTime] = useState(null); const [loading, setLoading] = useState(false); + if (!session) { + redirect( + asUrl("auth", "login") + "?redirectUrl=" + sharedEnv.ATTENDANCE_BASE_URL, + ); + } + const formatTime = (date: Date) => { return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }; const checkInOutHandler = async (status: string) => { + if (!session) return; setLoading(true); await logAction(status, session.user.id); if (status === "check-in") { diff --git a/apps/attendance/src/app/_components/install-app-ios.tsx b/apps/attendance/app/_components/install-app-ios.tsx similarity index 100% rename from apps/attendance/src/app/_components/install-app-ios.tsx rename to apps/attendance/app/_components/install-app-ios.tsx diff --git a/apps/attendance/src/app/_components/status-indicator.tsx b/apps/attendance/app/_components/status-indicator.tsx similarity index 100% rename from apps/attendance/src/app/_components/status-indicator.tsx rename to apps/attendance/app/_components/status-indicator.tsx diff --git a/apps/attendance/src/app/_components/time-display.tsx b/apps/attendance/app/_components/time-display.tsx similarity index 100% rename from apps/attendance/src/app/_components/time-display.tsx rename to apps/attendance/app/_components/time-display.tsx diff --git a/apps/attendance/src/app/_components/user-header.tsx b/apps/attendance/app/_components/user-header.tsx similarity index 100% rename from apps/attendance/src/app/_components/user-header.tsx rename to apps/attendance/app/_components/user-header.tsx diff --git a/apps/attendance/src/app/_components/view-admin-dashboard.tsx b/apps/attendance/app/_components/view-admin-dashboard.tsx similarity index 100% rename from apps/attendance/src/app/_components/view-admin-dashboard.tsx rename to apps/attendance/app/_components/view-admin-dashboard.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_actions/server-actions.ts b/apps/attendance/app/admin/dashboard/_actions/server-actions.ts similarity index 94% rename from apps/attendance/src/app/admin/dashboard/_actions/server-actions.ts rename to apps/attendance/app/admin/dashboard/_actions/server-actions.ts index c6e9561..c0b7d7f 100644 --- a/apps/attendance/src/app/admin/dashboard/_actions/server-actions.ts +++ b/apps/attendance/app/admin/dashboard/_actions/server-actions.ts @@ -1,8 +1,8 @@ "use server"; import { desc, eq } from "drizzle-orm"; -import { UserDetail } from "@/app/admin/dashboard/_components/user-detail/types"; -import { attendance, db } from "@spike/db"; +import { attendance, db, user } from "@spike/db"; +import { UserDetail } from "../_components/user-detail/types"; export async function calculateHours(userId: string): Promise { const records = await db @@ -64,7 +64,7 @@ export async function getUserDetail(userId: string): Promise { id: dbUser[0].id, userName: dbUser[0].name, email: dbUser[0].email, - // @ts-ignore + // @ts-expect-error - role is not typed in the db schema, but we know it exists role: dbUser[0].role, hoursInShop, attendanceRecords, diff --git a/apps/attendance/src/app/admin/dashboard/_components/admin-header.tsx b/apps/attendance/app/admin/dashboard/_components/admin-header.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/admin-header.tsx rename to apps/attendance/app/admin/dashboard/_components/admin-header.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/floating-settings.tsx b/apps/attendance/app/admin/dashboard/_components/floating-settings.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/floating-settings.tsx rename to apps/attendance/app/admin/dashboard/_components/floating-settings.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/attendance-overview-card.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-record.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/attendance-record.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/attendance-record.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/attendance-record.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx similarity index 99% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx index 91bab01..46289ea 100644 --- a/apps/attendance/src/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx +++ b/apps/attendance/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx @@ -44,7 +44,7 @@ export function EditableAttendanceRecord({ } const [time, modifier] = time12h.split(" "); - let [hours, minutes] = time.split(":"); + let [hours, minutes] = time.split(":"); // eslint-disable-line prefer-const if (hours === "12") { hours = "00"; diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/loading-state.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/loading-state.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/loading-state.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/loading-state.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/types.ts b/apps/attendance/app/admin/dashboard/_components/user-detail/types.ts similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/types.ts rename to apps/attendance/app/admin/dashboard/_components/user-detail/types.ts diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx similarity index 99% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx index e9df217..292fd5b 100644 --- a/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx +++ b/apps/attendance/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx @@ -2,10 +2,6 @@ import React, { useEffect, useState } from "react"; import { Sheet, SheetContent } from "@spike/ui/sheet"; import { Button } from "@spike/ui/button"; import { Plus } from "lucide-react"; -import { - getUserDetail, - createAttendanceRecord, -} from "@/app/admin/dashboard/_actions/server-actions"; import { LoadingState } from "./loading-state"; import { UserHeader } from "./user-header"; import { UserInfoCard } from "./user-info-card"; @@ -18,6 +14,10 @@ import type { UserDetailSheetProps, AttendanceRecord, } from "./types"; +import { + createAttendanceRecord, + getUserDetail, +} from "../../_actions/server-actions"; function UserDetailSheet({ userId, open, onOpenChange }: UserDetailSheetProps) { const [expandedWeeks, setExpandedWeeks] = useState>(new Set()); diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-header.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/user-header.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/user-header.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/user-header.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/user-info-card.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/user-info-card.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/user-info-card.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/user-info-card.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/utils.ts b/apps/attendance/app/admin/dashboard/_components/user-detail/utils.ts similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/utils.ts rename to apps/attendance/app/admin/dashboard/_components/user-detail/utils.ts diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/weekly-attendance-card.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx similarity index 100% rename from apps/attendance/src/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx rename to apps/attendance/app/admin/dashboard/_components/user-detail/weekly-attendance-item.tsx diff --git a/apps/attendance/src/app/admin/dashboard/_components/user-table.tsx b/apps/attendance/app/admin/dashboard/_components/user-table.tsx similarity index 90% rename from apps/attendance/src/app/admin/dashboard/_components/user-table.tsx rename to apps/attendance/app/admin/dashboard/_components/user-table.tsx index 80cd94c..6a3f96f 100644 --- a/apps/attendance/src/app/admin/dashboard/_components/user-table.tsx +++ b/apps/attendance/app/admin/dashboard/_components/user-table.tsx @@ -4,9 +4,9 @@ import { ColumnDef } from "@tanstack/table-core"; import React, { useState } from "react"; import { Eye, Download } from "lucide-react"; import { Button } from "@spike/ui/button"; -import { useDataTable } from "@/hooks/use-data-table"; -import { DataTable } from "@/components/data-table/data-table"; -import UserDetailSheet from "@/app/admin/dashboard/_components/user-detail/user-detail-sheet"; +import UserDetailSheet from "./user-detail/user-detail-sheet"; +import { useDataTable } from "~/hooks/use-data-table"; +import { DataTable } from "~/components/data-table/data-table"; export interface TableUser { id: string; @@ -93,6 +93,7 @@ export function UserTable({ users }: { users: TableUser[] }) { sorting: [{ id: "hours", desc: true }], columnPinning: { right: ["actions"] }, }, + // @ts-expect-error - getRowId is not defined in the type getRowId: (row) => row.id, }); @@ -113,7 +114,7 @@ export function UserTable({ users }: { users: TableUser[] }) { { + updateShopDays(days).catch((error) => { + console.error("Failed to update shop days:", error); + }); + }; + + return ( +
+ +
+ ); +} + +export default DaySelectionHandler; diff --git a/apps/attendance/src/app/admin/settings/_components/day-selector.tsx b/apps/attendance/app/admin/settings/_components/day-selector.tsx similarity index 100% rename from apps/attendance/src/app/admin/settings/_components/day-selector.tsx rename to apps/attendance/app/admin/settings/_components/day-selector.tsx diff --git a/apps/attendance/src/app/admin/settings/page.tsx b/apps/attendance/app/admin/settings/page.tsx similarity index 75% rename from apps/attendance/src/app/admin/settings/page.tsx rename to apps/attendance/app/admin/settings/page.tsx index e000ad0..3189f78 100644 --- a/apps/attendance/src/app/admin/settings/page.tsx +++ b/apps/attendance/app/admin/settings/page.tsx @@ -1,10 +1,10 @@ import React from "react"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; -import { AdminHeader } from "@/app/admin/dashboard/_components/admin-header"; -import DaySelectionHandler from "@/app/admin/settings/_components/day-selection-handler"; -import { getShopDays } from "@/app/admin/settings/_actions/server-actions"; import { auth } from "@spike/auth"; +import { AdminHeader } from "../dashboard/_components/admin-header"; +import { getShopDays } from "./_actions/server-actions"; +import DaySelectionHandler from "./_components/day-selection-handler"; async function SettingsPage(props: any) { const session = await auth.api.getSession({ diff --git a/apps/attendance/src/app/api/cron/route.ts b/apps/attendance/app/api/cron/route.ts similarity index 100% rename from apps/attendance/src/app/api/cron/route.ts rename to apps/attendance/app/api/cron/route.ts diff --git a/apps/attendance/src/app/favicon.ico b/apps/attendance/app/favicon.ico similarity index 100% rename from apps/attendance/src/app/favicon.ico rename to apps/attendance/app/favicon.ico diff --git a/apps/attendance/src/app/globals.css b/apps/attendance/app/globals.css similarity index 100% rename from apps/attendance/src/app/globals.css rename to apps/attendance/app/globals.css diff --git a/apps/attendance/src/app/layout.tsx b/apps/attendance/app/layout.tsx similarity index 73% rename from apps/attendance/src/app/layout.tsx rename to apps/attendance/app/layout.tsx index 28c8a66..783c95b 100644 --- a/apps/attendance/src/app/layout.tsx +++ b/apps/attendance/app/layout.tsx @@ -1,8 +1,8 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; -import IOSInstallPrompt from "@/app/_components/install-app-ios"; -import { NuqsAdapter } from 'nuqs/adapters/next/app' +import { NuqsAdapter } from "nuqs/adapters/next/app"; +import IOSInstallPrompt from "./_components/install-app-ios"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -29,10 +29,10 @@ export default function RootLayout({ - - + + - {children} + {children} ); diff --git a/apps/attendance/src/app/page.tsx b/apps/attendance/app/page.tsx similarity index 81% rename from apps/attendance/src/app/page.tsx rename to apps/attendance/app/page.tsx index 1e2bcc6..415fd32 100644 --- a/apps/attendance/src/app/page.tsx +++ b/apps/attendance/app/page.tsx @@ -1,7 +1,7 @@ -import AttendanceTracker from "@/app/_components/attendance-tracker"; import { headers } from "next/headers"; -import { getCheckedInTime, getStatus } from "@/app/_actions/server-actions"; import { auth } from "@spike/auth"; +import AttendanceTracker from "./_components/attendance-tracker"; +import { getCheckedInTime, getStatus } from "./_actions/server-actions"; export default async function Home() { const session = await auth.api.getSession({ diff --git a/apps/attendance/src/components/data-table/data-table-action-bar.tsx b/apps/attendance/components/data-table/data-table-action-bar.tsx similarity index 100% rename from apps/attendance/src/components/data-table/data-table-action-bar.tsx rename to apps/attendance/components/data-table/data-table-action-bar.tsx diff --git a/apps/attendance/src/components/data-table/data-table-column-header.tsx b/apps/attendance/components/data-table/data-table-column-header.tsx similarity index 100% rename from apps/attendance/src/components/data-table/data-table-column-header.tsx rename to apps/attendance/components/data-table/data-table-column-header.tsx diff --git a/apps/attendance/src/components/data-table/data-table-date-filter.tsx b/apps/attendance/components/data-table/data-table-date-filter.tsx similarity index 100% rename from apps/attendance/src/components/data-table/data-table-date-filter.tsx rename to apps/attendance/components/data-table/data-table-date-filter.tsx diff --git a/apps/attendance/src/components/data-table/data-table-pagination.tsx b/apps/attendance/components/data-table/data-table-pagination.tsx similarity index 100% rename from apps/attendance/src/components/data-table/data-table-pagination.tsx rename to apps/attendance/components/data-table/data-table-pagination.tsx diff --git a/apps/attendance/src/components/data-table/data-table-slider-filter.tsx b/apps/attendance/components/data-table/data-table-slider-filter.tsx similarity index 100% rename from apps/attendance/src/components/data-table/data-table-slider-filter.tsx rename to apps/attendance/components/data-table/data-table-slider-filter.tsx diff --git a/apps/attendance/src/components/data-table/data-table-view-options.tsx b/apps/attendance/components/data-table/data-table-view-options.tsx similarity index 100% rename from apps/attendance/src/components/data-table/data-table-view-options.tsx rename to apps/attendance/components/data-table/data-table-view-options.tsx diff --git a/apps/attendance/src/components/data-table/data-table.tsx b/apps/attendance/components/data-table/data-table.tsx similarity index 100% rename from apps/attendance/src/components/data-table/data-table.tsx rename to apps/attendance/components/data-table/data-table.tsx diff --git a/apps/attendance/src/config/data-table.ts b/apps/attendance/config/data-table.ts similarity index 100% rename from apps/attendance/src/config/data-table.ts rename to apps/attendance/config/data-table.ts diff --git a/apps/attendance/drizzle.config.ts b/apps/attendance/drizzle.config.ts deleted file mode 100644 index 6a7445c..0000000 --- a/apps/attendance/drizzle.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import 'dotenv/config'; -import { defineConfig } from 'drizzle-kit'; - -export default defineConfig({ - out: './drizzle', - schema: './src/db/schema.ts', - dialect: 'postgresql', - dbCredentials: { - url: process.env.DATABASE_URL!, - }, -}); diff --git a/apps/attendance/eslint.config.js b/apps/attendance/eslint.config.js new file mode 100644 index 0000000..a35ca08 --- /dev/null +++ b/apps/attendance/eslint.config.js @@ -0,0 +1,4 @@ +import { nextJsConfig } from "@spike/eslint-config/next-js"; + +/** @type {import("eslint").Linter.Config} */ +export default nextJsConfig; diff --git a/apps/attendance/src/hooks/use-callback-ref.ts b/apps/attendance/hooks/use-callback-ref.ts similarity index 100% rename from apps/attendance/src/hooks/use-callback-ref.ts rename to apps/attendance/hooks/use-callback-ref.ts diff --git a/apps/attendance/src/hooks/use-data-table.ts b/apps/attendance/hooks/use-data-table.ts similarity index 100% rename from apps/attendance/src/hooks/use-data-table.ts rename to apps/attendance/hooks/use-data-table.ts diff --git a/apps/attendance/src/hooks/use-debounced-callback.ts b/apps/attendance/hooks/use-debounced-callback.ts similarity index 100% rename from apps/attendance/src/hooks/use-debounced-callback.ts rename to apps/attendance/hooks/use-debounced-callback.ts diff --git a/apps/attendance/src/lib/data-table.ts b/apps/attendance/lib/data-table.ts similarity index 100% rename from apps/attendance/src/lib/data-table.ts rename to apps/attendance/lib/data-table.ts diff --git a/apps/attendance/src/lib/date-helper.tsx b/apps/attendance/lib/date-helper.tsx similarity index 100% rename from apps/attendance/src/lib/date-helper.tsx rename to apps/attendance/lib/date-helper.tsx diff --git a/apps/attendance/src/lib/format.ts b/apps/attendance/lib/format.ts similarity index 100% rename from apps/attendance/src/lib/format.ts rename to apps/attendance/lib/format.ts diff --git a/apps/attendance/src/lib/parsers.ts b/apps/attendance/lib/parsers.ts similarity index 100% rename from apps/attendance/src/lib/parsers.ts rename to apps/attendance/lib/parsers.ts diff --git a/apps/attendance/src/lib/types/attendance.ts b/apps/attendance/lib/types/attendance.ts similarity index 100% rename from apps/attendance/src/lib/types/attendance.ts rename to apps/attendance/lib/types/attendance.ts diff --git a/apps/attendance/package.json b/apps/attendance/package.json index 7b09a71..40c07be 100644 --- a/apps/attendance/package.json +++ b/apps/attendance/package.json @@ -2,11 +2,12 @@ "name": "spike-attendance", "version": "0.1.0", "private": true, + "type": "module", "scripts": { "dev": "next dev --turbopack --port 3005", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint --max-warnings 0" }, "dependencies": { "@spike/api": "workspace:*", @@ -36,6 +37,8 @@ "zod": "^4.0.15" }, "devDependencies": { + "@spike/eslint-config": "workspace:*", + "@spike/typescript-config": "workspace:*", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/pg": "^8.15.5", @@ -47,6 +50,5 @@ "tw-animate-css": "^1.3.6", "typescript": "5.9.2", "wrangler": "^4.28.1" - }, - "packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912" + } } diff --git a/apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx b/apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx deleted file mode 100644 index e583ba4..0000000 --- a/apps/attendance/src/app/admin/settings/_components/day-selection-handler.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client"; - -import React from 'react'; -import DaySelector from "@/app/admin/settings/_components/day-selector"; -import {updateShopDays} from "@/app/admin/settings/_actions/server-actions"; - -function DaySelectionHandler({ selectedDays }: { selectedDays: string[] }) { - const handleDaySelection = (days: string[]) => { - updateShopDays(days).catch((error) => { - console.error("Failed to update shop days:", error); - }); - }; - - return ( -
- -
- ); -} - -export default DaySelectionHandler; \ No newline at end of file diff --git a/apps/attendance/tsconfig.json b/apps/attendance/tsconfig.json index 03c211f..755c002 100644 --- a/apps/attendance/tsconfig.json +++ b/apps/attendance/tsconfig.json @@ -1,30 +1,33 @@ { + "extends": "@spike/typescript-config/nextjs.json", "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, "jsx": "preserve", - "incremental": true, "plugins": [ { "name": "next" } ], + "baseUrl": ".", "paths": { - "@/*": ["./src/*"] + "~/*": ["./app/*"], + "~/config/*": ["./config/*"], + "~/components/*": ["./components/*"], + "~/lib/*": ["./lib/*"], + "~/public/*": ["./public/*"], + "~/hooks/*": ["./hooks/*"] }, - "types": [ - "node" - ] + "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json", + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "incremental": true, + "resolveJsonModule": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "**/*.ts", + "**/*.tsx", + "next-env.d.ts", + "next.config.mjs", + ".next/types/**/*.ts" + ], + "exclude": ["node_modules", ".next"] } diff --git a/apps/attendance/src/types/data-table.ts b/apps/attendance/types/data-table.ts similarity index 100% rename from apps/attendance/src/types/data-table.ts rename to apps/attendance/types/data-table.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 756c3f0..9d7432f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -154,6 +154,12 @@ importers: specifier: ^4.0.15 version: 4.0.16 devDependencies: + '@spike/eslint-config': + specifier: workspace:* + version: link:../../tooling/eslint-config + '@spike/typescript-config': + specifier: workspace:* + version: link:../../tooling/typescript-config '@tailwindcss/postcss': specifier: ^4 version: 4.1.11 From 0434bfde0bae559ea27938604c46907395a59f9a Mon Sep 17 00:00:00 2001 From: Nathan <99501939+IncbomDev@users.noreply.github.com> Date: Mon, 11 Aug 2025 17:22:38 -0400 Subject: [PATCH 03/17] Update apps/attendance/components/data-table/data-table.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/attendance/components/data-table/data-table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/attendance/components/data-table/data-table.tsx b/apps/attendance/components/data-table/data-table.tsx index a3a9030..704b7e1 100644 --- a/apps/attendance/components/data-table/data-table.tsx +++ b/apps/attendance/components/data-table/data-table.tsx @@ -10,7 +10,7 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { cn } from "@/lib/utils"; +import { cn } from "@spike/ui/utils"; import {getCommonPinningStyles} from "@/lib/data-table"; interface DataTableProps extends React.ComponentProps<"div"> { From 4287afef0a1a1e81df0aafa2dce1407ff54f305a Mon Sep 17 00:00:00 2001 From: Nathan <99501939+IncbomDev@users.noreply.github.com> Date: Mon, 11 Aug 2025 17:23:33 -0400 Subject: [PATCH 04/17] Update apps/attendance/app/api/cron/route.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/attendance/app/api/cron/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/attendance/app/api/cron/route.ts b/apps/attendance/app/api/cron/route.ts index 25e9b5b..92e9fe7 100644 --- a/apps/attendance/app/api/cron/route.ts +++ b/apps/attendance/app/api/cron/route.ts @@ -1,4 +1,4 @@ -import {attendance, db, shop_days, user} from "@/index"; +import {attendance, db, shop_days, user} from "@spike/db"; import { and, eq, isNull, isNotNull } from 'drizzle-orm'; import { DateTime } from 'luxon'; From 26b5cdb927fa54e0d3b0c0b61ceff592263379f2 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 11 Aug 2025 17:43:28 -0400 Subject: [PATCH 05/17] fixed style and import errors --- .../attendance/app/_actions/server-actions.ts | 104 +++++++-------- .../app/_components/view-admin-dashboard.tsx | 11 +- .../editable-attendance-record.tsx | 3 +- .../user-detail/user-detail-sheet.tsx | 2 +- .../_components/user-detail/user-header.tsx | 5 +- .../user-detail/user-info-card.tsx | 2 +- apps/attendance/app/globals.css | 122 ------------------ apps/attendance/app/layout.tsx | 2 +- .../data-table/data-table-action-bar.tsx | 12 +- .../data-table/data-table-date-filter.tsx | 14 +- .../data-table/data-table-pagination.tsx | 6 +- .../data-table/data-table-slider-filter.tsx | 18 +-- .../data-table/data-table-view-options.tsx | 12 +- .../components/data-table/data-table.tsx | 8 +- apps/attendance/hooks/use-data-table.ts | 6 +- .../hooks/use-debounced-callback.ts | 2 +- apps/attendance/lib/data-table.ts | 4 +- apps/attendance/lib/parsers.ts | 4 +- apps/attendance/package.json | 1 + pnpm-lock.yaml | 3 + 20 files changed, 107 insertions(+), 234 deletions(-) delete mode 100644 apps/attendance/app/globals.css diff --git a/apps/attendance/app/_actions/server-actions.ts b/apps/attendance/app/_actions/server-actions.ts index bb4e466..a08c26a 100644 --- a/apps/attendance/app/_actions/server-actions.ts +++ b/apps/attendance/app/_actions/server-actions.ts @@ -2,50 +2,48 @@ import { auth } from "@spike/auth"; import { attendance, db } from "@spike/db"; -import { eq, and } from "drizzle-orm"; +import { eq, and, isNull } from "drizzle-orm"; import { headers } from "next/headers"; export async function logAction(action: string, userId: string) { const today = new Date().toISOString().split("T")[0]; - // Check if there's already an attendance record for today - const existingRecord = await db - .select() - .from(attendance) - .where(and(eq(attendance.userId, userId), eq(attendance.date, today))) - .limit(1); - - if (existingRecord.length > 0) { - // Update existing record - const updateData: any = { - updatedAt: new Date(), - }; - - if (action === "check-in") { - updateData.checkInTime = new Date(); - } else if (action === "check-out") { - updateData.checkOutTime = new Date(); - } - - await db - .update(attendance) - .set(updateData) - .where(eq(attendance.id, existingRecord[0].id)); - } else { - // Create new record - const recordData: any = { - userId: userId, + if (action === "check-in") { + // Always create a new record on check-in + await db.insert(attendance).values({ + userId, date: today, status: "present", - }; + checkInTime: new Date(), + } as any); + return; + } - if (action === "check-in") { - recordData.checkInTime = new Date(); - } else if (action === "check-out") { - recordData.checkOutTime = new Date(); + if (action === "check-out") { + // Find the latest (open) record for today without a check-out time + const openRecord = await db + .select() + .from(attendance) + .where( + and( + eq(attendance.userId, userId), + eq(attendance.date, today), + isNull(attendance.checkOutTime), + ), + ) + .limit(1); + + if (openRecord.length > 0) { + await db + .update(attendance) + .set({ + checkOutTime: new Date(), + updatedAt: new Date(), + } as any) + .where(eq(attendance.id, openRecord[0].id)); } - await db.insert(attendance).values(recordData); + return; } } @@ -54,43 +52,47 @@ export async function getStatus( ): Promise<"checked-in" | "checked-out"> { const today = new Date().toISOString().split("T")[0]; - // Get today's attendance record - const todayRecord = await db + // Any open (no checkOutTime) record today means user is currently checked in + const openRecord = await db .select() .from(attendance) - .where(and(eq(attendance.userId, userId), eq(attendance.date, today))) + .where( + and( + eq(attendance.userId, userId), + eq(attendance.date, today), + isNull(attendance.checkOutTime), + ), + ) .limit(1); - if (todayRecord.length === 0) { - return "checked-out"; - } - - const record = todayRecord[0]; - - // If user has checked in but not checked out, they're checked in - if (record.checkInTime && !record.checkOutTime) { + if (openRecord.length > 0) { return "checked-in"; } - // Otherwise they're checked out return "checked-out"; } export async function getCheckedInTime(userId: string): Promise { const today = new Date().toISOString().split("T")[0]; - // Get today's attendance record - const todayRecord = await db + // Get the current open session (no checkOutTime) for today + const openRecord = await db .select() .from(attendance) - .where(and(eq(attendance.userId, userId), eq(attendance.date, today))) + .where( + and( + eq(attendance.userId, userId), + eq(attendance.date, today), + isNull(attendance.checkOutTime), + ), + ) .limit(1); - if (todayRecord.length === 0 || !todayRecord[0].checkInTime) { + if (openRecord.length === 0 || !openRecord[0].checkInTime) { return null; } - return todayRecord[0].checkInTime; + return openRecord[0].checkInTime; } export async function signOut() { diff --git a/apps/attendance/app/_components/view-admin-dashboard.tsx b/apps/attendance/app/_components/view-admin-dashboard.tsx index e5d2072..cc55306 100644 --- a/apps/attendance/app/_components/view-admin-dashboard.tsx +++ b/apps/attendance/app/_components/view-admin-dashboard.tsx @@ -16,10 +16,13 @@ export function ViewAdminDashboard({ hasAccess }: { hasAccess: boolean }) { return (
- - diff --git a/apps/attendance/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx index 46289ea..680e1d6 100644 --- a/apps/attendance/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx +++ b/apps/attendance/app/admin/dashboard/_components/user-detail/editable-attendance-record.tsx @@ -269,7 +269,7 @@ export function EditableAttendanceRecord({ onClick={handleSave} size="sm" disabled={isLoading} - className="bg-green-600 hover:bg-green-700" + className="bg-green-600 hover:bg-green-700 px-2" > Save @@ -278,6 +278,7 @@ export function EditableAttendanceRecord({ onClick={handleCancel} variant="outline" size="sm" + className="px-2" disabled={isLoading} > diff --git a/apps/attendance/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx b/apps/attendance/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx index 292fd5b..02aa21b 100644 --- a/apps/attendance/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx +++ b/apps/attendance/app/admin/dashboard/_components/user-detail/user-detail-sheet.tsx @@ -197,7 +197,7 @@ function UserDetailSheet({ userId, open, onOpenChange }: UserDetailSheetProps) {