diff --git a/README.md b/README.md index a3f13f24..d0bf65c8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ O sistema otimiza o fluxo de atendimento com integração de dados em uma interf ## Tecnologias utilizadas - **Typescript**: Tipagem estática para maior segurança no desenvolvimento; -- **Next.js 15**: Framework React para renderização híbrida e roteamento eficiente; +- **Next.js 16**: Framework React para renderização híbrida e roteamento eficiente; - **Tailwind CSS**: Framework CSS utilitário; - **React Hook Form + Zod**: Gerenciamento de formulários com validação robusta. @@ -15,30 +15,31 @@ O sistema otimiza o fluxo de atendimento com integração de dados em uma interf ### Pré-requisitos -- Node.js (versão 18 ou superior) -- npm +- Node.js - versão 20.9.0 (LTS) ou superior +- NPM (Node package manager) +- Docker -### Passos para Configuração +### Instalação e configuração -#### 1. Clone o repositório +#### 1. Clone o repositório e instale as dependências ```bash git clone https://github.com/ipecode-br/abnmo-frontend cd abnmo-frontend +npm install ``` -#### 2. Configure variáveis de ambiente +#### 2. Configure as variáveis de ambiente -Copie o arquivo de exemplo `env.sample` e renomoeie para `.env.local`. Ou crie um arquivo com o mesmo nome contendo as seguintes variáveis de ambiente: +Copie o arquivo de exemplo `env.sample` e renomeie para `.env.local` ou execute o comando: -``` -NEXT_PUBLIC_API_URL= +```bash +cp env.sample .env.local ``` -#### 3. Instale dependências e inicie a aplicação +#### 3. Inicie a aplicação ```bash -npm install npm run dev ``` diff --git a/env.sample b/env.sample index a731a742..563225dc 100644 --- a/env.sample +++ b/env.sample @@ -1,2 +1 @@ -NEXT_PUBLIC_API_URL="http://localhost:3333" -NEXT_PUBLIC_API_IBGE="https://servicodados.ibge.gov.br/api/v1" \ No newline at end of file +NEXT_PUBLIC_API_URL="http://localhost:3333" \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index f3c0bff3..d7948163 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,48 +1,33 @@ -import { dirname } from 'node:path' -import { fileURLToPath } from 'node:url' - -import { FlatCompat } from '@eslint/eslintrc' -import pluginJs from '@eslint/js' -import eslintConfigPrettier from 'eslint-config-prettier' +import { defineConfig, globalIgnores } from 'eslint/config' +import nextVitals from 'eslint-config-next/core-web-vitals' +import nextTs from 'eslint-config-next/typescript' +import prettier from 'eslint-config-prettier/flat' import eslintPluginPrettier from 'eslint-plugin-prettier' -import pluginReact from 'eslint-plugin-react' import simpleImportSort from 'eslint-plugin-simple-import-sort' -import tseslint from 'typescript-eslint' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}) -const eslintConfig = [ - ...compat.extends('next/core-web-vitals', 'next/typescript'), - eslintConfigPrettier, - pluginJs.configs.recommended, - pluginReact.configs.flat.recommended, - ...tseslint.configs.recommended, - { - ignores: ['.next/**', 'node_modules/**'], - }, +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + prettier, + globalIgnores([ + '.next/**', + 'out/**', + 'build/**', + 'next-env.d.ts', + 'node_modules/**', + ]), { - files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}', './*.{js,mjs,cjs,ts,jsx,tsx}'], + files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'], plugins: { - prettier: eslintPluginPrettier, 'simple-import-sort': simpleImportSort, - }, - settings: { - react: { version: 'detect' }, + prettier: eslintPluginPrettier, }, rules: { - 'prettier/prettier': 'error', 'simple-import-sort/imports': 'error', 'simple-import-sort/exports': 'error', - // ignore - 'react/react-in-jsx-scope': 'off', - '@typescript-eslint/no-empty-object-type': 'off', + 'prettier/prettier': 'error', }, }, -] +]) export default eslintConfig diff --git a/package-lock.json b/package-lock.json index b6f09f19..2478a2fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,11 +21,11 @@ "date-fns": "4.1.0", "jsonwebtoken": "9.0.2", "lucide-react": "0.531.0", - "next": "15.5.3", + "next": "16.1.6", "prettier": "3.6.2", - "react": "^19.0.0", + "react": "19.2.4", "react-day-picker": "9.8.0", - "react-dom": "^19.0.0", + "react-dom": "19.2.4", "react-hook-form": "7.55.0", "recharts": "3.1.0", "sonner": "2.0.4", @@ -47,7 +47,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "9.33.0", - "eslint-config-next": "15.2.4", + "eslint-config-next": "16.1.6", "eslint-config-prettier": "10.1.1", "eslint-plugin-prettier": "5.2.6", "eslint-plugin-react": "7.37.5", @@ -1078,9 +1078,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", "license": "MIT", "optional": true, "dependencies": { @@ -1098,10 +1098,11 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -1128,10 +1129,11 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1344,10 +1346,20 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", - "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], @@ -1363,13 +1375,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.0" + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", - "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], @@ -1385,13 +1397,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.0" + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", - "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], @@ -1405,9 +1417,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", - "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], @@ -1421,9 +1433,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", - "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" ], @@ -1437,9 +1449,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", - "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" ], @@ -1453,9 +1465,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", - "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", "cpu": [ "ppc64" ], @@ -1468,10 +1480,26 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@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.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", - "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" ], @@ -1485,9 +1513,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", - "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" ], @@ -1501,9 +1529,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", - "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" ], @@ -1517,9 +1545,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", - "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" ], @@ -1533,9 +1561,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", - "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" ], @@ -1551,13 +1579,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.0" + "@img/sharp-libvips-linux-arm": "1.2.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", - "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" ], @@ -1573,13 +1601,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.0" + "@img/sharp-libvips-linux-arm64": "1.2.4" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", - "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", "cpu": [ "ppc64" ], @@ -1595,13 +1623,35 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.0" + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@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.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", - "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], @@ -1617,13 +1667,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.0" + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", - "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], @@ -1639,13 +1689,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.0" + "@img/sharp-libvips-linux-x64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", - "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], @@ -1661,13 +1711,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", - "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], @@ -1683,20 +1733,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", - "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@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.4.4" + "@emnapi/runtime": "^1.7.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1706,9 +1756,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", - "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", "cpu": [ "arm64" ], @@ -1725,9 +1775,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", - "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], @@ -1744,9 +1794,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", - "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], @@ -2299,24 +2349,25 @@ } }, "node_modules/@next/env": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.3.tgz", - "integrity": "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", + "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.2.4.tgz", - "integrity": "sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.6.tgz", + "integrity": "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "3.3.1" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.3.tgz", - "integrity": "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz", + "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==", "cpu": [ "arm64" ], @@ -2330,9 +2381,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.3.tgz", - "integrity": "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz", + "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==", "cpu": [ "x64" ], @@ -2346,9 +2397,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.3.tgz", - "integrity": "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz", + "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==", "cpu": [ "arm64" ], @@ -2362,9 +2413,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.3.tgz", - "integrity": "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz", + "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==", "cpu": [ "arm64" ], @@ -2378,9 +2429,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.3.tgz", - "integrity": "sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz", + "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==", "cpu": [ "x64" ], @@ -2394,9 +2445,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.3.tgz", - "integrity": "sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz", + "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==", "cpu": [ "x64" ], @@ -2410,9 +2461,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.3.tgz", - "integrity": "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz", + "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==", "cpu": [ "arm64" ], @@ -2426,9 +2477,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.3.tgz", - "integrity": "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz", + "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==", "cpu": [ "x64" ], @@ -3605,12 +3656,6 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", - "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", - "dev": true - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -4399,6 +4444,42 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", + "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.55.0", + "@typescript-eslint/types": "^8.55.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", + "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", @@ -4416,6 +4497,23 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", + "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/@typescript-eslint/type-utils": { "version": "8.29.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz", @@ -4959,17 +5057,20 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5271,6 +5372,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -5630,25 +5740,11 @@ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -5660,18 +5756,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "dev": true }, "node_modules/colorette": { "version": "2.0.20", @@ -6104,10 +6189,11 @@ "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -6214,9 +6300,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "devOptional": true, "license": "Apache-2.0", "engines": { @@ -6238,10 +6324,11 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -6407,27 +6494,28 @@ "dev": true }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -6439,21 +6527,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -6462,7 +6553,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -6687,24 +6778,24 @@ } }, "node_modules/eslint-config-next": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.2.4.tgz", - "integrity": "sha512-v4gYjd4eYIme8qzaJItpR5MMBXJ0/YV07u7eb50kEnlEmX7yhOjdUdzz70v4fiINYRjLf8X8TbogF0k7wlz6sA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.6.tgz", + "integrity": "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==", "dev": true, + "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.2.4", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@next/eslint-plugin-next": "16.1.6", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" }, "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "peerDependenciesMeta": { @@ -6713,6 +6804,261 @@ } } }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", + "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/type-utils": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.55.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/parser": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", + "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/scope-manager": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", + "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/type-utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", + "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/types": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", + "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", + "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.55.0", + "@typescript-eslint/tsconfig-utils": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", + "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", + "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.55.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-next/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-config-next/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint-config-next/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-config-next/node_modules/typescript-eslint": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.55.0.tgz", + "integrity": "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.55.0", + "@typescript-eslint/parser": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/eslint-config-prettier": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz", @@ -6781,10 +7127,11 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -6802,35 +7149,36 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", + "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", - "is-core-module": "^2.15.1", + "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", + "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "engines": { @@ -6950,17 +7298,49 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, + "node_modules/eslint-plugin-react-hooks/node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/eslint-plugin-react-hooks/node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -7170,6 +7550,7 @@ "resolved": "https://registry.npmjs.org/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", @@ -7186,6 +7567,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -7717,6 +8099,23 @@ "node": ">= 0.4" } }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -7946,13 +8345,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT", - "optional": true - }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -8159,6 +8551,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -10035,10 +10440,11 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -10413,13 +10819,14 @@ "dev": true }, "node_modules/next": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz", - "integrity": "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz", + "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", "license": "MIT", "dependencies": { - "@next/env": "15.5.3", + "@next/env": "16.1.6", "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -10428,18 +10835,18 @@ "next": "dist/bin/next" }, "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.3", - "@next/swc-darwin-x64": "15.5.3", - "@next/swc-linux-arm64-gnu": "15.5.3", - "@next/swc-linux-arm64-musl": "15.5.3", - "@next/swc-linux-x64-gnu": "15.5.3", - "@next/swc-linux-x64-musl": "15.5.3", - "@next/swc-win32-arm64-msvc": "15.5.3", - "@next/swc-win32-x64-msvc": "15.5.3", - "sharp": "^0.34.3" + "@next/swc-darwin-arm64": "16.1.6", + "@next/swc-darwin-x64": "16.1.6", + "@next/swc-linux-arm64-gnu": "16.1.6", + "@next/swc-linux-arm64-musl": "16.1.6", + "@next/swc-linux-x64-gnu": "16.1.6", + "@next/swc-linux-x64-musl": "16.1.6", + "@next/swc-win32-arm64-msvc": "16.1.6", + "@next/swc-win32-x64-msvc": "16.1.6", + "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -11215,9 +11622,10 @@ ] }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" @@ -11244,15 +11652,16 @@ } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", "peer": true, "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.2.4" } }, "node_modules/react-hook-form": { @@ -11724,14 +12133,15 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" }, "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==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -11787,16 +12197,16 @@ } }, "node_modules/sharp": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", - "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" + "@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" @@ -11805,28 +12215,30 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.3", - "@img/sharp-darwin-x64": "0.34.3", - "@img/sharp-libvips-darwin-arm64": "1.2.0", - "@img/sharp-libvips-darwin-x64": "1.2.0", - "@img/sharp-libvips-linux-arm": "1.2.0", - "@img/sharp-libvips-linux-arm64": "1.2.0", - "@img/sharp-libvips-linux-ppc64": "1.2.0", - "@img/sharp-libvips-linux-s390x": "1.2.0", - "@img/sharp-libvips-linux-x64": "1.2.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", - "@img/sharp-libvips-linuxmusl-x64": "1.2.0", - "@img/sharp-linux-arm": "0.34.3", - "@img/sharp-linux-arm64": "0.34.3", - "@img/sharp-linux-ppc64": "0.34.3", - "@img/sharp-linux-s390x": "0.34.3", - "@img/sharp-linux-x64": "0.34.3", - "@img/sharp-linuxmusl-arm64": "0.34.3", - "@img/sharp-linuxmusl-x64": "0.34.3", - "@img/sharp-wasm32": "0.34.3", - "@img/sharp-win32-arm64": "0.34.3", - "@img/sharp-win32-ia32": "0.34.3", - "@img/sharp-win32-x64": "0.34.3" + "@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": { @@ -11934,16 +12346,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -12065,6 +12467,20 @@ "node": ">=8" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -12441,13 +12857,14 @@ "dev": true }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, + "license": "MIT", "dependencies": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -12457,10 +12874,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -12471,10 +12892,11 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=12" @@ -12529,10 +12951,11 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.12" }, diff --git a/package.json b/package.json index c0541f50..d4fd79e7 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "start": "next start", "lint:prettier:check": "prettier --check .", "lint:prettier:fix": "prettier --write .", - "lint:eslint:check": "next lint . && tsc --noEmit", + "lint:eslint:check": "eslint . && tsc --noEmit", "lint:tsc": "tsc --noEmit", "prepare": "husky" }, @@ -29,11 +29,11 @@ "date-fns": "4.1.0", "jsonwebtoken": "9.0.2", "lucide-react": "0.531.0", - "next": "15.5.3", + "next": "16.1.6", "prettier": "3.6.2", - "react": "^19.0.0", + "react": "19.2.4", "react-day-picker": "9.8.0", - "react-dom": "^19.0.0", + "react-dom": "19.2.4", "react-hook-form": "7.55.0", "recharts": "3.1.0", "sonner": "2.0.4", @@ -55,7 +55,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "9.33.0", - "eslint-config-next": "15.2.4", + "eslint-config-next": "16.1.6", "eslint-config-prettier": "10.1.1", "eslint-plugin-prettier": "5.2.6", "eslint-plugin-react": "7.37.5", diff --git a/src/actions/appointments/get-appointments.ts b/src/actions/appointments/get-appointments.ts new file mode 100644 index 00000000..84b86bd8 --- /dev/null +++ b/src/actions/appointments/get-appointments.ts @@ -0,0 +1,52 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { AppointmentStatus } from '@/enums/appointments' +import { api } from '@/lib/api' +import type { Appointment } from '@/types/appointments' +import type { AppointmentsOrderBy, QueryOrder } from '@/types/orders' + +export type GetAppointmentsParams = { + status?: AppointmentStatus + order?: QueryOrder + orderBy?: AppointmentsOrderBy + startDate?: string + limit?: number +} + +interface GetAppointmentsProps { + params?: GetAppointmentsParams + cacheKey?: string +} + +export async function getAppointments({ + params, + cacheKey, +}: GetAppointmentsProps = {}) { + try { + const response = await api<{ appointments: Appointment[]; total: number }>( + '/appointments', + { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [NEXT_CACHE_TAGS.appointments.main, cacheKey] + : [NEXT_CACHE_TAGS.appointments.main], + }, + }, + ) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch appointments:', error) + return null + } +} diff --git a/src/actions/auth.ts b/src/actions/auth.ts index bb26f218..f7b7bcd0 100644 --- a/src/actions/auth.ts +++ b/src/actions/auth.ts @@ -6,16 +6,16 @@ import { ROUTES } from '@/constants/routes' import { definePermissionsFor } from '@/lib/permissions' import type { Action, Subject } from '@/lib/permissions/schemas' -import { getUserFromToken } from './token' +import { getUserFromToken } from './users/get-user-from-token' export async function canUser(action: Action, subject: Subject) { - const data = await getUserFromToken() + const user = await getUserFromToken() - if (!data || !data.role) { + if (!user || !user.role) { redirect(ROUTES.auth.signOut) } - const { can } = definePermissionsFor(data.role) + const { can } = definePermissionsFor(user.role) return can(action, subject) } diff --git a/src/actions/cache.ts b/src/actions/cache.ts deleted file mode 100644 index 13eddd38..00000000 --- a/src/actions/cache.ts +++ /dev/null @@ -1,14 +0,0 @@ -'use server' - -import { revalidateTag } from 'next/cache' - -export async function revalidateCache(tags: string | string[]) { - if (Array.isArray(tags)) { - for (const tag of tags) { - revalidateTag(tag) - } - return - } - - revalidateTag(tags) -} diff --git a/src/actions/ibge.ts b/src/actions/ibge.ts deleted file mode 100644 index bc652d14..00000000 --- a/src/actions/ibge.ts +++ /dev/null @@ -1,25 +0,0 @@ -'use server' - -import { env } from '@/config/env' - -export async function getCitiesByUF(UF: string) { - const REVALIDATE_IN_SECONDS = 86400 // 24 hours - - //Changed to /municipios as some districts may have the same name as the municipality - //What caused duplication of keys in the city select - const response: Array<{ nome: string }> = await fetch( - `${env.NEXT_PUBLIC_API_IBGE}/localidades/estados/${UF}/municipios`, - { - next: { - revalidate: REVALIDATE_IN_SECONDS, - tags: [`cities/${UF}`], - }, - }, - ).then(async (res) => await res.json()) - - if (!response) return null - - const cities = response.map((city) => city.nome) - - return cities -} diff --git a/src/actions/patients/get-patient.ts b/src/actions/patients/get-patient.ts index 6d4825cb..4c1d3799 100644 --- a/src/actions/patients/get-patient.ts +++ b/src/actions/patients/get-patient.ts @@ -1,18 +1,17 @@ 'use server' +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' import { NEXT_CACHE_TAGS } from '@/constants/cache' import { api } from '@/lib/api' -import type { Patient } from '@/types/patients' +import type { Patient } from '@/types/patients.d.ts' export async function getPatient(id: string) { - const REVALIDATE_IN_SECONDS = 3600 - try { const response = await api(`/patients/${id}`, { includeCookies: true, cache: 'force-cache', next: { - revalidate: REVALIDATE_IN_SECONDS, + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, tags: [NEXT_CACHE_TAGS.patient(id)], }, }) diff --git a/src/actions/patients/statistics/get-total-patients-by-status.ts b/src/actions/patients/statistics/get-total-patients-by-status.ts deleted file mode 100644 index 0ad2ac8e..00000000 --- a/src/actions/patients/statistics/get-total-patients-by-status.ts +++ /dev/null @@ -1,36 +0,0 @@ -'use server' - -import { NEXT_CACHE_TAGS } from '@/constants/cache' -import { api } from '@/lib/api' - -type StatisticType = 'total' | 'active' | 'inactive' - -export async function getTotalPatientsByStatus() { - const REVALIDATE_IN_SECONDS = 3600 - - try { - const response = await api>( - '/statistics/patients/total', - { - includeCookies: true, - cache: 'force-cache', - next: { - revalidate: REVALIDATE_IN_SECONDS, - tags: [NEXT_CACHE_TAGS.statistics.totalPatientsByStatus], - }, - }, - ) - - if (!response.data) { - return null - } - - return Object.entries(response.data).map(([label, value]) => ({ - label: label as StatisticType, - value, - })) - } catch (error) { - console.error('Failed to fetch patients statistics:', error) - return null - } -} diff --git a/src/actions/statistics/get-total-appointments-by-category.ts b/src/actions/statistics/get-total-appointments-by-category.ts new file mode 100644 index 00000000..3fe0d90a --- /dev/null +++ b/src/actions/statistics/get-total-appointments-by-category.ts @@ -0,0 +1,51 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { QueryPeriod } from '@/enums/queries' +import type { Specialty } from '@/enums/shared' +import { api } from '@/lib/api' + +type GetTotalAppointmentsByCategoryParams = { + period?: QueryPeriod + patientId?: string +} + +interface GetTotalAppointmentsByCategoryProps { + params?: GetTotalAppointmentsByCategoryParams + cacheKey?: string +} + +export async function getTotalAppointmentsByCategory({ + params, + cacheKey, +}: GetTotalAppointmentsByCategoryProps = {}) { + try { + const response = await api<{ + categories: Array<{ category: Specialty; total: number }> + total: number + }>('/statistics/appointments/by-category', { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [ + NEXT_CACHE_TAGS.statistics.totalAppointmentsByCategory.main, + cacheKey, + ] + : [NEXT_CACHE_TAGS.statistics.totalAppointmentsByCategory.main], + }, + }) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch total appointments by category:', error) + return null + } +} diff --git a/src/actions/statistics/get-total-appointments.ts b/src/actions/statistics/get-total-appointments.ts new file mode 100644 index 00000000..e585ce49 --- /dev/null +++ b/src/actions/statistics/get-total-appointments.ts @@ -0,0 +1,47 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { QueryPeriod } from '@/enums/queries' +import { api } from '@/lib/api' + +type GetTotalAppointmentsParams = { + period?: QueryPeriod + patientId?: string +} + +interface GetTotalAppointmentsProps { + params?: GetTotalAppointmentsParams + cacheKey?: string +} + +export async function getTotalAppointments({ + params, + cacheKey, +}: GetTotalAppointmentsProps = {}) { + try { + const response = await api<{ total: number }>( + '/statistics/appointments/total', + { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [NEXT_CACHE_TAGS.statistics.totalAppointments.main, cacheKey] + : [NEXT_CACHE_TAGS.statistics.totalAppointments.main], + }, + }, + ) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch total appointments statistic:', error) + return null + } +} diff --git a/src/actions/statistics/get-total-patients-with-appointments.ts b/src/actions/statistics/get-total-patients-with-appointments.ts new file mode 100644 index 00000000..c01c2d34 --- /dev/null +++ b/src/actions/statistics/get-total-patients-with-appointments.ts @@ -0,0 +1,49 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { QueryPeriod } from '@/enums/queries' +import { api } from '@/lib/api' + +type GetTotalPatientsWithAppointmentsParams = { + period?: QueryPeriod +} + +interface GetTotalPatientsWithAppointmentsProps { + params?: GetTotalPatientsWithAppointmentsParams + cacheKey?: string +} + +export async function getTotalPatientsWithAppointments({ + params, + cacheKey, +}: GetTotalPatientsWithAppointmentsProps = {}) { + try { + const response = await api<{ total: number }>( + '/statistics/patients/with-appointments', + { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [ + NEXT_CACHE_TAGS.statistics.totalPatientsWithAppointments.main, + cacheKey, + ] + : [NEXT_CACHE_TAGS.statistics.totalPatientsWithAppointments.main], + }, + }, + ) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch total patients with appointments:', error) + return null + } +} diff --git a/src/actions/statistics/get-total-patients-with-referrals.ts b/src/actions/statistics/get-total-patients-with-referrals.ts new file mode 100644 index 00000000..6f6c772e --- /dev/null +++ b/src/actions/statistics/get-total-patients-with-referrals.ts @@ -0,0 +1,49 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { QueryPeriod } from '@/enums/queries' +import { api } from '@/lib/api' + +type GetTotalPatientsWithReferralsParams = { + period?: QueryPeriod +} + +interface GetTotalPatientsWithReferralsProps { + params?: GetTotalPatientsWithReferralsParams + cacheKey?: string +} + +export async function getTotalPatientsWithReferrals({ + params, + cacheKey, +}: GetTotalPatientsWithReferralsProps = {}) { + try { + const response = await api<{ total: number }>( + '/statistics/patients/with-referrals', + { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [ + NEXT_CACHE_TAGS.statistics.totalPatientsWithReferrals.main, + cacheKey, + ] + : [NEXT_CACHE_TAGS.statistics.totalPatientsWithReferrals.main], + }, + }, + ) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch total patients with referrals:', error) + return null + } +} diff --git a/src/actions/statistics/get-total-patients.ts b/src/actions/statistics/get-total-patients.ts new file mode 100644 index 00000000..b538ab00 --- /dev/null +++ b/src/actions/statistics/get-total-patients.ts @@ -0,0 +1,48 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { QueryPeriod } from '@/enums/queries' +import { api } from '@/lib/api' + +type Status = 'total' | 'active' | 'inactive' + +type GetTotalPatientsParams = { + period?: QueryPeriod +} + +interface GetTotalPatientsProps { + params?: GetTotalPatientsParams + cacheKey?: string +} + +export async function getTotalPatients({ + params, + cacheKey, +}: GetTotalPatientsProps = {}) { + try { + const response = await api>( + '/statistics/patients/total', + { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [NEXT_CACHE_TAGS.statistics.totalPatients.main, cacheKey] + : [NEXT_CACHE_TAGS.statistics.totalPatients.main], + }, + }, + ) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch patients statistics:', error) + return null + } +} diff --git a/src/actions/statistics/get-total-referrals-by-category.ts b/src/actions/statistics/get-total-referrals-by-category.ts new file mode 100644 index 00000000..1e84e75f --- /dev/null +++ b/src/actions/statistics/get-total-referrals-by-category.ts @@ -0,0 +1,48 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { QueryPeriod } from '@/enums/queries' +import type { Specialty } from '@/enums/shared' +import { api } from '@/lib/api' + +type GetTotalReferralsByCategoryParams = { + period?: QueryPeriod + patientId?: string +} + +interface GetTotalReferralsByCategoryProps { + params?: GetTotalReferralsByCategoryParams + cacheKey?: string +} + +export async function getTotalReferralsByCategory({ + params, + cacheKey, +}: GetTotalReferralsByCategoryProps = {}) { + try { + const response = await api<{ + categories: Array<{ category: Specialty; total: number }> + total: number + }>('/statistics/referrals/by-category', { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [NEXT_CACHE_TAGS.statistics.totalReferralsByCategory.main, cacheKey] + : [NEXT_CACHE_TAGS.statistics.totalReferralsByCategory.main], + }, + }) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch total referrals by category:', error) + return null + } +} diff --git a/src/actions/statistics/get-total-referrals.ts b/src/actions/statistics/get-total-referrals.ts new file mode 100644 index 00000000..c82540d4 --- /dev/null +++ b/src/actions/statistics/get-total-referrals.ts @@ -0,0 +1,47 @@ +'use server' + +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' +import { NEXT_CACHE_TAGS } from '@/constants/cache' +import type { QueryPeriod } from '@/enums/queries' +import { api } from '@/lib/api' + +type GetTotalReferralsParams = { + period?: QueryPeriod + patientId?: string +} + +interface GetTotalReferralsProps { + params?: GetTotalReferralsParams + cacheKey?: string +} + +export async function getTotalReferrals({ + params, + cacheKey, +}: GetTotalReferralsProps = {}) { + try { + const response = await api<{ total: number }>( + '/statistics/referrals/total', + { + includeCookies: true, + cache: 'force-cache', + params, + next: { + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, + tags: cacheKey + ? [NEXT_CACHE_TAGS.statistics.totalReferrals.main, cacheKey] + : [NEXT_CACHE_TAGS.statistics.totalReferrals.main], + }, + }, + ) + + if (!response.data) { + return null + } + + return response.data + } catch (error) { + console.error('Failed to fetch total referrals statistic:', error) + return null + } +} diff --git a/src/actions/token.ts b/src/actions/token.ts deleted file mode 100644 index 4346449a..00000000 --- a/src/actions/token.ts +++ /dev/null @@ -1,25 +0,0 @@ -'use server' - -import jwt from 'jsonwebtoken' - -import type { Role } from '@/lib/permissions/schemas' - -import { getCookie } from './cookies' - -export async function getUserFromToken() { - const accessToken = await getCookie('access_token') - - if (!accessToken) return null - - const tokenWithoutPrefix = accessToken.slice(2) - const extractedToken = tokenWithoutPrefix.split('.').slice(0, 3).join('.') - - const payload = jwt.decode(extractedToken) as { sub: string; role: Role } - - if (!payload) return null - - const id = payload.sub - const role = payload.role - - return { id, role } -} diff --git a/src/actions/users.ts b/src/actions/users/get-current-user.ts similarity index 70% rename from src/actions/users.ts rename to src/actions/users/get-current-user.ts index 68f02607..61ab6ceb 100644 --- a/src/actions/users.ts +++ b/src/actions/users/get-current-user.ts @@ -2,16 +2,15 @@ import { redirect } from 'next/navigation' +import { DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS } from '@/config/cache' import { NEXT_CACHE_TAGS } from '@/constants/cache' import { ROUTES } from '@/constants/routes' import { api } from '@/lib/api' -import type { User } from '@/types/users' +import type { User } from '@/types/users.d.ts' -import { getUserFromToken } from './token' +import { getUserFromToken } from './get-user-from-token' export async function getCurrentUser() { - const REVALIDATE_IN_SECONDS = 3600 - const user = await getUserFromToken() if (!user?.id) return null @@ -20,7 +19,7 @@ export async function getCurrentUser() { includeCookies: true, cache: 'force-cache', next: { - revalidate: REVALIDATE_IN_SECONDS, + revalidate: DEFAULT_NEXT_CACHE_REVALIDATE_IN_SECONDS, tags: [NEXT_CACHE_TAGS.user(user.id)], }, }) diff --git a/src/actions/users/get-user-from-token.ts b/src/actions/users/get-user-from-token.ts new file mode 100644 index 00000000..66ee2686 --- /dev/null +++ b/src/actions/users/get-user-from-token.ts @@ -0,0 +1,15 @@ +'use server' + +import { COOKIES } from '@/constants/cookies' +import type { UserRole } from '@/enums/users' +import { extractTokenData } from '@/helpers/extract-token-data' + +export async function getUserFromToken() { + const user = await extractTokenData<{ sub: string; role: UserRole }>({ + cookie: COOKIES.accessToken, + }) + + if (!user?.sub) return null + + return { id: user.sub, role: user.role } +} diff --git a/src/app/(dashboard)/aprovacoes/page.tsx b/src/app/(dashboard)/_aprovacoes/aprovacoes-pendentes/page.tsx similarity index 100% rename from src/app/(dashboard)/aprovacoes/page.tsx rename to src/app/(dashboard)/_aprovacoes/aprovacoes-pendentes/page.tsx diff --git a/src/app/(dashboard)/aprovacoes/aprovados/page.tsx b/src/app/(dashboard)/_aprovacoes/aprovados/page.tsx similarity index 100% rename from src/app/(dashboard)/aprovacoes/aprovados/page.tsx rename to src/app/(dashboard)/_aprovacoes/aprovados/page.tsx diff --git a/src/app/(dashboard)/aprovacoes/pendentes/page.tsx b/src/app/(dashboard)/_aprovacoes/envios-pendentes/page.tsx similarity index 100% rename from src/app/(dashboard)/aprovacoes/pendentes/page.tsx rename to src/app/(dashboard)/_aprovacoes/envios-pendentes/page.tsx diff --git a/src/app/(dashboard)/_aprovacoes/page.tsx b/src/app/(dashboard)/_aprovacoes/page.tsx new file mode 100644 index 00000000..1ee4d2e2 --- /dev/null +++ b/src/app/(dashboard)/_aprovacoes/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from 'next' +import { redirect } from 'next/navigation' + +import { ROUTES } from '@/constants/routes' + +export const metadata: Metadata = { + title: 'Aprovações', +} + +export default function Page() { + redirect(ROUTES.dashboard.approvals.pendingApprovals) +} diff --git a/src/app/(dashboard)/_configuracoes/fontes/page.tsx b/src/app/(dashboard)/_configuracoes/fontes/page.tsx new file mode 100644 index 00000000..e0911549 --- /dev/null +++ b/src/app/(dashboard)/_configuracoes/fontes/page.tsx @@ -0,0 +1,11 @@ +import type { Metadata } from 'next' + +import { FontSizeCards } from '@/modules/settings/cards/fontsize/fontsize-cards' + +export const metadata: Metadata = { + title: 'Tamanho da fonte', +} + +export default function Page() { + return +} diff --git a/src/app/(dashboard)/configuracoes/layout.tsx b/src/app/(dashboard)/_configuracoes/layout.tsx similarity index 100% rename from src/app/(dashboard)/configuracoes/layout.tsx rename to src/app/(dashboard)/_configuracoes/layout.tsx diff --git a/src/app/(dashboard)/configuracoes/page.tsx b/src/app/(dashboard)/_configuracoes/page.tsx similarity index 100% rename from src/app/(dashboard)/configuracoes/page.tsx rename to src/app/(dashboard)/_configuracoes/page.tsx diff --git a/src/app/(dashboard)/_header/index.tsx b/src/app/(dashboard)/_header/index.tsx deleted file mode 100644 index 5bc39b04..00000000 --- a/src/app/(dashboard)/_header/index.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { BellIcon, CircleHelpIcon } from 'lucide-react' - -import { Breadcrumbs } from '@/components/breadcrumbs' -import { Button } from '@/components/ui/button' -import { Divider } from '@/components/ui/divider' - -export function DashboardHeader() { - return ( - <> -
-

