diff --git a/.claude/skills/document-subsystem/SKILL.md b/.claude/skills/document-subsystem/SKILL.md new file mode 100644 index 00000000..87a7c872 --- /dev/null +++ b/.claude/skills/document-subsystem/SKILL.md @@ -0,0 +1,32 @@ +--- +name: document-subsystem +description: Write a detailed reference doc (docs/.md) for one part of the Disscount app (auth, PWA, i18n, shopping-lists, price-history, etc.), in the style of docs/DEPLOYMENT.md. Explore the code first, then document what it is, how it works, automatic vs manual, key files, config/env, libraries, gotchas, and future TODOs. Use when the user asks to document, write docs for, or create a reference for a feature or subsystem. +--- + +# Document a subsystem + +Write a detailed, beginner-friendly reference doc for the part of the app named in the arguments (for example `auth`, `pwa`, `i18n`, `shopping-lists`, `price-history`). If no subsystem was given, ask which one before starting. + +## Steps + +1. **Explore first, do not guess.** Find and read the actual code for this subsystem before writing anything: the relevant routes, components, hooks, context, config, env vars, message files, and libraries. Follow imports until you understand the flow end to end. If something is ambiguous, check the code or ask. + +2. **Write `docs/.md`** (uppercase kebab of the subsystem, e.g. `AUTH.md`, `PWA.md`, `I18N.md`) covering: + - What it is and how it works, end to end (add a Mermaid flow diagram if it helps). + - What is automatic vs what needs manual work (a table). + - The key files and where they live (a table with paths and roles). + - Config, env vars, and feature flags involved. + - Libraries used, with versions read from `frontend/package.json` or `backend/pom.xml`. + - Gotchas and lessons that would bite someone (the traps, with the fix). + - A final "Future improvements & TODOs" section. + +Assume the reader is still learning, so explain the concepts clearly. Use tables and Mermaid diagrams wherever they make it easier to follow. + +## Conventions + +- The repo is PUBLIC: never include secrets, tokens, real IPs, or private URLs; use placeholders. +- No em dashes. +- Do not hard-wrap Markdown; write one physical line per paragraph and per bullet, and let the editor wrap. +- Match the structure and tone of `docs/DEPLOYMENT.md`. + +Do one subsystem per run so the exploration stays focused. The finished file is added to a PR into `dev`, then `master`. diff --git a/.gitignore b/.gitignore index fa63d3ed..12aa6c64 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,6 @@ frontend/yarn.lock *.bak *.orig -desktop.ini \ No newline at end of file +desktop.ini + +docs/disscount_* \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index ade7f624..4166322b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,6 +94,15 @@ Never edit the package.json or package-lock.json files directly, but instead use If you need docs about a library, always fetch the most recent documentation from the official website or repository, instead of searching in node modules or other places. +## Internationalization (i18n) + +The app uses `next-intl` (cookie-based locale, no URL routing). Locales: `hr` (default), `en`, `de`, `sl`; catalogs in `frontend/src/i18n/messages/.json`. + +- NEVER hardcode user-facing text. The `i18next/no-literal-string` ESLint rule (`pnpm lint`) fails on literal JSX text and on the `placeholder`, `alt`, `title`, `aria-label`, and `label` attributes. +- Add every new key to ALL four catalogs (`hr` is the reference) and read it with `useTranslations` (client) / `getTranslations` (server). Use `t.rich` for embedded links/icons, ICU `{count, plural, …}` for counts, `useFormatter` for numbers/currency/dates. Emails are localized separately via `getEmailTranslator(locale)`. +- Before committing run `pnpm i18n:check` (catalog parity) and `pnpm exec tsc --noEmit` (verifies every `t("key")` exists). +- Never use em dashes in any copy. + # Backend Development Guidelines NEVER run "mvn spring-boot:run" or any other development server command, because I always already have my dev server running. Also never run build commands. diff --git a/frontend/.gitignore b/frontend/.gitignore index 36e82fe4..2eabf9ca 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -17,6 +17,10 @@ /.next/ /out/ +# serwist (generated service worker) +public/sw* +public/swe-worker* + # production /build diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index f4438352..f9abddc0 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -1,5 +1,6 @@ import { defineConfig, globalIgnores } from "eslint/config"; import nextVitals from "eslint-config-next/core-web-vitals"; +import i18next from "eslint-plugin-i18next"; const eslintConfig = defineConfig([ ...nextVitals, @@ -11,6 +12,76 @@ const eslintConfig = defineConfig([ "build/**", "next-env.d.ts", ]), + + // Enforce that user-facing JSX text goes through next-intl (no hardcoded + // strings — this is what keeps translations from regressing). Only JSX text + // nodes are checked; plain .ts (schemas/constants/mock data) and technical + // attributes are ignored to avoid noise. + { + files: ["src/**/*.tsx"], + ignores: [ + "src/components/ui/**", // shadcn primitives (library code) + "src/components/custom/sidebar-08/**", // unused scaffold + "src/components/shadcn-studio/**", // unused scaffold + "src/emails/**", // localized via the email service, not next-intl + // Unused landing-page scaffolds (only HeroSection is rendered). + "src/app/(root)/components/sections/features-section.tsx", + "src/app/(root)/components/sections/features-section2.tsx", + "src/app/(root)/components/sections/pricing-section.tsx", + "src/app/(root)/components/sections/stats-section.tsx", + "src/app/(root)/components/sections/stores-section.tsx", + ], + plugins: { i18next }, + rules: { + "i18next/no-literal-string": [ + "error", + { + mode: "jsx-only", + message: + "Hardcoded string. Use next-intl (useTranslations) instead.", + // Check JSX text plus these user-visible attributes only. + "jsx-attributes": { + include: ["placeholder", "alt", "title", "aria-label", "label"], + }, + // Don't flag the key argument of translation/util calls, e.g. + // t("key"), tCommon("key"), nav.label("id"), form.register("email"), + // "x".localeCompare(y, "hr"). + callees: { + exclude: [ + "^t$", + "^t[A-Z]\\w*$", + "^tv$", + "rich", + "label", + "short", + "localeCompare", + "register", + "watch", + "setValue", + "getValues", + "useTranslations", + "getTranslations", + ], + }, + words: { + exclude: [ + // symbol / number / emoji-only text (separators, prices, etc.) + "^[^A-Za-zČĆŽŠĐčćžšđ]*$", + // hex color examples (e.g. #ffffff placeholder) + "^#[0-9A-Fa-f]{3,8}$", + // brand + proper names + universal tokens (not translated) + "Disscount", + "disscount", + "Jakov Jakovac", + "GitHub", + "LinkedIn", + "N/A", + ], + }, + }, + ], + }, + }, ]); export default eslintConfig; diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 826c6e9e..a436f5fe 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,6 +1,23 @@ import { withSentryConfig } from "@sentry/nextjs"; +import withSerwistInit from "@serwist/next"; +import createNextIntlPlugin from "next-intl/plugin"; +import { randomUUID } from "node:crypto"; import type { NextConfig } from "next"; +// Points at the per-request locale + messages config (src/i18n/request.ts). +const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts"); + +const withSerwist = withSerwistInit({ + swSrc: "src/app/sw.ts", + swDest: "public/sw.js", + // Service worker only runs in production builds; in dev it would fight + // Turbopack/HMR. The React Query persistence layer still works in dev. + disable: process.env.NODE_ENV === "development", + // Precache the offline fallback page so it's available with no connection. + additionalPrecacheEntries: [{ url: "/offline", revision: randomUUID() }], + reloadOnOnline: true, +}); + const nextConfig: NextConfig = { // Emit a minimal self-contained server build (.next/standalone) for a lean Docker image. output: "standalone", @@ -60,7 +77,8 @@ const nextConfig: NextConfig = { }, }; -export default withSentryConfig(nextConfig, { +export default withSerwist( + withSentryConfig(withNextIntl(nextConfig), { // For all available options, see: // https://www.npmjs.com/package/@sentry/webpack-plugin#options @@ -90,4 +108,5 @@ export default withSentryConfig(nextConfig, { removeDebugLogging: true, }, }, -}); + }), +); diff --git a/frontend/package.json b/frontend/package.json index c8dd52d3..31d0d9ac 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,9 +5,10 @@ "license": "BUSL-1.1", "scripts": { "dev": "next dev", - "build": "next build", + "build": "next build --webpack", "start": "next start", "lint": "eslint .", + "i18n:check": "node scripts/check-i18n.mjs", "email": "email dev --dir src/emails --port 3366" }, "dependencies": { @@ -26,8 +27,11 @@ "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@sentry/nextjs": "^10.62.0", + "@serwist/next": "^9.5.11", + "@tanstack/query-async-storage-persister": "^5.101.1", "@tanstack/react-query": "^5.90.12", "@tanstack/react-query-devtools": "^5.91.1", + "@tanstack/react-query-persist-client": "^5.101.1", "@tanstack/react-virtual": "^3.13.13", "@yudiel/react-qr-scanner": "^2.4.1", "axios": "^1.13.2", @@ -37,10 +41,12 @@ "cmdk": "^1.1.1", "disscount": "link:", "drizzle-orm": "0.45.2", + "idb-keyval": "^6.2.5", "kysely": "0.28.17", "lucide-react": "^0.561.0", "motion": "^12.23.26", "next": "16.2.9", + "next-intl": "^4.13.0", "pg": "8.21.0", "radix-ui": "^1.4.3", "react": "19.2.4", @@ -68,6 +74,9 @@ "drizzle-kit": "^0.31.10", "eslint": "^9", "eslint-config-next": "16.2.2", + "eslint-plugin-i18next": "^6.1.5", + "serwist": "^9.5.11", + "sharp": "^0.35.2", "tailwindcss": "^4", "tw-animate-css": "^1.4.0", "typescript": "^5.7" diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index e06b4cc6..9de76339 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@daveyplate/better-auth-ui': specifier: ^3.2.13 - version: 3.4.0(7cba73d98a64fa1f7427e1b78929c147) + version: 3.4.0(2e97e51b7de348ab45b8706c958356e1) '@hookform/resolvers': specifier: ^5.2.2 version: 5.4.0(react-hook-form@7.80.0(react@19.2.4)) @@ -53,12 +53,21 @@ importers: '@sentry/nextjs': specifier: ^10.62.0 version: 10.62.0(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.108.1) + '@serwist/next': + specifier: ^9.5.11 + version: 9.5.11(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(webpack@5.108.1) + '@tanstack/query-async-storage-persister': + specifier: ^5.101.1 + version: 5.101.1 '@tanstack/react-query': specifier: ^5.90.12 version: 5.101.1(react@19.2.4) '@tanstack/react-query-devtools': specifier: ^5.91.1 version: 5.101.1(@tanstack/react-query@5.101.1(react@19.2.4))(react@19.2.4) + '@tanstack/react-query-persist-client': + specifier: ^5.101.1 + version: 5.101.1(@tanstack/react-query@5.101.1(react@19.2.4))(react@19.2.4) '@tanstack/react-virtual': specifier: ^3.13.13 version: 3.14.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -86,6 +95,9 @@ importers: drizzle-orm: specifier: 0.45.2 version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0) + idb-keyval: + specifier: ^6.2.5 + version: 6.2.5 kysely: specifier: 0.28.17 version: 0.28.17 @@ -98,6 +110,9 @@ importers: next: specifier: 16.2.9 version: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next-intl: + specifier: ^4.13.0 + version: 4.13.0(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3) pg: specifier: 8.21.0 version: 8.21.0 @@ -174,6 +189,15 @@ importers: eslint-config-next: specifier: 16.2.2 version: 16.2.2(@typescript-eslint/parser@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint-plugin-i18next: + specifier: ^6.1.5 + version: 6.1.5 + serwist: + specifier: ^9.5.11 + version: 9.5.11(browserslist@4.28.4)(typescript@5.9.3) + sharp: + specifier: ^0.35.2 + version: 0.35.2 tailwindcss: specifier: ^4 version: 4.3.1 @@ -969,6 +993,18 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@formatjs/fast-memoize@3.1.6': + resolution: {integrity: sha512-H5aexk1Le7T9TPmscacZ+1pR6CTa2n1wq+HDVGXhH8TzUlQQpeXzZs91dRtmFHrbeNbjPFPfQujUqm7MHgVoXQ==} + + '@formatjs/icu-messageformat-parser@3.5.12': + resolution: {integrity: sha512-YyzzxVgYJ8DELmmkhn0Yr0rUj0dTJFf9Jp628K3S0ysInBWxLVDOS8i3RP91cCp4DMK4WYb4cVMhWA9i4knSJg==} + + '@formatjs/icu-skeleton-parser@2.1.10': + resolution: {integrity: sha512-XuSva+8ZGawk8VnD5VD6UeH8KarQ/Z022zgjHDoHmlNiAewstXuuzXc0Hk5pGFSdG+nNw5bfJKXqj1ZXHn9yUA==} + + '@formatjs/intl-localematcher@0.8.10': + resolution: {integrity: sha512-P/IC3qws3jH+1fEs+o0RIFgXKRaQlFehjS5W0FPAqdo6hgzawLl+eD0q0JjheQ3XtoOe5n8WSYfX06KQZI/QJA==} + '@hcaptcha/react-hcaptcha@2.0.2': resolution: {integrity: sha512-VbuH6VJ6m3BHmVBHs0fL9t+suZd7PQEqCzqL2BiUbBvbHI3XfvSgdiug2QiEPN8zskbPTIV/FfGPF53JCckrow==} @@ -1010,70 +1046,145 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-darwin-arm64@0.35.2': + resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-x64@0.34.5': resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] + '@img/sharp-darwin-x64@0.35.2': + resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.2': + resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + '@img/sharp-libvips-darwin-arm64@1.2.4': resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.3.1': + resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.4': resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.3.1': + resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-linux-arm64@1.2.4': resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm64@1.3.1': + resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm@1.3.1': + resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.3.1': + resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-riscv64@1.3.1': + resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.3.1': + resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-x64@1.3.1': + resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1081,6 +1192,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm64@0.35.2': + resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1088,6 +1206,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm@0.35.2': + resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1095,6 +1220,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-ppc64@0.35.2': + resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1102,6 +1234,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-riscv64@0.35.2': + resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1109,6 +1248,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-s390x@0.35.2': + resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1116,6 +1262,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-x64@0.35.2': + resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1123,6 +1276,13 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-arm64@0.35.2': + resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1130,29 +1290,63 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-x64@0.35.2': + resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] + '@img/sharp-wasm32@0.35.2': + resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.2': + resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + '@img/sharp-win32-arm64@0.34.5': resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] + '@img/sharp-win32-arm64@0.35.2': + resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + '@img/sharp-win32-ia32@0.34.5': resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] + '@img/sharp-win32-ia32@0.35.2': + resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-x64@0.34.5': resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] + '@img/sharp-win32-x64@0.35.2': + resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + '@inquirer/core@6.0.0': resolution: {integrity: sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==} engines: {node: '>=14.18.0'} @@ -1385,6 +1579,94 @@ packages: resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} engines: {node: '>=14'} + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + '@peculiar/asn1-android@2.8.0': resolution: {integrity: sha512-skLbS+IOGv1lUgDqtChr8xvtvEr3HMse/JGBaL2r1J1o/n7a8wqOrovMtlRq/UXLhxvmLaONP67hwtshgzwfzA==} @@ -2485,6 +2767,9 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@schummar/icu-type-parser@1.21.5': + resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} + '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -2635,6 +2920,57 @@ packages: peerDependencies: webpack: '>=5.0.0' + '@serwist/build@9.5.11': + resolution: {integrity: sha512-PQfW+LhADYFOOp0PhEnjlgJCyKor6cYa06d3rID1OpiKzkmCApJV1WYfdTBB96jXaWv6OWcWSbSV4tqDLxvaVA==} + engines: {node: '>=18.0.0'} + peerDependencies: + typescript: '>=5.0.0' + peerDependenciesMeta: + typescript: + optional: true + + '@serwist/next@9.5.11': + resolution: {integrity: sha512-omT32H7U21ihCymSvOG9QeRJBuOEomJx4JdzKhUoqOW3DR10tH3m84VOHj3BvK0OcA7av3qj5FsyNFBB+f0n8A==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@serwist/cli': ^9.5.11 + next: '>=14.0.0' + react: '>=18.0.0' + typescript: '>=5.0.0' + peerDependenciesMeta: + '@serwist/cli': + optional: true + typescript: + optional: true + + '@serwist/utils@9.5.11': + resolution: {integrity: sha512-zqxmwuHqWA3OwN82Wo8gFZ9QBemygJP3cap5JWAOG4UyJZgUZfmBXAXj+IMaD4eKZ/6pqrxHHDZ9uSWZmJ1mXA==} + peerDependencies: + browserslist: '>=4' + peerDependenciesMeta: + browserslist: + optional: true + + '@serwist/webpack-plugin@9.5.11': + resolution: {integrity: sha512-SlvO3A1UMcc1htCzMtLCtPQK6yISCO7B859ixLv7EiY/yayXjVxGm9vHqkJYpQ768PWyjEZXRY/X6EGRMA6wJQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + typescript: '>=5.0.0' + webpack: 4.4.0 || ^5.9.0 + peerDependenciesMeta: + typescript: + optional: true + webpack: + optional: true + + '@serwist/window@9.5.11': + resolution: {integrity: sha512-OrH9srhmifUvY36NuukHSZby24XTEk4pHh3pfY0GBQzA9ouU1fYh+ORWhKxH7/wkVHRr3sc4YAhjtpfL14PjjQ==} + peerDependencies: + typescript: '>=5.0.0' + peerDependenciesMeta: + typescript: + optional: true + '@simplewebauthn/browser@13.3.0': resolution: {integrity: sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==} @@ -2654,9 +2990,102 @@ packages: '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@swc/core-darwin-arm64@1.15.43': + resolution: {integrity: sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.43': + resolution: {integrity: sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.43': + resolution: {integrity: sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.43': + resolution: {integrity: sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-arm64-musl@1.15.43': + resolution: {integrity: sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@swc/core-linux-ppc64-gnu@1.15.43': + resolution: {integrity: sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==} + engines: {node: '>=10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-s390x-gnu@1.15.43': + resolution: {integrity: sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==} + engines: {node: '>=10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@swc/core-linux-x64-gnu@1.15.43': + resolution: {integrity: sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-x64-musl@1.15.43': + resolution: {integrity: sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@swc/core-win32-arm64-msvc@1.15.43': + resolution: {integrity: sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.43': + resolution: {integrity: sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.43': + resolution: {integrity: sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.43': + resolution: {integrity: sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/types@0.1.27': + resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==} + '@tailwindcss/node@4.3.1': resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} @@ -2749,18 +3178,33 @@ packages: '@tailwindcss/postcss@4.3.1': resolution: {integrity: sha512-dNJuNbdEJT/SWRuXTYP1WSamelsz3ztkUsdtWQPjrexysrTpaEPM40P/71knXiXLYEojqPOEGitVLLpPMS5T6A==} + '@tanstack/query-async-storage-persister@5.101.1': + resolution: {integrity: sha512-QuHhCTLyQjiVFUYRW5xtgeuiP1NEmqL5wnuFgPGeO6SjTLKeN9VnAhAKsKzde3AcEZEe4PL7FehfKbVhM/9kLA==} + '@tanstack/query-core@5.101.1': resolution: {integrity: sha512-Y6Y92dkXtNqx67m2pMSxUsA3zOCwv862JexZRP8/EPwvKXMPu9m8rv43spiXWzOUIggQ3SQApttALStzhA8B4g==} + '@tanstack/query-core@5.101.2': + resolution: {integrity: sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==} + '@tanstack/query-devtools@5.101.1': resolution: {integrity: sha512-37RQ9U2PxlXQiv1era2t+uHgVhmiyvxqTMu30+KoVf0rufiucu6rpGRKFJk61Wh5OAZFKqCQd6lxTzFWfLZiuQ==} + '@tanstack/query-persist-client-core@5.101.1': + resolution: {integrity: sha512-rR5Er6jmdI3Oo8o6Wc0ceM6glDU4umgePu2IxM3Gy2UvPqcQONduxxxSzU1+F17mpS09XHqHKmj0Irhfb2cGYg==} + '@tanstack/react-query-devtools@5.101.1': resolution: {integrity: sha512-OXFR9XKdEslraq3cpl3kCUeNvTIq/xGWEZiFZdn2bLB/q4WxSALMEDKYZ5yYjMQytsfnQxwQYqV4qtVEf0nuog==} peerDependencies: '@tanstack/react-query': ^5.101.1 react: ^18 || ^19 + '@tanstack/react-query-persist-client@5.101.1': + resolution: {integrity: sha512-bBSne+3+28EZ/Ch5a06J0YQi6d5yaT2G+vyHAyhzwzYA1FAEAFe6Ou6TsNVQaT6ef6pFShU73FL/NB4guTWNng==} + peerDependencies: + '@tanstack/react-query': ^5.101.1 + react: ^18 || ^19 + '@tanstack/react-query@5.101.1': resolution: {integrity: sha512-ZnONUuQKJe1bJMStXUL1s5uKN9FcfC28j5cK+iDZcdSHtUv1wtin1cGc/Oewhf2Oc4eKY7lggtpvT/AbMmhHew==} peerDependencies: @@ -2883,6 +3327,9 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} @@ -3422,6 +3869,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + browserslist@4.28.4: resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3528,6 +3980,10 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + 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==} @@ -4002,6 +4458,10 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-plugin-i18next@6.1.5: + resolution: {integrity: sha512-xCTfstbK9ZpQ6UFT5S1s6zbZMhKn2o2jRSQYiU2UkV7wt8y1m3WjkUYGkGlipgRdFInYI9+LAqE+JQU/L73HmA==} + engines: {node: '>=18.10.0'} + eslint-plugin-import@2.32.0: resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} engines: {node: '>=4'} @@ -4355,6 +4815,15 @@ packages: resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==} engines: {node: '>= 20'} + icu-minify@4.13.0: + resolution: {integrity: sha512-SIFMeUHZJjzS5RvIGvybKvWoHjDm9cGVEs2EpJ8PmywOdJLWyblPm7TdPLLoUtkJtwQD7iGhl2WMptZ+N0on+w==} + + idb-keyval@6.2.5: + resolution: {integrity: sha512-eKQkTnS0relYsSOYomx8ozIbmdsQCKUdhyuIaQ2DZgKuaxtyQQMkyD/wlnQN32pO3yutN1b1L8uqwcDKaJd7/Q==} + + idb@8.0.3: + resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -4398,6 +4867,9 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + intl-messageformat@11.2.9: + resolution: {integrity: sha512-cGzymZerpDhVXRKjKLgXKda9gI29TU2o88L7gwNMHp3WZVxA/0c5tX52udXbW9JklDApolvMXZG6Dhhdz5eirA==} + ip-address@10.2.0: resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} engines: {node: '>= 12'} @@ -4607,6 +5079,9 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + kysely@0.28.17: resolution: {integrity: sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==} engines: {node: '>=20.0.0'} @@ -4714,6 +5189,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==} + log-symbols@7.0.1: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} @@ -4909,6 +5387,10 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -4916,6 +5398,19 @@ packages: resolution: {integrity: sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==} engines: {node: '>= 0.4.0'} + next-intl-swc-plugin-extractor@4.13.0: + resolution: {integrity: sha512-6S/fJI0KXvLCL8nhBo9P8eGaJPzmwJBTCzX0NaUIj0VyU8U89d//T+vjMLdNIXl5MlLaYH7B9MbAjb8Mvu+tqQ==} + + next-intl@4.13.0: + resolution: {integrity: sha512-OvNq2v5XLx4EkQOsAhVE9g+6zdb83XHusADCXXtIW4LILYnjEVaeINdr1lkVWKSjzwNUiMSlH5N4K0OQTRiv6A==} + peerDependencies: + next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + next@16.2.9: resolution: {integrity: sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==} engines: {node: '>=20.9.0'} @@ -4937,6 +5432,9 @@ packages: sass: optional: true + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-exports-info@1.6.2: resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} engines: {node: '>= 0.4'} @@ -5111,6 +5609,9 @@ packages: engines: {node: '>=18'} hasBin: true + po-parser@2.1.1: + resolution: {integrity: sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -5154,6 +5655,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -5367,6 +5872,10 @@ packages: resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + requireindex@1.1.0: + resolution: {integrity: sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==} + engines: {node: '>=0.10.5'} + reselect@5.2.0: resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} @@ -5445,6 +5954,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -5453,6 +5967,14 @@ packages: server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + serwist@9.5.11: + resolution: {integrity: sha512-Bq6uwJFd4ET60BWI77v3VbazKHv6k7lECOiiCFwKyBu/slaCn0GHJ5L5RfsuJUKrnbD9lYUCDo6sqaKRM5M2vA==} + peerDependencies: + typescript: '>=5.0.0' + peerDependenciesMeta: + typescript: + optional: true + set-cookie-parser@3.1.1: resolution: {integrity: sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==} @@ -5472,6 +5994,10 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.35.2: + resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} + engines: {node: '>=20.9.0'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -5550,6 +6076,11 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + 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 + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -5699,6 +6230,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -5746,6 +6280,10 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + type-fest@5.7.0: resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} engines: {node: '>=20'} @@ -5826,6 +6364,11 @@ packages: '@types/react': optional: true + use-intl@4.13.0: + resolution: {integrity: sha512-fAFDrWaASxlhXOipcOyb5VDD+YONqj6+8O8EcG/J7RBoOUF3A8YahRWLN+mBxYMrlMQB8N6Voqk5X+YC+HSL0A==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -5874,6 +6417,9 @@ 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==} + webpack-sources@3.5.0: resolution: {integrity: sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==} engines: {node: '>=10.13.0'} @@ -5898,6 +6444,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + when-exit@2.1.5: resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} @@ -5979,6 +6528,9 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 + zod@4.4.1: + resolution: {integrity: sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -6249,21 +6801,21 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 - '@daveyplate/better-auth-tanstack@1.3.6(@tanstack/query-core@5.101.1)(@tanstack/react-query@5.101.1(react@19.2.4))(better-auth@1.6.14(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.21.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@daveyplate/better-auth-tanstack@1.3.6(@tanstack/query-core@5.101.2)(@tanstack/react-query@5.101.1(react@19.2.4))(better-auth@1.6.14(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.21.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@tanstack/query-core': 5.101.1 + '@tanstack/query-core': 5.101.2 '@tanstack/react-query': 5.101.1(react@19.2.4) better-auth: 1.6.14(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.21.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@daveyplate/better-auth-ui@3.4.0(7cba73d98a64fa1f7427e1b78929c147)': + '@daveyplate/better-auth-ui@3.4.0(2e97e51b7de348ab45b8706c958356e1)': dependencies: '@better-auth/api-key': 1.6.22(@better-auth/core@1.6.14(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.2)(better-auth@1.6.14(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.21.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@2.0.2(zod@4.4.3)) '@better-auth/passkey': 1.6.22(@better-auth/core@1.6.14(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.1.21)(better-auth@1.6.14(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.21.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.3.5(zod@4.4.3))(nanostores@1.3.0) '@better-fetch/fetch': 1.3.1 '@captchafox/react': 1.12.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@daveyplate/better-auth-tanstack': 1.3.6(@tanstack/query-core@5.101.1)(@tanstack/react-query@5.101.1(react@19.2.4))(better-auth@1.6.14(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.21.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@daveyplate/better-auth-tanstack': 1.3.6(@tanstack/query-core@5.101.2)(@tanstack/react-query@5.101.1(react@19.2.4))(better-auth@1.6.14(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(kysely@0.28.17)(pg@8.21.0))(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.21.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@hcaptcha/react-hcaptcha': 2.0.2 '@hookform/resolvers': 5.4.0(react-hook-form@7.80.0(react@19.2.4)) '@instantdb/react': 1.0.49(react@19.2.4) @@ -6629,6 +7181,18 @@ snapshots: '@floating-ui/utils@0.2.11': {} + '@formatjs/fast-memoize@3.1.6': {} + + '@formatjs/icu-messageformat-parser@3.5.12': + dependencies: + '@formatjs/icu-skeleton-parser': 2.1.10 + + '@formatjs/icu-skeleton-parser@2.1.10': {} + + '@formatjs/intl-localematcher@0.8.10': + dependencies: + '@formatjs/fast-memoize': 3.1.6 + '@hcaptcha/react-hcaptcha@2.0.2': {} '@hexagon/base64@1.1.28': {} @@ -6654,103 +7218,206 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@img/colour@1.1.0': - optional: true + '@img/colour@1.1.0': {} '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true + '@img/sharp-darwin-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.1 + optional: true + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true + '@img/sharp-darwin-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.1 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true + '@img/sharp-libvips-darwin-arm64@1.3.1': + optional: true + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true + '@img/sharp-libvips-darwin-x64@1.3.1': + optional: true + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true + '@img/sharp-libvips-linux-arm64@1.3.1': + optional: true + '@img/sharp-libvips-linux-arm@1.2.4': optional: true + '@img/sharp-libvips-linux-arm@1.3.1': + optional: true + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true + '@img/sharp-libvips-linux-ppc64@1.3.1': + optional: true + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true + '@img/sharp-libvips-linux-riscv64@1.3.1': + optional: true + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true + '@img/sharp-libvips-linux-s390x@1.3.1': + optional: true + '@img/sharp-libvips-linux-x64@1.2.4': optional: true + '@img/sharp-libvips-linux-x64@1.3.1': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + optional: true + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true + '@img/sharp-linux-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.1 + optional: true + '@img/sharp-linux-arm@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.2.4 optional: true + '@img/sharp-linux-arm@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.1 + optional: true + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true + '@img/sharp-linux-ppc64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.1 + optional: true + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true + '@img/sharp-linux-riscv64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.1 + optional: true + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true + '@img/sharp-linux-s390x@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.1 + optional: true + '@img/sharp-linux-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.2.4 optional: true + '@img/sharp-linux-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.1 + optional: true + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true + '@img/sharp-linuxmusl-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + optional: true + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true + '@img/sharp-linuxmusl-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + optional: true + '@img/sharp-wasm32@0.34.5': dependencies: '@emnapi/runtime': 1.11.1 optional: true + '@img/sharp-wasm32@0.35.2': + dependencies: + '@emnapi/runtime': 1.11.1 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + '@img/sharp-win32-arm64@0.34.5': optional: true + '@img/sharp-win32-arm64@0.35.2': + optional: true + '@img/sharp-win32-ia32@0.34.5': optional: true + '@img/sharp-win32-ia32@0.35.2': + optional: true + '@img/sharp-win32-x64@0.34.5': optional: true + '@img/sharp-win32-x64@0.35.2': + optional: true + '@inquirer/core@6.0.0': dependencies: '@inquirer/type': 1.5.5 @@ -6992,6 +7659,66 @@ snapshots: '@opentelemetry/semantic-conventions@1.41.1': {} + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.4 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + '@peculiar/asn1-android@2.8.0': dependencies: '@peculiar/asn1-schema': 2.8.0 @@ -8088,6 +8815,8 @@ snapshots: '@rtsao/scc@1.1.0': {} + '@schummar/icu-type-parser@1.21.5': {} + '@selderee/plugin-htmlparser2@0.11.0': dependencies: domhandler: 5.0.3 @@ -8273,6 +9002,68 @@ snapshots: - encoding - supports-color + '@serwist/build@9.5.11(browserslist@4.28.2)(typescript@5.9.3)': + dependencies: + '@serwist/utils': 9.5.11(browserslist@4.28.2) + common-tags: 1.8.2 + glob: 13.0.6 + pretty-bytes: 6.1.1 + source-map: 0.8.0-beta.0 + type-fest: 5.6.0 + zod: 4.4.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - browserslist + + '@serwist/next@9.5.11(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(webpack@5.108.1)': + dependencies: + '@serwist/build': 9.5.11(browserslist@4.28.2)(typescript@5.9.3) + '@serwist/utils': 9.5.11(browserslist@4.28.2) + '@serwist/webpack-plugin': 9.5.11(browserslist@4.28.2)(typescript@5.9.3)(webpack@5.108.1) + '@serwist/window': 9.5.11(browserslist@4.28.2)(typescript@5.9.3) + browserslist: 4.28.2 + glob: 13.0.6 + kolorist: 1.8.0 + next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + semver: 7.7.4 + serwist: 9.5.11(browserslist@4.28.2)(typescript@5.9.3) + zod: 4.4.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - webpack + + '@serwist/utils@9.5.11(browserslist@4.28.2)': + optionalDependencies: + browserslist: 4.28.2 + + '@serwist/utils@9.5.11(browserslist@4.28.4)': + optionalDependencies: + browserslist: 4.28.4 + + '@serwist/webpack-plugin@9.5.11(browserslist@4.28.2)(typescript@5.9.3)(webpack@5.108.1)': + dependencies: + '@serwist/build': 9.5.11(browserslist@4.28.2)(typescript@5.9.3) + '@serwist/utils': 9.5.11(browserslist@4.28.2) + pretty-bytes: 6.1.1 + zod: 4.4.1 + optionalDependencies: + typescript: 5.9.3 + webpack: 5.108.1 + transitivePeerDependencies: + - browserslist + + '@serwist/window@9.5.11(browserslist@4.28.2)(typescript@5.9.3)': + dependencies: + '@types/trusted-types': 2.0.7 + serwist: 9.5.11(browserslist@4.28.2)(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - browserslist + '@simplewebauthn/browser@13.3.0': {} '@simplewebauthn/server@13.3.2': @@ -8294,10 +9085,70 @@ snapshots: '@standard-schema/utils@0.3.0': {} + '@swc/core-darwin-arm64@1.15.43': + optional: true + + '@swc/core-darwin-x64@1.15.43': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.43': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.43': + optional: true + + '@swc/core-linux-arm64-musl@1.15.43': + optional: true + + '@swc/core-linux-ppc64-gnu@1.15.43': + optional: true + + '@swc/core-linux-s390x-gnu@1.15.43': + optional: true + + '@swc/core-linux-x64-gnu@1.15.43': + optional: true + + '@swc/core-linux-x64-musl@1.15.43': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.43': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.43': + optional: true + + '@swc/core-win32-x64-msvc@1.15.43': + optional: true + + '@swc/core@1.15.43': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.27 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.43 + '@swc/core-darwin-x64': 1.15.43 + '@swc/core-linux-arm-gnueabihf': 1.15.43 + '@swc/core-linux-arm64-gnu': 1.15.43 + '@swc/core-linux-arm64-musl': 1.15.43 + '@swc/core-linux-ppc64-gnu': 1.15.43 + '@swc/core-linux-s390x-gnu': 1.15.43 + '@swc/core-linux-x64-gnu': 1.15.43 + '@swc/core-linux-x64-musl': 1.15.43 + '@swc/core-win32-arm64-msvc': 1.15.43 + '@swc/core-win32-ia32-msvc': 1.15.43 + '@swc/core-win32-x64-msvc': 1.15.43 + + '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 + '@swc/types@0.1.27': + dependencies: + '@swc/counter': 0.1.3 + '@tailwindcss/node@4.3.1': dependencies: '@jridgewell/remapping': 2.3.5 @@ -8367,16 +9218,33 @@ snapshots: postcss: 8.5.15 tailwindcss: 4.3.1 + '@tanstack/query-async-storage-persister@5.101.1': + dependencies: + '@tanstack/query-core': 5.101.1 + '@tanstack/query-persist-client-core': 5.101.1 + '@tanstack/query-core@5.101.1': {} + '@tanstack/query-core@5.101.2': {} + '@tanstack/query-devtools@5.101.1': {} + '@tanstack/query-persist-client-core@5.101.1': + dependencies: + '@tanstack/query-core': 5.101.1 + '@tanstack/react-query-devtools@5.101.1(@tanstack/react-query@5.101.1(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/query-devtools': 5.101.1 '@tanstack/react-query': 5.101.1(react@19.2.4) react: 19.2.4 + '@tanstack/react-query-persist-client@5.101.1(@tanstack/react-query@5.101.1(react@19.2.4))(react@19.2.4)': + dependencies: + '@tanstack/query-persist-client-core': 5.101.1 + '@tanstack/react-query': 5.101.1(react@19.2.4) + react: 19.2.4 + '@tanstack/react-query@5.101.1(react@19.2.4)': dependencies: '@tanstack/query-core': 5.101.1 @@ -8509,6 +9377,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/trusted-types@2.0.7': {} + '@types/use-sync-external-store@0.0.6': {} '@types/wrap-ansi@3.0.0': {} @@ -9045,6 +9915,14 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.40 + caniuse-lite: 1.0.30001799 + electron-to-chromium: 1.5.379 + node-releases: 2.0.50 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + browserslist@4.28.4: dependencies: baseline-browser-mapping: 2.10.40 @@ -9144,6 +10022,8 @@ snapshots: commander@8.3.0: {} + common-tags@1.8.2: {} + commondir@1.0.1: {} compare-versions@6.1.1: {} @@ -9680,6 +10560,10 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-plugin-i18next@6.1.5: + dependencies: + requireindex: 1.1.0 + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): dependencies: '@rtsao/scc': 1.1.0 @@ -10115,6 +10999,14 @@ snapshots: - kerberos - supports-color + icu-minify@4.13.0: + dependencies: + '@formatjs/icu-messageformat-parser': 3.5.12 + + idb-keyval@6.2.5: {} + + idb@8.0.3: {} + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -10152,6 +11044,11 @@ snapshots: internmap@2.0.3: {} + intl-messageformat@11.2.9: + dependencies: + '@formatjs/fast-memoize': 3.1.6 + '@formatjs/icu-messageformat-parser': 3.5.12 + ip-address@10.2.0: {} is-array-buffer@3.0.5: @@ -10352,6 +11249,8 @@ snapshots: kleur@4.1.5: {} + kolorist@1.8.0: {} + kysely@0.28.17: {} language-subtag-registry@0.3.23: {} @@ -10426,6 +11325,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash.sortby@4.7.0: {} + log-symbols@7.0.1: dependencies: is-unicode-supported: 2.1.0 @@ -10538,10 +11439,31 @@ snapshots: negotiator@0.6.3: {} + negotiator@1.0.0: {} + neo-async@2.6.2: {} netmask@2.1.1: {} + next-intl-swc-plugin-extractor@4.13.0: {} + + next-intl@4.13.0(next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + dependencies: + '@formatjs/intl-localematcher': 0.8.10 + '@parcel/watcher': 2.5.6 + '@swc/core': 1.15.43 + icu-minify: 4.13.0 + negotiator: 1.0.0 + next: 16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next-intl-swc-plugin-extractor: 4.13.0 + po-parser: 2.1.1 + react: 19.2.4 + use-intl: 4.13.0(react@19.2.4) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@swc/helpers' + next@16.2.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@next/env': 16.2.9 @@ -10567,6 +11489,8 @@ snapshots: - '@babel/core' - babel-plugin-macros + node-addon-api@7.1.1: {} + node-exports-info@1.6.2: dependencies: array.prototype.flatmap: 1.3.3 @@ -10750,6 +11674,8 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + po-parser@2.1.1: {} + possible-typed-array-names@1.1.0: {} postal-mime@2.7.4: {} @@ -10782,6 +11708,8 @@ snapshots: prettier@3.8.5: {} + pretty-bytes@6.1.1: {} + prismjs@1.30.0: {} progress@2.0.3: {} @@ -11082,6 +12010,8 @@ snapshots: transitivePeerDependencies: - supports-color + requireindex@1.1.0: {} + reselect@5.2.0: {} resend@6.16.0(@react-email/render@2.0.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)): @@ -11187,10 +12117,30 @@ snapshots: semver@6.3.1: {} + semver@7.7.4: {} + semver@7.8.5: {} server-only@0.0.1: {} + serwist@9.5.11(browserslist@4.28.2)(typescript@5.9.3): + dependencies: + '@serwist/utils': 9.5.11(browserslist@4.28.2) + idb: 8.0.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - browserslist + + serwist@9.5.11(browserslist@4.28.4)(typescript@5.9.3): + dependencies: + '@serwist/utils': 9.5.11(browserslist@4.28.4) + idb: 8.0.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - browserslist + set-cookie-parser@3.1.1: {} set-function-length@1.2.2: @@ -11247,6 +12197,38 @@ snapshots: '@img/sharp-win32-x64': 0.34.5 optional: true + sharp@0.35.2: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.2 + '@img/sharp-darwin-x64': 0.35.2 + '@img/sharp-freebsd-wasm32': 0.35.2 + '@img/sharp-libvips-darwin-arm64': 1.3.1 + '@img/sharp-libvips-darwin-x64': 1.3.1 + '@img/sharp-libvips-linux-arm': 1.3.1 + '@img/sharp-libvips-linux-arm64': 1.3.1 + '@img/sharp-libvips-linux-ppc64': 1.3.1 + '@img/sharp-libvips-linux-riscv64': 1.3.1 + '@img/sharp-libvips-linux-s390x': 1.3.1 + '@img/sharp-libvips-linux-x64': 1.3.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + '@img/sharp-linux-arm': 0.35.2 + '@img/sharp-linux-arm64': 0.35.2 + '@img/sharp-linux-ppc64': 0.35.2 + '@img/sharp-linux-riscv64': 0.35.2 + '@img/sharp-linux-s390x': 0.35.2 + '@img/sharp-linux-x64': 0.35.2 + '@img/sharp-linuxmusl-arm64': 0.35.2 + '@img/sharp-linuxmusl-x64': 0.35.2 + '@img/sharp-webcontainers-wasm32': 0.35.2 + '@img/sharp-win32-arm64': 0.35.2 + '@img/sharp-win32-ia32': 0.35.2 + '@img/sharp-win32-x64': 0.35.2 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -11348,6 +12330,10 @@ snapshots: source-map@0.6.1: {} + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + split2@4.2.0: {} stable-hash@0.0.5: {} @@ -11510,6 +12496,10 @@ snapshots: tr46@0.0.3: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + tree-kill@1.2.2: {} ts-api-utils@2.5.0(typescript@5.9.3): @@ -11553,6 +12543,10 @@ snapshots: type-fest@0.7.1: {} + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 + type-fest@5.7.0: dependencies: tagged-tag: 1.0.0 @@ -11655,6 +12649,12 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + update-browserslist-db@1.2.3(browserslist@4.28.4): dependencies: browserslist: 4.28.4 @@ -11672,6 +12672,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + use-intl@4.13.0(react@19.2.4): + dependencies: + '@formatjs/fast-memoize': 3.1.6 + '@schummar/icu-type-parser': 1.21.5 + icu-minify: 4.13.0 + intl-messageformat: 11.2.9 + react: 19.2.4 + use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.4): dependencies: detect-node-es: 1.1.0 @@ -11731,6 +12739,8 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + webpack-sources@3.5.0: {} webpack-virtual-modules@0.6.2: @@ -11783,6 +12793,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 + when-exit@2.1.5: {} which-boxed-primitive@1.1.1: @@ -11871,6 +12887,8 @@ snapshots: dependencies: zod: 4.4.3 + zod@4.4.1: {} + zod@4.4.3: {} zxing-wasm@3.1.0(@types/emscripten@1.41.5): diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml index 0d2b6a23..76c46b88 100644 --- a/frontend/pnpm-workspace.yaml +++ b/frontend/pnpm-workspace.yaml @@ -1,6 +1,8 @@ allowBuilds: '@openapitools/openapi-generator-cli': set this to true or false + '@parcel/watcher': set this to true or false '@sentry/cli': set this to true or false + '@swc/core': set this to true or false core-js: set this to true or false esbuild: set this to true or false sharp: set this to true or false diff --git a/frontend/public/icons/apple-touch-icon-180.png b/frontend/public/icons/apple-touch-icon-180.png new file mode 100644 index 00000000..f5f23d0c Binary files /dev/null and b/frontend/public/icons/apple-touch-icon-180.png differ diff --git a/frontend/public/icons/icon-192.png b/frontend/public/icons/icon-192.png new file mode 100644 index 00000000..955b8566 Binary files /dev/null and b/frontend/public/icons/icon-192.png differ diff --git a/frontend/public/icons/icon-512.png b/frontend/public/icons/icon-512.png new file mode 100644 index 00000000..6511f954 Binary files /dev/null and b/frontend/public/icons/icon-512.png differ diff --git a/frontend/public/icons/icon-maskable-512.png b/frontend/public/icons/icon-maskable-512.png new file mode 100644 index 00000000..0e7dd3b6 Binary files /dev/null and b/frontend/public/icons/icon-maskable-512.png differ diff --git a/frontend/public/screenshots/screenshot-narrow.png b/frontend/public/screenshots/screenshot-narrow.png new file mode 100644 index 00000000..f108aa24 Binary files /dev/null and b/frontend/public/screenshots/screenshot-narrow.png differ diff --git a/frontend/public/screenshots/screenshot-wide.png b/frontend/public/screenshots/screenshot-wide.png new file mode 100644 index 00000000..8fe69dda Binary files /dev/null and b/frontend/public/screenshots/screenshot-wide.png differ diff --git a/frontend/public/splash/apple-splash-1080-2340.png b/frontend/public/splash/apple-splash-1080-2340.png new file mode 100644 index 00000000..5636deb8 Binary files /dev/null and b/frontend/public/splash/apple-splash-1080-2340.png differ diff --git a/frontend/public/splash/apple-splash-1125-2436.png b/frontend/public/splash/apple-splash-1125-2436.png new file mode 100644 index 00000000..9d458fcd Binary files /dev/null and b/frontend/public/splash/apple-splash-1125-2436.png differ diff --git a/frontend/public/splash/apple-splash-1170-2532.png b/frontend/public/splash/apple-splash-1170-2532.png new file mode 100644 index 00000000..1db208d7 Binary files /dev/null and b/frontend/public/splash/apple-splash-1170-2532.png differ diff --git a/frontend/public/splash/apple-splash-1179-2556.png b/frontend/public/splash/apple-splash-1179-2556.png new file mode 100644 index 00000000..c1a01432 Binary files /dev/null and b/frontend/public/splash/apple-splash-1179-2556.png differ diff --git a/frontend/public/splash/apple-splash-1206-2622.png b/frontend/public/splash/apple-splash-1206-2622.png new file mode 100644 index 00000000..e6b7f149 Binary files /dev/null and b/frontend/public/splash/apple-splash-1206-2622.png differ diff --git a/frontend/public/splash/apple-splash-1242-2208.png b/frontend/public/splash/apple-splash-1242-2208.png new file mode 100644 index 00000000..a019eda0 Binary files /dev/null and b/frontend/public/splash/apple-splash-1242-2208.png differ diff --git a/frontend/public/splash/apple-splash-1242-2688.png b/frontend/public/splash/apple-splash-1242-2688.png new file mode 100644 index 00000000..aef04bc6 Binary files /dev/null and b/frontend/public/splash/apple-splash-1242-2688.png differ diff --git a/frontend/public/splash/apple-splash-1284-2778.png b/frontend/public/splash/apple-splash-1284-2778.png new file mode 100644 index 00000000..1dfffd43 Binary files /dev/null and b/frontend/public/splash/apple-splash-1284-2778.png differ diff --git a/frontend/public/splash/apple-splash-1290-2796.png b/frontend/public/splash/apple-splash-1290-2796.png new file mode 100644 index 00000000..330bb22e Binary files /dev/null and b/frontend/public/splash/apple-splash-1290-2796.png differ diff --git a/frontend/public/splash/apple-splash-1320-2868.png b/frontend/public/splash/apple-splash-1320-2868.png new file mode 100644 index 00000000..a7216d58 Binary files /dev/null and b/frontend/public/splash/apple-splash-1320-2868.png differ diff --git a/frontend/public/splash/apple-splash-1536-2048.png b/frontend/public/splash/apple-splash-1536-2048.png new file mode 100644 index 00000000..075103c7 Binary files /dev/null and b/frontend/public/splash/apple-splash-1536-2048.png differ diff --git a/frontend/public/splash/apple-splash-1620-2160.png b/frontend/public/splash/apple-splash-1620-2160.png new file mode 100644 index 00000000..7b0e0ce0 Binary files /dev/null and b/frontend/public/splash/apple-splash-1620-2160.png differ diff --git a/frontend/public/splash/apple-splash-1640-2360.png b/frontend/public/splash/apple-splash-1640-2360.png new file mode 100644 index 00000000..8fbda9dc Binary files /dev/null and b/frontend/public/splash/apple-splash-1640-2360.png differ diff --git a/frontend/public/splash/apple-splash-1668-2224.png b/frontend/public/splash/apple-splash-1668-2224.png new file mode 100644 index 00000000..30ef9295 Binary files /dev/null and b/frontend/public/splash/apple-splash-1668-2224.png differ diff --git a/frontend/public/splash/apple-splash-1668-2388.png b/frontend/public/splash/apple-splash-1668-2388.png new file mode 100644 index 00000000..9d6118c7 Binary files /dev/null and b/frontend/public/splash/apple-splash-1668-2388.png differ diff --git a/frontend/public/splash/apple-splash-2048-2732.png b/frontend/public/splash/apple-splash-2048-2732.png new file mode 100644 index 00000000..56ef87bb Binary files /dev/null and b/frontend/public/splash/apple-splash-2048-2732.png differ diff --git a/frontend/public/splash/apple-splash-750-1334.png b/frontend/public/splash/apple-splash-750-1334.png new file mode 100644 index 00000000..ecbf99f0 Binary files /dev/null and b/frontend/public/splash/apple-splash-750-1334.png differ diff --git a/frontend/public/splash/apple-splash-828-1792.png b/frontend/public/splash/apple-splash-828-1792.png new file mode 100644 index 00000000..0353d191 Binary files /dev/null and b/frontend/public/splash/apple-splash-828-1792.png differ diff --git a/frontend/scripts/check-i18n.mjs b/frontend/scripts/check-i18n.mjs new file mode 100644 index 00000000..a67c1162 --- /dev/null +++ b/frontend/scripts/check-i18n.mjs @@ -0,0 +1,73 @@ +// Verifies that every locale catalog in src/i18n/messages/ has exactly the same +// set of keys as the reference locale (hr). Run via `pnpm i18n:check`. +// Exits non-zero on any missing/extra key so CI fails when a translation is +// forgotten in one language. +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const MESSAGES_DIR = join( + dirname(fileURLToPath(import.meta.url)), + "..", + "src", + "i18n", + "messages", +); + +const REFERENCE = "hr"; +const LOCALES = ["hr", "en", "de", "sl"]; + +function flattenKeys(obj, prefix = "") { + const keys = []; + + for (const [key, value] of Object.entries(obj)) { + const path = prefix ? `${prefix}.${key}` : key; + + if (value && typeof value === "object" && !Array.isArray(value)) { + keys.push(...flattenKeys(value, path)); + } else { + keys.push(path); + } + } + + return keys.sort(); +} + +function loadKeys(locale) { + const raw = readFileSync(join(MESSAGES_DIR, `${locale}.json`), "utf8"); + return flattenKeys(JSON.parse(raw)); +} + +const reference = loadKeys(REFERENCE); +let hasError = false; + +for (const locale of LOCALES) { + if (locale === REFERENCE) continue; + + const keys = loadKeys(locale); + const missing = reference.filter((key) => !keys.includes(key)); + const extra = keys.filter((key) => !reference.includes(key)); + + if (missing.length > 0) { + hasError = true; + console.error(`\n❌ ${locale}.json is MISSING ${missing.length} key(s):`); + missing.forEach((key) => console.error(` - ${key}`)); + } + + if (extra.length > 0) { + hasError = true; + console.error(`\n❌ ${locale}.json has ${extra.length} EXTRA key(s):`); + extra.forEach((key) => console.error(` + ${key}`)); + } +} + +if (hasError) { + console.error( + "\ni18n parity check failed. Every locale must define the same keys as hr.\n", + ); + process.exit(1); +} + +console.log( + `✅ i18n parity OK — ${reference.length} keys across ${LOCALES.join(", ")}.`, +); diff --git a/frontend/scripts/generate-ios-splash.mjs b/frontend/scripts/generate-ios-splash.mjs new file mode 100644 index 00000000..74286450 --- /dev/null +++ b/frontend/scripts/generate-ios-splash.mjs @@ -0,0 +1,46 @@ +// One-off generator for iOS launch (splash) screens. Run from the frontend dir: +// node scripts/generate-ios-splash.mjs +// iOS ignores the web manifest for launch screens and instead shows the image +// referenced by a device-matched . +// The device list is shared with the component +// (src/constants/ios-splash-screens.json) so the tags and files never drift. +import sharp from "sharp"; +import { readFile, mkdir } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); +const SRC_LOGO = path.join(ROOT, "public/disscount-logo.png"); +const OUT = path.join(ROOT, "public/splash"); + +const BACKGROUND = { r: 255, g: 255, b: 255, alpha: 1 }; +const LOGO_RATIO = 0.35; // logo occupies ~35% of the shorter edge + +const devices = JSON.parse( + await readFile(path.join(ROOT, "src/constants/ios-splash-screens.json"), "utf8"), +); + +await mkdir(OUT, { recursive: true }); + +for (const device of devices) { + const pxWidth = device.width * device.ratio; + const pxHeight = device.height * device.ratio; + const logoSize = Math.round(Math.min(pxWidth, pxHeight) * LOGO_RATIO); + + const logo = await sharp(SRC_LOGO) + .resize(logoSize, logoSize, { + fit: "contain", + background: { r: 0, g: 0, b: 0, alpha: 0 }, + }) + .png() + .toBuffer(); + + await sharp({ + create: { width: pxWidth, height: pxHeight, channels: 4, background: BACKGROUND }, + }) + .composite([{ input: logo, gravity: "centre" }]) + .png() + .toFile(path.join(OUT, `apple-splash-${pxWidth}-${pxHeight}.png`)); +} + +console.log(`Generated ${devices.length} iOS splash screens in public/splash/`); diff --git a/frontend/scripts/generate-pwa-icons.mjs b/frontend/scripts/generate-pwa-icons.mjs new file mode 100644 index 00000000..ae0c66cb --- /dev/null +++ b/frontend/scripts/generate-pwa-icons.mjs @@ -0,0 +1,47 @@ +// One-off generator for the PWA icon set. Run from the frontend dir: +// node scripts/generate-pwa-icons.mjs +// Produces the icons referenced by app/manifest.ts and layout metadata. +import sharp from "sharp"; +import { mkdir } from "node:fs/promises"; +import path from "node:path"; + +const SRC = "public/disscount-logo.png"; +const OUT = "public/icons"; + +const transparent = { r: 0, g: 0, b: 0, alpha: 0 }; +const white = { r: 255, g: 255, b: 255, alpha: 1 }; + +// "any"-purpose icon: the logo on a transparent canvas. +async function generateTransparent(size, file) { + await sharp(SRC) + .resize(size, size, { fit: "contain", background: transparent }) + .png() + .toFile(path.join(OUT, file)); +} + +// maskable / apple icon: the logo centered on a solid background with padding +// so OS masks (circle, squircle) never clip it, and iOS gets no transparency. +async function generateOnBackground(size, logoRatio, background, file) { + const logoSize = Math.round(size * logoRatio); + + const logo = await sharp(SRC) + .resize(logoSize, logoSize, { fit: "contain", background: transparent }) + .png() + .toBuffer(); + + await sharp({ + create: { width: size, height: size, channels: 4, background }, + }) + .composite([{ input: logo, gravity: "centre" }]) + .png() + .toFile(path.join(OUT, file)); +} + +await mkdir(OUT, { recursive: true }); + +await generateTransparent(192, "icon-192.png"); +await generateTransparent(512, "icon-512.png"); +await generateOnBackground(512, 0.66, white, "icon-maskable-512.png"); +await generateOnBackground(180, 0.82, white, "apple-touch-icon-180.png"); + +console.log(`Generated PWA icons in ${OUT}/`); diff --git a/frontend/scripts/generate-pwa-screenshots.mjs b/frontend/scripts/generate-pwa-screenshots.mjs new file mode 100644 index 00000000..9d221d36 --- /dev/null +++ b/frontend/scripts/generate-pwa-screenshots.mjs @@ -0,0 +1,67 @@ +// One-off generator for the manifest `screenshots` (branded promo cards shown +// in Chrome's richer install dialog). Run from the frontend dir: +// node scripts/generate-pwa-screenshots.mjs +// These are placeholder brand cards — swap them for real app captures whenever +// you have them; the manifest wiring stays the same. +import sharp from "sharp"; +import { mkdir } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); +const SRC_LOGO = path.join(ROOT, "public/disscount-logo.png"); +const OUT = path.join(ROOT, "public/screenshots"); + +const NAVY = "#143a5a"; +const GRAY = "#6b7280"; +const GREEN = "#2ec50d"; +const TAGLINE = "Pronađi najbolje cijene u Hrvatskoj"; + +async function generateScreenshot({ width, height, logoSize, logoTop, fontSize, file }) { + const centerX = width / 2; + const wordmarkY = logoTop + logoSize + fontSize; + const accentY = wordmarkY + fontSize * 0.35; + const taglineY = accentY + fontSize * 0.9; + + const svg = ` + + + Disscount + + ${TAGLINE} + `; + + const logo = await sharp(SRC_LOGO) + .resize(logoSize, logoSize, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } }) + .png() + .toBuffer(); + + await sharp(Buffer.from(svg)) + .composite([{ input: logo, top: logoTop, left: Math.round((width - logoSize) / 2) }]) + .png() + .toFile(path.join(OUT, file)); +} + +await mkdir(OUT, { recursive: true }); + +await generateScreenshot({ + width: 1080, + height: 1920, + logoSize: 420, + logoTop: 560, + fontSize: 110, + file: "screenshot-narrow.png", +}); + +await generateScreenshot({ + width: 1920, + height: 1080, + logoSize: 300, + logoTop: 230, + fontSize: 96, + file: "screenshot-wide.png", +}); + +console.log("Generated PWA screenshots in public/screenshots/"); diff --git a/frontend/src/app/(root)/components/sections/hero-actions.tsx b/frontend/src/app/(root)/components/sections/hero-actions.tsx index 01073169..fb44f3aa 100644 --- a/frontend/src/app/(root)/components/sections/hero-actions.tsx +++ b/frontend/src/app/(root)/components/sections/hero-actions.tsx @@ -2,6 +2,7 @@ import { useCallback, Suspense } from "react"; import { useRouter } from "next/navigation"; +import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { ScanBarcode } from "lucide-react"; import { Card } from "@/components/ui/card"; @@ -11,6 +12,7 @@ import SearchBar from "@/components/custom/search-bar"; import SearchBarSkeleton from "@/components/custom/search-bar-skeleton"; export default function HeroActions() { + const t = useTranslations("hero"); const { openScanner } = useCameraScanner(); const router = useRouter(); @@ -28,7 +30,7 @@ export default function HeroActions() { }>

