diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml
index 9f3b571..76f3d3e 100644
--- a/.github/workflows/node.yml
+++ b/.github/workflows/node.yml
@@ -17,7 +17,7 @@ jobs:
key: ${{runner.os}}-node_modules-${{hashFiles('package*.json')}}-${{hashFiles('.github/workflows/node.yml')}}
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
cache: 'npm'
diff --git a/.nvmrc b/.nvmrc
index 0df78c8..a03e161 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-24.8.0
\ No newline at end of file
+24.10.0
\ No newline at end of file
diff --git a/eslint.config.js b/eslint.config.js
index da8816f..e379bf7 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -1,14 +1,20 @@
import js from '@eslint/js';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
+import { defineConfig, globalIgnores } from 'eslint/config';
import globals from 'globals';
import tseslint from 'typescript-eslint';
-export default tseslint.config(
- { ignores: ['dist', 'coverage'] },
+export default defineConfig([
+ globalIgnores(['dist', 'coverage']),
{
- extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended
+ // reactHooks.configs['recommended-latest'],
+ // reactRefresh.configs.vite
+ ],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser
@@ -18,11 +24,10 @@ export default tseslint.config(
'react-refresh': reactRefresh
},
rules: {
- ...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'react-hooks/exhaustive-deps': 'off'
}
}
-);
+]);
diff --git a/index.html b/index.html
index 7d7b651..af4f100 100644
--- a/index.html
+++ b/index.html
@@ -2,9 +2,15 @@
-
+
- React template
+
+ React.js template
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index e78602d..31b16fd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,31 +12,31 @@
"@reduxjs/toolkit": "^2.9.0",
"axios": "^1.12.2",
"js-cookie": "^3.0.5",
- "react": "^19.1.1",
- "react-dom": "^19.1.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
"react-redux": "^9.2.0",
- "react-router": "^7.9.1"
+ "react-router": "^7.9.4"
},
"devDependencies": {
- "@eslint/js": "^9.35.0",
+ "@eslint/js": "^9.37.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
"@types/js-cookie": "^3.0.6",
- "@types/node": "^24.5.2",
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
+ "@types/node": "^24.7.2",
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.1",
"@types/react-redux": "^7.1.34",
"@vitejs/plugin-react-swc": "^4.1.0",
"@vitest/coverage-v8": "^3.2.4",
- "eslint": "^9.35.0",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
+ "eslint": "^9.37.0",
+ "eslint-plugin-react-hooks": "^7.0.0",
+ "eslint-plugin-react-refresh": "^0.4.23",
"globals": "^16.4.0",
"jsdom": "^27.0.0",
- "sass-embedded": "^1.92.1",
- "typescript": "~5.9.2",
- "typescript-eslint": "^8.44.0",
- "vite": "^7.1.6",
+ "sass-embedded": "^1.93.2",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.46.0",
+ "vite": "^7.1.9",
"vitest": "^3.2.4"
}
},
@@ -113,6 +113,153 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz",
+ "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
+ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.4",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.4",
+ "@babel/types": "^7.28.4",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
+ "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.3",
+ "@babel/types": "^7.28.2",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
@@ -133,14 +280,38 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/parser": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
- "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
+ "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.2"
+ "@babel/types": "^7.28.4"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -159,10 +330,44 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
+ "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.4",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/types": {
- "version": "7.28.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
- "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
+ "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -828,19 +1033,22 @@
}
},
"node_modules/@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
+ "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
"dev": true,
"license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.16.0"
+ },
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
+ "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -888,9 +1096,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.35.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz",
- "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==",
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
+ "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -911,13 +1119,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
+ "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.2",
+ "@eslint/core": "^0.16.0",
"levn": "^0.4.1"
},
"engines": {
@@ -1029,6 +1237,17 @@
"@jridgewell/trace-mapping": "^0.3.24"
}
},
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -2073,19 +2292,19 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "24.5.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz",
- "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
+ "version": "24.7.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz",
+ "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~7.12.0"
+ "undici-types": "~7.14.0"
}
},
"node_modules/@types/react": {
- "version": "19.1.13",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz",
- "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==",
+ "version": "19.2.2",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
+ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -2093,13 +2312,13 @@
}
},
"node_modules/@types/react-dom": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
- "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.1.tgz",
+ "integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==",
"dev": true,
"license": "MIT",
"peerDependencies": {
- "@types/react": "^19.0.0"
+ "@types/react": "^19.2.0"
}
},
"node_modules/@types/react-redux": {
@@ -2132,17 +2351,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz",
- "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz",
+ "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/type-utils": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/scope-manager": "8.46.0",
+ "@typescript-eslint/type-utils": "8.46.0",
+ "@typescript-eslint/utils": "8.46.0",
+ "@typescript-eslint/visitor-keys": "8.46.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -2156,7 +2375,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.44.0",
+ "@typescript-eslint/parser": "^8.46.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -2172,16 +2391,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz",
- "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz",
+ "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/scope-manager": "8.46.0",
+ "@typescript-eslint/types": "8.46.0",
+ "@typescript-eslint/typescript-estree": "8.46.0",
+ "@typescript-eslint/visitor-keys": "8.46.0",
"debug": "^4.3.4"
},
"engines": {
@@ -2197,14 +2416,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz",
- "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz",
+ "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.44.0",
- "@typescript-eslint/types": "^8.44.0",
+ "@typescript-eslint/tsconfig-utils": "^8.46.0",
+ "@typescript-eslint/types": "^8.46.0",
"debug": "^4.3.4"
},
"engines": {
@@ -2219,14 +2438,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz",
- "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz",
+ "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0"
+ "@typescript-eslint/types": "8.46.0",
+ "@typescript-eslint/visitor-keys": "8.46.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2237,9 +2456,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz",
- "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz",
+ "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2254,15 +2473,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz",
- "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz",
+ "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
+ "@typescript-eslint/types": "8.46.0",
+ "@typescript-eslint/typescript-estree": "8.46.0",
+ "@typescript-eslint/utils": "8.46.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -2279,9 +2498,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz",
- "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz",
+ "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2293,16 +2512,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz",
- "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz",
+ "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.44.0",
- "@typescript-eslint/tsconfig-utils": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/project-service": "8.46.0",
+ "@typescript-eslint/tsconfig-utils": "8.46.0",
+ "@typescript-eslint/types": "8.46.0",
+ "@typescript-eslint/visitor-keys": "8.46.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -2348,16 +2567,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz",
- "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz",
+ "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0"
+ "@typescript-eslint/scope-manager": "8.46.0",
+ "@typescript-eslint/types": "8.46.0",
+ "@typescript-eslint/typescript-estree": "8.46.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2372,13 +2591,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz",
- "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz",
+ "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/types": "8.46.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2701,6 +2920,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.8.12",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.12.tgz",
+ "integrity": "sha512-vAPMQdnyKCBtkmQA6FMCBvU9qFIppS3nzyXnEM+Lo2IAhG4Mpjv9cCxMudhgV3YdNNJv6TNqXy97dfRVL2LmaQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
"node_modules/bidi-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
@@ -2735,6 +2964,40 @@
"node": ">=8"
}
},
+ "node_modules/browserslist": {
+ "version": "4.26.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz",
+ "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.8.9",
+ "caniuse-lite": "^1.0.30001746",
+ "electron-to-chromium": "^1.5.227",
+ "node-releases": "^2.0.21",
+ "update-browserslist-db": "^1.1.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/buffer-builder": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz",
@@ -2775,6 +3038,27 @@
"node": ">=6"
}
},
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001748",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz",
+ "integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
"node_modules/chai": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
@@ -2882,6 +3166,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
@@ -3059,6 +3350,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.230",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.230.tgz",
+ "integrity": "sha512-A6A6Fd3+gMdaed9wX83CvHYJb4UuapPD5X5SLq72VZJzxHSY0/LUweGXRWmQlh2ln7KV7iw7jnwXK7dlPoOnHQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -3173,6 +3471,16 @@
"@esbuild/win32-x64": "0.25.9"
}
},
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -3187,20 +3495,20 @@
}
},
"node_modules/eslint": {
- "version": "9.35.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz",
- "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==",
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
+ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
+ "@eslint/config-helpers": "^0.4.0",
+ "@eslint/core": "^0.16.0",
"@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.35.0",
- "@eslint/plugin-kit": "^0.3.5",
+ "@eslint/js": "9.37.0",
+ "@eslint/plugin-kit": "^0.4.0",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
@@ -3248,22 +3556,29 @@
}
},
"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.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.0.tgz",
+ "integrity": "sha512-fNXaOwvKwq2+pXiRpXc825Vd63+KM4DLL40Rtlycb8m7fYpp6efrTp1sa6ZbP/Ap58K2bEKFXRmhURE+CJAQWw==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.22.4 || ^4.0.0",
+ "zod-validation-error": "^3.0.3 || ^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-refresh": {
- "version": "0.4.20",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
- "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.23.tgz",
+ "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -3586,6 +3901,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -3764,6 +4089,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/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -4085,6 +4427,19 @@
}
}
},
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -4106,6 +4461,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -4340,6 +4708,13 @@
"license": "MIT",
"optional": true
},
+ "node_modules/node-releases": {
+ "version": "2.0.23",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz",
+ "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -4609,24 +4984,24 @@
"license": "MIT"
},
"node_modules/react": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
- "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
+ "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
- "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
+ "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"license": "MIT",
"dependencies": {
- "scheduler": "^0.26.0"
+ "scheduler": "^0.27.0"
},
"peerDependencies": {
- "react": "^19.1.1"
+ "react": "^19.2.0"
}
},
"node_modules/react-is": {
@@ -4660,9 +5035,9 @@
}
},
"node_modules/react-router": {
- "version": "7.9.1",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz",
- "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==",
+ "version": "7.9.4",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz",
+ "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -4837,9 +5212,9 @@
"license": "MIT"
},
"node_modules/sass": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.92.1.tgz",
- "integrity": "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz",
+ "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -4859,9 +5234,9 @@
}
},
"node_modules/sass-embedded": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.92.1.tgz",
- "integrity": "sha512-28YwLnF5atAhogt3E4hXzz/NB9dwKffyw08a7DEasLh94P7+aELkG3ENSHYCWB9QFN14hYNLfwr9ozUsPDhcDQ==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.93.2.tgz",
+ "integrity": "sha512-FvQdkn2dZ8DGiLgi0Uf4zsj7r/BsiLImNa5QJ10eZalY6NfZyjrmWGFcuCN5jNwlDlXFJnftauv+UtvBKLvepQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4881,30 +5256,30 @@
"node": ">=16.0.0"
},
"optionalDependencies": {
- "sass-embedded-all-unknown": "1.92.1",
- "sass-embedded-android-arm": "1.92.1",
- "sass-embedded-android-arm64": "1.92.1",
- "sass-embedded-android-riscv64": "1.92.1",
- "sass-embedded-android-x64": "1.92.1",
- "sass-embedded-darwin-arm64": "1.92.1",
- "sass-embedded-darwin-x64": "1.92.1",
- "sass-embedded-linux-arm": "1.92.1",
- "sass-embedded-linux-arm64": "1.92.1",
- "sass-embedded-linux-musl-arm": "1.92.1",
- "sass-embedded-linux-musl-arm64": "1.92.1",
- "sass-embedded-linux-musl-riscv64": "1.92.1",
- "sass-embedded-linux-musl-x64": "1.92.1",
- "sass-embedded-linux-riscv64": "1.92.1",
- "sass-embedded-linux-x64": "1.92.1",
- "sass-embedded-unknown-all": "1.92.1",
- "sass-embedded-win32-arm64": "1.92.1",
- "sass-embedded-win32-x64": "1.92.1"
+ "sass-embedded-all-unknown": "1.93.2",
+ "sass-embedded-android-arm": "1.93.2",
+ "sass-embedded-android-arm64": "1.93.2",
+ "sass-embedded-android-riscv64": "1.93.2",
+ "sass-embedded-android-x64": "1.93.2",
+ "sass-embedded-darwin-arm64": "1.93.2",
+ "sass-embedded-darwin-x64": "1.93.2",
+ "sass-embedded-linux-arm": "1.93.2",
+ "sass-embedded-linux-arm64": "1.93.2",
+ "sass-embedded-linux-musl-arm": "1.93.2",
+ "sass-embedded-linux-musl-arm64": "1.93.2",
+ "sass-embedded-linux-musl-riscv64": "1.93.2",
+ "sass-embedded-linux-musl-x64": "1.93.2",
+ "sass-embedded-linux-riscv64": "1.93.2",
+ "sass-embedded-linux-x64": "1.93.2",
+ "sass-embedded-unknown-all": "1.93.2",
+ "sass-embedded-win32-arm64": "1.93.2",
+ "sass-embedded-win32-x64": "1.93.2"
}
},
"node_modules/sass-embedded-all-unknown": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.92.1.tgz",
- "integrity": "sha512-5t6/YZf+vhO3OY/49h8RCL6Cwo78luva0M+TnTM9gu9ASffRXAuOVLNKciSXa3loptyemDDS6IU5/dVH5w0KmA==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.93.2.tgz",
+ "integrity": "sha512-GdEuPXIzmhRS5J7UKAwEvtk8YyHQuFZRcpnEnkA3rwRUI27kwjyXkNeIj38XjUQ3DzrfMe8HcKFaqWGHvblS7Q==",
"cpu": [
"!arm",
"!arm64",
@@ -4915,13 +5290,13 @@
"license": "MIT",
"optional": true,
"dependencies": {
- "sass": "1.92.1"
+ "sass": "1.93.2"
}
},
"node_modules/sass-embedded-android-arm": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.92.1.tgz",
- "integrity": "sha512-4EjpVVzuksERdgAd4BqeSXFnWtWN3DSRyEIUPJ7BhcS9sfDh2Gf6miI2kNTvIQLJ2XIJynDDcEQ8a1U9KwKUTQ==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.93.2.tgz",
+ "integrity": "sha512-I8bpO8meZNo5FvFx5FIiE7DGPVOYft0WjuwcCCdeJ6duwfkl6tZdatex1GrSigvTsuz9L0m4ngDcX/Tj/8yMow==",
"cpu": [
"arm"
],
@@ -4936,9 +5311,9 @@
}
},
"node_modules/sass-embedded-android-arm64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.92.1.tgz",
- "integrity": "sha512-Q+UruGb7yKawHagVmVDRRKsnc4mJZvWMBnuRCu2coJo2FofyqBmXohVGXbxko97sYceA9TJTrUEx3WVKQUNCbQ==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.93.2.tgz",
+ "integrity": "sha512-346f4iVGAPGcNP6V6IOOFkN5qnArAoXNTPr5eA/rmNpeGwomdb7kJyQ717r9rbJXxOG8OAAUado6J0qLsjnjXQ==",
"cpu": [
"arm64"
],
@@ -4953,9 +5328,9 @@
}
},
"node_modules/sass-embedded-android-riscv64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.92.1.tgz",
- "integrity": "sha512-nCY5btLlX7W7Jc6cCL6D2Yklpiu540EJ2G08YVGu12DrAMCBzqM347CSRf2ojp1H8jyhvmLkaFwnrJWzh+6S+w==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.93.2.tgz",
+ "integrity": "sha512-hSMW1s4yJf5guT9mrdkumluqrwh7BjbZ4MbBW9tmi1DRDdlw1Wh9Oy1HnnmOG8x9XcI1qkojtPL6LUuEJmsiDg==",
"cpu": [
"riscv64"
],
@@ -4970,9 +5345,9 @@
}
},
"node_modules/sass-embedded-android-x64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.92.1.tgz",
- "integrity": "sha512-qYWR3bftJ77aLYwYDFuzDI4dcwVVixxqQxlIQWNGkHRCexj614qGSSHemr18C2eVj3mjXAQxTQxU68U7pkGPAA==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.93.2.tgz",
+ "integrity": "sha512-JqktiHZduvn+ldGBosE40ALgQ//tGCVNAObgcQ6UIZznEJbsHegqStqhRo8UW3x2cgOO2XYJcrInH6cc7wdKbw==",
"cpu": [
"x64"
],
@@ -4987,9 +5362,9 @@
}
},
"node_modules/sass-embedded-darwin-arm64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.92.1.tgz",
- "integrity": "sha512-g2yQ3txjMYLKMjL2cW1xRO9nnV3ijf95NbX/QShtV6tiVUETZNWDsRMDEwBNGYY6PTE/UZerjJL1R/2xpQg6WA==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.93.2.tgz",
+ "integrity": "sha512-qI1X16qKNeBJp+M/5BNW7v/JHCDYWr1/mdoJ7+UMHmP0b5AVudIZtimtK0hnjrLnBECURifd6IkulybR+h+4UA==",
"cpu": [
"arm64"
],
@@ -5004,9 +5379,9 @@
}
},
"node_modules/sass-embedded-darwin-x64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.92.1.tgz",
- "integrity": "sha512-eH+fgxLQhTEPjZPCgPAVuX5e514Qp/4DMAUMtlNShv4cr4TD5qOp1XlsPYR/b7uE7p2cKFkUpUn/bHNqJ2ay4A==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.93.2.tgz",
+ "integrity": "sha512-4KeAvlkQ0m0enKUnDGQJZwpovYw99iiMb8CTZRSsQm8Eh7halbJZVmx67f4heFY/zISgVOCcxNg19GrM5NTwtA==",
"cpu": [
"x64"
],
@@ -5021,9 +5396,9 @@
}
},
"node_modules/sass-embedded-linux-arm": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.92.1.tgz",
- "integrity": "sha512-cT3w8yoQTqrtZvWLJeutEGmawITDTY4J6oSVQjeDcPnnoPt0gOFxem8YMznraACXvahw/2+KJDH33BTNgiPo0A==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.93.2.tgz",
+ "integrity": "sha512-N3+D/ToHtzwLDO+lSH05Wo6/KRxFBPnbjVHASOlHzqJnK+g5cqex7IFAp6ozzlRStySk61Rp6d+YGrqZ6/P0PA==",
"cpu": [
"arm"
],
@@ -5038,9 +5413,9 @@
}
},
"node_modules/sass-embedded-linux-arm64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.92.1.tgz",
- "integrity": "sha512-dNmlpGeZkry1BofhAdGFBXrpM69y9LlYuNnncf+HfsOOUtj8j0q1RwS+zb5asknhKFUOAG8GCGRY1df7Rwu35g==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.93.2.tgz",
+ "integrity": "sha512-9ftX6nd5CsShJqJ2WRg+ptaYvUW+spqZfJ88FbcKQBNFQm6L87luj3UI1rB6cP5EWrLwHA754OKxRJyzWiaN6g==",
"cpu": [
"arm64"
],
@@ -5055,9 +5430,9 @@
}
},
"node_modules/sass-embedded-linux-musl-arm": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.92.1.tgz",
- "integrity": "sha512-nPBos6lI31ef2zQhqTZhFOU7ar4impJbLIax0XsqS269YsiCwjhk11VmUloJTpFlJuKMiVXNo7dPx+katxhD/Q==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.93.2.tgz",
+ "integrity": "sha512-XBTvx66yRenvEsp3VaJCb3HQSyqCsUh7R+pbxcN5TuzueybZi0LXvn9zneksdXcmjACMlMpIVXi6LyHPQkYc8A==",
"cpu": [
"arm"
],
@@ -5072,9 +5447,9 @@
}
},
"node_modules/sass-embedded-linux-musl-arm64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.92.1.tgz",
- "integrity": "sha512-TfiEBkCyNzVoOhjHXUT+vZ6+p0ueDbvRw6f4jHdkvljZzXdXMby4wh7BU1odl69rgRTkSvYKhgbErRLDR/F7pQ==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.93.2.tgz",
+ "integrity": "sha512-+3EHuDPkMiAX5kytsjEC1bKZCawB9J6pm2eBIzzLMPWbf5xdx++vO1DpT7hD4bm4ZGn0eVHgSOKIfP6CVz6tVg==",
"cpu": [
"arm64"
],
@@ -5089,9 +5464,9 @@
}
},
"node_modules/sass-embedded-linux-musl-riscv64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.92.1.tgz",
- "integrity": "sha512-R+RcJA4EYpJDE9JM1GgPYgZo7x94FlxZ6jPodOQkEaZ1S9kvXVCuP5X/0PXRPhu08KJOfeMsAElzfdAjUf7KJg==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.93.2.tgz",
+ "integrity": "sha512-0sB5kmVZDKTYzmCSlTUnjh6mzOhzmQiW/NNI5g8JS4JiHw2sDNTvt1dsFTuqFkUHyEOY3ESTsfHHBQV8Ip4bEA==",
"cpu": [
"riscv64"
],
@@ -5106,9 +5481,9 @@
}
},
"node_modules/sass-embedded-linux-musl-x64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.92.1.tgz",
- "integrity": "sha512-/HolYRGXJjx8nLw6oj5ZrkR7PFM7X/5kE4MYZaFMpDIPIcw3bqB2fUXLo/MYlRLsw7gBAT6hJAMBrNdKuTphfw==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.93.2.tgz",
+ "integrity": "sha512-t3ejQ+1LEVuHy7JHBI2tWHhoMfhedUNDjGJR2FKaLgrtJntGnyD1RyX0xb3nuqL/UXiEAtmTmZY+Uh3SLUe1Hg==",
"cpu": [
"x64"
],
@@ -5123,9 +5498,9 @@
}
},
"node_modules/sass-embedded-linux-riscv64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.92.1.tgz",
- "integrity": "sha512-b9bxe0CMsbSsLx3nrR0cq8xpIkoAC6X36o4DGMITF3m2v3KsojC7ru9X0Gz+zUFr6rwpq/0lTNzFLNu6sPNo3w==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.93.2.tgz",
+ "integrity": "sha512-e7AndEwAbFtXaLy6on4BfNGTr3wtGZQmypUgYpSNVcYDO+CWxatKVY4cxbehMPhxG9g5ru+eaMfynvhZt7fLaA==",
"cpu": [
"riscv64"
],
@@ -5140,9 +5515,9 @@
}
},
"node_modules/sass-embedded-linux-x64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.92.1.tgz",
- "integrity": "sha512-xuiK5Jp5NldW4bvlC7AuX1Wf7o0gLZ3md/hNg+bkTvxtCDgnUHtfdo8Q+xWP11bD9QX31xXFWpmUB8UDLi6XQQ==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.93.2.tgz",
+ "integrity": "sha512-U3EIUZQL11DU0xDDHXexd4PYPHQaSQa2hzc4EzmhHqrAj+TyfYO94htjWOd+DdTPtSwmLp+9cTWwPZBODzC96w==",
"cpu": [
"x64"
],
@@ -5157,9 +5532,9 @@
}
},
"node_modules/sass-embedded-unknown-all": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.92.1.tgz",
- "integrity": "sha512-AT9oXvtNY4N+Nd0wvoWqq9A5HjdH/X3aUH4boQUtXyaJ/9DUwnQmBpP5Gtn028ZS8exOGBdobmmWAuigv0k/OA==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.93.2.tgz",
+ "integrity": "sha512-7VnaOmyewcXohiuoFagJ3SK5ddP9yXpU0rzz+pZQmS1/+5O6vzyFCUoEt3HDRaLctH4GT3nUGoK1jg0ae62IfQ==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -5170,13 +5545,13 @@
"!win32"
],
"dependencies": {
- "sass": "1.92.1"
+ "sass": "1.93.2"
}
},
"node_modules/sass-embedded-win32-arm64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.92.1.tgz",
- "integrity": "sha512-KvmpQjY9yTBMtTYz4WBqetlv9bGaDW1aStcu7MSTbH7YiSybX/9fnxlCAEQv1WlIidQhcJAiyk0Eae+LGK7cIQ==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.93.2.tgz",
+ "integrity": "sha512-Y90DZDbQvtv4Bt0GTXKlcT9pn4pz8AObEjFF8eyul+/boXwyptPZ/A1EyziAeNaIEIfxyy87z78PUgCeGHsx3Q==",
"cpu": [
"arm64"
],
@@ -5191,9 +5566,9 @@
}
},
"node_modules/sass-embedded-win32-x64": {
- "version": "1.92.1",
- "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.92.1.tgz",
- "integrity": "sha512-B6Nz/GbH7Vkpb2TkQHsGcczWM5t+70VWopWF1x5V5yxLpA8ZzVQ7NTKKi+jDoVY2Efu6ZyzgT9n5KgG2kWliXA==",
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.93.2.tgz",
+ "integrity": "sha512-BbSucRP6PVRZGIwlEBkp+6VQl2GWdkWFMN+9EuOTPrLxCJZoq+yhzmbjspd3PeM8+7WJ7AdFu/uRYdO8tor1iQ==",
"cpu": [
"x64"
],
@@ -5237,9 +5612,9 @@
}
},
"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": {
@@ -5727,9 +6102,9 @@
}
},
"node_modules/typescript": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
- "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -5741,16 +6116,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz",
- "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==",
+ "version": "8.46.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.0.tgz",
+ "integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.44.0",
- "@typescript-eslint/parser": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0"
+ "@typescript-eslint/eslint-plugin": "8.46.0",
+ "@typescript-eslint/parser": "8.46.0",
+ "@typescript-eslint/typescript-estree": "8.46.0",
+ "@typescript-eslint/utils": "8.46.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -5765,12 +6140,43 @@
}
},
"node_modules/undici-types": {
- "version": "7.12.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
- "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==",
+ "version": "7.14.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
+ "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
"dev": true,
"license": "MIT"
},
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -5798,9 +6204,9 @@
"license": "MIT"
},
"node_modules/vite": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.6.tgz",
- "integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==",
+ "version": "7.1.9",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz",
+ "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6239,6 +6645,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -6251,6 +6664,29 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.3.tgz",
+ "integrity": "sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
}
}
}
diff --git a/package.json b/package.json
index fcd4ddc..941c2e7 100644
--- a/package.json
+++ b/package.json
@@ -18,31 +18,31 @@
"@reduxjs/toolkit": "^2.9.0",
"axios": "^1.12.2",
"js-cookie": "^3.0.5",
- "react": "^19.1.1",
- "react-dom": "^19.1.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
"react-redux": "^9.2.0",
- "react-router": "^7.9.1"
+ "react-router": "^7.9.4"
},
"devDependencies": {
- "@eslint/js": "^9.35.0",
+ "@eslint/js": "^9.37.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
"@types/js-cookie": "^3.0.6",
- "@types/node": "^24.5.2",
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
+ "@types/node": "^24.7.2",
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.1",
"@types/react-redux": "^7.1.34",
"@vitejs/plugin-react-swc": "^4.1.0",
"@vitest/coverage-v8": "^3.2.4",
- "eslint": "^9.35.0",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
+ "eslint": "^9.37.0",
+ "eslint-plugin-react-hooks": "^7.0.0",
+ "eslint-plugin-react-refresh": "^0.4.23",
"globals": "^16.4.0",
"jsdom": "^27.0.0",
- "sass-embedded": "^1.92.1",
- "typescript": "~5.9.2",
- "typescript-eslint": "^8.44.0",
- "vite": "^7.1.6",
+ "sass-embedded": "^1.93.2",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.46.0",
+ "vite": "^7.1.9",
"vitest": "^3.2.4"
},
"repository": {
@@ -52,4 +52,4 @@
"bugs": {
"url": "https://github.com/josetomashl/react-template/issues"
}
-}
\ No newline at end of file
+}
diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss
index 42e3f3f..3e19a7c 100644
--- a/src/assets/styles/main.scss
+++ b/src/assets/styles/main.scss
@@ -116,7 +116,7 @@
height: 100dvh;
overflow: hidden;
- > #outlet {
+ > #navigation_outlet {
flex-grow: 1;
overflow: auto;
}
diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx
index a534821..756606b 100644
--- a/src/components/Icon/index.tsx
+++ b/src/components/Icon/index.tsx
@@ -1,4 +1,4 @@
-import { ICONS } from '@/plugins/constants/icons';
+import { ICONS } from '@/plugins/data/icons';
export type IconNames = keyof typeof ICONS;
type Props = {
diff --git a/src/components/Notifications/index.tsx b/src/components/Notification/index.tsx
similarity index 76%
rename from src/components/Notifications/index.tsx
rename to src/components/Notification/index.tsx
index 6bc8e4d..d38146f 100644
--- a/src/components/Notifications/index.tsx
+++ b/src/components/Notification/index.tsx
@@ -1,23 +1,10 @@
import { Icon, type IconNames } from '@/components/Icon';
-import { useAppSelector } from '@/store';
import { type NotificationItem, removeNotification } from '@/store/modules/root';
import { css } from '@/utils';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import styles from './styles.module.scss';
-export const NotificationsContainer = () => {
- const notifications = useAppSelector((state) => state.root.notifications);
-
- return (
-
- {notifications.map((notification) => (
-
- ))}
-
- );
-};
-
export function Notification({ notification }: { notification: NotificationItem }) {
const dispatch = useDispatch();
diff --git a/src/components/Notifications/styles.module.scss b/src/components/Notification/styles.module.scss
similarity index 90%
rename from src/components/Notifications/styles.module.scss
rename to src/components/Notification/styles.module.scss
index 17e520e..2a480ff 100644
--- a/src/components/Notifications/styles.module.scss
+++ b/src/components/Notification/styles.module.scss
@@ -1,13 +1,3 @@
-.notificationsContainer {
- position: fixed;
- top: 20px;
- right: 20px;
- display: flex;
- flex-direction: column;
- gap: 10px;
- z-index: 1000;
-}
-
.notification {
min-width: max(25vw, 120px);
padding: 12px 20px;
diff --git a/src/components/NotificationsContainer/index.tsx b/src/components/NotificationsContainer/index.tsx
new file mode 100644
index 0000000..c53e564
--- /dev/null
+++ b/src/components/NotificationsContainer/index.tsx
@@ -0,0 +1,15 @@
+import { Notification } from '@/components/Notification';
+import { useAppSelector } from '@/store/hooks';
+import styles from './styles.module.scss';
+
+export const NotificationsContainer = () => {
+ const notifications = useAppSelector((state) => state.root.notifications);
+
+ return (
+
+ {notifications.map((notification) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/NotificationsContainer/styles.module.scss b/src/components/NotificationsContainer/styles.module.scss
new file mode 100644
index 0000000..ede7541
--- /dev/null
+++ b/src/components/NotificationsContainer/styles.module.scss
@@ -0,0 +1,9 @@
+.notificationsContainer {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ z-index: 1000;
+}
diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx
new file mode 100644
index 0000000..fff03d2
--- /dev/null
+++ b/src/components/Pagination/index.tsx
@@ -0,0 +1,61 @@
+import { Icon } from '@/components/Icon';
+import { useAppSelector } from '@/store/hooks';
+import styles from './styles.module.scss';
+
+type Props = {
+ module: 'posts'; // add more when necessary
+ onPageChange: (page: number) => Promise | void;
+ onPageSizeChange: (pageSize: number) => Promise | void;
+};
+
+export function Pagination(props: Props) {
+ const module = useAppSelector((state) => state[props.module]);
+ const { page, pageSize, total } = module.pagination;
+
+ const totalPages = Math.ceil(total / pageSize);
+ const canPrev = page > 0;
+ const canNext = page < totalPages - 1;
+
+ const handlePageChange = (p: number) => {
+ props.onPageChange(p);
+ };
+ const handlePageSizeChange = (p: number) => {
+ props.onPageSizeChange(p);
+ };
+
+ const handlePrev = () => handlePageChange(Math.max(page - 1, 0));
+ const handleNext = () => handlePageChange(Math.min(page + 1, totalPages - 1));
+
+ return (
+
+
{total} entries
+
+
+
+
+ Page {totalPages === 0 ? 0 : page + 1} of {totalPages}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Pagination/styles.module.scss b/src/components/Pagination/styles.module.scss
new file mode 100644
index 0000000..fdecad9
--- /dev/null
+++ b/src/components/Pagination/styles.module.scss
@@ -0,0 +1,7 @@
+.paginationContainer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75em;
+ gap: 10px;
+}
diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx
index 29e10c4..01e2321 100644
--- a/src/components/Table/index.tsx
+++ b/src/components/Table/index.tsx
@@ -1,99 +1,73 @@
-import { useAppSelector } from '@/store';
-import { Icon } from '../Icon';
-import { Spinner } from '../Spinner';
+import { Icon } from '@/components/Icon';
+import { toDate, toDateTime, toPrice, toTime } from '@/plugins/transformers';
import styles from './styles.module.scss';
-type HeaderItem = { key: string; label: string };
+type HeaderFormat = 'date' | 'time' | 'datetime' | 'price' | undefined;
+
+type Header = { key: string; label: string | null; format: HeaderFormat };
+type Item = Record;
type Props = {
- headers: HeaderItem[];
- module: 'users' | 'posts';
- onPageChange: (page: number) => Promise | void;
- onPageSizeChange: (pageSize: number) => Promise | void;
+ headers: Header[];
+ items: Item[];
};
-export function Table(props: Props) {
- const module = useAppSelector((state) => state[props.module]);
- const { page, pageSize, total } = module.pagination;
-
- const totalPages = Math.ceil(total / pageSize);
- const canPrev = page > 0;
- const canNext = page < totalPages - 1;
-
- const handlePageChange = (p: number) => {
- props.onPageChange(p);
- };
- const handlePageSizeChange = (p: number) => {
- props.onPageSizeChange(p);
- };
-
- const handlePrev = () => handlePageChange(Math.max(page - 1, 0));
- const handleNext = () => handlePageChange(Math.min(page + 1, totalPages - 1));
-
- if (module.loading) {
- return ;
+const formatValue = (value: string | number, format: HeaderFormat): string | null => {
+ switch (format) {
+ case 'price':
+ return toPrice(value);
+ case 'date':
+ return typeof value === 'string' ? toDate(value) : String(value);
+ case 'time':
+ return typeof value === 'string' ? toTime(value) : String(value);
+ case 'datetime':
+ return typeof value === 'string' ? toDateTime(value) : String(value);
+ default:
+ return String(value);
}
+};
+
+export function Table({ headers, items }: Props) {
+ if (!headers.length) return null;
return (
- {props.headers.map((item) => (
- | {item.label} |
+ {headers.map(({ key, label }) => (
+ {label ?? ''} |
))}
- {module.list.map((row, rowIndex) => (
+ {items.map((row, rowIndex) => (
- {props.headers.map((header, headerIndex) => (
- |
- {typeof row[header.key as keyof typeof row] === 'boolean' ? (
-
- ) : (
- row[header.key as keyof typeof row] || 'N/A'
- )}
- |
- ))}
+ {headers.map(({ key, format }, colIndex) => {
+ const value = row[key];
+
+ if (typeof value === 'boolean') {
+ return (
+
+
+ |
+ );
+ }
+
+ if (typeof value === 'string' || typeof value === 'number') {
+ return (
+
+ {formatValue(value, format) ?? '-'}
+ |
+ );
+ }
+
+ return - | ;
+ })}
))}
-
-
{total} entries
-
-
-
-
- Page {totalPages === 0 ? 0 : page + 1} of {totalPages}
-
-
-
-
-
-
-
-
);
}
diff --git a/src/plugins/constants/modules/defaults.ts b/src/constants/defaults.ts
similarity index 100%
rename from src/plugins/constants/modules/defaults.ts
rename to src/constants/defaults.ts
diff --git a/src/plugins/constants/modules/posts.ts b/src/constants/posts.ts
similarity index 100%
rename from src/plugins/constants/modules/posts.ts
rename to src/constants/posts.ts
diff --git a/src/plugins/constants/modules/users.ts b/src/constants/users.ts
similarity index 100%
rename from src/plugins/constants/modules/users.ts
rename to src/constants/users.ts
diff --git a/src/contexts/authContext.tsx b/src/contexts/authContext.tsx
index 5535ebb..3462625 100644
--- a/src/contexts/authContext.tsx
+++ b/src/contexts/authContext.tsx
@@ -1,22 +1,20 @@
import { createContext, useState, type PropsWithChildren } from 'react';
import { useNavigate } from 'react-router';
-import type { AuthResponse, LoginRequest, RefreshRequest, RegisterRequest } from '@/dtos/Auth';
+import type { AuthResponse, LoginRequest, RegisterRequest } from '@/dtos/Auth';
import type { UserItem } from '@/dtos/User';
import { useCookie } from '@/hooks/useCookie';
import { useLocalStorage } from '@/hooks/useLocalStorage';
import axiosInstance, { BaseResponse } from '@/plugins/axios';
-import { CookieKeys } from '@/plugins/constants/cookies';
-import { StorageKeys } from '@/plugins/constants/storage';
+import { CookieKeys } from '@/plugins/data/cookies';
+import { StorageKeys } from '@/plugins/data/storage';
interface AuthContextType {
isLoading: boolean;
me: UserItem | null;
token: string | null;
- refreshToken: string | null;
login: (data: LoginRequest) => Promise;
register: (data: RegisterRequest) => Promise;
- refresh: (data: RefreshRequest) => Promise;
logout: () => void;
}
@@ -28,16 +26,14 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
const [isLoading, setIsLoading] = useState(false);
const [me, setMe] = useLocalStorage(StorageKeys.USER, null);
const [token, setToken] = useCookie(CookieKeys.USER_TOKEN, null);
- const [refreshToken, setRefreshToken] = useCookie(CookieKeys.USER_REFRESH_TOKEN, null);
const login = async (data: LoginRequest) => {
setIsLoading(true);
const res = await axiosInstance.post>('/login', data);
setIsLoading(false);
if (res.data) {
- setMe(res.data.customer);
+ setMe(res.data.user);
setToken(res.data.token);
- setRefreshToken(res.data.refreshToken);
navigate('/');
}
};
@@ -47,21 +43,8 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
const res = await axiosInstance.post>('/register', data);
setIsLoading(false);
if (res.data) {
- setMe(res.data.customer);
+ setMe(res.data.user);
setToken(res.data.token);
- setRefreshToken(res.data.refreshToken);
- navigate('/');
- }
- };
-
- const refresh = async (data: RefreshRequest) => {
- setIsLoading(true);
- const res = await axiosInstance.post>('/refresh', data);
- setIsLoading(false);
- if (res.data) {
- setMe(res.data.customer);
- setToken(res.data.token);
- setRefreshToken(res.data.refreshToken);
navigate('/');
}
};
@@ -70,14 +53,11 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
setIsLoading(true);
setMe(null);
setToken(null);
- setRefreshToken(null);
setIsLoading(false);
};
return (
-
- {children}
-
+ {children}
);
};
diff --git a/src/dtos/Auth.ts b/src/dtos/Auth.ts
index 51eb9bb..e209ef6 100644
--- a/src/dtos/Auth.ts
+++ b/src/dtos/Auth.ts
@@ -4,17 +4,15 @@ export interface LoginRequest {
email: string;
password: string;
}
+
export interface RegisterRequest {
name: string;
surname: string;
email: string;
password: string;
}
-export interface RefreshRequest {
- refreshToken: string;
-}
+
export interface AuthResponse {
- customer: UserItem;
+ user: UserItem;
token: string;
- refreshToken: string;
}
diff --git a/src/dtos/Post.ts b/src/dtos/Post.ts
index a068903..ee87ca9 100644
--- a/src/dtos/Post.ts
+++ b/src/dtos/Post.ts
@@ -1,16 +1,40 @@
-import { POSTS } from '@/plugins/constants/modules/posts';
+import { POSTS } from '@/constants/posts';
+import type { TagKV } from './Tag';
export interface PostKV {
- hash: string;
+ id: string;
title: string;
}
+
export interface PostList extends PostKV {
- author: string;
+ tags: TagKV[];
+ status: keyof typeof POSTS.status;
+ updatedAt: string;
+ deletedAt: string | null;
+}
+
+export interface PostItem extends PostList {
+ content: string;
+ user: {
+ id: string;
+ full_name: string;
+ email: string;
+ role: string;
+ posts: PostKV[];
+ };
+ createdAt: string;
+}
+
+export interface UpdatePostBody {
+ title: string;
+ content: string;
tags: string[];
status: keyof typeof POSTS.status;
}
-export interface PostItem extends PostKV {
+
+export interface CreatePostBody {
+ title: string;
content: string;
- createdAt: Date;
- updatedAt: Date;
+ tags: string[];
+ status: keyof typeof POSTS.status;
}
diff --git a/src/dtos/Tag.ts b/src/dtos/Tag.ts
new file mode 100644
index 0000000..603ab9e
--- /dev/null
+++ b/src/dtos/Tag.ts
@@ -0,0 +1,14 @@
+export interface TagKV {
+ id: string;
+ name: string;
+}
+
+export interface TagList extends TagKV {
+ description?: string;
+ updatedAt: string;
+ deletedAt: string | null;
+}
+
+export interface TagItem extends TagList {
+ createdAt: string;
+}
diff --git a/src/dtos/User.ts b/src/dtos/User.ts
index 829e96a..de3c792 100644
--- a/src/dtos/User.ts
+++ b/src/dtos/User.ts
@@ -1,22 +1,24 @@
-import { USERS } from '@/plugins/constants/modules/users';
+import { USERS } from '@/constants/users';
+import type { PostList } from './Post';
export type UserRole = keyof typeof USERS.roles;
export type UserStatus = keyof typeof USERS.status;
export interface UserKV {
- hash: string;
+ id: string;
full_name: string;
}
export interface UserList extends UserKV {
email: string;
- createdAt: Date;
+ role: UserRole;
+ updatedAt: string;
+ deletedAt: string | null;
}
export interface UserItem extends UserList {
name: string;
surname: string;
- role: UserRole;
- status: UserStatus;
- updatedAt: Date;
+ posts: PostList[];
+ createdAt: string;
}
diff --git a/src/layouts/Navigation.tsx b/src/layouts/Navigation.tsx
index 1455214..644fada 100644
--- a/src/layouts/Navigation.tsx
+++ b/src/layouts/Navigation.tsx
@@ -6,7 +6,7 @@ export function NavigationLayout() {
return (
-
diff --git a/src/main.tsx b/src/main.tsx
index b7908a3..65ad34d 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,14 +1,15 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router';
+
import '@/assets/styles/main.scss';
import '@/assets/styles/reset.scss';
-import { NotificationsContainer } from '@/components/Notifications';
+import { NotificationsContainer } from '@/components/NotificationsContainer';
import { AuthProvider } from '@/contexts/authContext';
import { TranslationProvider } from '@/contexts/translationContext';
import { AppRoutes } from '@/routes';
import { store } from '@/store';
-import { StrictMode } from 'react';
-import { createRoot } from 'react-dom/client';
-import { Provider } from 'react-redux';
-import { BrowserRouter } from 'react-router';
createRoot(document.getElementById('root') as HTMLElement).render(
diff --git a/src/pages/auth/login/index.tsx b/src/pages/auth/login/index.tsx
index e138dc3..48b04b0 100644
--- a/src/pages/auth/login/index.tsx
+++ b/src/pages/auth/login/index.tsx
@@ -1,7 +1,7 @@
import { Input } from '@/components/Input';
import { useAuth } from '@/hooks/useAuth';
import { useTitle } from '@/hooks/useTitle';
-import { REGEXP } from '@/plugins/regex';
+import { REGEX } from '@/plugins/data/regex';
import { type FormEvent, useState } from 'react';
import styles from './styles.module.scss';
@@ -39,7 +39,7 @@ export function LoginPage() {
errors: !valid ? [...prev.errors, 'email'] : prev.errors.filter((e) => e !== 'email')
}));
}}
- regExp={REGEXP.email}
+ regExp={REGEX.email}
errorMessage='Please enter a valid email address'
required
/>
@@ -55,7 +55,7 @@ export function LoginPage() {
}));
}}
required
- regExp={REGEXP.password}
+ regExp={REGEX.password}
errorMessage='Password must be at least 8 characters long and contain at least one number, one uppercase letter and one lowercase letter'
/>
diff --git a/src/pages/page1/index.tsx b/src/pages/page1/index.tsx
index d7150da..b546898 100644
--- a/src/pages/page1/index.tsx
+++ b/src/pages/page1/index.tsx
@@ -23,7 +23,7 @@ export function Page1() {
return (
Page 1
- {/*
Post id: {post.hash}
*/}
+ {/*
Post id: {post.id}
*/}
);
}
diff --git a/src/plugins/axios.ts b/src/plugins/axios.ts
index 4ba5884..9b45696 100644
--- a/src/plugins/axios.ts
+++ b/src/plugins/axios.ts
@@ -1,7 +1,7 @@
import axios from 'axios';
import Cookies from 'js-cookie';
-import { CookieKeys } from '@/plugins/constants/cookies';
+import { CookieKeys } from '@/plugins/data/cookies';
import { store } from '@/store';
import { pushNotification } from '@/store/modules/root';
diff --git a/src/plugins/constants/cookies.ts b/src/plugins/constants/cookies.ts
deleted file mode 100644
index 6fadb89..0000000
--- a/src/plugins/constants/cookies.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type CookieKey = 'USER_HASH' | 'USER_TOKEN' | 'USER_REFRESH_TOKEN';
-
-export const CookieKeys: Record = {
- USER_HASH: 'customer_hash',
- USER_TOKEN: 'customer_token',
- USER_REFRESH_TOKEN: 'customer_refresh_token'
-};
diff --git a/src/plugins/constants/colors.ts b/src/plugins/data/colors.ts
similarity index 100%
rename from src/plugins/constants/colors.ts
rename to src/plugins/data/colors.ts
diff --git a/src/plugins/data/cookies.ts b/src/plugins/data/cookies.ts
new file mode 100644
index 0000000..bccb993
--- /dev/null
+++ b/src/plugins/data/cookies.ts
@@ -0,0 +1,6 @@
+export type CookieKey = 'USER_HASH' | 'USER_TOKEN';
+
+export const CookieKeys: Record = {
+ USER_HASH: 'customer_hash',
+ USER_TOKEN: 'customer_token'
+};
diff --git a/src/plugins/constants/icons.ts b/src/plugins/data/icons.ts
similarity index 100%
rename from src/plugins/constants/icons.ts
rename to src/plugins/data/icons.ts
diff --git a/src/plugins/regex.ts b/src/plugins/data/regex.ts
similarity index 90%
rename from src/plugins/regex.ts
rename to src/plugins/data/regex.ts
index c240ecb..5e6344c 100644
--- a/src/plugins/regex.ts
+++ b/src/plugins/data/regex.ts
@@ -1,4 +1,4 @@
-export const REGEXP = {
+export const REGEX = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/,
phone: /^\+?\s*(?:[0-9]\s*){9,15}$/,
diff --git a/src/plugins/constants/storage.ts b/src/plugins/data/storage.ts
similarity index 100%
rename from src/plugins/constants/storage.ts
rename to src/plugins/data/storage.ts
diff --git a/src/plugins/rules.ts b/src/plugins/rules.ts
index d89da30..5692ac1 100644
--- a/src/plugins/rules.ts
+++ b/src/plugins/rules.ts
@@ -1,12 +1,12 @@
-import { REGEXP } from './regex';
+import { REGEX } from './data/regex';
const required = (v: unknown) => !!v || v === 0 || 'Campo obligatorio.';
-const email = (v: string) => !v || REGEXP.email.test(v) || 'Introduce un email válido.';
-const password = (v: string) => !v || REGEXP.password.test(v) || 'Introduce una contraseña válida.';
-const phone = (v: string) => !v || REGEXP.phone.test(v) || 'Introduce un teléfono válido.';
-const url = (v: string) => !v || REGEXP.url.test(v) || 'Introduce una url válida.';
+const email = (v: string) => !v || REGEX.email.test(v) || 'Introduce un email válido.';
+const password = (v: string) => !v || REGEX.password.test(v) || 'Introduce una contraseña válida.';
+const phone = (v: string) => !v || REGEX.phone.test(v) || 'Introduce un teléfono válido.';
+const url = (v: string) => !v || REGEX.url.test(v) || 'Introduce una url válida.';
const currency = (v: string) =>
- REGEXP.currency.test(v) || 'Introduce un importe válido como 1234.56, 1234,56 ó -1234.56.';
+ REGEX.currency.test(v) || 'Introduce un importe válido como 1234.56, 1234,56 ó -1234.56.';
const match = (v1: string, v2: string, fieldName?: string) =>
v1 === v2 || (fieldName ? `Debe coincidir con el campo "${fieldName}".` : 'Las campos no coinciden.');
const maxLength = (v: string, n = 3) => !v || v.length <= n || `Máximo ${n} caracteres.`;
diff --git a/src/plugins/transformers.ts b/src/plugins/transformers.ts
index d019d82..df5c441 100644
--- a/src/plugins/transformers.ts
+++ b/src/plugins/transformers.ts
@@ -44,9 +44,10 @@ export function toPrice(str: number | string, isAbsolute?: boolean): string | nu
}
/**
- * Formats a date string into a localized format with flexible placeholders.
+ * Formats a date string into a localized string.
*
- * Supported placeholders:
+ * @param date - Input date string. If invalid, current date is used.
+ * @param format - Format string using supported placeholders. Defaults to 'DD/MM/YYYY'. Supported placeholders:
* - D: Day of month (no leading zero)
* - DD: Day of month (2 digits)
* - M: Month number (no leading zero)
@@ -55,9 +56,6 @@ export function toPrice(str: number | string, isAbsolute?: boolean): string | nu
* - MMMM: Full month name (e.g., 'january')
* - YY: Last 2 digits of year
* - YYYY: Full year
- *
- * @param date - Input date string. If invalid, current date is used.
- * @param format - Format string using supported placeholders.
* @param locale - BCP 47 locale string (e.g., 'es', 'en-US'). Defaults to 'es'.
* @returns A formatted, localized date string.
*/
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index c2b433c..4eafc2a 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -1,8 +1,9 @@
+import { Route, Routes } from 'react-router';
+
import { AuthMiddleware } from '@/middlewares/Auth';
import { LoginPage } from '@/pages/auth/login';
import { RegisterPage } from '@/pages/auth/register';
-import { Route, Routes } from 'react-router';
-import { ProtectedRoutes } from './protected';
+import { ProtectedRoutes } from '@/routes/protected';
export function AppRoutes() {
return (
diff --git a/src/routes/protected.tsx b/src/routes/protected.tsx
index 4b4be1e..fd1b71a 100644
--- a/src/routes/protected.tsx
+++ b/src/routes/protected.tsx
@@ -1,12 +1,12 @@
import { Route, Routes } from 'react-router';
+import { USERS } from '@/constants/users';
import { NavigationLayout } from '@/layouts/Navigation';
import { RoleMiddleware } from '@/middlewares/Role';
import { NotFoundPage } from '@/pages/404';
import { HomePage } from '@/pages/home';
import { Page1 } from '@/pages/page1';
import { Page2 } from '@/pages/page2';
-import { USERS } from '@/plugins/constants/modules/users';
export function ProtectedRoutes() {
return (
diff --git a/src/store/hooks.ts b/src/store/hooks.ts
new file mode 100644
index 0000000..efbc8a5
--- /dev/null
+++ b/src/store/hooks.ts
@@ -0,0 +1,11 @@
+import { useDispatch, useSelector } from 'react-redux';
+
+import { store } from '@/store';
+
+// type AppStore = typeof store;
+export type AppDispatch = typeof store.dispatch;
+export type AppState = ReturnType;
+
+// export const useAppStore = useStore.withTypes();
+export const useAppDispatch = useDispatch.withTypes();
+export const useAppSelector = useSelector.withTypes();
diff --git a/src/store/index.ts b/src/store/index.ts
index af657bb..89cd055 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,22 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
-import { useDispatch, useSelector } from 'react-redux';
import { postsSlice } from '@/store/modules/posts';
import { rootSlice } from '@/store/modules/root';
-import { usersSlice } from '@/store/modules/users';
export const store = configureStore({
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
reducer: {
root: rootSlice.reducer,
- users: usersSlice.reducer,
posts: postsSlice.reducer
}
});
-
-export type AppStore = typeof store;
-export type AppDispatch = typeof store.dispatch;
-export type AppState = ReturnType;
-
-export const useAppDispatch = useDispatch.withTypes();
-export const useAppSelector = useSelector.withTypes();
diff --git a/src/store/modules/posts.ts b/src/store/modules/posts.ts
index 0728591..88327e7 100644
--- a/src/store/modules/posts.ts
+++ b/src/store/modules/posts.ts
@@ -1,7 +1,7 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import type { Pagination } from '@/dtos';
-import type { PostItem, PostList } from '@/dtos/Post';
+import type { CreatePostBody, PostItem, PostList, UpdatePostBody } from '@/dtos/Post';
import axiosInstance, { type BaseResponse } from '@/plugins/axios';
import { createAppAsyncThunk } from '@/store/thunk';
@@ -50,9 +50,31 @@ export const requestPost = createAppAsyncThunk('posts/getItem', async (id: strin
}
});
-export const createPost = createAppAsyncThunk('posts/postItem', async (data: { title: string; content: string }) => {
+export const createPost = createAppAsyncThunk('posts/postItem', async (data: CreatePostBody) => {
try {
- const response = await axiosInstance.post>(`/posts`, data);
+ const response = await axiosInstance.post>(`/posts`, data);
+ return response.data;
+ } catch {
+ return;
+ }
+});
+export const updatePost = createAppAsyncThunk(
+ 'posts/updateItem',
+ async (data: { id: string; payload: UpdatePostBody }) => {
+ try {
+ const response = await axiosInstance.patch>(
+ `/posts/${data.id}`,
+ data.payload
+ );
+ return response.data;
+ } catch {
+ return;
+ }
+ }
+);
+export const deletePost = createAppAsyncThunk('posts/deleteItem', async (id: string) => {
+ try {
+ const response = await axiosInstance.delete>(`/posts/${id}`);
return response.data;
} catch {
return;
@@ -126,6 +148,33 @@ export const postsSlice = createSlice({
}
state.loading = false;
});
+ builder
+ .addCase(updatePost.pending, (state) => {
+ state.loading = true;
+ })
+ .addCase(updatePost.rejected, (state) => {
+ state.loading = false;
+ })
+ .addCase(updatePost.fulfilled, (state, action) => {
+ if (action.payload) {
+ state.item = action.payload;
+ }
+ state.loading = false;
+ });
+ builder
+ .addCase(deletePost.pending, (state) => {
+ state.loading = true;
+ })
+ .addCase(deletePost.rejected, (state) => {
+ state.loading = false;
+ })
+ .addCase(deletePost.fulfilled, (state, action) => {
+ if (action.payload) {
+ state.item = null;
+ state.list.filter((i) => i.id !== action.payload!.id);
+ }
+ state.loading = false;
+ });
}
});
diff --git a/src/store/modules/root.ts b/src/store/modules/root.ts
index c43e030..ed749cd 100644
--- a/src/store/modules/root.ts
+++ b/src/store/modules/root.ts
@@ -1,10 +1,9 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
-export type NotificationType = 'success' | 'error' | 'info' | 'warning';
export interface NotificationItem {
id: string;
message: string;
- type: NotificationType;
+ type: 'success' | 'error' | 'info' | 'warning';
}
interface RootState {
diff --git a/src/store/modules/users.ts b/src/store/modules/users.ts
deleted file mode 100644
index ec450bc..0000000
--- a/src/store/modules/users.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
-
-import type { Pagination } from '@/dtos';
-import type { UserItem, UserList } from '@/dtos/User';
-import axiosInstance, { type BaseResponse } from '@/plugins/axios';
-import { createAppAsyncThunk } from '@/store/thunk';
-
-interface UsersState {
- loading: boolean;
- list: UserList[];
- item: UserItem | null;
- pagination: {
- page: number;
- pageSize: number;
- total: number;
- };
-}
-
-const initialState: UsersState = {
- loading: false,
- list: [],
- item: null,
- pagination: {
- page: 0,
- pageSize: 10,
- total: 0
- }
-};
-
-export const usersSlice = createSlice({
- name: 'users',
- initialState,
- reducers: {
- setList: (state, action: PayloadAction) => {
- state.list = action.payload;
- },
- setItem: (state, action: PayloadAction) => {
- state.item = action.payload;
- },
- setPage: (state, action: PayloadAction) => {
- state.pagination.page = action.payload;
- },
- setPageSize: (state, action: PayloadAction) => {
- state.pagination.pageSize = action.payload;
- },
- resetUsers: (state) => {
- state.loading = false;
- state.list = [];
- state.item = null;
- state.pagination = initialState.pagination;
- }
- },
- extraReducers: (builder) => {
- builder
- .addCase(requestUsers.pending, (state) => {
- state.loading = true;
- })
- .addCase(requestUsers.rejected, (state) => {
- state.list = [];
- state.item = null;
- state.pagination = initialState.pagination;
- state.loading = false;
- })
- .addCase(requestUsers.fulfilled, (state, action) => {
- if (action.payload) {
- state.list = action.payload.items;
- state.pagination = {
- page: action.payload.page,
- pageSize: action.payload.pageSize,
- total: action.payload.total
- };
- }
- state.loading = false;
- });
- builder
- .addCase(requestUser.pending, (state) => {
- state.loading = true;
- })
- .addCase(requestUser.rejected, (state) => {
- state.item = null;
- state.loading = false;
- })
- .addCase(requestUser.fulfilled, (state, action) => {
- if (action.payload) {
- state.item = action.payload;
- }
- state.loading = false;
- });
- }
-});
-
-export const { setList, setItem, setPage, setPageSize } = usersSlice.actions;
-
-export const requestUsers = createAppAsyncThunk('users/getList', async (data: { page: number; pageSize: number }) => {
- try {
- const response = await axiosInstance.get>>('/users', {
- params: {
- page: data.page,
- pageSize: data.pageSize
- }
- });
- return response.data;
- } catch {
- return;
- }
-});
-
-export const requestUser = createAppAsyncThunk('users/getItem', async (id: string) => {
- try {
- const response = await axiosInstance.get>(`/users/${id}`);
- return response.data;
- } catch {
- return;
- }
-});
diff --git a/src/store/thunk.ts b/src/store/thunk.ts
index 19bc4c1..7988a56 100644
--- a/src/store/thunk.ts
+++ b/src/store/thunk.ts
@@ -1,5 +1,5 @@
import { createAsyncThunk } from '@reduxjs/toolkit';
-import type { AppDispatch, AppState } from '@/store';
+import { AppDispatch, AppState } from '@/store/hooks';
export const createAppAsyncThunk = createAsyncThunk.withTypes<{ state: AppState; dispatch: AppDispatch }>();
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 503e5f1..66afa39 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -7,3 +7,17 @@
export function css(...classes: string[]) {
return classes.join(' ');
}
+
+/**
+ * Checks if a given string can be parsed into a valid date.
+ *
+ * @param str - The string to check.
+ * @returns `true` if the string represents a valid date, otherwise `false`.
+ */
+export function isDate(str?: string): boolean {
+ if (!str || typeof str !== 'string' || str.trim() === '') {
+ return false;
+ }
+ const date = new Date(str);
+ return !isNaN(date.getTime());
+}
diff --git a/tests/hooks/useAuth.test.tsx b/tests/hooks/useAuth.test.tsx
index 611153f..f0e67e1 100644
--- a/tests/hooks/useAuth.test.tsx
+++ b/tests/hooks/useAuth.test.tsx
@@ -25,8 +25,6 @@ describe('useAuth', () => {
const { result } = renderHook(() => useAuth(), { wrapper });
expect(result.current.login).toBeDefined();
expect(result.current.logout).toBeDefined();
- expect(result.current.refresh).toBeDefined();
- expect(result.current.refreshToken).toBeDefined();
expect(result.current.register).toBeDefined();
expect(result.current.token).toBeDefined();
});
diff --git a/tests/plugins/regex.test.ts b/tests/plugins/regex.test.ts
index bb9e6bb..c0bb0f4 100644
--- a/tests/plugins/regex.test.ts
+++ b/tests/plugins/regex.test.ts
@@ -1,82 +1,82 @@
import { describe, expect, it } from 'vitest';
-import { REGEXP } from '@/plugins/regex';
+import { REGEX } from '@/plugins/data/regex';
-describe('REGEXP', () => {
+describe('REGEX', () => {
describe('email', () => {
it('matches valid emails', () => {
- expect(REGEXP.email.test('test@example.com')).toBe(true);
- expect(REGEXP.email.test('user.name+tag@domain.co')).toBe(true);
+ expect(REGEX.email.test('test@example.com')).toBe(true);
+ expect(REGEX.email.test('user.name+tag@domain.co')).toBe(true);
});
it('does not match invalid emails', () => {
- expect(REGEXP.email.test('testexample.com')).toBe(false);
- expect(REGEXP.email.test('test@.com')).toBe(false);
- expect(REGEXP.email.test('test@com')).toBe(false);
- expect(REGEXP.email.test('test@com.')).toBe(false);
+ expect(REGEX.email.test('testexample.com')).toBe(false);
+ expect(REGEX.email.test('test@.com')).toBe(false);
+ expect(REGEX.email.test('test@com')).toBe(false);
+ expect(REGEX.email.test('test@com.')).toBe(false);
});
});
describe('password', () => {
it('matches valid passwords', () => {
- expect(REGEXP.password.test('Password1')).toBe(true);
- expect(REGEXP.password.test('Abcdefg1')).toBe(true);
+ expect(REGEX.password.test('Password1')).toBe(true);
+ expect(REGEX.password.test('Abcdefg1')).toBe(true);
});
it('does not match invalid passwords', () => {
- expect(REGEXP.password.test('password')).toBe(false); // no uppercase, no digit
- expect(REGEXP.password.test('PASSWORD')).toBe(false); // no lowercase, no digit
- expect(REGEXP.password.test('Password')).toBe(false); // no digit
- expect(REGEXP.password.test('Pass1')).toBe(false); // too short
+ expect(REGEX.password.test('password')).toBe(false); // no uppercase, no digit
+ expect(REGEX.password.test('PASSWORD')).toBe(false); // no lowercase, no digit
+ expect(REGEX.password.test('Password')).toBe(false); // no digit
+ expect(REGEX.password.test('Pass1')).toBe(false); // too short
});
});
describe('phone', () => {
it('matches valid phone numbers', () => {
- expect(REGEXP.phone.test('+34 123456789')).toBe(true);
- expect(REGEXP.phone.test('+34123456789')).toBe(true);
- expect(REGEXP.phone.test('+12345 67890 12345')).toBe(true);
- expect(REGEXP.phone.test('123456789')).toBe(true);
- expect(REGEXP.phone.test('123 45 67 89')).toBe(true);
+ expect(REGEX.phone.test('+34 123456789')).toBe(true);
+ expect(REGEX.phone.test('+34123456789')).toBe(true);
+ expect(REGEX.phone.test('+12345 67890 12345')).toBe(true);
+ expect(REGEX.phone.test('123456789')).toBe(true);
+ expect(REGEX.phone.test('123 45 67 89')).toBe(true);
});
it('does not match invalid phone numbers', () => {
- expect(REGEXP.phone.test('+123')).toBe(false); // too short
- expect(REGEXP.phone.test('+12 34 562 901 234 562')).toBe(false); // too long
- expect(REGEXP.phone.test('+12345abcde')).toBe(false); // with letters
- expect(REGEXP.phone.test('abcde')).toBe(false); // only letters
+ expect(REGEX.phone.test('+123')).toBe(false); // too short
+ expect(REGEX.phone.test('+12 34 562 901 234 562')).toBe(false); // too long
+ expect(REGEX.phone.test('+12345abcde')).toBe(false); // with letters
+ expect(REGEX.phone.test('abcde')).toBe(false); // only letters
});
});
describe('url', () => {
it('matches valid urls', () => {
- expect(REGEXP.url.test('http://example.com')).toBe(true);
- expect(REGEXP.url.test('https://example.com/path')).toBe(true);
- expect(REGEXP.url.test('ftp://example.com')).toBe(true);
- expect(REGEXP.url.test('http://example.co.uk')).toBe(true);
- expect(REGEXP.url.test('')).toBe(true); // optional
+ expect(REGEX.url.test('http://example.com')).toBe(true);
+ expect(REGEX.url.test('https://example.com/path')).toBe(true);
+ expect(REGEX.url.test('ftp://example.com')).toBe(true);
+ expect(REGEX.url.test('http://example.co.uk')).toBe(true);
+ expect(REGEX.url.test('')).toBe(true); // optional
});
it('does not match invalid urls', () => {
- expect(REGEXP.url.test('example')).toBe(false);
- expect(REGEXP.url.test('http:/example.com')).toBe(false);
- expect(REGEXP.url.test('http://')).toBe(false);
+ expect(REGEX.url.test('example')).toBe(false);
+ expect(REGEX.url.test('http:/example.com')).toBe(false);
+ expect(REGEX.url.test('http://')).toBe(false);
});
});
describe('currency', () => {
it('matches valid currency formats', () => {
- expect(REGEXP.currency.test('123')).toBe(true);
- expect(REGEXP.currency.test('-123')).toBe(true);
- expect(REGEXP.currency.test('123.45')).toBe(true);
- expect(REGEXP.currency.test('123,45')).toBe(true);
- expect(REGEXP.currency.test('123/45')).toBe(true);
- expect(REGEXP.currency.test('-123.45')).toBe(true);
- expect(REGEXP.currency.test('.45')).toBe(true);
- expect(REGEXP.currency.test('0.99')).toBe(true);
+ expect(REGEX.currency.test('123')).toBe(true);
+ expect(REGEX.currency.test('-123')).toBe(true);
+ expect(REGEX.currency.test('123.45')).toBe(true);
+ expect(REGEX.currency.test('123,45')).toBe(true);
+ expect(REGEX.currency.test('123/45')).toBe(true);
+ expect(REGEX.currency.test('-123.45')).toBe(true);
+ expect(REGEX.currency.test('.45')).toBe(true);
+ expect(REGEX.currency.test('0.99')).toBe(true);
});
it('does not match invalid currency formats', () => {
- expect(REGEXP.currency.test('123.456')).toBe(false); // too many decimals
- expect(REGEXP.currency.test('123,456')).toBe(false); // too many decimals
- expect(REGEXP.currency.test('abc')).toBe(false);
- expect(REGEXP.currency.test('--123')).toBe(false);
- expect(REGEXP.currency.test('123..45')).toBe(false);
+ expect(REGEX.currency.test('123.456')).toBe(false); // too many decimals
+ expect(REGEX.currency.test('123,456')).toBe(false); // too many decimals
+ expect(REGEX.currency.test('abc')).toBe(false);
+ expect(REGEX.currency.test('--123')).toBe(false);
+ expect(REGEX.currency.test('123..45')).toBe(false);
});
});
});
diff --git a/tests/plugins/rules.test.ts b/tests/plugins/rules.test.ts
index 33bf878..b204357 100644
--- a/tests/plugins/rules.test.ts
+++ b/tests/plugins/rules.test.ts
@@ -2,9 +2,9 @@ import { describe, expect, it } from 'vitest';
import { RULES } from '@/plugins/rules';
-// Mock REGEXP used in rules.ts
+// Mock REGEX used in rules.ts
// vi.mock('@/plugins/regexps', () => ({
-// REGEXP: {
+// REGEX: {
// email: { test: (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) },
// password: { test: (v: string) => /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/.test(v) },
// phone: { test: (v: string) => /^\+?\d{9,15}$/.test(v) },
diff --git a/tests/utils/index.test.ts b/tests/utils/index.test.ts
index 7d37e0d..e8e346d 100644
--- a/tests/utils/index.test.ts
+++ b/tests/utils/index.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
-import { css } from '@/utils';
+import { css, isDate } from '@/utils';
describe('utils test suite', () => {
describe('css method', () => {
@@ -19,5 +19,35 @@ describe('utils test suite', () => {
it('handles empty strings in arguments', () => {
expect(css('a', '', 'b')).toBe('a b');
});
+
+ describe('isDate method', () => {
+ it('returns true for valid ISO date string', () => {
+ expect(isDate('2023-01-01')).toBe(true);
+ });
+
+ it('returns true for valid date-time string', () => {
+ expect(isDate('2023-01-01T12:00:00Z')).toBe(true);
+ });
+
+ it('returns false for invalid date string', () => {
+ expect(isDate('not-a-date')).toBe(false);
+ });
+
+ it('returns false for empty string', () => {
+ expect(isDate('')).toBe(false);
+ });
+
+ it('returns false for undefined', () => {
+ expect(isDate(undefined)).toBe(false);
+ });
+
+ it('returns false for string with only spaces', () => {
+ expect(isDate(' ')).toBe(false);
+ });
+
+ it('returns true for valid date with slashes', () => {
+ expect(isDate('12/31/2023')).toBe(true);
+ });
+ });
});
});
diff --git a/tsconfig.app.json b/tsconfig.app.json
index be94bb8..f9e2135 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -1,10 +1,11 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "target": "es2024",
+ "target": "es2022",
"useDefineForClassFields": true,
- "lib": ["ES2024", "DOM", "DOM.Iterable"],
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
+ "types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
@@ -19,6 +20,7 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
diff --git a/tsconfig.node.json b/tsconfig.node.json
index 7e31f3d..3ece748 100644
--- a/tsconfig.node.json
+++ b/tsconfig.node.json
@@ -4,6 +4,7 @@
"target": "ES2024",
"lib": ["ES2024"],
"module": "ESNext",
+ "types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
@@ -17,6 +18,7 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
diff --git a/vite.config.ts b/vite.config.ts
index 7cfe903..bfc3c05 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -21,9 +21,9 @@ export default defineConfig(({ mode }) => {
preprocessorOptions: {
scss: {
additionalData: `
- @use "@/assets/styles/variables" as *;
- @use "@/assets/styles/mixins" as *;
- `
+ @use "@/assets/styles/variables" as *;
+ @use "@/assets/styles/mixins" as *;
+ `
}
}
},
diff --git a/vitest.config.ts b/vitest.config.ts
index 476a4b6..baa96fe 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -1,4 +1,4 @@
-import path from 'node:path';
+import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config';
export default defineConfig({
@@ -11,7 +11,7 @@ export default defineConfig({
reporter: ['text', 'json', 'html']
},
alias: {
- '@': path.resolve(__dirname, './src')
+ '@': resolve(__dirname, './src')
}
}
});