diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..61f6bf4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +.next +node_modules +npm-debug.log +dist diff --git a/.env.sample b/.env.sample index 2c721e5..a431a2f 100644 --- a/.env.sample +++ b/.env.sample @@ -1,2 +1,2 @@ -VITE_API_BASE_URL=https://api.east-guilan-ce.ir - +NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8000 +NEXT_PUBLIC_SITE_URL=http://localhost:8080 diff --git a/.gitignore b/.gitignore index a0377b4..ffe15f1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ pnpm-debug.log* lerna-debug.log* node_modules +.next dist dist-ssr .env diff --git a/README.md b/README.md index dd05475..e22204a 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,41 @@ # Frontend ## Stack -- Vite + React 18 with TypeScript. -- `@tanstack/react-query` for data fetching and caching. -- shadcn/ui primitives (button, card, tabs, dialog, etc.) with Tailwind CSS. -- Sonner & Toast UI for notifications, Markdown rendering, RTL layout, and Persian-digit helpers. +- Next.js App Router with React 18 and TypeScript. +- `@tanstack/react-query` for client-side authenticated flows. +- Tailwind CSS and shadcn/ui components. +- `next-themes`, Sonner, and toast helpers for RTL UI and notifications. + +## Environment +Copy `.env.sample` to `.env`. + +Required variables: +- `NEXT_PUBLIC_API_BASE_URL` +- `NEXT_PUBLIC_SITE_URL` ## Development - -### Install dependencies ```bash npm install +npm run dev ``` -### Configure API base URL -```bash -cp .env.sample .env -``` +The local dev server runs on `http://localhost:8080`. -### Run dev server -```bash -npm run dev -- --host -``` - -### Production build +## Production build ```bash npm run build +npm run start ``` -The Vite build reads `VITE_API_BASE_URL` from `.env`. +The production runtime serves on port `3000` inside Docker. Dockerfiles live only in `guilan-ace-deployment`. -## Features -- **Public site**: homepage, events list/detail, blog list, auth flows, profile, payments. -- **Admin dashboard**: staff-only portal with vertical tabs, user filtering, event filtering, popup detail with registrations/payments, and inline event editing/deletion. -- **Utils**: Persian digit formatting, price conversion (Rial → Toman), shared API client with JWT token refresh handling, and helper components (scroll area, table, dialog). +## Routes +- Public SEO pages: `/`, `/about`, `/blog`, `/blog/[slug]`, `/events`, `/events/[slug]` +- Client-heavy flows: `/auth`, `/profile`, `/logout`, `/payments/result`, `/reset-password/*`, `/verify-email/*` +- Admin: `/admin/*` -## Testing & linting +## Validation ```bash npm run lint +npm run build ``` - -JavaScript/TypeScript linting is configured through ESLint + `typescript-eslint`. Run lint before commits to keep code healthy. diff --git a/bun.lockb b/bun.lockb deleted file mode 100644 index d3914e8..0000000 Binary files a/bun.lockb and /dev/null differ diff --git a/eslint.config.js b/eslint.config.js index 40f72cc..3ded898 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,11 +1,11 @@ import js from "@eslint/js"; +import nextPlugin from "@next/eslint-plugin-next"; import globals from "globals"; import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ["dist"] }, + { ignores: ["dist", ".next", "next-env.d.ts"] }, { extends: [js.configs.recommended, ...tseslint.configs.recommended], files: ["**/*.{ts,tsx}"], @@ -14,12 +14,12 @@ export default tseslint.config( globals: globals.browser, }, plugins: { + "@next/next": nextPlugin, "react-hooks": reactHooks, - "react-refresh": reactRefresh, }, rules: { + ...nextPlugin.configs.recommended.rules, ...reactHooks.configs.recommended.rules, - "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], "@typescript-eslint/no-unused-vars": "off", }, }, diff --git a/index.html b/index.html deleted file mode 100644 index 00bb4be..0000000 --- a/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - انجمن علمی کامپیوتر گیلان - Guilan ACE - - - - - - - - - -
- - - diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..a807c0d --- /dev/null +++ b/next.config.ts @@ -0,0 +1,25 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + output: "standalone", + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "api.east-guilan-ce.ir", + }, + { + protocol: "http", + hostname: "127.0.0.1", + port: "8000", + }, + { + protocol: "http", + hostname: "localhost", + port: "8000", + }, + ], + }, +}; + +export default nextConfig; diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index 142bcf0..0000000 --- a/nginx.conf +++ /dev/null @@ -1,33 +0,0 @@ -server { - listen 80; - server_name localhost; - root /usr/share/nginx/html; - index index.html; - - # Enable gzip compression - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json; - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - - # Handle Next.js static export - location / { - try_files $uri $uri.html $uri/ /index.html; - } - - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Disable access to hidden files - location ~ /\. { - deny all; - } -} diff --git a/package-lock.json b/package-lock.json index b37b414..94dab25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "vite_react_shadcn_ts", + "name": "guilan-ace-frontend", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "vite_react_shadcn_ts", + "name": "guilan-ace-frontend", "version": "0.0.0", "dependencies": { "@hookform/resolvers": "^3.10.0", @@ -46,16 +46,15 @@ "input-otp": "^1.4.2", "jspdf": "^2.5.1", "lucide-react": "^0.462.0", + "next": "^15.4.6", "next-themes": "^0.3.0", "react": "^18.3.1", "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", - "react-helmet-async": "^2.0.5", "react-hook-form": "^7.61.1", "react-markdown": "^9.0.3", "react-qr-code": "^2.0.11", "react-resizable-panels": "^2.1.9", - "react-router-dom": "^6.30.1", "recharts": "^2.15.4", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", @@ -68,22 +67,20 @@ }, "devDependencies": { "@eslint/js": "^9.32.0", + "@next/eslint-plugin-next": "^16.2.6", "@tailwindcss/typography": "^0.5.16", "@types/node": "^22.16.5", "@types/react": "^18.3.26", "@types/react-dom": "^18.3.7", - "@vitejs/plugin-react-swc": "^3.11.0", "autoprefixer": "^10.4.21", "eslint": "^9.32.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^15.15.0", - "lovable-tagger": "^1.1.10", "postcss": "^8.5.6", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", - "typescript-eslint": "^8.38.0", - "vite": "^5.4.19" + "typescript-eslint": "^8.38.0" } }, "node_modules/@alloc/quick-lru": { @@ -98,42 +95,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.9.tgz", - "integrity": "sha512-aI3jjAAO1fh7vY/pBGsn1i9LDbRP43+asrRlkPuTXW5yHXtd1NgTEMudbBoDDxrf1daEEfPJqR+JBMakzrR4Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/runtime": { "version": "7.28.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", @@ -143,443 +104,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/types": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.9.tgz", - "integrity": "sha512-OwS2CM5KocvQ/k7dFJa8i5bNGJP0hXWfVCfDkqRFP1IreH1JDC7wG6eCYCi0+McbfT8OR/kNqsI0UU0xP9H6PQ==", - "dev": true, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://package-mirror.liara.ir/repository/npm/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "license": "MIT", + "optional": true, "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "tslib": "^2.4.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -849,6 +381,472 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -914,6 +912,180 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@next/env": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/env/-/env-15.5.18.tgz", + "integrity": "sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.2.6", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.6.tgz", + "integrity": "sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://package-mirror.liara.ir/repository/npm/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://package-mirror.liara.ir/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.18.tgz", + "integrity": "sha512-w0WvQf1n+txiwns/9pwIQteCJpZTbxzO2SE0FLcwuD4v0WEh1JPOjdyxWL21XwJsdpx8cFRjyzxzCS/siP7HcQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.18.tgz", + "integrity": "sha512-znn71QmDuxm+BOaglihMZfvyySMnNljkVIY5Z2TCssBmm+WqL6c19VhtH5ktFkHa8EZ2bnTUpcNcmNSQsg67og==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.18.tgz", + "integrity": "sha512-yPPe5MNL+igZUa+OsqQJisqSfh6oarIuA1Q0BDxljGJhRQyZeP+WRHh7rs/jZUGMh5aY0YdIjXZG0VohkKkUdw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.18.tgz", + "integrity": "sha512-glaCczEWIrHsokFZ3pP08U4BpKxwIdnT+txdOM32OBgpL9Yw4aqx8NejmgtZQZOdstQ5f0L3CasIZudzCuD+nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.18.tgz", + "integrity": "sha512-oUfg2EgJmU3R0OCOWiokGFUTvZiPfXtriXiuF3YNxRoROCdgvTedHIzYoeKH34gsZxS/V7mHbfq2hpAHwhH1/A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.18.tgz", + "integrity": "sha512-JLxSP3KTd9iu/bvUMQxH7RJo9xKSHf55/6RPE4a6FTSZygGn7uvZbCej0AHXydwkggQGSD9UddSjwv6Xz5ESfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.18.tgz", + "integrity": "sha512-ir1v7enP52K2HNz3tQQvwF+x7VNxBk1ciiZ18WBPvxf4C59IqdfmHPJYK3vH7rSxpuCVw/8C712wTXNAtEp+NA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.18.tgz", + "integrity": "sha512-LIu5me6QTANCd25E7I5uIEfvgQ06RK7tvHAbYo3zCb3VpxQEPvMcSpd87NwUABDT6MbGPdEGR5VRiK4PPTJhQg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2313,472 +2485,6 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, - "node_modules/@remix-run/router": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", - "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@swc/core": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.2.tgz", - "integrity": "sha512-YWqn+0IKXDhqVLKoac4v2tV6hJqB/wOh8/Br8zjqeqBkKa77Qb0Kw2i7LOFzjFNZbZaPH6AlMGlBwNrxaauaAg==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.23" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.13.2", - "@swc/core-darwin-x64": "1.13.2", - "@swc/core-linux-arm-gnueabihf": "1.13.2", - "@swc/core-linux-arm64-gnu": "1.13.2", - "@swc/core-linux-arm64-musl": "1.13.2", - "@swc/core-linux-x64-gnu": "1.13.2", - "@swc/core-linux-x64-musl": "1.13.2", - "@swc/core-win32-arm64-msvc": "1.13.2", - "@swc/core-win32-ia32-msvc": "1.13.2", - "@swc/core-win32-x64-msvc": "1.13.2" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.2.tgz", - "integrity": "sha512-44p7ivuLSGFJ15Vly4ivLJjg3ARo4879LtEBAabcHhSZygpmkP8eyjyWxrH3OxkY1eRZSIJe8yRZPFw4kPXFPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.2.tgz", - "integrity": "sha512-Lb9EZi7X2XDAVmuUlBm2UvVAgSCbD3qKqDCxSI4jEOddzVOpNCnyZ/xEampdngUIyDDhhJLYU9duC+Mcsv5Y+A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.2.tgz", - "integrity": "sha512-9TDe/92ee1x57x+0OqL1huG4BeljVx0nWW4QOOxp8CCK67Rpc/HHl2wciJ0Kl9Dxf2NvpNtkPvqj9+BUmM9WVA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.2.tgz", - "integrity": "sha512-KJUSl56DBk7AWMAIEcU83zl5mg3vlQYhLELhjwRFkGFMvghQvdqQ3zFOYa4TexKA7noBZa3C8fb24rI5sw9Exg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.2.tgz", - "integrity": "sha512-teU27iG1oyWpNh9CzcGQ48ClDRt/RCem7mYO7ehd2FY102UeTws2+OzLESS1TS1tEZipq/5xwx3FzbVgiolCiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.2.tgz", - "integrity": "sha512-dRPsyPyqpLD0HMRCRpYALIh4kdOir8pPg4AhNQZLehKowigRd30RcLXGNVZcc31Ua8CiPI4QSgjOIxK+EQe4LQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.2.tgz", - "integrity": "sha512-CCxETW+KkYEQDqz1SYC15YIWYheqFC+PJVOW76Maa/8yu8Biw+HTAcblKf2isrlUtK8RvrQN94v3UXkC2NzCEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.2.tgz", - "integrity": "sha512-Wv/QTA6PjyRLlmKcN6AmSI4jwSMRl0VTLGs57PHTqYRwwfwd7y4s2fIPJVBNbAlXd795dOEP6d/bGSQSyhOX3A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.2.tgz", - "integrity": "sha512-PuCdtNynEkUNbUXX/wsyUC+t4mamIU5y00lT5vJcAvco3/r16Iaxl5UCzhXYaWZSNVZMzPp9qN8NlSL8M5pPxw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.2.tgz", - "integrity": "sha512-qlmMkFZJus8cYuBURx1a3YAG2G7IW44i+FEYV5/32ylKkzGNAr9tDJSA53XNnNXkAB5EXSPsOz7bn5C3JlEtdQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@swc/types": { - "version": "0.1.23", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", - "integrity": "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, "node_modules/@tailwindcss/typography": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", @@ -3256,20 +2962,6 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" }, - "node_modules/@vitejs/plugin-react-swc": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz", - "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.27", - "@swc/core": "^1.12.11" - }, - "peerDependencies": { - "vite": "^4 || ^5 || ^6 || ^7" - } - }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -3555,7 +3247,6 @@ "version": "1.0.30001727", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true, "funding": [ { "type": "opencollective", @@ -3700,6 +3391,12 @@ "url": "https://polar.sh/cva" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://package-mirror.liara.ir/repository/npm/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -3999,6 +3696,16 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://package-mirror.liara.ir/repository/npm/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -4103,45 +3810,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4342,16 +4010,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4909,14 +4567,6 @@ "node": ">=12" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -5225,456 +4875,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lovable-tagger": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/lovable-tagger/-/lovable-tagger-1.1.10.tgz", - "integrity": "sha512-LbYaxi6vgrqg7Sq93/cRbIM78EP+X+GUU7spx804yqB2bxfiOej8UvcZHeE4WqMjAFI2dHGhXpy8r6SnvmrzGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.8", - "esbuild": "^0.25.0", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12", - "tailwindcss": "^3.4.17" - }, - "peerDependencies": { - "vite": ">=5.0.0 <8.0.0" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/lovable-tagger/node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" - } - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -5689,16 +4889,6 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, - "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -6595,6 +5785,58 @@ "dev": true, "license": "MIT" }, + "node_modules/next": { + "version": "15.5.18", + "resolved": "https://package-mirror.liara.ir/repository/npm/next/-/next-15.5.18.tgz", + "integrity": "sha512-eKL8zUJkX9Y5lE+RX/2YJoItVdGlIscyVyboeD9wSpp0PaGqjoA4tTpT2qPqz9ax+5IzGESyLSeZ/RCwbSZ2uQ==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.18", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.18", + "@next/swc-darwin-x64": "15.5.18", + "@next/swc-linux-arm64-gnu": "15.5.18", + "@next/swc-linux-arm64-musl": "15.5.18", + "@next/swc-linux-x64-gnu": "15.5.18", + "@next/swc-linux-x64-musl": "15.5.18", + "@next/swc-win32-arm64-msvc": "15.5.18", + "@next/swc-win32-x64-msvc": "15.5.18", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, "node_modules/next-themes": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", @@ -6605,6 +5847,43 @@ "react-dom": "^16.8 || ^17 || ^18" } }, + "node_modules/next/node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://package-mirror.liara.ir/repository/npm/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://package-mirror.liara.ir/repository/npm/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -7097,24 +6376,6 @@ "react": "^18.3.1" } }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" - }, - "node_modules/react-helmet-async": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.5.tgz", - "integrity": "sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==", - "dependencies": { - "invariant": "^2.2.4", - "react-fast-compare": "^3.2.2", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.6.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-hook-form": { "version": "7.61.1", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.61.1.tgz", @@ -7232,38 +6493,6 @@ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, - "node_modules/react-router": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", - "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", - "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0", - "react-router": "6.30.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -7511,42 +6740,6 @@ "node": ">= 0.8.15" } }, - "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", - "fsevents": "~2.3.2" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7580,10 +6773,10 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, + "version": "7.8.0", + "resolved": "https://package-mirror.liara.ir/repository/npm/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7592,10 +6785,50 @@ "node": ">=10" } }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://package-mirror.liara.ir/repository/npm/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } }, "node_modules/shebang-command": { "version": "2.0.0", @@ -7805,6 +7038,29 @@ "inline-style-parser": "0.2.4" } }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://package-mirror.liara.ir/repository/npm/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -8327,66 +7583,6 @@ "d3-timer": "^3.0.1" } }, - "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", diff --git a/package.json b/package.json index 19325c1..a574077 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,13 @@ { - "name": "vite_react_shadcn_ts", + "name": "guilan-ace-frontend", "private": true, "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", - "build": "vite build", - "build:dev": "vite build --mode development", + "dev": "next dev --port 8080", + "build": "next build", "lint": "eslint .", - "preview": "vite preview" + "start": "next start --port 3000" }, "dependencies": { "@hookform/resolvers": "^3.10.0", @@ -45,17 +44,19 @@ "cmdk": "^1.1.1", "date-fns": "^3.6.0", "embla-carousel-react": "^8.6.0", + "html2canvas": "^1.4.1", "input-otp": "^1.4.2", + "jspdf": "^2.5.1", "lucide-react": "^0.462.0", + "next": "^15.4.6", "next-themes": "^0.3.0", "react": "^18.3.1", "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", - "react-helmet-async": "^2.0.5", "react-hook-form": "^7.61.1", "react-markdown": "^9.0.3", + "react-qr-code": "^2.0.11", "react-resizable-panels": "^2.1.9", - "react-router-dom": "^6.30.1", "recharts": "^2.15.4", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", @@ -64,28 +65,23 @@ "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", "vaul": "^0.9.9", - "zod": "^3.25.76", - "react-qr-code": "^2.0.11", - "jspdf": "^2.5.1", - "html2canvas": "^1.4.1" + "zod": "^3.25.76" }, "devDependencies": { "@eslint/js": "^9.32.0", + "@next/eslint-plugin-next": "^16.2.6", "@tailwindcss/typography": "^0.5.16", "@types/node": "^22.16.5", "@types/react": "^18.3.26", "@types/react-dom": "^18.3.7", - "@vitejs/plugin-react-swc": "^3.11.0", "autoprefixer": "^10.4.21", "eslint": "^9.32.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^15.15.0", - "lovable-tagger": "^1.1.10", "postcss": "^8.5.6", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", - "typescript-eslint": "^8.38.0", - "vite": "^5.4.19" + "typescript-eslint": "^8.38.0" } } diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index 6018e70..0000000 --- a/public/robots.txt +++ /dev/null @@ -1,14 +0,0 @@ -User-agent: Googlebot -Allow: / - -User-agent: Bingbot -Allow: / - -User-agent: Twitterbot -Allow: / - -User-agent: facebookexternalhit -Allow: / - -User-agent: * -Allow: / diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 0194362..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Toaster } from "@/components/ui/toaster"; -import { Toaster as Sonner } from "@/components/ui/sonner"; -import { TooltipProvider } from "@/components/ui/tooltip"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { HelmetProvider } from "react-helmet-async"; -import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; -import Layout from "@/components/Layout"; -import { AuthProvider } from "@/contexts/AuthContext"; -import AdminLayout from "@/pages/AdminLayout"; -import AdminUsers from "@/pages/AdminUsers"; -import AdminEvents from "@/pages/AdminEvents"; -import AdminEventEdit from "@/pages/AdminEventEdit"; -import AdminEventDetail from "@/pages/AdminEventDetail"; -import AboutUs from "@/pages/AboutUs"; -import Auth from "@/pages/Auth"; -import Blog from "@/pages/Blog"; -import EventDetail from "@/pages/EventDetail"; -import EventFreeSuccessPage from "@/pages/EventFreeSuccessPage"; -import Events from "@/pages/Events"; -import Home from "@/pages/Home"; -import Logout from "@/pages/Logout"; -import NotFound from "@/pages/NotFound"; -import PaymentResult from "@/pages/PaymentResult"; -import Profile from "@/pages/Profile"; -import ResetPasswordConfirm from "@/pages/ResetPasswordConfirm"; -import ResetPasswordRequest from "@/pages/ResetPasswordRequest"; -import VerifyEmail from "@/pages/VerifyEmail"; - -const queryClient = new QueryClient(); - -const App = () => ( - - - - - - - - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - }> - } /> - } /> - } /> - } /> - } /> - - - - } /> - - - - - - -); - -export default App; diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx new file mode 100644 index 0000000..e200ba5 --- /dev/null +++ b/src/app/about/page.tsx @@ -0,0 +1,65 @@ +import type { Metadata } from "next"; +import AboutUs from "@/views/AboutUs"; +import { siteUrl } from "@/lib/site"; + +const title = "درباره ما | انجمن علمی کامپیوتر شرق گیلان"; +const description = + "آشنایی با تاریخچه، مأموریت‌ها و دستاوردهای انجمن علمی کامپیوتر شرق گیلان و راه‌های مشارکت دانشجویان در برنامه‌های انجمن."; + +export const metadata: Metadata = { + title, + description, + keywords: [ + "انجمن علمی کامپیوتر شرق گیلان", + "دانشگاه گیلان", + "انجمن علمی دانشجویی", + "رویدادهای فناوری", + ], + alternates: { canonical: "/about" }, + openGraph: { + title, + description, + url: `${siteUrl}/about`, + siteName: "انجمن علمی کامپیوتر شرق گیلان", + type: "website", + images: [`${siteUrl}/favicon.ico`], + locale: "fa_IR", + }, + twitter: { + card: "summary_large_image", + title, + description, + images: [`${siteUrl}/favicon.ico`], + }, +}; + +const structuredData = { + "@context": "https://schema.org", + "@type": "AboutPage", + name: title, + description, + url: `${siteUrl}/about`, + mainEntity: { + "@type": "Organization", + name: "انجمن علمی کامپیوتر شرق گیلان", + url: siteUrl, + logo: `${siteUrl}/favicon.ico`, + sameAs: [ + "https://instagram.com/guilance.ir", + "https://t.me/guilance", + "https://t.me/guilancea", + ], + }, +}; + +export default function AboutPage() { + return ( + <> + - )} - - -
-
-

رویدادها

- -
- setSearch(e.target.value)} - className="max-w-md" - /> -
- - {loading ? ( -

در حال بارگذاری...

- ) : events.length === 0 ? ( -

رویدادی یافت نشد

- ) : ( -
- {events.map((event) => ( - - -
- {event.title} -
- - {/* این رپر حالا قدِ باقی‌مانده رو می‌گیره */} -
- -
- {event.title} - {modeFa(event.event_type)} -
- {formatJalali(event.start_time, false)} -
- - -
-
- ظرفیت رویداد - - {formatNumberPersian(Number(event?.capacity ?? 0) - Number(event?.registration_count ?? 0))}/{formatNumberPersian(Number(event?.capacity ?? 0))} نفر - -
-
- هزینه‌ی ثبت‌نام - {labelPrice(event)} -
- {isAvailable(event) ? ( - - ) : ( - - )} -
-
-
-
- - ))} -
- )} -
-
- - ); -} diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx deleted file mode 100644 index 7130b54..0000000 --- a/src/pages/Index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -// Update this page (the content is just a fallback if you fail to update the page) - -const Index = () => { - return ( -
-
-