- ili + {t("or")}

@@ -53,7 +55,7 @@ export default function HeroActions() { className="w-full text-lg" > - Skeniraj barkod + {t("scanBarcode")}
diff --git a/frontend/src/app/(root)/components/sections/hero-section.tsx b/frontend/src/app/(root)/components/sections/hero-section.tsx index d6ab2be8..5c1923b9 100644 --- a/frontend/src/app/(root)/components/sections/hero-section.tsx +++ b/frontend/src/app/(root)/components/sections/hero-section.tsx @@ -1,29 +1,19 @@ import Image from "next/image"; +import { getTranslations } from "next-intl/server"; import HeroActions from "@/app/(root)/components/sections/hero-actions"; -const tagLines: string[] = [ - "Usporedi trgovine i cijene!", - "Uštedi pri svakoj kupnji!", - "Nikad ne propusti akciju!", - "Zaboravi kartice - olakšaj novčanik!", - "Izradi i podijeli popis za kupnju!", - "Prati povijest cijena!", - "Skeniraj barkod i usporedi cijene!", - "Uživaj u pametnom kupovanju!", - "Pronađi najbolje ponude u Hrvatskoj!", - "Kupuj kvalitetno i jeftino!", - "Kupuj pametno, uštedi više!", - "Tvoj partner u pametnoj kupovini!", - "Zaboravi na kataloge i letke!", -]; - -function getTagLine(): string { - const randomIndex = Math.floor(Math.random() * tagLines.length); - return tagLines[randomIndex]; +function pickRandom(taglines: string[]): string { + const randomIndex = Math.floor(Math.random() * taglines.length); + return taglines[randomIndex]; } -export default function HeroSection() { - const tagLine: string = getTagLine(); +export default async function HeroSection() { + const t = await getTranslations("hero"); + const tCommon = await getTranslations("common"); + + // `taglines` is an array message; `t.raw` returns it untransformed. + const taglines = t.raw("taglines") as string[]; + const tagLine = pickRandom(taglines); return (
@@ -32,7 +22,7 @@ export default function HeroSection() { {/* App logo */} Disscount logo { + const t = await getTranslations("pages.home"); + + return { + title: t("metaTitle"), + description: t("metaDescription"), + }; +} export default function Home() { return ( diff --git a/frontend/src/app/(user)/digital-cards/components/digital-card-item.tsx b/frontend/src/app/(user)/digital-cards/components/digital-card-item.tsx index bc4f4d9b..b9f25d56 100644 --- a/frontend/src/app/(user)/digital-cards/components/digital-card-item.tsx +++ b/frontend/src/app/(user)/digital-cards/components/digital-card-item.tsx @@ -13,6 +13,7 @@ import { } from "@/components/ui/dropdown-menu"; import { digitalCardService } from "@/lib/api"; import { toast } from "sonner"; +import { useTranslations } from "next-intl"; import { useQueryClient } from "@tanstack/react-query"; import { formatDate } from "@/utils/strings"; import { useUser } from "@/context/user-context"; @@ -27,19 +28,21 @@ export default function DigitalCardItem({ handleEdit, }: IDigitalCardItemProps) { const queryClient = useQueryClient(); + const t = useTranslations("pages.digitalCards"); + const tCommon = useTranslations("common"); const deleteDigitalCardMutation = digitalCardService.useDeleteDigitalCard(); async function handleDelete(d: DigitalCardDto) { - if (confirm(`Jeste li sigurni da želite obrisati karticu "${d.title}"?`)) { + if (confirm(t("deleteConfirm", { title: d.title }))) { deleteDigitalCardMutation.mutate(d.id, { onSuccess: async () => { - toast.success("Kartica obrisana."); + toast.success(t("deleted")); await queryClient.invalidateQueries({ queryKey: ["digitalCards"], }); }, onError: (error: Error) => { - toast.error("Greška prilikom brisanja: " + error.message); + toast.error(t("deleteError", { message: error.message })); }, }); } @@ -69,7 +72,7 @@ export default function DigitalCardItem({ handleEdit(digitalCard)}> - Uredi + {t("edit")} - Obriši + {tCommon("delete")} @@ -96,7 +99,7 @@ export default function DigitalCardItem({ )}

- Stvoreno {formatDate(digitalCard.createdAt)} + {t("createdLabel", { date: formatDate(digitalCard.createdAt) })}

diff --git a/frontend/src/app/(user)/digital-cards/components/digital-cards-client.tsx b/frontend/src/app/(user)/digital-cards/components/digital-cards-client.tsx index 609a0788..e5073774 100644 --- a/frontend/src/app/(user)/digital-cards/components/digital-cards-client.tsx +++ b/frontend/src/app/(user)/digital-cards/components/digital-cards-client.tsx @@ -3,6 +3,7 @@ import { useState, Suspense } from "react"; import { usePathname } from "next/navigation"; import { Search, Plus, Loader2, CreditCard } from "lucide-react"; +import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import SearchBar from "@/components/custom/search-bar"; import SearchBarSkeleton from "@/components/custom/search-bar-skeleton"; @@ -21,6 +22,7 @@ import { useQueryClient } from "@tanstack/react-query"; import BlockLoadingSpinner from "@/components/custom/block-loading-spinner"; export default function DigitalCardsClient({ query }: { query: string }) { + const t = useTranslations("pages.digitalCards"); const pathname = usePathname(); const queryClient = useQueryClient(); const [isModalOpen, setIsModalOpen] = useState(false); @@ -64,13 +66,13 @@ export default function DigitalCardsClient({ query }: { query: string }) { setIsModalOpen(true)} icon={} - label="Dodaj digitalnu karticu" + label={t("add")} />
}>

{query.length > 0 - ? `Rezultati pretrage za "${query}" (${matchingDigitalCards.length})` - : `Moje digitalne kartice${ - isUserLoading ? "" : ` (${matchingDigitalCards.length})` - }`} + ? t("searchResults", { + query, + count: matchingDigitalCards.length, + }) + : t("heading", { + count: isUserLoading + ? "" + : ` (${matchingDigitalCards.length})`, + })}

{/* */} @@ -119,12 +126,10 @@ export default function DigitalCardsClient({ query }: { query: string }) {

- Nema digitalnih kartica + {t("emptyTitle")}

-

- Stvorite svoju prvu digitalnu karticu… -

+

{t("emptyDescription")}

)} diff --git a/frontend/src/app/(user)/digital-cards/components/forms/digital-card-modal.tsx b/frontend/src/app/(user)/digital-cards/components/forms/digital-card-modal.tsx index fb7dbb82..4993f902 100644 --- a/frontend/src/app/(user)/digital-cards/components/forms/digital-card-modal.tsx +++ b/frontend/src/app/(user)/digital-cards/components/forms/digital-card-modal.tsx @@ -3,6 +3,7 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Save } from "lucide-react"; +import { useTranslations } from "next-intl"; import { Dialog, DialogContent, @@ -51,6 +52,8 @@ export default function DigitalCardModal({ digitalCard, }: IDigitalCardModalProps) { const queryClient = useQueryClient(); + const t = useTranslations("pages.digitalCards.modal"); + const tCommon = useTranslations("common"); const createMutation = digitalCardService.useCreateDigitalCard(); const updateMutation = digitalCardService.useUpdateDigitalCard(); @@ -73,7 +76,7 @@ export default function DigitalCardModal({ { id: digitalCard.id, data }, { onSuccess: async () => { - toast.success("Digitalna kartica je uspješno ažurirana!"); + toast.success(t("updated")); await queryClient.invalidateQueries({ queryKey: ["digitalCards"], }); @@ -84,8 +87,7 @@ export default function DigitalCardModal({ const message = error instanceof Error ? error.message : String(error); form.setError("root", { - message: - message || "Došlo je do greške prilikom ažuriranja kartice", + message: message || t("updateError"), }); }, } @@ -93,7 +95,7 @@ export default function DigitalCardModal({ } else { createMutation.mutate(data, { onSuccess: async () => { - toast.success("Digitalna kartica je uspješno kreirana!"); + toast.success(t("created")); await queryClient.invalidateQueries({ queryKey: ["digitalCards"], }); @@ -104,7 +106,7 @@ export default function DigitalCardModal({ const message = error instanceof Error ? error.message : String(error); form.setError("root", { - message: message || "Došlo je do greške prilikom kreiranja kartice", + message: message || t("createError"), }); }, }); @@ -123,7 +125,7 @@ export default function DigitalCardModal({ - {digitalCard ? "Uredi digitalnu karticu" : "Nova digitalna kartica"} + {digitalCard ? t("editTitle") : t("createTitle")} @@ -140,7 +142,7 @@ export default function DigitalCardModal({ name="title" render={({ field }) => ( - Naziv kartice + {t("titleLabel")} @@ -154,7 +156,7 @@ export default function DigitalCardModal({ name="value" render={({ field }) => ( - Vrijednost/Kod + {t("valueLabel")} @@ -169,22 +171,28 @@ export default function DigitalCardModal({ name="type" render={({ field }) => ( - Tip kartice + {t("typeLabel")} @@ -197,21 +205,23 @@ export default function DigitalCardModal({ name="codeType" render={({ field }) => ( - Tip koda + {t("codeTypeLabel")} @@ -225,7 +235,7 @@ export default function DigitalCardModal({ name="color" render={({ field }) => ( - Boja kartice + {t("colorLabel")} ( - Napomena + {t("noteLabel")}