Visão Geral

- - - -
- - - -
-
- - - ) -} diff --git a/src/app/(dashboard)/_sidebar/index.tsx b/src/app/(dashboard)/_sidebar/index.tsx deleted file mode 100644 index 58523cf9..00000000 --- a/src/app/(dashboard)/_sidebar/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { - BoltIcon, - HeartHandshakeIcon, - LayoutDashboardIcon, - Share2Icon, - UserRoundCheckIcon, - Users2Icon, -} from 'lucide-react' - -import { getCurrentUser } from '@/actions/users' -import { Divider } from '@/components/ui/divider' -import { ROUTES } from '@/constants/routes' - -import { SidebarAccount } from './account' -import { DashboardSidebarContainer } from './container' -import { SidebarHeader } from './header' -import { DashboardSidebarMenuSection } from './menu-section' - -export async function DashboardSidebar() { - const user = await getCurrentUser() - - return ( - - - - - - - - - - - - ) -} - -const SIDEBAR_SECTIONS = [ - { - id: 'common', - links: [ - { - label: 'Visão Geral', - icon: , - path: ROUTES.dashboard.main, - }, - { - label: 'Pacientes', - icon: , - path: ROUTES.dashboard.patients.main, - }, - { - label: 'Encaminhados', - icon: , - path: ROUTES.dashboard.referrals.main, - }, - { - label: 'Aprovações', - icon: , - path: ROUTES.dashboard.approvals.main, - }, - { - label: 'Equipe', - icon: , - path: ROUTES.dashboard.teams.main, - }, - ], - }, - { - id: 'others', - links: [ - { - label: 'Configurações', - icon: , - path: ROUTES.dashboard.settings.main, - }, - // { - // label: 'Suporte', - // icon: , - // path: ROUTES.dashboard.support.main, - // }, - ], - }, -] diff --git a/src/app/(dashboard)/aprovacoes/layout.tsx b/src/app/(dashboard)/aprovacoes/layout.tsx deleted file mode 100644 index 910f2fb1..00000000 --- a/src/app/(dashboard)/aprovacoes/layout.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { DashboardContainer } from '@/components/dashboard/container' -import { DashboardTabButtons } from '@/components/dashboard/tab-buttons' -import { ROUTES } from '@/constants/routes' - -export default function Layout({ - children, -}: Readonly<{ children: React.ReactNode }>) { - const tabButtons = [ - { - title: 'Aprovações pendentes', - path: ROUTES.dashboard.approvals.main, - }, - { - title: 'Envios pendentes', - path: ROUTES.dashboard.approvals.pending, - }, - { - title: 'Aprovados', - path: ROUTES.dashboard.approvals.approved, - }, - ] - - return ( - <> - - - - {children} - - - ) -} diff --git a/src/app/(dashboard)/atendimentos/listagem/page.tsx b/src/app/(dashboard)/atendimentos/listagem/page.tsx new file mode 100644 index 00000000..8ca7fc4d --- /dev/null +++ b/src/app/(dashboard)/atendimentos/listagem/page.tsx @@ -0,0 +1,5 @@ +import { AppointmentsList } from '@/modules/appointments/list' + +export default function Page() { + return +} diff --git a/src/app/(dashboard)/atendimentos/page.tsx b/src/app/(dashboard)/atendimentos/page.tsx new file mode 100644 index 00000000..b2e17583 --- /dev/null +++ b/src/app/(dashboard)/atendimentos/page.tsx @@ -0,0 +1,21 @@ +import { PatientsWithAppointmentsPercentageCard } from '@/modules/appointments/patients-with-appointments-percentage-card' +import { AppointmentsPeriodTabSelect } from '@/modules/appointments/period-tab-select' +import { TotalAppointmentsCard } from '@/modules/appointments/total-appointments-card' +import { TotalAppointmentsByCategoryCard } from '@/modules/appointments/total-by-category-card' +import { TotalAppointmentsByStateCard } from '@/modules/appointments/total-by-state-card' + +export const dynamic = 'force-dynamic' + +export default function Page() { + return ( +
+ + + + + + + +
+ ) +} diff --git a/src/app/(dashboard)/configuracoes/fontes/page.tsx b/src/app/(dashboard)/configuracoes/fontes/page.tsx deleted file mode 100644 index 61cb28ad..00000000 --- a/src/app/(dashboard)/configuracoes/fontes/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Metadata } from 'next' - -import { SettingsContainer } from '@/modules/settings/layout/settings-container' - -import { FontSizeCards } from '../../../../modules/settings/cards/fontsize/fontsize-cards' - -export const metadata: Metadata = { - title: 'Tamanho da fonte', -} - -export default function Page() { - return ( - - - - ) -} diff --git a/src/app/(dashboard)/encaminhados/encaminhamentos/referrals-skeleton.tsx b/src/app/(dashboard)/encaminhados/encaminhamentos/referrals-skeleton.tsx deleted file mode 100644 index 348ad8b1..00000000 --- a/src/app/(dashboard)/encaminhados/encaminhamentos/referrals-skeleton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Skeleton } from '@/components/ui/skeleton' -import { TableBody, TableCell, TableRow } from '@/components/ui/table' - -export default function ReferralsSkeleton() { - const skeletons = Array.from({ length: 10 }).map((_, index) => index) - - return ( - - {skeletons.map((skeleton) => ( - - -
- -
-
- - - - - - - - - - - - - - - -
- ))} -
- ) -} diff --git a/src/app/(dashboard)/encaminhados/encaminhamentos/referrals-table.tsx b/src/app/(dashboard)/encaminhados/encaminhamentos/referrals-table.tsx deleted file mode 100644 index e647a61a..00000000 --- a/src/app/(dashboard)/encaminhados/encaminhamentos/referrals-table.tsx +++ /dev/null @@ -1,212 +0,0 @@ -'use client' -import { Eye } from 'lucide-react' -import { useState } from 'react' - -import { DataTableHeader } from '@/components/data-table/header' -import { DataTableHeaderActions } from '@/components/data-table/header/actions' -import { DataTableHeaderOrderBy } from '@/components/data-table/header/order-by' -import { DataTableHeaderSearch } from '@/components/data-table/header/search' -import { Pagination } from '@/components/pagination' -import { Card } from '@/components/ui/card' -import { TabSelect } from '@/components/ui/tab-select' -import { - Table, - TableBody, - TableButton, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table' -import { Tag } from '@/components/ui/tag' -import { - PATIENT_CONDITIONS, - PatientCondition, - PATIENTS_REFERRALS_ORDER_OPTIONS, -} from '@/types/patients' - -import ReferralsSkeleton from './referrals-skeleton' - -const referrals = [ - { - id: 1, - nome: 'Ana Silva', - encaminhadoEm: '03/03/2025', - profissional: 'João Pereira', - especialidade: 'Medicina', - quadroGeral: 'stable', - }, - { - id: 2, - nome: 'Carlos Mendes', - encaminhadoEm: '03/03/2025', - profissional: 'Mariana Costa', - especialidade: 'Psicologia', - quadroGeral: 'outbreak', - }, - { - id: 3, - nome: 'Beatriz Rocha', - encaminhadoEm: '03/03/2025', - profissional: 'Lucas Andrade', - especialidade: 'Nutrição', - quadroGeral: 'stable', - }, - { - id: 4, - nome: 'Rafael Gomes', - encaminhadoEm: '03/03/2025', - profissional: 'Fernanda Lima', - especialidade: 'Enfermagem', - quadroGeral: 'outbreak', - }, - { - id: 5, - nome: 'Juliana Santos', - encaminhadoEm: '03/03/2025', - profissional: 'Pedro Albuquerque', - especialidade: 'Advocacia', - quadroGeral: 'stable', - }, - { - id: 6, - nome: 'Marcos Oliveira', - encaminhadoEm: '03/03/2025', - profissional: 'Camila Duarte', - especialidade: 'Medicina', - quadroGeral: 'stable', - }, - { - id: 7, - nome: 'Paula Ferreira', - encaminhadoEm: '03/03/2025', - profissional: 'Ricardo Matos', - especialidade: 'Psicologia', - quadroGeral: 'outbreak', - }, - { - id: 8, - nome: 'Daniel Azevedo', - encaminhadoEm: '03/03/2025', - profissional: 'Larissa Pires', - especialidade: 'Nutrição', - quadroGeral: 'stable', - }, - { - id: 9, - nome: 'Gabriela Martins', - encaminhadoEm: '03/03/2025', - profissional: 'Diego Amaral', - especialidade: 'Enfermagem', - quadroGeral: 'outbreak', - }, - { - id: 10, - nome: 'Vitor Souza', - encaminhadoEm: '03/03/2025', - profissional: 'Letícia Moura', - especialidade: 'Psicologia', - quadroGeral: 'stable', - }, -] - -export function ReferralsTable() { - const [isLoading] = useState(false) - const [isReferralsEmpty] = useState(referrals.length <= 0) - const [statusFilter, setStatusFilter] = useState('all') - - const filterOptions = [ - { - label: 'Todos', - isActive: statusFilter === 'all', - onClick: () => setStatusFilter('all'), - }, - { - label: 'Em surto', - isActive: statusFilter === 'outbreak', - onClick: () => setStatusFilter('outbreak'), - }, - { - label: 'Estável', - isActive: statusFilter === 'stable', - onClick: () => setStatusFilter('stable'), - }, - ] - - return ( -
- - - - - - - - - - - - - Nome do paciente - Encaminhado em - Profissional - Especialidade - Quadro geral - Observações - - - - {isLoading && } - - {!isLoading && isReferralsEmpty && ( - - - -
- Nenhum paciente encontrado -
-
-
-
- )} - - {!isLoading && !isReferralsEmpty && ( - - {referrals?.map((forwarding) => { - const condition = - PATIENT_CONDITIONS[forwarding.quadroGeral as PatientCondition] - const ConditionIcon = condition.icon - return ( - - {forwarding.nome} - - {forwarding.encaminhadoEm} - {forwarding.profissional} - - - {forwarding.especialidade} - - - - - - {condition.label} - - - - - - - - - ) - })} - - )} -
-
- - -
- ) -} diff --git a/src/app/(dashboard)/encaminhados/layout.tsx b/src/app/(dashboard)/encaminhados/layout.tsx deleted file mode 100644 index 971fba1b..00000000 --- a/src/app/(dashboard)/encaminhados/layout.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { DashboardContainer } from '@/components/dashboard/container' -import { DashboardTabButtons } from '@/components/dashboard/tab-buttons' -import { ROUTES } from '@/constants/routes' - -export default function Layout({ - children, -}: Readonly<{ children: React.ReactNode }>) { - const tabButtons = [ - { - title: 'Dados gerais', - path: ROUTES.dashboard.referrals.main, - }, - { - title: 'Encaminhamentos', - path: ROUTES.dashboard.referrals.list, - }, - ] - - return ( - <> - - - - {children} - - - ) -} diff --git a/src/app/(dashboard)/encaminhados/page.tsx b/src/app/(dashboard)/encaminhados/page.tsx deleted file mode 100644 index 1cb2d050..00000000 --- a/src/app/(dashboard)/encaminhados/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { ReferralsByCategoryCard } from '@/modules/referrals/by-category-card' -import { ReferralsByCityCards } from '@/modules/referrals/by-city-cards' -import { ReferralsPeriodTab } from '@/modules/referrals/period-tab' -import { ReferralsSummaryCard } from '@/modules/referrals/summary-card' - -export default function Page() { - return ( -
- - - - - - -
- ) -} diff --git a/src/app/(dashboard)/encaminhados/encaminhamentos/page.tsx b/src/app/(dashboard)/encaminhamentos/listagem/page.tsx similarity index 62% rename from src/app/(dashboard)/encaminhados/encaminhamentos/page.tsx rename to src/app/(dashboard)/encaminhamentos/listagem/page.tsx index ec02309c..a1d1b7fc 100644 --- a/src/app/(dashboard)/encaminhados/encaminhamentos/page.tsx +++ b/src/app/(dashboard)/encaminhamentos/listagem/page.tsx @@ -1,11 +1,11 @@ import type { Metadata } from 'next' -import { ReferralsTable } from './referrals-table' +import { ReferralsList } from '@/modules/referrals/list' export const metadata: Metadata = { title: 'Encaminhamentos', } export default function Page() { - return + return } diff --git a/src/app/(dashboard)/encaminhamentos/page.tsx b/src/app/(dashboard)/encaminhamentos/page.tsx new file mode 100644 index 00000000..4839e3a8 --- /dev/null +++ b/src/app/(dashboard)/encaminhamentos/page.tsx @@ -0,0 +1,21 @@ +import { PatientsWithReferralsPercentageCard } from '@/modules/referrals/patients-with-referrals-percentage-card' +import { ReferralsPeriodTabSelect } from '@/modules/referrals/period-tab-select' +import { TotalReferralsByCategoryCard } from '@/modules/referrals/total-by-category-card' +import { TotalReferralsByStateCard } from '@/modules/referrals/total-by-state-card' +import { TotalReferralsCard } from '@/modules/referrals/total-referrals-card' + +export const dynamic = 'force-dynamic' + +export default function Page() { + return ( +
+ + + + + + + +
+ ) +} diff --git a/src/app/(dashboard)/equipe/convites/page.tsx b/src/app/(dashboard)/equipe/convites/page.tsx new file mode 100644 index 00000000..7dfb5b91 --- /dev/null +++ b/src/app/(dashboard)/equipe/convites/page.tsx @@ -0,0 +1,11 @@ +import type { Metadata } from 'next' + +import { UserInvitesList } from '@/modules/users/invites/list' + +export const metadata: Metadata = { + title: 'Convites', +} + +export default function Page() { + return +} diff --git a/src/app/(dashboard)/equipes/page.tsx b/src/app/(dashboard)/equipe/page.tsx similarity index 61% rename from src/app/(dashboard)/equipes/page.tsx rename to src/app/(dashboard)/equipe/page.tsx index 3491eecc..cb8a7896 100644 --- a/src/app/(dashboard)/equipes/page.tsx +++ b/src/app/(dashboard)/equipe/page.tsx @@ -1,11 +1,11 @@ import type { Metadata } from 'next' -import { TeamListTable } from '@/modules/teams/table' +import { UsersList } from '@/modules/users/list' export const metadata: Metadata = { title: 'Equipe', } export default function Page() { - return + return } diff --git a/src/app/(dashboard)/equipes/especialistas/page.tsx b/src/app/(dashboard)/equipes/especialistas/page.tsx deleted file mode 100644 index ce1bad3a..00000000 --- a/src/app/(dashboard)/equipes/especialistas/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { Metadata } from 'next' - -export const metadata: Metadata = { - title: 'Especialistas', -} - -export default function Page() { - return

Página de Especialistas

-} diff --git a/src/app/(dashboard)/equipes/layout.tsx b/src/app/(dashboard)/equipes/layout.tsx deleted file mode 100644 index 526c1949..00000000 --- a/src/app/(dashboard)/equipes/layout.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { DashboardContainer } from '@/components/dashboard/container' -import { DashboardTabButtons } from '@/components/dashboard/tab-buttons' -import { ROUTES } from '@/constants/routes' - -export default function Layout({ - children, -}: Readonly<{ children: React.ReactNode }>) { - const tabButtons = [ - { - title: 'Todos', - path: ROUTES.dashboard.teams.main, - }, - { - title: 'Especialistas', - path: ROUTES.dashboard.teams.specialists, - }, - ] - - return ( - <> - - - {children} - - - ) -} diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx index 82ed90c5..b507ec3f 100644 --- a/src/app/(dashboard)/layout.tsx +++ b/src/app/(dashboard)/layout.tsx @@ -1,11 +1,12 @@ import { redirect } from 'next/navigation' import { canUser } from '@/actions/auth' -import { DashboardSidebar } from '@/app/(dashboard)/_sidebar' +import { DashboardHeader } from '@/components/dashboard/header' +import { DashboardTabButtons } from '@/components/dashboard/tab-buttons' +import { DashboardWrapper } from '@/components/dashboard/wrapper' +import { DashboardSidebar } from '@/components/sidebar' import { ROUTES } from '@/constants/routes' -import { DashboardHeader } from './_header' - export default async function Layout({ children, }: Readonly<{ children: React.ReactNode }>) { @@ -16,13 +17,17 @@ export default async function Layout({ } return ( -
+ <> -
+ - {children} -
-
+ + +
+ {children} +
+ + ) } diff --git a/src/app/(dashboard)/pacientes/[id]/documentos/page.tsx b/src/app/(dashboard)/pacientes/[id]/_documentos/page.tsx similarity index 100% rename from src/app/(dashboard)/pacientes/[id]/documentos/page.tsx rename to src/app/(dashboard)/pacientes/[id]/_documentos/page.tsx diff --git a/src/app/(dashboard)/pacientes/[id]/atendimentos/page.tsx b/src/app/(dashboard)/pacientes/[id]/atendimentos/page.tsx new file mode 100644 index 00000000..38b69806 --- /dev/null +++ b/src/app/(dashboard)/pacientes/[id]/atendimentos/page.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from 'next' + +import { PatientAppointmentsList } from '@/modules/patients/appointments-list' + +export const metadata: Metadata = { + title: 'Atendimentos', +} + +interface PageParams { + params: Promise<{ id: string }> +} + +export default async function Page({ params }: Readonly) { + const patientId = (await params).id + + return +} diff --git a/src/app/(dashboard)/pacientes/[id]/encaminhamentos/page.tsx b/src/app/(dashboard)/pacientes/[id]/encaminhamentos/page.tsx new file mode 100644 index 00000000..43e138b6 --- /dev/null +++ b/src/app/(dashboard)/pacientes/[id]/encaminhamentos/page.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from 'next' + +import { PatientReferralsList } from '@/modules/patients/referrals-list' + +export const metadata: Metadata = { + title: 'Encaminhamentos', +} + +interface PageParams { + params: Promise<{ id: string }> +} + +export default async function Page({ params }: Readonly) { + const patientId = (await params).id + + return +} diff --git a/src/app/(dashboard)/pacientes/[id]/historico/loading.tsx b/src/app/(dashboard)/pacientes/[id]/historico/loading.tsx new file mode 100644 index 00000000..07b3e8f7 --- /dev/null +++ b/src/app/(dashboard)/pacientes/[id]/historico/loading.tsx @@ -0,0 +1,34 @@ +import { + ChartBarDecreasingIcon, + ClipboardCheckIcon, + ClipboardPasteIcon, +} from 'lucide-react' + +import { DashboardCardChart } from '@/components/dashboard/cards/chart' +import { SummaryCard } from '@/components/summary-card' +import { Skeleton } from '@/components/ui/skeleton' + +export default function Loading() { + return ( +
+ + + +
+ +
+
+ +
+ +
+
+
+ ) +} diff --git a/src/app/(dashboard)/pacientes/[id]/historico/page.tsx b/src/app/(dashboard)/pacientes/[id]/historico/page.tsx index b1c163dc..3beea3a6 100644 --- a/src/app/(dashboard)/pacientes/[id]/historico/page.tsx +++ b/src/app/(dashboard)/pacientes/[id]/historico/page.tsx @@ -1,7 +1,18 @@ +import { + ChartBarDecreasingIcon, + ClipboardCheckIcon, + ClipboardPasteIcon, +} from 'lucide-react' import type { Metadata } from 'next' -import { redirect } from 'next/navigation' -import { ROUTES } from '@/constants/routes' +import { getTotalAppointments } from '@/actions/statistics/get-total-appointments' +import { getTotalAppointmentsByCategory } from '@/actions/statistics/get-total-appointments-by-category' +import { getTotalReferrals } from '@/actions/statistics/get-total-referrals' +import { getTotalReferralsByCategory } from '@/actions/statistics/get-total-referrals-by-category' +import { BarChart } from '@/components/charts/bar' +import { DashboardCardChart } from '@/components/dashboard/cards/chart' +import { SummaryCard } from '@/components/summary-card' +import { SPECIALTIES } from '@/enums/shared' export const metadata: Metadata = { title: 'Histórico', @@ -16,9 +27,82 @@ export default async function Page({ }: Readonly) { const patientId = (await params).id - if (!patientId) { - redirect(ROUTES.dashboard.patients.main) - } + const actionParams = { params: { patientId } } - return
Histórico do paciente {patientId}
+ const [ + totalAppointments, + totalAppointmentsByCategory, + totalReferrals, + totalReferralsByCategory, + ] = await Promise.all([ + getTotalAppointments(actionParams), + getTotalAppointmentsByCategory(actionParams), + getTotalReferrals(actionParams), + getTotalReferralsByCategory(actionParams), + ]) + + const totalAppointmentCategories = + totalAppointmentsByCategory?.categories ?? [] + const totalAppointmentsByCategoryValues = totalAppointmentCategories.map( + (item) => ({ + label: SPECIALTIES[item.category], + value: Number(item.total), + }), + ) + + const totalReferralCategories = totalReferralsByCategory?.categories ?? [] + const totalReferralsByCategoryValues = totalReferralCategories.map( + (item) => ({ + label: SPECIALTIES[item.category], + value: Number(item.total), + }), + ) + + return ( +
+ + + + +
+ {totalAppointmentsByCategoryValues.length > 0 ? ( +
+ +
+ ) : ( +

+ Nenhum atendimento registrado. +

+ )} +
+
+ +
+ {totalReferralsByCategoryValues.length > 0 ? ( +
+ +
+ ) : ( +

+ Nenhum atendimento registrado. +

+ )} +
+
+
+ ) } diff --git a/src/app/(dashboard)/pacientes/[id]/informacoes/loading.tsx b/src/app/(dashboard)/pacientes/[id]/informacoes/loading.tsx new file mode 100644 index 00000000..3dd0f40e --- /dev/null +++ b/src/app/(dashboard)/pacientes/[id]/informacoes/loading.tsx @@ -0,0 +1,5 @@ +import { PageLoader } from '@/components/ui/page-loader' + +export default function Loading() { + return +} diff --git a/src/app/(dashboard)/pacientes/[id]/informacoes/page.tsx b/src/app/(dashboard)/pacientes/[id]/informacoes/page.tsx index 558aabe1..c7166177 100644 --- a/src/app/(dashboard)/pacientes/[id]/informacoes/page.tsx +++ b/src/app/(dashboard)/pacientes/[id]/informacoes/page.tsx @@ -1,55 +1,91 @@ -import { UserRoundIcon } from 'lucide-react' +import { User2Icon } from 'lucide-react' import type { Metadata } from 'next' import { redirect } from 'next/navigation' import { getPatient } from '@/actions/patients/get-patient' +import { + SectionHeader, + SectionHeaderActions, + SectionHeaderTitle, +} from '@/components/section-header' +import { Card } from '@/components/ui/card' +import { Divider } from '@/components/ui/divider' import { ROUTES } from '@/constants/routes' +import { NewAppointmentButton } from '@/modules/appointments/new-appointment-button' +import { NewPatientSupportButton } from '@/modules/patient-supports/new-patient-support-button' +import { PatientSupportCardActions } from '@/modules/patient-supports/patient-support-card-actions' +import { DeactivatePatientButton } from '@/modules/patients/deactivate-button' import { PatientForm } from '@/modules/patients/form' -import { InactivatePatientButton } from '@/modules/patients/inactivate-button' -import { ReferPatientButton } from '@/modules/referrals/referral-button' +import { NewReferralButton } from '@/modules/referrals/new-referral-button' +import { formatPhoneNumber } from '@/utils/formatters/format-phone-number' export const metadata: Metadata = { title: 'Informações do paciente', } -interface PatientHistoryPageParams { +interface PageParams { params: Promise<{ id: string }> } -export default async function Page({ - params, -}: Readonly) { +export default async function Page({ params }: Readonly) { const patientId = (await params).id - if (!patientId) { - redirect(ROUTES.dashboard.patients.main) - } - const patient = await getPatient(patientId) if (!patient) { - return

Paciente não encontrado

+ redirect(ROUTES.dashboard.patients.main) } + const patientSupports = patient.supports ?? [] const isPatientActive = patient.status === 'active' return ( <> -
-
- - - -

Informações do paciente

-
- -
- {isPatientActive && } - -
-
+ + } + /> + + {isPatientActive && ( + + + + + + )} + + +
+

Rede de apoio

+ {patientSupports.length > 0 ? ( +
+ {patientSupports.map((support) => ( + +

{support.name}

+
+ + {support.kinship} + + + {formatPhoneNumber(support.phone)} +
+ {isPatientActive && ( + + )} +
+ ))} +
+ ) : ( +

+ Nenhum contato de apoio cadastrado para este paciente. +

+ )} + + {isPatientActive && } +
) } diff --git a/src/app/(dashboard)/pacientes/[id]/layout.tsx b/src/app/(dashboard)/pacientes/[id]/layout.tsx deleted file mode 100644 index f1df025b..00000000 --- a/src/app/(dashboard)/pacientes/[id]/layout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { DashboardContainer } from '@/components/dashboard/container' -import { DashboardTabButtons } from '@/components/dashboard/tab-buttons' -import { ROUTES } from '@/constants/routes' - -interface LayoutProps { - params: Promise<{ id: string }> - children: React.ReactNode -} - -export default async function Layout({ - params, - children, -}: Readonly) { - const patientId = (await params).id - - const tabButtons = [ - { - title: 'Informações do paciente', - path: ROUTES.dashboard.patients.details.info(patientId), - }, - { - title: 'Histórico do paciente', - path: ROUTES.dashboard.patients.details.history(patientId), - }, - { - title: 'Documentos', - path: ROUTES.dashboard.patients.details.documents(patientId), - }, - ] - - return ( - <> - - - {children} - - - ) -} diff --git a/src/app/(dashboard)/pacientes/cadastrar/page.tsx b/src/app/(dashboard)/pacientes/cadastrar/page.tsx index 3b9124c6..9ff4ee47 100644 --- a/src/app/(dashboard)/pacientes/cadastrar/page.tsx +++ b/src/app/(dashboard)/pacientes/cadastrar/page.tsx @@ -1,25 +1,24 @@ -import { UserRoundPlusIcon } from 'lucide-react' +import { UserPlus2Icon } from 'lucide-react' import type { Metadata } from 'next' -import { DashboardContainer } from '@/components/dashboard/container' +import { SectionHeader, SectionHeaderTitle } from '@/components/section-header' import { PatientForm } from '@/modules/patients/form' export const metadata: Metadata = { - title: 'Cadastrar novo paciente', + title: 'Cadastrar paciente', } export default function Page() { return ( - -
- - - - -

Cadastrar novo paciente

-
+ <> + + } + /> + -
+ ) } diff --git a/src/app/(dashboard)/pacientes/page.tsx b/src/app/(dashboard)/pacientes/page.tsx index db02417c..9e7f2b3d 100644 --- a/src/app/(dashboard)/pacientes/page.tsx +++ b/src/app/(dashboard)/pacientes/page.tsx @@ -1,19 +1,11 @@ import type { Metadata } from 'next' -import { Suspense } from 'react' -import { DashboardContainer } from '@/components/dashboard/container' -import { PatientsListTable } from '@/modules/patients/list/table' +import { PatientsList } from '@/modules/patients/patients-list' export const metadata: Metadata = { title: 'Pacientes', } export default function Page() { - return ( - - Carregando...}> - - - - ) + return } diff --git a/src/app/(dashboard)/page.tsx b/src/app/(dashboard)/page.tsx index 3bb85f53..771e0a91 100644 --- a/src/app/(dashboard)/page.tsx +++ b/src/app/(dashboard)/page.tsx @@ -1,31 +1,28 @@ import { Suspense } from 'react' -import { DashboardContainer } from '@/components/dashboard/container' import { Skeleton } from '@/components/ui/skeleton' -import AppointmentsCard from '@/modules/dashboard/appointments-card' -import { PatientsByCityCard } from '@/modules/dashboard/patients-by-city-card' -import { PatientsByGenderCard } from '@/modules/dashboard/patients-by-gender-card' -import { PatientsByStatusCards } from '@/modules/dashboard/patients-by-status-cards' +import { DashboardPatientsByCityCard } from '@/modules/dashboard/patients-by-city-card' +import { DashboardPatientsByGenderCard } from '@/modules/dashboard/patients-by-gender-card' +import { DashboardSummaryCards } from '@/modules/dashboard/summary-cards' +import { DashboardUpcomingAppointmentsCard } from '@/modules/dashboard/upcoming-appointments-card' export const dynamic = 'force-dynamic' export default function Page() { return ( - -
- - } - > - - +
+ + } + > + + - - + + - -
- + +
) } diff --git a/src/app/(dashboard)/perfil/loading.tsx b/src/app/(dashboard)/perfil/loading.tsx new file mode 100644 index 00000000..d03d7a42 --- /dev/null +++ b/src/app/(dashboard)/perfil/loading.tsx @@ -0,0 +1,5 @@ +import { PageLoader } from '@/components/ui/page-loader' + +export default function Loading() { + return +} diff --git a/src/app/(dashboard)/perfil/page.tsx b/src/app/(dashboard)/perfil/page.tsx index 5d9d0513..2b8cfd35 100644 --- a/src/app/(dashboard)/perfil/page.tsx +++ b/src/app/(dashboard)/perfil/page.tsx @@ -1,156 +1,47 @@ -'use client' +import { redirect } from 'next/navigation' -import { ArrowLeftIcon, Loader2 } from 'lucide-react' -import { useEffect, useState } from 'react' - -import { getCurrentUser } from '@/actions/users' -import { DashboardContainer } from '@/components/dashboard/container' +import { getCurrentUser } from '@/actions/users/get-current-user' import { Avatar } from '@/components/ui/avatar' -import { Button } from '@/components/ui/button' -import { DatePicker } from '@/components/ui/date-picker' import { Divider } from '@/components/ui/divider' -import { Input } from '@/components/ui/input' -import { NavButton } from '@/components/ui/nav-button' -import { ChangePasswordButton } from '@/modules/profile/change-password-button' -import { ProfileForm } from '@/modules/profile/profile-form' - -interface UserProfile { - category?: string - register?: string - avatar_url?: string | null | undefined - entryDate?: string | null | undefined - role: 'specialist' -} - -export default function Page() { - const [user, setUser] = useState(null) - const [loading, setLoading] = useState(true) - - useEffect(() => { - async function fetchUser() { - try { - const data = await getCurrentUser() +import { ROUTES } from '@/constants/routes' +import { ChangeUserPasswordButton } from '@/modules/profile/change-password-button' +import { UserProfileForm } from '@/modules/profile/profile-form' +import { formatDate } from '@/utils/formatters/format-date' - if (!data) { - setUser(null) - return - } - - const formattedUser: UserProfile = { - ...data, - role: 'specialist', - entryDate: data.created_at - ? new Date(data.created_at).toLocaleDateString('pt-BR') - : undefined, - category: (data as UserProfile).category ?? '', - register: (data as UserProfile).register ?? '', - avatar_url: (data as UserProfile).avatar_url ?? '', - } - - setUser(formattedUser) - } catch (error) { - console.error('Erro ao carregar dados do usuário:', error) - } finally { - setLoading(false) - } - } - - fetchUser() - }, []) - - if (loading) { - return ( -
- -
- ) - } +export default async function Page() { + const user = await getCurrentUser() if (!user) { - return ( -
- Não foi possível carregar suas informações. -
- ) + redirect(ROUTES.auth.signOut) } - const isEspecialist = user.role?.toLowerCase() === 'especialist' + const showUpdatedDate = new Date(user.updated_at) >= new Date(user.created_at) return ( -
- - - - Voltar - -
-
- - -
-
- Imagem - - Min 400x400px, PNG ou JPEG - -
-
-
- - -
+ <> +
+ +
+

{user.name}

+ {user.email} +
+
-
- - -
+ - {isEspecialist && ( - <> -
- - -
+ -
- - -
- - )} + -
- -
-
-
- - - -
-
+ + + + + + Conta registrada em {formatDate(user.created_at)}. + {showUpdatedDate && + ` Última atualização realizada em ${formatDate(user.updated_at)}.`} + + ) } diff --git a/src/app/(dashboard)/suporte/page.tsx b/src/app/(dashboard)/suporte/page.tsx deleted file mode 100644 index 8483aa01..00000000 --- a/src/app/(dashboard)/suporte/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { Metadata } from 'next' - -export const metadata: Metadata = { - title: 'Suporte', -} - -import { DashboardContainer } from '@/components/dashboard/container' - -export default function Page() { - return ( - - Suporte - - ) -} diff --git a/src/app/paciente/_action-help/index.tsx b/src/app/_paciente/_action-help/index.tsx similarity index 100% rename from src/app/paciente/_action-help/index.tsx rename to src/app/_paciente/_action-help/index.tsx diff --git a/src/app/paciente/_components/documents-tab.tsx b/src/app/_paciente/_components/documents-tab.tsx similarity index 100% rename from src/app/paciente/_components/documents-tab.tsx rename to src/app/_paciente/_components/documents-tab.tsx diff --git a/src/app/paciente/_header/accessibility-dropdown.tsx b/src/app/_paciente/_header/accessibility-dropdown.tsx similarity index 100% rename from src/app/paciente/_header/accessibility-dropdown.tsx rename to src/app/_paciente/_header/accessibility-dropdown.tsx diff --git a/src/app/paciente/_header/index.tsx b/src/app/_paciente/_header/index.tsx similarity index 77% rename from src/app/paciente/_header/index.tsx rename to src/app/_paciente/_header/index.tsx index 9ffd7333..fc270247 100644 --- a/src/app/paciente/_header/index.tsx +++ b/src/app/_paciente/_header/index.tsx @@ -1,13 +1,13 @@ -import logo from '@images/brand/icon.svg' import { SettingsIcon } from 'lucide-react' import Image from 'next/image' import Link from 'next/link' -import { getCurrentUser } from '@/actions/users' -import { PatientHeaderAccessibilityDropdown } from '@/app/paciente/_header/accessibility-dropdown' -import { PatientHeaderUserDropdown } from '@/app/paciente/_header/user-dropdown' +import { getCurrentUser } from '@/actions/users/get-current-user' +import { PatientHeaderAccessibilityDropdown } from '@/app/_paciente/_header/accessibility-dropdown' +import { PatientHeaderUserDropdown } from '@/app/_paciente/_header/user-dropdown' import { Breadcrumbs } from '@/components/breadcrumbs' import { Button } from '@/components/ui/button' +import { IMAGES } from '@/constants/images' import { ROUTES } from '@/constants/routes' import { PatientDocumentsTab } from '../_components/documents-tab' @@ -24,7 +24,7 @@ export async function PatientHeader() { href={ROUTES.patient.main} className='focus-visible:ring-ring rounded-full focus-visible:ring-2 focus-visible:ring-offset-4 focus-visible:outline-none' > - SVM + SVM diff --git a/src/app/paciente/_header/user-dropdown.tsx b/src/app/_paciente/_header/user-dropdown.tsx similarity index 89% rename from src/app/paciente/_header/user-dropdown.tsx rename to src/app/_paciente/_header/user-dropdown.tsx index d288ebb3..83a236dd 100644 --- a/src/app/paciente/_header/user-dropdown.tsx +++ b/src/app/_paciente/_header/user-dropdown.tsx @@ -5,8 +5,7 @@ import { useRouter } from 'next/navigation' import { useTransition } from 'react' import { toast } from 'sonner' -import { revalidateCache } from '@/actions/cache' -import { getUserFromToken } from '@/actions/token' +import { getUserFromToken } from '@/actions/users/get-user-from-token' import { Avatar } from '@/components/ui/avatar' import { Divider } from '@/components/ui/divider' import { DropdownMenu } from '@/components/ui/dropdown' @@ -15,8 +14,9 @@ import { DropdownMenuItem } from '@/components/ui/dropdown/item' import { DropdownMenuTrigger } from '@/components/ui/dropdown/trigger' import { NEXT_CACHE_TAGS } from '@/constants/cache' import { ROUTES } from '@/constants/routes' +import { revalidateServerCache } from '@/helpers/revalidate-server-cache' import { api } from '@/lib/api' -import type { User } from '@/types/users' +import type { User } from '@/types/users.d.ts' interface PatientHeaderUserDropdownProps { user: User @@ -43,7 +43,7 @@ export function PatientHeaderUserDropdown({ return } - revalidateCache(NEXT_CACHE_TAGS.user(user.id)) + revalidateServerCache(NEXT_CACHE_TAGS.user(user.id)) toast.success(response.message) router.replace(ROUTES.auth.signIn) }) diff --git a/src/app/paciente/documentos/enviados/page.tsx b/src/app/_paciente/documentos/enviados/page.tsx similarity index 100% rename from src/app/paciente/documentos/enviados/page.tsx rename to src/app/_paciente/documentos/enviados/page.tsx diff --git a/src/app/paciente/documentos/enviados/table.tsx b/src/app/_paciente/documentos/enviados/table.tsx similarity index 100% rename from src/app/paciente/documentos/enviados/table.tsx rename to src/app/_paciente/documentos/enviados/table.tsx diff --git a/src/app/paciente/documentos/layout.tsx b/src/app/_paciente/documentos/layout.tsx similarity index 100% rename from src/app/paciente/documentos/layout.tsx rename to src/app/_paciente/documentos/layout.tsx diff --git a/src/app/paciente/documentos/pendentes/page.tsx b/src/app/_paciente/documentos/pendentes/page.tsx similarity index 95% rename from src/app/paciente/documentos/pendentes/page.tsx rename to src/app/_paciente/documentos/pendentes/page.tsx index 1ed59ab7..4b4af455 100644 --- a/src/app/paciente/documentos/pendentes/page.tsx +++ b/src/app/_paciente/documentos/pendentes/page.tsx @@ -1,9 +1,9 @@ import { Metadata } from 'next' import React from 'react' -import { getCurrentUser } from '@/actions/users' +import { getCurrentUser } from '@/actions/users/get-current-user' import { PatientRequirementCard } from '@/modules/patients/cards/patient-requirement-card' -import type { PatientRequirement } from '@/types/patient-requirements' +import type { PatientRequirement } from '@/types/patient-requirements.d.ts' export const metadata: Metadata = { title: 'Pendentes', diff --git a/src/app/paciente/layout.tsx b/src/app/_paciente/layout.tsx similarity index 90% rename from src/app/paciente/layout.tsx rename to src/app/_paciente/layout.tsx index d9798bbc..5b4aba6a 100644 --- a/src/app/paciente/layout.tsx +++ b/src/app/_paciente/layout.tsx @@ -1,7 +1,7 @@ import { redirect } from 'next/navigation' import { canUser } from '@/actions/auth' -import { PatientHeader } from '@/app/paciente/_header' +import { PatientHeader } from '@/app/_paciente/_header' import { Divider } from '@/components/ui/divider' import { ROUTES } from '@/constants/routes' diff --git a/src/app/paciente/page.tsx b/src/app/_paciente/page.tsx similarity index 100% rename from src/app/paciente/page.tsx rename to src/app/_paciente/page.tsx diff --git a/src/app/paciente/triagem/hooks.ts b/src/app/_paciente/triagem/hooks.ts similarity index 100% rename from src/app/paciente/triagem/hooks.ts rename to src/app/_paciente/triagem/hooks.ts diff --git a/src/app/paciente/triagem/laudo-medico/medical-report-form-schema.tsx b/src/app/_paciente/triagem/laudo-medico/medical-report-form-schema.tsx similarity index 100% rename from src/app/paciente/triagem/laudo-medico/medical-report-form-schema.tsx rename to src/app/_paciente/triagem/laudo-medico/medical-report-form-schema.tsx diff --git a/src/app/paciente/triagem/laudo-medico/medical-report-form.tsx b/src/app/_paciente/triagem/laudo-medico/medical-report-form.tsx similarity index 91% rename from src/app/paciente/triagem/laudo-medico/medical-report-form.tsx rename to src/app/_paciente/triagem/laudo-medico/medical-report-form.tsx index d5a95d57..bf89b75f 100644 --- a/src/app/paciente/triagem/laudo-medico/medical-report-form.tsx +++ b/src/app/_paciente/triagem/laudo-medico/medical-report-form.tsx @@ -10,10 +10,9 @@ import { FormField } from '@/components/form/form-field' import { RadioInput } from '@/components/form/radio-input' import { TextInput } from '@/components/form/text-input' import { Button } from '@/components/ui/button' -import { YES_OR_NO } from '@/constants/enums' import { ROUTES } from '@/constants/routes' import { PATIENT_STORAGE_KEYS } from '@/constants/storage-keys' -import { convertObjectToOptions } from '@/helpers/convert-object-to-options' +import { YES_OR_NO_OPTIONS } from '@/enums/shared' import { useScreening } from '../hooks' import { @@ -36,7 +35,6 @@ export function ScreeningMedicalReportForm() { const { setValue, watch } = formMethods const hasDisability = watch('has_disability') === 'yes' const takeMedication = watch('take_medication') === 'yes' - const yesOrNoOptions = convertObjectToOptions(YES_OR_NO) useEffect(() => { const savedFormData = getStoredFormData(screeningMedicalReportFormSchema) @@ -65,7 +63,7 @@ export function ScreeningMedicalReportForm() { name='has_disability' label='Você possui alguma deficiência?' isRequired - options={yesOrNoOptions} + options={YES_OR_NO_OPTIONS} /> {hasDisability && ( @@ -87,7 +85,7 @@ export function ScreeningMedicalReportForm() { name='take_medication' label='Faz uso de medicamentos?' isRequired - options={yesOrNoOptions} + options={YES_OR_NO_OPTIONS} /> {takeMedication && ( @@ -98,7 +96,7 @@ export function ScreeningMedicalReportForm() { name='has_nmo_diagnosis' label='Você possui um Diagnóstico de NMO?' isRequired - options={yesOrNoOptions} + options={YES_OR_NO_OPTIONS} />
diff --git a/src/app/paciente/triagem/laudo-medico/page.tsx b/src/app/_paciente/triagem/laudo-medico/page.tsx similarity index 100% rename from src/app/paciente/triagem/laudo-medico/page.tsx rename to src/app/_paciente/triagem/laudo-medico/page.tsx diff --git a/src/app/paciente/triagem/layout.tsx b/src/app/_paciente/triagem/layout.tsx similarity index 100% rename from src/app/paciente/triagem/layout.tsx rename to src/app/_paciente/triagem/layout.tsx diff --git a/src/app/paciente/triagem/progress.tsx b/src/app/_paciente/triagem/progress.tsx similarity index 100% rename from src/app/paciente/triagem/progress.tsx rename to src/app/_paciente/triagem/progress.tsx diff --git a/src/app/paciente/triagem/rede-de-apoio/page.tsx b/src/app/_paciente/triagem/rede-de-apoio/page.tsx similarity index 100% rename from src/app/paciente/triagem/rede-de-apoio/page.tsx rename to src/app/_paciente/triagem/rede-de-apoio/page.tsx diff --git a/src/app/paciente/triagem/rede-de-apoio/support-network-cards.tsx b/src/app/_paciente/triagem/rede-de-apoio/support-network-cards.tsx similarity index 91% rename from src/app/paciente/triagem/rede-de-apoio/support-network-cards.tsx rename to src/app/_paciente/triagem/rede-de-apoio/support-network-cards.tsx index d032a8e2..f3332a1a 100644 --- a/src/app/paciente/triagem/rede-de-apoio/support-network-cards.tsx +++ b/src/app/_paciente/triagem/rede-de-apoio/support-network-cards.tsx @@ -1,6 +1,6 @@ import { Trash2Icon } from 'lucide-react' -import type { ScreeningSupportNetworkFormSchema } from '@/app/paciente/triagem/rede-de-apoio/support-network-form-schema' +import type { ScreeningSupportNetworkFormSchema } from '@/app/_paciente/triagem/rede-de-apoio/support-network-form-schema' import { Avatar } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' diff --git a/src/app/paciente/triagem/rede-de-apoio/support-network-form-schema.ts b/src/app/_paciente/triagem/rede-de-apoio/support-network-form-schema.ts similarity index 100% rename from src/app/paciente/triagem/rede-de-apoio/support-network-form-schema.ts rename to src/app/_paciente/triagem/rede-de-apoio/support-network-form-schema.ts diff --git a/src/app/paciente/triagem/rede-de-apoio/support-network-form.tsx b/src/app/_paciente/triagem/rede-de-apoio/support-network-form.tsx similarity index 97% rename from src/app/paciente/triagem/rede-de-apoio/support-network-form.tsx rename to src/app/_paciente/triagem/rede-de-apoio/support-network-form.tsx index 18b135ad..6e98f166 100644 --- a/src/app/paciente/triagem/rede-de-apoio/support-network-form.tsx +++ b/src/app/_paciente/triagem/rede-de-apoio/support-network-form.tsx @@ -7,7 +7,7 @@ import { useEffect, useState, useTransition } from 'react' import { FormProvider, useForm } from 'react-hook-form' import { toast } from 'sonner' -import { ScreeningSupportNetworkCards } from '@/app/paciente/triagem/rede-de-apoio/support-network-cards' +import { ScreeningSupportNetworkCards } from '@/app/_paciente/triagem/rede-de-apoio/support-network-cards' import { FormContainer } from '@/components/form/form-container' import { TextInput } from '@/components/form/text-input' import { Button } from '@/components/ui/button' diff --git a/src/app/paciente/triagem/seus-dados/page.tsx b/src/app/_paciente/triagem/seus-dados/page.tsx similarity index 100% rename from src/app/paciente/triagem/seus-dados/page.tsx rename to src/app/_paciente/triagem/seus-dados/page.tsx diff --git a/src/app/paciente/triagem/seus-dados/patient-data-form-schema.tsx b/src/app/_paciente/triagem/seus-dados/patient-data-form-schema.tsx similarity index 100% rename from src/app/paciente/triagem/seus-dados/patient-data-form-schema.tsx rename to src/app/_paciente/triagem/seus-dados/patient-data-form-schema.tsx diff --git a/src/app/paciente/triagem/seus-dados/patient-data-form.tsx b/src/app/_paciente/triagem/seus-dados/patient-data-form.tsx similarity index 91% rename from src/app/paciente/triagem/seus-dados/patient-data-form.tsx rename to src/app/_paciente/triagem/seus-dados/patient-data-form.tsx index c29f5384..cba30295 100644 --- a/src/app/paciente/triagem/seus-dados/patient-data-form.tsx +++ b/src/app/_paciente/triagem/seus-dados/patient-data-form.tsx @@ -10,11 +10,11 @@ import { FormContainer } from '@/components/form/form-container' import { SelectInput } from '@/components/form/select-input' import { TextInput } from '@/components/form/text-input' import { Button } from '@/components/ui/button' -import { BRAZILIAN_STATES_OPTIONS, type UF } from '@/constants/enums' import { ROUTES } from '@/constants/routes' import { PATIENT_STORAGE_KEYS } from '@/constants/storage-keys' +import { PATIENT_GENDER_OPTIONS } from '@/enums/patients' +import { BRAZILIAN_STATES_OPTIONS, type UF } from '@/enums/shared' import { useCities } from '@/hooks/cities' -import { GENDER_OPTIONS } from '@/types/patients' import { useScreening } from '../hooks' import { @@ -37,14 +37,15 @@ export function ScreeningPatientDataForm() { state: '', phone: '', cpf: '', - }, + } as unknown as ScreeningPatientDataFormSchema, mode: 'onBlur', }) const { clearErrors, setValue, watch, reset } = formMethods + // eslint-disable-next-line react-hooks/incompatible-library const UF = watch('state') as UF const cities = useCities(UF) - function handleSelectState(value: string) { + function handleSelectState(value: UF) { setValue('state', value) setValue('city', '') clearErrors('state') @@ -82,7 +83,7 @@ export function ScreeningPatientDataForm() { diff --git a/src/app/api/sair/route.ts b/src/app/api/sair/route.ts index 05806e49..aaaf83f2 100644 --- a/src/app/api/sair/route.ts +++ b/src/app/api/sair/route.ts @@ -1,14 +1,15 @@ import { cookies } from 'next/headers' import { type NextRequest, NextResponse } from 'next/server' +import { COOKIES } from '@/constants/cookies' import { ROUTES } from '@/constants/routes' -export async function GET(request: NextRequest) { +export async function POST(request: NextRequest) { const redirectUrl = request.nextUrl.clone() redirectUrl.pathname = ROUTES.auth.signIn const cookieStore = await cookies() - cookieStore.delete('access_token') + cookieStore.delete(COOKIES.accessToken) return NextResponse.redirect(redirectUrl) } diff --git a/src/app/conta/cadastrar/page.tsx b/src/app/conta/cadastrar/page.tsx index 22db6fd1..14fe072a 100644 --- a/src/app/conta/cadastrar/page.tsx +++ b/src/app/conta/cadastrar/page.tsx @@ -1,27 +1,60 @@ -import image from '@images/brand/icon.svg' import type { Metadata } from 'next' +import { redirect } from 'next/navigation' import { AuthCard } from '@/components/auth/auth-card' import { Divider } from '@/components/ui/divider' import { NavLink } from '@/components/ui/nav-link' import { ROUTES } from '@/constants/routes' - -import { SignUpForm } from './sign-up-form' +import type { UserRole } from '@/enums/users' +import { extractTokenData } from '@/helpers/extract-token-data' +import { SignUpForm } from '@/modules/auth/sign-up-form' export const metadata: Metadata = { title: 'Cadastrar conta', } -export default function Page() { +interface PageProps { + searchParams: Promise<{ token?: string }> +} + +export default async function Page({ searchParams }: Readonly) { + const { token } = await searchParams + + if (!token) { + redirect(ROUTES.auth.signIn) + } + + const tokenValue = await extractTokenData<{ + email: string + role: UserRole + }>({ token }) + + const CARD_CONTENT = { + valid: { + title: 'Cadastrar conta', + description: 'Este convite expirou ou é inválido', + }, + invalid: { + title: 'Convite inválido', + description: 'Este convite expirou ou é inválido', + }, + } + + const isTokenValid = !!tokenValue + const content = isTokenValid ? CARD_CONTENT['valid'] : CARD_CONTENT['invalid'] + return ( - - - - + + {isTokenValid ? ( + + ) : ( +

+ Por favor, solicite um novo convite para realizar o cadastro da sua + conta. +

+ )} + +

Já tem uma conta?{' '} diff --git a/src/app/conta/cadastrar/sign-up-form-schema.tsx b/src/app/conta/cadastrar/sign-up-form-schema.tsx deleted file mode 100644 index 1fc62e1f..00000000 --- a/src/app/conta/cadastrar/sign-up-form-schema.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { z } from 'zod' - -import { PASSWORD_MIN_LENGTH } from '@/constants/auth' -import { - NAME_REGEX, - NON_SPECIAL_CHAR_REGEX, - PASSWORD_REGEX, -} from '@/constants/regex' - -export const signUpFormSchema = z - .object({ - name: z - .string() - .min(1, 'Insira seu nome completo') - .min(3, 'O nome deve conter mais de 3 caracteres') - .regex( - NON_SPECIAL_CHAR_REGEX, - 'Números e caracteres especiais são inválidos', - ) - .regex(NAME_REGEX, 'Insira seu nome e sobrenome'), - email: z.string().email('Insira um e-mail válido'), - password: z - .string() - .min(1, 'Insira sua senha') - .min( - PASSWORD_MIN_LENGTH, - `Sua senha precisa conter ${PASSWORD_MIN_LENGTH} ou mais caracteres`, - ) - .regex(PASSWORD_REGEX, 'Senha inválida'), - confirmPassword: z.string(), - consent: z.boolean(), - }) - .refine((data) => data.password === data.confirmPassword, { - message: 'Suas senhas não coincidem', - path: ['confirmPassword'], - }) - .refine((data) => data.consent === true, { - message: 'Seu consentimento é obrigatório', - path: ['consent'], - }) - -export type SignUpFormSchema = z.infer - -export const signUpFormDefaultValues: SignUpFormSchema = { - name: '', - email: '', - password: '', - confirmPassword: '', - consent: false, -} diff --git a/src/app/conta/cadastrar/sign-up-form.tsx b/src/app/conta/cadastrar/sign-up-form.tsx deleted file mode 100644 index 9c4802f3..00000000 --- a/src/app/conta/cadastrar/sign-up-form.tsx +++ /dev/null @@ -1,108 +0,0 @@ -'use client' - -import { zodResolver } from '@hookform/resolvers/zod' -import { MailIcon, User2Icon } from 'lucide-react' -import { useRouter } from 'next/navigation' -import { FormProvider, useForm } from 'react-hook-form' -import { toast } from 'sonner' - -import { CheckboxInput } from '@/components/form/checkbox-input' -import { FormContainer } from '@/components/form/form-container' -import { FormField } from '@/components/form/form-field' -import { PasswordInput } from '@/components/form/password-input' -import { TextInput } from '@/components/form/text-input' -import { Alert } from '@/components/ui/alert' -import { Button } from '@/components/ui/button' -import { NavLink } from '@/components/ui/nav-link' -import { ROUTES } from '@/constants/routes' -import { api } from '@/lib/api' - -import { - signUpFormDefaultValues, - type SignUpFormSchema, - signUpFormSchema, -} from './sign-up-form-schema' - -export function SignUpForm() { - const router = useRouter() - const formMethods = useForm({ - resolver: zodResolver(signUpFormSchema), - defaultValues: signUpFormDefaultValues, - mode: 'onBlur', - }) - - const isSubmitting = formMethods.formState.isSubmitting - const errorMessage = formMethods.formState.errors.root?.message - - async function registerUser({ name, email, password }: SignUpFormSchema) { - const response = await api('/register', { - method: 'POST', - body: JSON.stringify({ name, email, password }), - }) - - if (!response.success) { - return formMethods.setError('root', { message: response.message }) - } - - toast.success(response.message) - router.push(ROUTES.patient.main) - } - - // TODO: add link to policies - - return ( - - - - - - - - - - - Li e concordo com os Termos de Uso e{' '} - Política de Privacidade - - } - /> - - - - {errorMessage && ( - - {errorMessage} - - )} - - - ) -} diff --git a/src/app/conta/entrar/admin/page.tsx b/src/app/conta/entrar/admin/page.tsx deleted file mode 100644 index 8f30356e..00000000 --- a/src/app/conta/entrar/admin/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import image from '@images/brand/icon.svg' -import type { Metadata } from 'next' - -import { AuthCard } from '@/components/auth/auth-card' -import { SignInForm } from '@/modules/auth/sign-in-form' - -export const metadata: Metadata = { - title: 'Acessar painel administrativo', -} - -export default function Page() { - return ( - - - - ) -} diff --git a/src/app/conta/entrar/page.tsx b/src/app/conta/entrar/page.tsx index a47b493f..a0668892 100644 --- a/src/app/conta/entrar/page.tsx +++ b/src/app/conta/entrar/page.tsx @@ -1,26 +1,22 @@ -import image from '@images/brand/icon.svg' import type { Metadata } from 'next' import { AuthCard } from '@/components/auth/auth-card' -import { Divider } from '@/components/ui/divider' -import { NavLink } from '@/components/ui/nav-link' -import { ROUTES } from '@/constants/routes' import { SignInForm } from '@/modules/auth/sign-in-form' export const metadata: Metadata = { title: 'Acessar conta', } +// TODO: redirect patients to new screening flow when it's ready export default function Page() { return ( - + - + {/*

Não tem uma conta?{' '} @@ -28,9 +24,9 @@ export default function Page() { href={ROUTES.auth.signUp} className='text-foreground font-medium whitespace-nowrap' > - Crie sua conta aqui + Registre-se como paciente aqui -

+

*/}
) } diff --git a/src/app/conta/nova-senha/new-password-form-schema.tsx b/src/app/conta/nova-senha/new-password-form-schema.tsx deleted file mode 100644 index 5674ae55..00000000 --- a/src/app/conta/nova-senha/new-password-form-schema.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { z } from 'zod' - -import { PASSWORD_MIN_LENGTH } from '@/constants/auth' -import { PASSWORD_REGEX } from '@/constants/regex' - -export const newPasswordFormSchema = z - .object({ - password: z - .string() - .min(1, 'Insira sua senha') - .min( - PASSWORD_MIN_LENGTH, - `Sua senha precisa conter ${PASSWORD_MIN_LENGTH} ou mais caracteres`, - ) - .regex(PASSWORD_REGEX, 'Senha inválida'), - confirmPassword: z.string(), - }) - .refine((data) => data.password === data.confirmPassword, { - message: 'Suas senhas não coincidem', - path: ['confirmPassword'], - }) - -export type NewPasswordFormSchema = z.infer - -export const newPasswordFormDefaultValues: NewPasswordFormSchema = { - password: '', - confirmPassword: '', -} diff --git a/src/app/conta/nova-senha/new-password-form.tsx b/src/app/conta/nova-senha/new-password-form.tsx deleted file mode 100644 index 2efd2fb3..00000000 --- a/src/app/conta/nova-senha/new-password-form.tsx +++ /dev/null @@ -1,81 +0,0 @@ -'use client' - -import { zodResolver } from '@hookform/resolvers/zod' -import { redirect } from 'next/navigation' -import { useState } from 'react' -import { FormProvider, useForm } from 'react-hook-form' - -import { FormContainer } from '@/components/form/form-container' -import { FormField } from '@/components/form/form-field' -import { PasswordInput } from '@/components/form/password-input' -import { Alert } from '@/components/ui/alert' -import { Button } from '@/components/ui/button' -import { ROUTES } from '@/constants/routes' -import { wait } from '@/utils/wait' - -import { - newPasswordFormDefaultValues, - type NewPasswordFormSchema, - newPasswordFormSchema, -} from './new-password-form-schema' - -export function NewPasswordForm() { - const [success, setSuccess] = useState(false) - - const formMethods = useForm({ - resolver: zodResolver(newPasswordFormSchema), - defaultValues: newPasswordFormDefaultValues, - mode: 'onBlur', - }) - const isSubmitting = formMethods.formState.isSubmitting - const errorMessage = formMethods.formState.errors.root?.message - - async function saveNewPassword(data: NewPasswordFormSchema) { - // TODO: implement function when API is available - setSuccess(false) - await wait(500) - console.log(data) - - setSuccess(true) - - setTimeout(() => redirect(ROUTES.auth.signIn), 2000) - } - - return ( - - - - - - - - - - {success && ( - - Senha atualizada com sucesso. - - )} - - {errorMessage && ( - - {errorMessage} - - )} - - - ) -} diff --git a/src/app/conta/nova-senha/page.tsx b/src/app/conta/nova-senha/page.tsx index 6e2bc452..75e6d2a6 100644 --- a/src/app/conta/nova-senha/page.tsx +++ b/src/app/conta/nova-senha/page.tsx @@ -1,33 +1,30 @@ -import image from '@images/brand/icon.svg' import type { Metadata } from 'next' +import { redirect } from 'next/navigation' import { AuthCard } from '@/components/auth/auth-card' import { Divider } from '@/components/ui/divider' import { NavLink } from '@/components/ui/nav-link' import { ROUTES } from '@/constants/routes' - -import { NewPasswordForm } from './new-password-form' +import { ResetPasswordForm } from '@/modules/auth/reset-password-form' export const metadata: Metadata = { title: 'Redefinir senha', } interface PageProps { - searchParams: Promise<{ - token?: string - }> + searchParams: Promise<{ token?: string }> } export default async function Page({ searchParams }: Readonly) { const { token } = await searchParams if (!token) { - console.error('Token is missing') + redirect(ROUTES.auth.signIn) } return ( - - + + diff --git a/src/app/conta/recuperar-senha/page.tsx b/src/app/conta/recuperar-senha/page.tsx index ba142e97..d6275666 100644 --- a/src/app/conta/recuperar-senha/page.tsx +++ b/src/app/conta/recuperar-senha/page.tsx @@ -1,12 +1,10 @@ -import image from '@images/brand/icon.svg' import type { Metadata } from 'next' import { AuthCard } from '@/components/auth/auth-card' import { Divider } from '@/components/ui/divider' import { NavLink } from '@/components/ui/nav-link' import { ROUTES } from '@/constants/routes' - -import { RecoverForm } from './recover-form' +import { RecoverPasswordForm } from '@/modules/auth/recover-password-form' export const metadata: Metadata = { title: 'Recuperar senha', @@ -15,11 +13,10 @@ export const metadata: Metadata = { export default function Page() { return ( - + diff --git a/src/app/conta/recuperar-senha/recover-form-schema.tsx b/src/app/conta/recuperar-senha/recover-form-schema.tsx deleted file mode 100644 index 2e6da5f0..00000000 --- a/src/app/conta/recuperar-senha/recover-form-schema.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { z } from 'zod' - -export const recoverFormSchema = z.object({ - email: z.string().email('Insira um e-mail válido'), -}) - -export type RecoverFormSchema = z.infer - -export const recoverFormDefaultValues: RecoverFormSchema = { - email: '', -} diff --git a/src/app/conta/recuperar-senha/recover-form.tsx b/src/app/conta/recuperar-senha/recover-form.tsx deleted file mode 100644 index bc94be85..00000000 --- a/src/app/conta/recuperar-senha/recover-form.tsx +++ /dev/null @@ -1,84 +0,0 @@ -'use client' - -import { zodResolver } from '@hookform/resolvers/zod' -import { MailIcon } from 'lucide-react' -import { useState } from 'react' -import { FormProvider, useForm } from 'react-hook-form' - -import { FormContainer } from '@/components/form/form-container' -import { TextInput } from '@/components/form/text-input' -import { Alert } from '@/components/ui/alert' -import { Button } from '@/components/ui/button' -import { wait } from '@/utils/wait' - -import { - recoverFormDefaultValues, - type RecoverFormSchema, - recoverFormSchema, -} from './recover-form-schema' - -type SuccessMessage = { - message: string - email: string -} - -export function RecoverForm() { - const [successMessage, setSuccessMessage] = useState( - null, - ) - - const formMethods = useForm({ - resolver: zodResolver(recoverFormSchema), - defaultValues: recoverFormDefaultValues, - mode: 'onBlur', - }) - const isSubmitting = formMethods.formState.isSubmitting - const errorMessage = formMethods.formState.errors.root?.message - - async function sendRecoverEmail({ email }: RecoverFormSchema) { - // TODO: implement function when API is available - setSuccessMessage(null) - await wait(500) - - if (email === 'erro@ipecode.com.br') { - return formMethods.setError('root', { - message: 'Ocorreu algum erro. Por favor, tente novamente.', - }) - } - - setSuccessMessage({ - message: 'E-mail de recuperação de senha enviado para ', - email, - }) - } - - return ( - - - - - - - {successMessage && ( - - {successMessage.message} - {successMessage.email} - - )} - - {errorMessage && ( - - {errorMessage} - - )} - - - ) -} diff --git a/src/app/globals.css b/src/app/globals.css index 3504c5d9..692c8a45 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -27,7 +27,7 @@ --color-warning: #a16207; --color-error: #df1c41; - --color-ring: #008b62; + --color-ring: #036246; --inset-shadow-md: inset 0 1px 6px rgba(0, 0, 0, 0.1); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index b5096967..664c3a57 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,7 @@ import './globals.css' import type { Metadata } from 'next' import { inter } from '@/lib/fonts' +import { cn } from '@/utils/class-name-merge' import { Providers } from './providers' @@ -20,7 +21,7 @@ export default function RootLayout({ }: Readonly<{ children: React.ReactNode }>) { return ( - + {children} diff --git a/src/components/auth/auth-card.tsx b/src/components/auth/auth-card.tsx index 9de5074f..1a4cb3eb 100644 --- a/src/components/auth/auth-card.tsx +++ b/src/components/auth/auth-card.tsx @@ -3,16 +3,17 @@ import Image from 'next/image' import type { ReactNode } from 'react' import { Divider } from '@/components/ui/divider' +import { IMAGES } from '@/constants/images' interface AuthCardProps { - image: string | StaticImport + image?: string | StaticImport title: string description?: string children: ReactNode } export function AuthCard({ - image, + image = IMAGES.icon, title, description, children, @@ -20,7 +21,15 @@ export function AuthCard({ return (
- {image && } + {image && ( + + )}

{title}

{description &&

{description}

}
diff --git a/src/components/breadcrumbs.tsx b/src/components/breadcrumbs.tsx index 0ee8366c..6ab3b15e 100644 --- a/src/components/breadcrumbs.tsx +++ b/src/components/breadcrumbs.tsx @@ -5,16 +5,28 @@ import Link from 'next/link' import { usePathname } from 'next/navigation' import React from 'react' -import { BREADCRUMBS, type BreadcrumbType } from '@/constants/breadcrumbs' +import { + type Breadcrumb, + BREADCRUMBS, + type Breadcrumbs, + type BreadcrumbSection, +} from '@/constants/breadcrumbs' import { cn } from '@/utils/class-name-merge' export function Breadcrumbs() { const pathnames = usePathname().split('/') + const section = pathnames[1] + const breadcrumbSection = BREADCRUMBS[section as BreadcrumbSection] + + if (!breadcrumbSection) { + return null + } + const breadcrumbs = [] for (const path of pathnames) { - const breadcrumb = BREADCRUMBS[path as BreadcrumbType] + const breadcrumb = breadcrumbSection[path as Breadcrumb] if (breadcrumb) { breadcrumbs.push(breadcrumb) } diff --git a/src/components/charts/bar.tsx b/src/components/charts/bar.tsx index ecb45e44..babcda36 100644 --- a/src/components/charts/bar.tsx +++ b/src/components/charts/bar.tsx @@ -49,6 +49,8 @@ export function BarChart({ axisLine={false} tickLine={false} tick={{ fontSize: '0.75rem', fill: 'var(--color-disabled)' }} + domain={[0, 'dataMax']} + allowDecimals={false} />
- {value} -
+
+ {label}: + {value} +
+
) { const Icon = icon + return (
- -

{title}

+ +

{title}

{menu &&
{menu}
}
diff --git a/src/components/dashboard/container.tsx b/src/components/dashboard/container.tsx deleted file mode 100644 index 44c26a58..00000000 --- a/src/components/dashboard/container.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { cn } from '@/utils/class-name-merge' - -export function DashboardContainer({ - className, - ...props -}: Readonly>) { - return ( -
- ) -} diff --git a/src/components/dashboard/header.tsx b/src/components/dashboard/header.tsx new file mode 100644 index 00000000..f5221ff6 --- /dev/null +++ b/src/components/dashboard/header.tsx @@ -0,0 +1,49 @@ +'use client' + +import { usePathname } from 'next/navigation' + +import { Breadcrumbs } from '@/components/breadcrumbs' +import { Divider } from '@/components/ui/divider' +import { SECTION_TITLES, type SectionTitle } from '@/constants/section-titles' + +export function DashboardHeader() { + const pathnames = usePathname().split('/') + + const section = pathnames[1] as SectionTitle + const sectionTitle = SECTION_TITLES[section || 'default'] + + const showBreadcrumbs = !!pathnames[2] + + return ( +
+

{sectionTitle}

+ + {showBreadcrumbs && ( + <> + + + + )} + + {/*
+ + + +
*/} +
+ ) +} diff --git a/src/components/dashboard/tab-buttons.tsx b/src/components/dashboard/tab-buttons.tsx index f64648d3..40005b28 100644 --- a/src/components/dashboard/tab-buttons.tsx +++ b/src/components/dashboard/tab-buttons.tsx @@ -1,16 +1,41 @@ +'use client' + +import { usePathname } from 'next/navigation' + +import { + SECTION_TAB_BUTTONS, + type SectionTabButton, +} from '@/constants/section-tab-buttons' + import { Divider } from '../ui/divider' import { TabButtons } from '../ui/tab-buttons' -interface DashboardTabButtonsProps { - buttons: Array<{ title: string; path: string }> -} +export function DashboardTabButtons() { + const segments = usePathname().split('/').filter(Boolean) + const section = segments[0] + + const buttons = SECTION_TAB_BUTTONS[section as SectionTabButton] + + const hidePatientDetailsButtons = + (section === 'pacientes' && segments.length < 2) || + segments[1] === 'cadastrar' + + if (!buttons || hidePatientDetailsButtons) { + return null + } + + // Resolve dynamic paths (e.g., for patients section with patient ID) + const resolvedButtons = buttons.map((button) => ({ + title: button.title, + path: + typeof button.path === 'function' + ? button.path(segments[1]) + : button.path, + })) -export function DashboardTabButtons({ - buttons, -}: Readonly) { return ( <> - + ) diff --git a/src/components/dashboard/wrapper.tsx b/src/components/dashboard/wrapper.tsx new file mode 100644 index 00000000..0312a789 --- /dev/null +++ b/src/components/dashboard/wrapper.tsx @@ -0,0 +1,21 @@ +'use client' + +import { useSidebar } from '@/store/sidebar' +import { cn } from '@/utils/class-name-merge' + +export function DashboardWrapper({ + children, +}: Readonly<{ children: React.ReactNode }>) { + const expanded = useSidebar((state) => state.expanded) + + return ( +
+ {children} +
+ ) +} diff --git a/src/components/data-table/filters/date.tsx b/src/components/data-table/filters/date.tsx deleted file mode 100644 index 4d968d86..00000000 --- a/src/components/data-table/filters/date.tsx +++ /dev/null @@ -1,56 +0,0 @@ -'use client' - -import { DatePicker } from '@/components/ui/date-picker' -import { QUERY_PARAMS } from '@/constants/params' -import { useParams } from '@/hooks/params' -import { cn } from '@/utils/class-name-merge' - -import { DataTableFilterContainer } from './container' - -// TODO: implement date pickers -export function DataTableFilterDate({ - className, - ...props -}: Readonly>) { - const { getParam, updateParams } = useParams() - - const pageParam = QUERY_PARAMS.page - const startDateParam = QUERY_PARAMS.startDate - const endDateParam = QUERY_PARAMS.endDate - const startDate = getParam(startDateParam) || '' - const endDate = getParam(endDateParam) || '' - - function handleSelectStartDate(value: string) { - updateParams({ - set: [{ key: startDateParam, value: value }], - remove: [pageParam], - }) - } - - function handleSelectEndDate(value: string) { - updateParams({ - set: [{ key: endDateParam, value: value }], - remove: [pageParam], - }) - } - - return ( -
- - - - - - -
- ) -} diff --git a/src/components/data-table/filters/index.tsx b/src/components/data-table/filters/index.tsx deleted file mode 100644 index 04a97312..00000000 --- a/src/components/data-table/filters/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -'use client' - -import { XIcon } from 'lucide-react' - -import { Button } from '@/components/ui/button' -import { useParams } from '@/hooks/params' -import { cn } from '@/utils/class-name-merge' - -interface DataTableFiltersProps extends React.ComponentProps<'section'> { - queries: string[] -} - -export function DataTableFilters({ - queries, - className, - children, - ...props -}: Readonly) { - const { updateParams } = useParams() - - return ( -
- {children} - - -
- ) -} diff --git a/src/components/data-table/filters/status.tsx b/src/components/data-table/filters/status.tsx deleted file mode 100644 index 8e984b8e..00000000 --- a/src/components/data-table/filters/status.tsx +++ /dev/null @@ -1,63 +0,0 @@ -'use client' - -import { type LucideIcon } from 'lucide-react' - -import { Select, type SelectOption } from '@/components/ui/select' -import { QUERY_PARAMS } from '@/constants/params' -import { useParams } from '@/hooks/params' -import { cn } from '@/utils/class-name-merge' - -import { - DataTableFilterContainer, - type DataTableFilterContainerProps, -} from './container' - -type StatusOption = SelectOption & { - icon?: LucideIcon - color?: string -} - -interface DataTableFilterStatusProps - extends Omit { - options: StatusOption[] -} - -export function DataTableFilterStatus({ - options, - className, - ...props -}: Readonly) { - const { getParam, updateParams } = useParams() - - const pageParam = QUERY_PARAMS.page - const statusParam = QUERY_PARAMS.status - const status = getParam(statusParam) || '' - - function handleSelect(value: string) { - if (value === 'reset') { - updateParams({ remove: [statusParam, pageParam] }) - return - } - - updateParams({ - set: [{ key: statusParam, value: value }], - remove: [pageParam], - }) - } - - return ( - - - ) -} diff --git a/src/components/filters/clear-filters-button.tsx b/src/components/filters/clear-filters-button.tsx new file mode 100644 index 00000000..a9b5d9ea --- /dev/null +++ b/src/components/filters/clear-filters-button.tsx @@ -0,0 +1,31 @@ +'use client' + +import { XIcon } from 'lucide-react' + +import { Button, type ButtonProps } from '@/components/ui/button' +import { useParams } from '@/hooks/params' +import { cn } from '@/utils/class-name-merge' + +export function ClearFiltersButton({ + className, + ...props +}: Readonly) { + const { clearParams, searchParams } = useParams() + + if (searchParams.size === 0) { + return null + } + + return ( + + ) +} diff --git a/src/components/data-table/filters/container.tsx b/src/components/filters/container.tsx similarity index 61% rename from src/components/data-table/filters/container.tsx rename to src/components/filters/container.tsx index a1b1b0e9..962483f4 100644 --- a/src/components/data-table/filters/container.tsx +++ b/src/components/filters/container.tsx @@ -1,16 +1,15 @@ import { cn } from '@/utils/class-name-merge' -export interface DataTableFilterContainerProps - extends React.ComponentProps<'div'> { +export interface FilterContainerProps extends React.ComponentProps<'div'> { title: string } -export function DataTableFilterContainer({ +export function FilterContainer({ title, className, children, ...props -}: Readonly) { +}: Readonly) { return (
diff --git a/src/components/filters/date.tsx b/src/components/filters/date.tsx new file mode 100644 index 00000000..45d6645d --- /dev/null +++ b/src/components/filters/date.tsx @@ -0,0 +1,63 @@ +'use client' + +import { FilterContainer } from '@/components/filters/container' +import { DatePicker } from '@/components/ui/date-picker' +import { QUERY_PARAM_KEYS } from '@/enums/params' +import { useParams } from '@/hooks/params' +import { cn } from '@/utils/class-name-merge' + +interface FilterDateProps extends React.ComponentProps<'div'> { + allowFutureDates?: boolean +} + +export function FilterDate({ + allowFutureDates, + className, + ...props +}: Readonly) { + const { getParams, updateParams } = useParams() + + const pageParam = QUERY_PARAM_KEYS.page + const startDateParam = QUERY_PARAM_KEYS.startDate + const endDateParam = QUERY_PARAM_KEYS.endDate + const [startDate, endDate] = getParams([startDateParam, endDateParam]) + + function handleSelectStartDate(value: string) { + updateParams({ + set: [{ key: startDateParam, value: value }], + remove: [pageParam], + }) + } + + function handleSelectEndDate(value: string) { + updateParams({ + set: [{ key: endDateParam, value: value }], + remove: [pageParam], + }) + } + + return ( +
+ + + + + + +
+ ) +} diff --git a/src/components/filters/filter-select.tsx b/src/components/filters/filter-select.tsx new file mode 100644 index 00000000..3bb03976 --- /dev/null +++ b/src/components/filters/filter-select.tsx @@ -0,0 +1,37 @@ +'use client' + +import { Select, type SelectProps } from '@/components/ui/select' +import { QUERY_PARAM_KEYS, type QueryParamKey } from '@/enums/params' +import { useParams } from '@/hooks/params' + +interface FilterSelectProps extends SelectProps { + param: QueryParamKey +} + +export function FilterSelect({ param, ...props }: Readonly) { + const { getParam, updateParams } = useParams() + + const pageParam = QUERY_PARAM_KEYS.page + const selectValue = getParam(param) || '' + + function handleSelect(value: string) { + if (!value || value === 'reset') { + updateParams({ remove: [param, pageParam] }) + return + } + + updateParams({ + set: [{ key: param, value: value }], + remove: [pageParam], + }) + } + + return ( + setQuery(e.target.value)} - className={cn('w-52 pr-10', className)} - {...props} + placeholder={placeholder} + className='w-full pr-10' /> {query && ( @@ -52,7 +57,7 @@ export function DataTableHeaderSearch({ size='icon' variant='ghost' title='Limpar pesquisa' - className='absolute top-0 right-0 size-9 [&_svg]:size-4' + className='absolute top-1 right-1 size-7 rounded-md [&_svg]:size-4' onClick={() => setQuery('')} > diff --git a/src/components/data-table/header/filter-button.tsx b/src/components/filters/show-filter-button.tsx similarity index 90% rename from src/components/data-table/header/filter-button.tsx rename to src/components/filters/show-filter-button.tsx index 8447805e..044e4707 100644 --- a/src/components/data-table/header/filter-button.tsx +++ b/src/components/filters/show-filter-button.tsx @@ -5,7 +5,7 @@ import { ListFilterIcon } from 'lucide-react' import { Button, type ButtonProps } from '@/components/ui/button' import { cn } from '@/utils/class-name-merge' -export function DataTableHeaderFilterButton({ +export function ShowFilterButton({ className, ...props }: Readonly) { diff --git a/src/components/form/checkbox-input.tsx b/src/components/form/checkbox-input.tsx index dab41b65..9126e6af 100644 --- a/src/components/form/checkbox-input.tsx +++ b/src/components/form/checkbox-input.tsx @@ -46,7 +46,10 @@ export function CheckboxInput({ {...props} {...field} /> - ) } diff --git a/src/app/(dashboard)/_sidebar/account.tsx b/src/components/sidebar/account.tsx similarity index 57% rename from src/app/(dashboard)/_sidebar/account.tsx rename to src/components/sidebar/account.tsx index d7b5a194..bcbb72a9 100644 --- a/src/app/(dashboard)/_sidebar/account.tsx +++ b/src/components/sidebar/account.tsx @@ -1,25 +1,24 @@ 'use client' -import { EllipsisVerticalIcon, LogOutIcon } from 'lucide-react' +import { EllipsisVerticalIcon, LogOutIcon, UserCircle2Icon } from 'lucide-react' import { useRouter } from 'next/navigation' import { useTransition } from 'react' import { toast } from 'sonner' -import { revalidateCache } from '@/actions/cache' -import { getUserFromToken } from '@/actions/token' import { Avatar } from '@/components/ui/avatar' -import { DropdownMenu } from '@/components/ui/dropdown' -import { DropdownMenuContent } from '@/components/ui/dropdown/content' -import { DropdownMenuItem } from '@/components/ui/dropdown/item' -import { DropdownMenuTrigger } from '@/components/ui/dropdown/trigger' import { NEXT_CACHE_TAGS } from '@/constants/cache' import { ROUTES } from '@/constants/routes' +import { revalidateClientCache } from '@/helpers/revalidate-client-cache' +import { revalidateServerCache } from '@/helpers/revalidate-server-cache' import { api } from '@/lib/api' import { useSidebar } from '@/store/sidebar' -import type { User } from '@/types/users' +import type { User } from '@/types/users.d.ts' + +import { Divider } from '../ui/divider' +import { Menu, MenuContent, MenuItem, MenuTrigger } from '../ui/menu' interface SidebarAccountProps { - user: User + user?: User | null } export function SidebarAccount({ user }: Readonly) { @@ -30,8 +29,6 @@ export function SidebarAccount({ user }: Readonly) { async function logout() { startTransition(async () => { - const user = await getUserFromToken() - if (!user?.id) return const response = await api('/logout', { method: 'POST' }) @@ -41,7 +38,9 @@ export function SidebarAccount({ user }: Readonly) { return } - revalidateCache(NEXT_CACHE_TAGS.user(user.id)) + revalidateServerCache(NEXT_CACHE_TAGS.user(user.id)) + revalidateClientCache('all') + toast.success(response.message) router.push(ROUTES.auth.signIn) }) @@ -55,28 +54,32 @@ export function SidebarAccount({ user }: Readonly) { data-visible={expanded} className='space-y-1 truncate pr-8 opacity-0 transition-opacity duration-750 data-[visible=true]:opacity-100' > -

{user.name}

-

{user.email}

+

{user?.name}

+

{user?.email}

- - + - - - - + + + router.push(ROUTES.dashboard.profile)}> + + Meu perfil + + + Sair - - - + + +
) } diff --git a/src/app/(dashboard)/_sidebar/container.tsx b/src/components/sidebar/container.tsx similarity index 70% rename from src/app/(dashboard)/_sidebar/container.tsx rename to src/components/sidebar/container.tsx index bf7f9e2c..d32ef552 100644 --- a/src/app/(dashboard)/_sidebar/container.tsx +++ b/src/components/sidebar/container.tsx @@ -7,7 +7,7 @@ import { Button } from '@/components/ui/button' import { useSidebar } from '@/store/sidebar' import { cn } from '@/utils/class-name-merge' -export function DashboardSidebarContainer({ +export function SidebarContainer({ className, ...props }: Readonly>) { @@ -15,12 +15,12 @@ export function DashboardSidebarContainer({ const toogleSidebar = useSidebar((state) => state.toogleSidebar) return ( -
+