Welcome to Your Blank App

-

Start building your amazing project here!

-
-
- ); -}; - -export default Index; diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx deleted file mode 100644 index 34fc289..0000000 --- a/src/pages/NotFound.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useLocation } from "react-router-dom"; -import { useEffect } from "react"; -import { toPersianDigits } from "@/lib/utils"; - -const NotFound = () => { - const location = useLocation(); - - useEffect(() => { - console.error("404 Error: User attempted to access non-existent route:", location.pathname); - }, [location.pathname]); - - return ( -
-
-

{toPersianDigits("404")}

-

Oops! Page not found

- - Return to Home - -
-
- ); -}; - -export default NotFound; diff --git a/src/pages/AboutUs.tsx b/src/views/AboutUs.tsx similarity index 99% rename from src/pages/AboutUs.tsx rename to src/views/AboutUs.tsx index 53959cc..0cac8fb 100644 --- a/src/pages/AboutUs.tsx +++ b/src/views/AboutUs.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { Helmet } from 'react-helmet-async'; -import { Link } from 'react-router-dom'; +import { Helmet } from '@/lib/helmet'; +import { Link } from '@/lib/router'; // ------ داده‌های قابل تنظیم ------ const SITE_URL = 'https://east-guilan-ce.ir'; diff --git a/src/pages/AdminEventDetail.tsx b/src/views/AdminEventDetail.tsx similarity index 99% rename from src/pages/AdminEventDetail.tsx rename to src/views/AdminEventDetail.tsx index 143eb1f..48ca146 100644 --- a/src/pages/AdminEventDetail.tsx +++ b/src/views/AdminEventDetail.tsx @@ -1,5 +1,7 @@ +"use client"; + import * as React from 'react'; -import { useParams, Link, Navigate } from 'react-router-dom'; +import { useParams, Link, Navigate } from '@/lib/router'; import { useQuery } from '@tanstack/react-query'; import { api } from '@/lib/api'; import { Badge } from '@/components/ui/badge'; diff --git a/src/pages/AdminEventEdit.tsx b/src/views/AdminEventEdit.tsx similarity index 92% rename from src/pages/AdminEventEdit.tsx rename to src/views/AdminEventEdit.tsx index 8c978f5..a520673 100644 --- a/src/pages/AdminEventEdit.tsx +++ b/src/views/AdminEventEdit.tsx @@ -1,5 +1,7 @@ +"use client"; + import * as React from 'react'; -import { useNavigate, useParams, Navigate } from 'react-router-dom'; +import { useNavigate, useParams, Navigate } from '@/lib/router'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useAuth } from '@/contexts/AuthContext'; import { api } from '@/lib/api'; @@ -42,6 +44,7 @@ export default function AdminEventEdit() { const navigate = useNavigate(); const eventId = Number(id); const { toast } = useToast(); + const queryClient = useQueryClient(); const detailQuery = useQuery({ queryKey: ['admin', 'edit-event', eventId], @@ -51,8 +54,8 @@ export default function AdminEventEdit() { const [formData, setFormData] = React.useState({ title: '', - status: 'draft', - event_type: 'online', + status: 'draft' as NonNullable, + event_type: 'online' as NonNullable, price: '', capacity: '', start_time: '', @@ -147,7 +150,7 @@ export default function AdminEventEdit() { event_type: formData.event_type, price: formData.price ? Number(formData.price) * 10 : 0, capacity: formData.capacity ? Number(formData.capacity) : null, - start_time: formData.start_time || null, + start_time: formData.start_time || undefined, end_time: formData.end_time || null, registration_start_date: formData.registration_start_date || null, registration_end_date: formData.registration_end_date || null, @@ -167,7 +170,12 @@ export default function AdminEventEdit() { /> setFilters((prev) => ({ ...prev, status: value }))}> + - + setFilters((prev) => ({ + ...prev, + type: value as 'all' | EventListItemSchema['event_type'], + })) + } + > {{ @@ -144,7 +168,15 @@ const AdminEventsPage: React.FC = () => { ترکیبی - + setFilters((prev) => ({ + ...prev, + sort: value as (typeof eventSortOptions)[number]['value'], + })) + } + > {eventSortOptions.find((option) => option.value === filters.sort)?.label || diff --git a/src/pages/AdminLayout.tsx b/src/views/AdminLayout.tsx similarity index 86% rename from src/pages/AdminLayout.tsx rename to src/views/AdminLayout.tsx index a51bb29..7a43b23 100644 --- a/src/pages/AdminLayout.tsx +++ b/src/views/AdminLayout.tsx @@ -1,4 +1,7 @@ -import { Outlet, Navigate, NavLink, useLocation } from 'react-router-dom'; +"use client"; + +import type { ReactNode } from 'react'; +import { Navigate, NavLink, useLocation } from '@/lib/router'; import { useMemo } from 'react'; import { useAuth } from '@/contexts/AuthContext'; @@ -7,7 +10,7 @@ const navItems = [ { to: '/admin/events', label: 'مدیریت رویدادها' }, ] as const; -export default function AdminLayout() { +export default function AdminLayout({ children }: { children: ReactNode }) { const location = useLocation(); const { user, isAuthenticated, loading } = useAuth(); const isAdmin = useMemo( @@ -40,7 +43,7 @@ export default function AdminLayout() { className={({ isActive }) => [ 'rounded-full px-4 py-2 text-sm transition', - (isActive || location.pathname.startsWith(item.to)) + (isActive || location.pathname?.startsWith(item.to)) ? 'bg-primary text-primary-foreground shadow' : 'bg-card text-muted-foreground hover:text-foreground border', ].join(' ') @@ -53,7 +56,7 @@ export default function AdminLayout() {
- + {children}
); diff --git a/src/pages/AdminUsers.tsx b/src/views/AdminUsers.tsx similarity index 99% rename from src/pages/AdminUsers.tsx rename to src/views/AdminUsers.tsx index c01a240..c500f09 100644 --- a/src/pages/AdminUsers.tsx +++ b/src/views/AdminUsers.tsx @@ -1,3 +1,5 @@ +"use client"; + import * as React from 'react'; import { useQuery, diff --git a/src/pages/Auth.tsx b/src/views/Auth.tsx similarity index 99% rename from src/pages/Auth.tsx rename to src/views/Auth.tsx index 751b74f..fede829 100644 --- a/src/pages/Auth.tsx +++ b/src/views/Auth.tsx @@ -1,6 +1,8 @@ +"use client"; + import { useEffect, useState, useMemo } from 'react'; -import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate } from 'react-router-dom'; +import { Helmet } from '@/lib/helmet'; +import { Link, useNavigate } from '@/lib/router'; import { useAuth } from '@/contexts/AuthContext'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@/components/ui/button'; diff --git a/src/pages/Blog.tsx b/src/views/Blog.tsx similarity index 62% rename from src/pages/Blog.tsx rename to src/views/Blog.tsx index 82b1643..4256d3a 100644 --- a/src/pages/Blog.tsx +++ b/src/views/Blog.tsx @@ -1,36 +1,42 @@ -import { useEffect, useState, useCallback } from 'react'; -import { Link } from 'react-router-dom'; -import { api } from '@/lib/api'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; +"use client"; -interface Post { - id: number; - title: string; - slug: string; - excerpt?: string; - author: { - username: string; - first_name: string; - last_name: string; - }; - created_at: string; - category?: { - name: string; - }; -} +import { useCallback, useEffect, useState } from "react"; +import { Link, useLocation, useNavigate } from "@/lib/router"; +import { api } from "@/lib/api"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import type * as Types from "@/lib/types"; -export default function Blog() { - const [posts, setPosts] = useState([]); - const [search, setSearch] = useState(''); - const [loading, setLoading] = useState(true); +type BlogProps = { + initialPosts?: Types.PostListSchema[]; + initialSearch?: string; +}; + +export default function Blog({ + initialPosts = [], + initialSearch = "", +}: BlogProps) { + const navigate = useNavigate(); + const location = useLocation(); + const [posts, setPosts] = useState(initialPosts); + const [search, setSearch] = useState(initialSearch); + const [loading, setLoading] = useState(!initialPosts.length && !initialSearch); + + useEffect(() => { + setPosts(initialPosts); + }, [initialPosts]); + + useEffect(() => { + setSearch(initialSearch); + }, [initialSearch]); const loadPosts = useCallback(async () => { try { + setLoading(true); const data = await api.getPosts({ search: search || undefined }); - setPosts(data as Post[]); + setPosts(data); } catch (error) { - console.error('Error loading posts:', error); + console.error("Error loading posts:", error); } finally { setLoading(false); } @@ -40,6 +46,18 @@ export default function Blog() { loadPosts(); }, [loadPosts]); + useEffect(() => { + const params = new URLSearchParams(); + if (search.trim()) { + params.set("search", search.trim()); + } + const basePath = location.pathname || "/blog"; + const nextPath = params.size + ? `${basePath}?${params.toString()}` + : basePath; + navigate(nextPath, { replace: true }); + }, [location.pathname, navigate, search]); + return (
@@ -70,7 +88,7 @@ export default function Blog() { {post.category?.name && ( {post.category.name} )} - {new Date(post.created_at).toLocaleDateString('fa-IR')} + {new Date(post.created_at).toLocaleDateString("fa-IR")} diff --git a/src/pages/EventDetail.tsx b/src/views/EventDetail.tsx similarity index 53% rename from src/pages/EventDetail.tsx rename to src/views/EventDetail.tsx index e1b672c..be19a93 100644 --- a/src/pages/EventDetail.tsx +++ b/src/views/EventDetail.tsx @@ -1,43 +1,86 @@ -import { useEffect, useMemo, useState } from 'react'; -import { Helmet } from 'react-helmet-async'; -import { useParams, useNavigate } from 'react-router-dom'; -import { api } from '@/lib/api'; -import type * as Types from '@/lib/types'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Button } from '@/components/ui/button'; -import { useToast } from '@/hooks/use-toast'; -import Markdown from '@/components/Markdown'; -import CouponDialogFa from '@/components/CouponDialogFa'; -import { formatJalali, formatNumberPersian, formatToman, getThumbUrl, resolveErrorMessage, toPersianDigits } from '@/lib/utils'; -import { useAuth } from '@/contexts/AuthContext'; +"use client"; -const typeLabel: Record = { online: 'آنلاین', on_site: 'حضوری', hybrid: 'آنلاین و حضوری' }; +import { useEffect, useMemo, useState } from "react"; +import { Helmet } from "@/lib/helmet"; +import { useNavigate, useParams } from "@/lib/router"; +import { api } from "@/lib/api"; +import type * as Types from "@/lib/types"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { useToast } from "@/hooks/use-toast"; +import Markdown from "@/components/Markdown"; +import CouponDialogFa from "@/components/CouponDialogFa"; +import { + formatJalali, + formatNumberPersian, + formatToman, + getThumbUrl, + resolveErrorMessage, + toPersianDigits, +} from "@/lib/utils"; +import { useAuth } from "@/contexts/AuthContext"; +import { siteUrl } from "@/lib/site"; -export default function EventDetail() { +const typeLabel: Record = { + online: "آنلاین", + on_site: "حضوری", + hybrid: "آنلاین و حضوری", +}; + +type EventDetailProps = { + initialEvent?: Types.EventDetailSchema | null; +}; + +function buildPaymentSnapshot( + event: Types.EventDetailSchema, + eventThumb: string | null, + payload: { + baseAmount: number; + discountAmount: number; + amount: number; + }, +) { + return JSON.stringify({ + event_id: event.id, + slug: event.slug, + title: event.title, + thumb: eventThumb, + base_amount: payload.baseAmount, + discount_amount: payload.discountAmount, + amount: payload.amount, + started_at: new Date().toISOString(), + success_markdown: event.registration_success_markdown, + }); +} + +export default function EventDetail({ initialEvent = null }: EventDetailProps) { const { slug } = useParams<{ slug: string }>(); const { isAuthenticated } = useAuth(); const navigate = useNavigate(); const { toast } = useToast(); - const [event, setEvent] = useState(null); - const [eventThumb, setEventThumb] = useState(null); - const [loading, setLoading] = useState(true); + const [event, setEvent] = useState(initialEvent); + const [eventThumb, setEventThumb] = useState( + initialEvent ? getThumbUrl(initialEvent) : null, + ); + const [loading, setLoading] = useState(!initialEvent); + const [open, setOpen] = useState(false); + const [submitting, setSubmitting] = useState(false); + const [alreadyRegistered, setAlreadyRegistered] = useState(false); + const [nowTs, setNowTs] = useState(() => Date.now()); const basePrice = Number(event?.price ?? 0); const isFree = useMemo(() => basePrice <= 0, [basePrice]); - const [open, setOpen] = useState(false); - const [submitting, setSubmitting] = useState(false); - const siteUrl = 'https://east-guilan-ce.ir'; - const siteName = 'انجمن علمی کامپیوتر شرق گیلان'; + const siteName = "انجمن علمی کامپیوتر شرق گیلان"; const defaultDescription = - 'جزئیات کامل رویدادهای انجمن علمی کامپیوتر شرق گیلان شامل زمان، مکان و شرایط ثبت‌نام.'; + "جزئیات کامل رویدادهای انجمن علمی کامپیوتر شرق گیلان شامل زمان، مکان و شرایط ثبت‌نام."; - const toAbsoluteUrl = (url?: string | null) => { - if (!url) return undefined; - if (url.startsWith('http')) return url; - const normalizedSite = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl; - const normalizedPath = url.startsWith('/') ? url.slice(1) : url; + const toAbsoluteUrl = (value?: string | null) => { + if (!value) return undefined; + if (value.startsWith("http")) return value; + const normalizedSite = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl; + const normalizedPath = value.startsWith("/") ? value.slice(1) : value; return `${normalizedSite}/${normalizedPath}`; }; @@ -55,185 +98,215 @@ export default function EventDetail() { : `${siteUrl}/favicon.ico`; const pageTitle = event ? `${event.title} | ${siteName}` : `جزئیات رویداد | ${siteName}`; const pageDescription = sanitizeDescription(event?.description); - const pageRobots = event?.status === 'draft' ? 'noindex, nofollow' : 'index, follow'; - - const [alreadyRegistered, setAlreadyRegistered] = useState(false); + const pageRobots = event?.status === "draft" ? "noindex, nofollow" : "index, follow"; useEffect(() => { let cancelled = false; - async function check() { - if (isAuthenticated && event?.id) { - try { - const res = await api.getRegistrationStatus(event.id); - if (!cancelled) setAlreadyRegistered(res.is_registered); - } catch { /* ignore */ } + + async function checkRegistration() { + if (!isAuthenticated || !event?.id) { + return; + } + + try { + const res = await api.getRegistrationStatus(event.id); + if (!cancelled) { + setAlreadyRegistered(res.is_registered); + } + } catch { + // Ignore registration status failures on the detail page. } } - check(); - return () => { cancelled = true; }; - }, [isAuthenticated, event?.id]); + + checkRegistration(); + return () => { + cancelled = true; + }; + }, [event?.id, isAuthenticated]); const goSuccess = (registrationId?: string) => { - const q = registrationId ? `?registration_id=${registrationId}` : ''; + if (!event) return; + const query = registrationId ? `?registration_id=${registrationId}` : ""; setAlreadyRegistered(true); - - toast({ title: 'ثبت‌نام با موفقیت انجام شد!', variant: 'success' }); - navigate(`/events/${event!.slug}/success${q}`); + toast({ title: "ثبت‌نام با موفقیت انجام شد!", variant: "success" }); + navigate(`/events/${event.slug}/success${query}`); }; const handleMainCTA = async () => { if (!event) return; + if (!isAuthenticated) { - toast({ title: 'ابتدا وارد شوید', description: 'برای ثبت‌نام در رویداد باید وارد حساب کاربری خود شوید.', variant: 'destructive' }); - navigate('/auth'); + toast({ + title: "ابتدا وارد شوید", + description: "برای ثبت‌نام در رویداد باید وارد حساب کاربری خود شوید.", + variant: "destructive", + }); + navigate("/auth"); return; } - if (isFree) { - try { - setSubmitting(true); - const res = await api.registerForEvent(event.id); - goSuccess(res.ticket_id); - } catch (error: unknown) { - const msg = resolveErrorMessage(error, ''); - if (msg.includes('already registered') || msg.includes('ثبت‌نام')) { - setAlreadyRegistered(true); - toast({ title: 'شما قبلاً ثبت‌نام کرده‌اید', variant: 'destructive' }); - return; - } - throw error; - } finally { - setSubmitting(false); - } - } else { - setOpen(true); - } - }; - const handleContinueFromModal = async (coupon?: string, finalAmount?: number) => { - if (!event) return; - if (!isAuthenticated) { - toast({ title: 'ابتدا وارد شوید', description: 'برای ثبت‌نام در رویداد باید وارد حساب کاربری خود شوید.', variant: 'destructive' }); - navigate('/auth'); + if (!isFree) { + setOpen(true); return; } try { setSubmitting(true); + const res = await api.registerForEvent(event.id); + goSuccess(res.ticket_id); + } catch (error: unknown) { + const msg = resolveErrorMessage(error, ""); + if (msg.includes("already registered")) { + setAlreadyRegistered(true); + toast({ title: "شما قبلاً ثبت‌نام کرده‌اید", variant: "destructive" }); + return; + } + toast({ + title: "خطا در ثبت‌نام", + description: msg || "لطفاً دوباره تلاش کنید.", + variant: "destructive", + }); + } finally { + setSubmitting(false); + } + }; + const handleContinueFromModal = async (coupon?: string, finalAmount?: number) => { + if (!event) return; + + if (!isAuthenticated) { + toast({ + title: "ابتدا وارد شوید", + description: "برای ثبت‌نام در رویداد باید وارد حساب کاربری خود شوید.", + variant: "destructive", + }); + navigate("/auth"); + return; + } + + try { + setSubmitting(true); const reg = await api.registerForEvent(event.id, coupon); - - if (finalAmount === 0) { - sessionStorage.setItem('payment:last', JSON.stringify({ - event_id: event.id, - slug: event.slug, - title: event.title, - thumb: eventThumb, - base_amount: Number(event.price ?? 0), - discount_amount: Number(event.price ?? 0), - amount: 0, - started_at: new Date().toISOString(), - success_markdown: event.registration_success_markdown, - })); - api.ChangeRegistrationStatus(reg.id, 'confirmed') - goSuccess(reg?.ticket_id); - return; + if (finalAmount === 0) { + window.sessionStorage.setItem( + "payment:last", + buildPaymentSnapshot(event, eventThumb, { + baseAmount: Number(event.price ?? 0), + discountAmount: Number(event.price ?? 0), + amount: 0, + }), + ); + await api.ChangeRegistrationStatus(reg.id, "confirmed"); + goSuccess(reg.ticket_id); + return; } - const description = `پرداخت رویداد: ${event.title}`; const result = await api.createPayment({ event_id: event.id, - description, - discount_code: (coupon ?? '').trim() || null, + description: `پرداخت رویداد: ${event.title}`, + discount_code: (coupon ?? "").trim() || null, }); - if (!result?.start_pay_url || Number(result.amount) === 0) { - sessionStorage.setItem('payment:last', JSON.stringify({ - event_id: event.id, - slug: event.slug, - title: event.title, - thumb: eventThumb, - base_amount: result.base_amount, - discount_amount: result.discount_amount ?? result.base_amount, - amount: 0, - started_at: new Date().toISOString(), - success_markdown: event.registration_success_markdown, - })); - goSuccess(reg?.ticket_id); + window.sessionStorage.setItem( + "payment:last", + buildPaymentSnapshot(event, eventThumb, { + baseAmount: result.base_amount, + discountAmount: result.discount_amount ?? result.base_amount, + amount: 0, + }), + ); + goSuccess(reg.ticket_id); return; } - - sessionStorage.setItem('payment:last', JSON.stringify({ - event_id: event.id, - slug: event.slug, - title: event.title, - thumb: eventThumb, - base_amount: result.base_amount, - discount_amount: result.discount_amount, - amount: result.amount, - started_at: new Date().toISOString(), - success_markdown: event.registration_success_markdown, - })); + window.sessionStorage.setItem( + "payment:last", + buildPaymentSnapshot(event, eventThumb, { + baseAmount: result.base_amount, + discountAmount: result.discount_amount, + amount: result.amount, + }), + ); window.location.href = result.start_pay_url; - } catch (error: unknown) { - - const msg = resolveErrorMessage(error, ''); - if (msg.includes('already registered') || msg.includes('ثبت‌نام')) { + const msg = resolveErrorMessage(error, ""); + if (msg.includes("already registered")) { setAlreadyRegistered(true); - toast({ title: 'شما قبلاً ثبت‌نام کرده‌اید', variant: 'destructive' }); + toast({ title: "شما قبلاً ثبت‌نام کرده‌اید", variant: "destructive" }); return; } - toast({ title: 'خطا در پردازش پرداخت', description: msg || 'لطفاً دوباره تلاش کنید.', variant: 'destructive' }); + toast({ + title: "خطا در پردازش پرداخت", + description: msg || "لطفاً دوباره تلاش کنید.", + variant: "destructive", + }); } finally { setSubmitting(false); setOpen(false); } }; - useEffect(() => { - (async () => { + let cancelled = false; + + async function loadEvent() { try { if (!slug) return; + + if (initialEvent && initialEvent.slug === slug) { + setEvent(initialEvent); + setEventThumb(getThumbUrl(initialEvent)); + return; + } + const data = await api.getEventBySlug(slug); + if (cancelled) return; setEvent(data); setEventThumb(getThumbUrl(data)); } catch (error: unknown) { + if (cancelled) return; toast({ - title: 'خطا در بارگذاری رویداد', - description: resolveErrorMessage(error, 'لطفاً دوباره تلاش کنید.'), - variant: 'destructive', + title: "خطا در بارگذاری رویداد", + description: resolveErrorMessage(error, "لطفاً دوباره تلاش کنید."), + variant: "destructive", }); } finally { - setLoading(false); + if (!cancelled) { + setLoading(false); + } } - })(); - }, [slug, toast]); + } + + loadEvent(); + return () => { + cancelled = true; + }; + }, [initialEvent, slug, toast]); - - const [nowTs, setNowTs] = useState(() => Date.now()); useEffect(() => { - const id = window.setInterval(() => setNowTs(Date.now()), 1000); - return () => window.clearInterval(id); + const timer = window.setInterval(() => setNowTs(Date.now()), 1000); + return () => window.clearInterval(timer); }, []); - const rsTs = useMemo(() => ( - event?.registration_start_date ? new Date(event.registration_start_date).getTime() : null - ), [event?.registration_start_date]); + const rsTs = useMemo( + () => (event?.registration_start_date ? new Date(event.registration_start_date).getTime() : null), + [event?.registration_start_date], + ); - const deadlineTs = useMemo(() => ( - event?.registration_end_date ? new Date(event.registration_end_date).getTime() : null - ), [event?.registration_end_date]); + const deadlineTs = useMemo( + () => (event?.registration_end_date ? new Date(event.registration_end_date).getTime() : null), + [event?.registration_end_date], + ); - const remainingMs = useMemo(() => ( - deadlineTs != null ? Math.max(0, deadlineTs - nowTs) : null - ), [deadlineTs, nowTs]); + const remainingMs = useMemo( + () => (deadlineTs != null ? Math.max(0, deadlineTs - nowTs) : null), + [deadlineTs, nowTs], + ); const formatCountdownTwoDigit = (value: number) => - toPersianDigits(value.toString().padStart(2, '0')); + toPersianDigits(value.toString().padStart(2, "0")); const formatCountdownNumber = (value: number) => formatNumberPersian(value); const formatRemainingWords = (ms: number) => { @@ -242,39 +315,45 @@ export default function EventDetail() { const hours = Math.floor((total % 86400) / 3600); const minutes = Math.floor((total % 3600) / 60); const seconds = total % 60; - if (days === 0) return `${formatCountdownTwoDigit(hours)} ساعت و ${formatCountdownTwoDigit(minutes)} دقیقه و ${formatCountdownTwoDigit(seconds)} ثانیه`; + + if (days === 0) { + return `${formatCountdownTwoDigit(hours)} ساعت و ${formatCountdownTwoDigit(minutes)} دقیقه و ${formatCountdownTwoDigit(seconds)} ثانیه`; + } + return `${formatCountdownNumber(days)} روز و ${formatCountdownTwoDigit(hours)} ساعت و ${formatCountdownTwoDigit(minutes)} دقیقه و ${formatCountdownTwoDigit(seconds)} ثانیه`; }; const meta = useMemo(() => { if (!event) return null; - const rs = rsTs; - const re = deadlineTs; - const registrationOpen = (rs == null || nowTs >= rs) && (re == null || nowTs <= re); + const registrationOpen = + (rsTs == null || nowTs >= rsTs) && (deadlineTs == null || nowTs <= deadlineTs); const unlimited = event.capacity == null; - const remaining = unlimited ? Infinity : Math.max(0, (event.capacity || 0) - (event.registration_count || 0)); + const remaining = unlimited + ? Number.POSITIVE_INFINITY + : Math.max(0, (event.capacity || 0) - (event.registration_count || 0)); const full = !unlimited && remaining <= 0; return { registrationOpen, remaining, full }; - }, [event, rsTs, deadlineTs, nowTs]); + }, [deadlineTs, event, nowTs, rsTs]); + const eventStructuredData = useMemo(() => { if (!event) return null; const attendanceModeMap: Record = { - online: 'https://schema.org/OnlineEventAttendanceMode', - on_site: 'https://schema.org/OfflineEventAttendanceMode', - hybrid: 'https://schema.org/MixedEventAttendanceMode', + online: "https://schema.org/OnlineEventAttendanceMode", + on_site: "https://schema.org/OfflineEventAttendanceMode", + hybrid: "https://schema.org/MixedEventAttendanceMode", }; const statusMap: Record = { - published: 'https://schema.org/EventScheduled', - completed: 'https://schema.org/EventCompleted', - cancelled: 'https://schema.org/EventCancelled', - draft: 'https://schema.org/EventPostponed', + published: "https://schema.org/EventScheduled", + completed: "https://schema.org/EventCompleted", + cancelled: "https://schema.org/EventCancelled", + draft: "https://schema.org/EventPostponed", }; const data: Record = { - '@context': 'https://schema.org', - '@type': 'Event', + "@context": "https://schema.org", + "@type": "Event", name: event.title, description: pageDescription, startDate: event.start_time, @@ -282,7 +361,7 @@ export default function EventDetail() { eventAttendanceMode: attendanceModeMap[event.event_type] ?? attendanceModeMap.hybrid, eventStatus: statusMap[event.status] ?? statusMap.published, organizer: { - '@type': 'Organization', + "@type": "Organization", name: siteName, url: siteUrl, }, @@ -296,14 +375,14 @@ export default function EventDetail() { data.image = [primaryImage]; } - if (event.event_type === 'online') { + if (event.event_type === "online") { data.location = { - '@type': 'VirtualLocation', + "@type": "VirtualLocation", url: event.online_link || canonicalUrl, }; } else { const location: Record = { - '@type': 'Place', + "@type": "Place", name: event.location || event.address || siteName, }; if (event.address) { @@ -316,11 +395,11 @@ export default function EventDetail() { } const offers: Record = { - '@type': 'Offer', + "@type": "Offer", url: canonicalUrl, - priceCurrency: 'IRR', + priceCurrency: "IRR", price: String(event.price ?? 0), - availability: meta?.full ? 'https://schema.org/SoldOut' : 'https://schema.org/InStock', + availability: meta?.full ? "https://schema.org/SoldOut" : "https://schema.org/InStock", }; if (event.registration_start_date) { @@ -331,9 +410,8 @@ export default function EventDetail() { } data.offers = offers; - return data; - }, [event, pageDescription, canonicalUrl, primaryImage, meta?.full, siteName, siteUrl]); + }, [canonicalUrl, event, meta?.full, pageDescription, primaryImage, siteName]); const helmet = ( @@ -370,23 +448,26 @@ export default function EventDetail() { if (loading) { return withHelmet( -
در حال بارگذاری رویداد...
- ); - } - if (!event) { - return withHelmet( -
رویداد مورد نظر یافت نشد.
+
+ در حال بارگذاری رویداد... +
, + ); + } + + if (!event) { + return withHelmet( +
+ رویداد مورد نظر یافت نشد. +
, ); } - const beforeStart = rsTs != null && nowTs < rsTs; const ended = deadlineTs !== null && remainingMs === 0; - const showCountdown = !beforeStart && deadlineTs !== null && remainingMs! > 0; + const showCountdown = !beforeStart && deadlineTs !== null && (remainingMs ?? 0) > 0; return withHelmet(
- {/* --- نوار اطلاع/تایمر زیر نوار ناوبری با رنگ‌های مناسب Light/Dark --- */} {beforeStart && (
@@ -395,13 +476,13 @@ export default function EventDetail() {
)} - {showCountdown && ( + {showCountdown && remainingMs != null && (
زمان باقی‌مانده تا پایان ثبت‌نام: - {formatRemainingWords(remainingMs!)} + {formatRemainingWords(remainingMs)}
@@ -417,7 +498,6 @@ export default function EventDetail() { )}
- {/* محتوا */}
@@ -449,16 +529,15 @@ export default function EventDetail() { - {/* گالری */} {event.gallery_images?.length ? (

گالری تصاویر

- {event.gallery_images.map((g) => ( + {event.gallery_images.map((image) => ( {g.title ))} @@ -467,7 +546,6 @@ export default function EventDetail() { ) : null}
- {/* سایدبار اطلاعات */}
@@ -477,20 +555,18 @@ export default function EventDetail() { {event.address &&
آدرس: {event.address}
} - -
ظرفیت کل: {event.capacity == null ? 'نامحدود' : formatNumberPersian(event.capacity)}
- {meta && ( - <> - {!event.capacity ? null : ( -
- ظرفیت باقی‌مانده: {meta.remaining === Infinity ? 'نامحدود' : formatNumberPersian(meta.remaining)} -
- )} - +
+ ظرفیت کل: {event.capacity == null ? "نامحدود" : formatNumberPersian(event.capacity)} +
+ {meta && event.capacity != null && ( +
+ ظرفیت باقی‌مانده:{" "} + {meta.remaining === Number.POSITIVE_INFINITY + ? "نامحدود" + : formatNumberPersian(meta.remaining)} +
)} -
هزینه حضور: {event.price ? formatToman(event.price) : 'رایگان'}
- - {/* نمایش زمان شروع/پایان ثبت‌نام در UI حذف شده */} +
هزینه حضور: {event.price ? formatToman(event.price) : "رایگان"}
{!isFree && ( @@ -536,11 +611,3 @@ export default function EventDetail() {
); } - - - - - - - - diff --git a/src/pages/EventFreeSuccessPage.tsx b/src/views/EventFreeSuccessPage.tsx similarity index 98% rename from src/pages/EventFreeSuccessPage.tsx rename to src/views/EventFreeSuccessPage.tsx index e62e199..3264dd5 100644 --- a/src/pages/EventFreeSuccessPage.tsx +++ b/src/views/EventFreeSuccessPage.tsx @@ -1,11 +1,13 @@ -import { useLocation, useParams, Link } from "react-router-dom"; +"use client"; + +import { useLocation, useParams, Link } from "@/lib/router"; import { useQuery } from "@tanstack/react-query"; import { Button } from "@/components/ui/button"; import { api } from "@/lib/api"; import PaymentResult from "@/components/PaymentResult"; import { formatJalali } from "@/lib/utils"; import Markdown from '@/components/Markdown'; -import { Helmet } from "react-helmet-async"; +import { Helmet } from "@/lib/helmet"; export default function EventFreeSuccessPage() { const { slug } = useParams(); diff --git a/src/views/Events.tsx b/src/views/Events.tsx new file mode 100644 index 0000000..82a8b7c --- /dev/null +++ b/src/views/Events.tsx @@ -0,0 +1,262 @@ +"use client"; + +import { useCallback, useEffect, useMemo, useState } from "react"; +import { Helmet } from "@/lib/helmet"; +import { Link, useLocation, useNavigate } from "@/lib/router"; +import { api } from "@/lib/api"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import type * as Types from "@/lib/types"; +import { formatJalali, formatNumberPersian, formatToman, getThumbUrl } from "@/lib/utils"; +import { siteUrl } from "@/lib/site"; + +type EventsProps = { + initialEvents?: Types.EventListItemSchema[]; + initialSearch?: string; +}; + +function labelPrice(event: Types.EventListItemSchema) { + const price = Number(event?.price ?? 0); + return price <= 0 ? "رایگان" : formatToman(price); +} + +function modeFa(eventType: Types.EventListItemSchema["event_type"]) { + return eventType === "online" ? "آنلاین" : "حضوری"; +} + +function spotsLeft(event: Types.EventListItemSchema) { + const cap = Number(event.capacity); + const used = Number(event.registration_count); + return cap - used; +} + +function isAvailable(event: Types.EventListItemSchema) { + const now = new Date(); + const end = new Date(event.registration_end_date ?? event.start_time); + const timeOk = end.getTime() > now.getTime(); + return timeOk && spotsLeft(event) > 0; +} + +function notAvailableReasonFa(event: Types.EventListItemSchema) { + const now = new Date(); + const end = new Date(event.registration_end_date ?? event.start_time); + if (end.getTime() <= now.getTime()) return "ثبت‌نام پایان‌یافته"; + if (spotsLeft(event) <= 0) return "ظرفیت تکمیل"; + return "غیرقابل ثبت‌نام"; +} + +export default function Events({ + initialEvents = [], + initialSearch = "", +}: EventsProps) { + const navigate = useNavigate(); + const location = useLocation(); + const [events, setEvents] = useState(initialEvents); + const [search, setSearch] = useState(initialSearch); + const [loading, setLoading] = useState(!initialEvents.length && !initialSearch); + + const siteName = "East Guilan CE"; + const pageTitle = `Events | ${siteName}`; + const pageDescription = + "Discover upcoming and past events organized by the East Guilan Computer Engineering Association, including workshops, competitions, and community programs."; + const canonicalUrl = `${siteUrl}/events`; + + const toAbsoluteUrl = (url?: string | null) => { + if (!url) return undefined; + if (url.startsWith("http")) return url; + const normalizedSite = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl; + const normalizedPath = url.startsWith("/") ? url.slice(1) : url; + return `${normalizedSite}/${normalizedPath}`; + }; + + const ogImage = useMemo(() => { + if (!events.length) return `${siteUrl}/favicon.ico`; + return toAbsoluteUrl(getThumbUrl(events[0])) ?? `${siteUrl}/favicon.ico`; + }, [events]); + + const listStructuredData = useMemo(() => { + if (!events.length) return null; + + const itemListElement = events.map((eventItem, index) => { + const listItem: Record = { + "@type": "ListItem", + position: index + 1, + url: `${siteUrl}/events/${eventItem.slug}`, + name: eventItem.title, + description: eventItem.description, + startDate: eventItem.start_time, + }; + + if (eventItem.end_time) { + listItem.endDate = eventItem.end_time; + } + + const imageUrl = toAbsoluteUrl(getThumbUrl(eventItem)); + if (imageUrl) { + listItem.image = imageUrl; + } + + const placeName = eventItem.location || eventItem.address; + if (placeName) { + const place: Record = { + "@type": "Place", + name: placeName, + }; + if (eventItem.address) { + place.address = eventItem.address; + } + listItem.location = place; + } + + return listItem; + }); + + return { + "@context": "https://schema.org", + "@type": "ItemList", + name: pageTitle, + description: pageDescription, + url: canonicalUrl, + numberOfItems: events.length, + itemListElement, + }; + }, [canonicalUrl, events, pageDescription, pageTitle]); + + useEffect(() => { + setEvents(initialEvents); + }, [initialEvents]); + + useEffect(() => { + setSearch(initialSearch); + }, [initialSearch]); + + const loadEvents = useCallback(async () => { + try { + setLoading(true); + const data = await api.getEvents({ + search: search || undefined, + statuses: ["published", "completed"], + limit: 30, + }); + setEvents(data); + } catch (error) { + console.error("Error loading events:", error); + } finally { + setLoading(false); + } + }, [search]); + + useEffect(() => { + loadEvents(); + }, [loadEvents]); + + useEffect(() => { + const params = new URLSearchParams(); + if (search.trim()) { + params.set("search", search.trim()); + } + const basePath = location.pathname || "/events"; + const nextPath = params.size + ? `${basePath}?${params.toString()}` + : basePath; + navigate(nextPath, { replace: true }); + }, [location.pathname, navigate, search]); + + return ( + <> + + {pageTitle} + + + + + + + + + + + + + + {listStructuredData && ( + + )} + + +
+
+

رویدادها

+ +
+ setSearch(e.target.value)} + className="max-w-md" + /> +
+ + {loading ? ( +

در حال بارگذاری...

+ ) : events.length === 0 ? ( +

رویدادی یافت نشد

+ ) : ( +
+ {events.map((event) => ( + + +
+ {event.title} +
+ +
+ +
+ {event.title} + {modeFa(event.event_type)} +
+ {formatJalali(event.start_time, false)} +
+ + +
+
+ ظرفیت رویداد + + {formatNumberPersian(Number(event?.capacity ?? 0) - Number(event?.registration_count ?? 0))} + / + {formatNumberPersian(Number(event?.capacity ?? 0))} نفر + +
+
+ هزینه‌ی ثبت‌نام + {labelPrice(event)} +
+ {isAvailable(event) ? ( + + ) : ( + + )} +
+
+
+
+ + ))} +
+ )} +
+
+ + ); +} diff --git a/src/pages/Home.tsx b/src/views/Home.tsx similarity index 98% rename from src/pages/Home.tsx rename to src/views/Home.tsx index 49a2bbe..1001021 100644 --- a/src/pages/Home.tsx +++ b/src/views/Home.tsx @@ -4,8 +4,8 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Helmet } from "react-helmet-async"; -import { Link } from "react-router-dom"; +import { Helmet } from "@/lib/helmet"; +import { Link } from "@/lib/router"; const heroTitle = "انجمن علمی کامپیوتر دانشگاه گیلان"; const heroDescription = diff --git a/src/pages/Logout.tsx b/src/views/Logout.tsx similarity index 94% rename from src/pages/Logout.tsx rename to src/views/Logout.tsx index a285cdf..3423a79 100644 --- a/src/pages/Logout.tsx +++ b/src/views/Logout.tsx @@ -1,5 +1,7 @@ +"use client"; + import { useEffect } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "@/lib/router"; import { Loader2 } from "lucide-react"; import { useAuth } from "@/contexts/AuthContext"; import { useToast } from "@/hooks/use-toast"; diff --git a/src/pages/PaymentResult.tsx b/src/views/PaymentResult.tsx similarity index 97% rename from src/pages/PaymentResult.tsx rename to src/views/PaymentResult.tsx index 8c41fdb..9b41145 100644 --- a/src/pages/PaymentResult.tsx +++ b/src/views/PaymentResult.tsx @@ -1,6 +1,8 @@ +"use client"; + import { useEffect, useMemo, useState, useRef } from 'react'; -import { Helmet } from 'react-helmet-async'; -import { useSearchParams, Link } from 'react-router-dom'; +import { Helmet } from '@/lib/helmet'; +import { useSearchParams, Link } from '@/lib/router'; import QRCode from 'react-qr-code'; import jsPDF from 'jspdf'; import html2canvas from 'html2canvas'; @@ -9,6 +11,7 @@ import { Button } from '@/components/ui/button'; import { api } from '@/lib/api'; import { formatNumberPersian, formatToman, toPersianDigits } from '@/lib/utils'; import Markdown from '@/components/Markdown'; +import { siteUrl } from '@/lib/site'; type SavedPayment = { event_id: number; @@ -19,7 +22,7 @@ type SavedPayment = { discount_amount?: number; amount?: number; started_at?: string; - success_markdown?: string; + success_markdown?: string | null; }; @@ -63,7 +66,6 @@ export default function PaymentResult() { const receiptRef = useRef(null); const successMarkdown = data?.success_markdown ?? ''; - const siteUrl = 'https://east-guilan-ce.ir'; const siteName = 'East Guilan CE'; const canonicalUrl = `${siteUrl}/payments/result`; const toAbsoluteUrl = (url?: string | null) => { @@ -112,13 +114,13 @@ export default function PaymentResult() { const qrValue = useMemo(() => { // لینک قابل بررسی/اشتراک‌گذاری - const base = window.location.origin; + const base = typeof window !== 'undefined' ? window.location.origin : siteUrl; const url = new URL(`${base}/payments/result`); if (refId) url.searchParams.set('ref_id', refId); if (eventId) url.searchParams.set('event_id', String(eventId)); url.searchParams.set('status', ok ? 'success' : 'failed'); return url.toString(); - }, [refId, eventId, ok]); + }, [eventId, ok, refId]); const handleDownloadPdf = async () => { const el = receiptRef.current; diff --git a/src/pages/Profile.tsx b/src/views/Profile.tsx similarity index 99% rename from src/pages/Profile.tsx rename to src/views/Profile.tsx index 4802f12..ec25f01 100644 --- a/src/pages/Profile.tsx +++ b/src/views/Profile.tsx @@ -1,7 +1,9 @@ +"use client"; + import type * as Types from '@/lib/types'; import { useEffect, useRef, useState, useMemo } from 'react'; -import { Navigate, Link } from 'react-router-dom'; -import { Helmet } from 'react-helmet-async'; +import { Navigate, Link } from '@/lib/router'; +import { Helmet } from '@/lib/helmet'; import { useAuth } from '@/contexts/AuthContext'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -191,7 +193,7 @@ export default function Profile() { if (!me?.major) return '—'; if (majors) { const f = majors.find(m => m.code === me.major || m.label === me.major); - return f.label; + return f?.label ?? me.major; } return me.major; }, [majors, me?.major]); @@ -210,8 +212,9 @@ export default function Profile() { if (!me?.university) return '—'; if (universities) { const f = universities.find(u => u.code === me.university || u.label === me.university); - return f.label; + return f?.label ?? me.university; } + return me.university; }, [universities, me?.university]); const [uploading, setUploading] = useState(false); diff --git a/src/pages/ResetPasswordConfirm.tsx b/src/views/ResetPasswordConfirm.tsx similarity index 97% rename from src/pages/ResetPasswordConfirm.tsx rename to src/views/ResetPasswordConfirm.tsx index 36b7327..8352d4e 100644 --- a/src/pages/ResetPasswordConfirm.tsx +++ b/src/views/ResetPasswordConfirm.tsx @@ -1,5 +1,7 @@ +"use client"; + import { useState } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; +import { useParams, useNavigate } from '@/lib/router'; import { useToast } from '@/hooks/use-toast'; import { api } from '@/lib/api'; import { resolveErrorMessage } from '@/lib/utils'; diff --git a/src/pages/ResetPasswordRequest.tsx b/src/views/ResetPasswordRequest.tsx similarity index 99% rename from src/pages/ResetPasswordRequest.tsx rename to src/views/ResetPasswordRequest.tsx index 9d8f236..0975da8 100644 --- a/src/pages/ResetPasswordRequest.tsx +++ b/src/views/ResetPasswordRequest.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useState } from 'react'; import { useToast } from '@/hooks/use-toast'; import { api } from '@/lib/api'; diff --git a/src/pages/VerifyEmail.tsx b/src/views/VerifyEmail.tsx similarity index 98% rename from src/pages/VerifyEmail.tsx rename to src/views/VerifyEmail.tsx index 518e9a5..3462105 100644 --- a/src/pages/VerifyEmail.tsx +++ b/src/views/VerifyEmail.tsx @@ -1,5 +1,7 @@ +"use client"; + import { useEffect } from "react"; -import { useParams, Link } from "react-router-dom"; +import { useParams, Link } from "@/lib/router"; import { useQuery } from "@tanstack/react-query"; import { api } from "@/lib/api"; import { diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/tsconfig.app.json b/tsconfig.app.json deleted file mode 100644 index 0b0e43e..0000000 --- a/tsconfig.app.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": false, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitAny": false, - "noFallthroughCasesInSwitch": false, - - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["src"] -} diff --git a/tsconfig.json b/tsconfig.json index 2518773..64a86d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,42 @@ { - "files": [], - "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }], "compilerOptions": { + "target": "ES2017", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, "baseUrl": ".", "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] }, - "noImplicitAny": false, - "noUnusedParameters": false, - "skipLibCheck": true, - "allowJs": true, - "noUnusedLocals": false, - "strictNullChecks": false - } -} + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json deleted file mode 100644 index 3133162..0000000 --- a/tsconfig.node.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "lib": ["ES2023"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "strict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noFallthroughCasesInSwitch": true - }, - "include": ["vite.config.ts"] -} diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index da25c6d..0000000 --- a/vite.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react-swc"; -import path from "path"; -import { componentTagger } from "lovable-tagger"; - -// https://vitejs.dev/config/ -export default defineConfig(({ mode }) => ({ - server: { - host: "::", - port: 8080, - }, - plugins: [react(), mode === "development" && componentTagger()].filter(Boolean), - resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - }, - }, -}));