From 978ad775458b9db847e86661b075a98aa157ff77 Mon Sep 17 00:00:00 2001 From: aelassas Date: Sun, 19 Jan 2025 10:56:56 +0100 Subject: [PATCH 01/42] Add AUD currency to mobile app ; update dependencies --- api/package-lock.json | 163 +++--- api/package.json | 10 +- backend/package-lock.json | 476 ++++++++++------- backend/package.json | 34 +- backend/src/common/langHelper.ts | 6 +- frontend/package-lock.json | 492 ++++++++++------- frontend/package.json | 38 +- frontend/src/common/langHelper.ts | 6 +- mobile/config/env.config.ts | 6 +- mobile/package-lock.json | 855 ++++++++++++++---------------- mobile/package.json | 38 +- 11 files changed, 1124 insertions(+), 1000 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index bd24b6681..64a901b74 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -21,7 +21,7 @@ "@types/express": "^4.17.21", "@types/jest": "^29.5.14", "@types/multer": "^1.4.12", - "@types/node": "^22.10.5", + "@types/node": "^22.10.7", "@types/nodemailer": "^6.4.17", "@types/supertest": "^6.0.2", "@types/validator": "^13.12.2", @@ -42,7 +42,7 @@ "i18n-js": "^4.5.1", "jest": "^29.7.0", "jose": "^5.9.6", - "mongoose": "^8.9.3", + "mongoose": "^8.9.5", "multer": "^1.4.5-lts.1", "nanoid": "^5.0.9", "nocache": "^4.0.0", @@ -55,13 +55,13 @@ "winston": "^3.17.0" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^8.19.0", - "@typescript-eslint/parser": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.31.0", "nodemon": "^3.1.9", - "npm-check-updates": "^17.1.13", + "npm-check-updates": "^17.1.14", "tsx": "^4.19.2" } }, @@ -3296,9 +3296,9 @@ } }, "node_modules/@types/node": { - "version": "22.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", - "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -3407,21 +3407,21 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", - "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/type-utils": "8.19.0", - "@typescript-eslint/utils": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3437,16 +3437,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -3462,14 +3462,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3480,16 +3480,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", - "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3504,9 +3504,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "dev": true, "license": "MIT", "engines": { @@ -3518,20 +3518,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3571,16 +3571,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", - "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3595,13 +3595,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -4263,11 +4263,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -5867,9 +5868,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -5877,7 +5878,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -5948,9 +5949,10 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6924,6 +6926,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -8008,11 +8011,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -8177,9 +8181,9 @@ } }, "node_modules/mongoose": { - "version": "8.9.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.3.tgz", - "integrity": "sha512-G50GNPdMqhoiRAJ/24GYAzg13yxXDD3FOOFeYiFwtHmHpAJem3hxbYIxAhLJGWbYEiUZL0qFMu2LXYkgGAmo+Q==", + "version": "8.9.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.5.tgz", + "integrity": "sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg==", "license": "MIT", "dependencies": { "bson": "^6.10.1", @@ -8419,9 +8423,9 @@ } }, "node_modules/npm-check-updates": { - "version": "17.1.13", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.13.tgz", - "integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==", + "version": "17.1.14", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.14.tgz", + "integrity": "sha512-dr4bXIxETubLI1tFGeock5hN8yVjahvaVpx+lPO4/O2md3zJuxB7FgH3MIoTvQSCgsgkIRpe0skti01IEAA5tA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -10043,6 +10047,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -10108,16 +10113,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-node": { diff --git a/api/package.json b/api/package.json index 23eec14ba..991a031db 100644 --- a/api/package.json +++ b/api/package.json @@ -29,7 +29,7 @@ "@types/express": "^4.17.21", "@types/jest": "^29.5.14", "@types/multer": "^1.4.12", - "@types/node": "^22.10.5", + "@types/node": "^22.10.7", "@types/nodemailer": "^6.4.17", "@types/supertest": "^6.0.2", "@types/validator": "^13.12.2", @@ -50,7 +50,7 @@ "i18n-js": "^4.5.1", "jest": "^29.7.0", "jose": "^5.9.6", - "mongoose": "^8.9.3", + "mongoose": "^8.9.5", "multer": "^1.4.5-lts.1", "nanoid": "^5.0.9", "nocache": "^4.0.0", @@ -63,13 +63,13 @@ "winston": "^3.17.0" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^8.19.0", - "@typescript-eslint/parser": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.31.0", "nodemon": "^3.1.9", - "npm-check-updates": "^17.1.13", + "npm-check-updates": "^17.1.14", "tsx": "^4.19.2" } } diff --git a/backend/package-lock.json b/backend/package-lock.json index 47f4333b1..690d26c73 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,16 +10,16 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.3.1", - "@mui/material": "^6.3.1", - "@mui/x-data-grid": "^7.23.5", - "@mui/x-date-pickers": "^7.23.3", - "@types/node": "^22.10.5", - "@types/react": "^19.0.2", - "@types/react-dom": "^19.0.2", + "@mui/icons-material": "^6.4.0", + "@mui/material": "^6.4.0", + "@mui/x-data-grid": "^7.24.0", + "@mui/x-date-pickers": "^7.24.0", + "@types/node": "^22.10.7", + "@types/react": "^19.0.7", + "@types/react-dom": "^19.0.3", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.19.0", - "@typescript-eslint/parser": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -28,23 +28,23 @@ "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", "history": "^5.3.0", - "localized-strings": "^1.0.0", + "localized-strings": "^2.0.3", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router-dom": "^7.1.1", - "react-toastify": "^11.0.2", + "react-router-dom": "^7.1.3", + "react-toastify": "^11.0.3", "typescript": "^5.2.2", "validator": "^13.12.0", "vite": "^6.0.7" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", - "babel-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", + "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "eslint-config-airbnb": "^19.0.4", - "eslint-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", - "npm-check-updates": "^17.1.13", - "stylelint": "^16.12.0", - "stylelint-config-standard": "^36.0.1", + "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", + "npm-check-updates": "^17.1.14", + "stylelint": "^16.13.2", + "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" } @@ -1381,10 +1381,20 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-+E/LyaAeuABniD/RvUezWVXKpeuvwLEA9//nE9952zBaOdBd2mQ3pPoM8cUe2X6IcMByfuSLzmYqnYshG60+HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3" + } + }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.3.1.tgz", - "integrity": "sha512-2OmnEyoHpj5//dJJpMuxOeLItCCHdf99pjMFfUFdBteCunAK9jW+PwEo4mtdGcLs7P+IgZ+85ypd52eY4AigoQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.0.tgz", + "integrity": "sha512-6u74wi+9zeNlukrCtYYET8Ed/n9AS27DiaXCZKAD3TRGFaqiyYSsQgN2disW83pI/cM1Q2lJY1JX4YfwvNtlNw==", "license": "MIT", "funding": { "type": "opencollective", @@ -1392,9 +1402,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.3.1.tgz", - "integrity": "sha512-nJmWj1PBlwS3t1PnoqcixIsftE+7xrW3Su7f0yrjPw4tVjYrgkhU0hrRp+OlURfZ3ptdSkoBkalee9Bhf1Erfw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.0.tgz", + "integrity": "sha512-zF0Vqt8a+Zp2Oz8P+WvJflba6lLe3PhxIz1NNqn+n4A+wKLPbkeqY8ShmKjPyiCTg0RMbPrp993oUDl9xGsDlQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -1407,7 +1417,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.3.1", + "@mui/material": "^6.4.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1418,16 +1428,16 @@ } }, "node_modules/@mui/material": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.3.1.tgz", - "integrity": "sha512-ynG9ayhxgCsHJ/dtDcT1v78/r2GwQyP3E0hPz3GdPRl0uFJz/uUTtI5KFYwadXmbC+Uv3bfB8laZ6+Cpzh03gA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.0.tgz", + "integrity": "sha512-hNIgwdM9U3DNmowZ8mU59oFmWoDKjc92FqQnQva3Pxh6xRKWtD2Ej7POUHMX8Dwr1OpcSUlT2+tEMeLb7WYsIg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.3.1", - "@mui/system": "^6.3.1", + "@mui/core-downloads-tracker": "^6.4.0", + "@mui/system": "^6.4.0", "@mui/types": "^7.2.21", - "@mui/utils": "^6.3.1", + "@mui/utils": "^6.4.0", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", @@ -1446,7 +1456,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.3.1", + "@mui/material-pigment-css": "^6.4.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1467,13 +1477,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.3.1.tgz", - "integrity": "sha512-g0u7hIUkmXmmrmmf5gdDYv9zdAig0KoxhIQn1JN8IVqApzf/AyRhH3uDGx5mSvs8+a1zb4+0W6LC260SyTTtdQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.0.tgz", + "integrity": "sha512-rNHci8MP6NOdEWAfZ/RBMO5Rhtp1T6fUDMSmingg9F1T6wiUeodIQ+NuTHh2/pMoUSeP9GdHdgMhMmfsXxOMuw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.3.1", + "@mui/utils": "^6.4.0", "prop-types": "^15.8.1" }, "engines": { @@ -1494,9 +1504,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.3.1.tgz", - "integrity": "sha512-/7CC0d2fIeiUxN5kCCwYu4AWUDd9cCTxWCyo0v/Rnv6s8uk6hWgJC3VLZBoDENBHf/KjqDZuYJ2CR+7hD6QYww==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz", + "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1528,16 +1538,16 @@ } }, "node_modules/@mui/system": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.3.1.tgz", - "integrity": "sha512-AwqQ3EAIT2np85ki+N15fF0lFXX1iFPqenCzVOSl3QXKy2eifZeGd9dGtt7pGMoFw5dzW4dRGGzRpLAq9rkl7A==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.0.tgz", + "integrity": "sha512-wTDyfRlaZCo2sW2IuOsrjeE5dl0Usrs6J7DxE3GwNCVFqS5wMplM2YeNiV3DO7s53RfCqbho+gJY6xaB9KThUA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.3.1", - "@mui/styled-engine": "^6.3.1", + "@mui/private-theming": "^6.4.0", + "@mui/styled-engine": "^6.4.0", "@mui/types": "^7.2.21", - "@mui/utils": "^6.3.1", + "@mui/utils": "^6.4.0", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1582,9 +1592,9 @@ } }, "node_modules/@mui/utils": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.3.1.tgz", - "integrity": "sha512-sjGjXAngoio6lniQZKJ5zGfjm+LD2wvLwco7FbKe1fu8A7VIFmz2SwkLb+MDPLNX1lE7IscvNNyh1pobtZg2tw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1612,14 +1622,14 @@ } }, "node_modules/@mui/x-data-grid": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.23.5.tgz", - "integrity": "sha512-JmwdfaegpwO9Ei3PYCKy1FFip9AcdMGzZ0VTqzWE93pvDBVGxs/MZKT0g/8PYHJ6yzA5sBHHBxFN8sKfs7kVsg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.0.tgz", + "integrity": "sha512-goYTKDp+e+dXw7E+WndWUhWXTjX3aTqN8W2dCKhXnmE9Gu8dFwG6Azl7GK9l2m5YHGuqYmpWqcSG9etLdwYaVg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.23.5", + "@mui/x-internals": "7.24.0", "clsx": "^2.1.1", "prop-types": "^15.8.1", "reselect": "^5.1.1" @@ -1648,35 +1658,15 @@ } } }, - "node_modules/@mui/x-data-grid/node_modules/@mui/x-internals": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.23.5.tgz", - "integrity": "sha512-PS6p9qL7otbQ2edSF83GgTicssE0Q84Ta+X/5tSwoCnToEKClka1Wc/cXlsjhRVLmoqz8uTqaiNcZAgnyQWNYQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "@mui/utils": "^5.16.6 || ^6.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/@mui/x-date-pickers": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.23.3.tgz", - "integrity": "sha512-bjTYX/QzD5ZhVZNNnastMUS3j2Hy4p4IXmJgPJ0vKvQBvUdfEO+ZF42r3PJNNde0FVT1MmTzkmdTlz0JZ6ukdw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.0.tgz", + "integrity": "sha512-oBM9Yp2H3tJ7qoHB4APQJYxZG4rz6JD4CwLzbzD9o3r+E1HGpGSLhwK3rDEz9VEjbOq8893Z2TGYLLWoyjeFXQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.23.0", + "@mui/x-internals": "7.24.0", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -1695,7 +1685,7 @@ "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", - "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", "dayjs": "^1.10.7", "luxon": "^3.0.2", "moment": "^2.29.4", @@ -1735,9 +1725,9 @@ } }, "node_modules/@mui/x-internals": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.23.0.tgz", - "integrity": "sha512-bPclKpqUiJYIHqmTxSzMVZi6MH51cQsn5U+8jskaTlo3J4QiMeCYJn/gn7YbeR9GOZFp8hetyHjoQoVHKRXCig==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.0.tgz", + "integrity": "sha512-lYa/XLltxNMY8YAFDopIHrXda2EAoqMCilyGMuPMz+WTG+b+StlUKqtj8cgFPQ/sa5dQ2fR7R3KJdjLREKUrlQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", @@ -2130,9 +2120,9 @@ "peer": true }, "node_modules/@types/node": { - "version": "22.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", - "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2151,18 +2141,18 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.2.tgz", - "integrity": "sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", + "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", "license": "MIT", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", - "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", + "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -2184,20 +2174,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", - "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/type-utils": "8.19.0", - "@typescript-eslint/utils": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2213,15 +2203,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -2237,13 +2227,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2254,15 +2244,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", - "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2277,9 +2267,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2290,19 +2280,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2316,15 +2306,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", - "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2339,12 +2329,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2795,9 +2785,9 @@ } }, "node_modules/babel-plugin-react-compiler": { - "version": "19.0.0-beta-b2e8e9c-20241220", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-b2e8e9c-20241220.tgz", - "integrity": "sha512-aigv5VrOTLUOCeq/t1fuZvvs9Ze1GUfxnleWfiGoZcR0Lo34w3JcGgNiJBoxqlWfFiVnZhzaja9Aa1p6PWAtPg==", + "version": "19.0.0-beta-e552027-20250112", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-e552027-20250112.tgz", + "integrity": "sha512-pUTT0mAZ4XLewC6bvqVeX015nVRLVultcSQlkzGdC10G6YV6K2h4E7cwGlLAuLKWTj3Z08mTO9uTnPP/opUBsg==", "dev": true, "license": "MIT", "dependencies": { @@ -2810,6 +2800,27 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -2870,6 +2881,31 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2877,6 +2913,27 @@ "devOptional": true, "license": "MIT" }, + "node_modules/cacheable": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.8.7.tgz", + "integrity": "sha512-AbfG7dAuYNjYxFUtL1lAqmlWdxczCJ47w7cFjhGcnGnUdwSo6VgmSojfoW3tUI12HUkgTJ5kqj78yyq6TsFtlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.6.0", + "keyv": "^5.2.3" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.2.3.tgz", + "integrity": "sha512-AGKecUfzrowabUv0bH1RIR5Vf7w+l4S3xtQAypKaUpTdIR1EbrAcTxHCrpo9Q+IWeUlFE2palRtgIQcgm+PQJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.0.2" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -4158,9 +4215,9 @@ } }, "node_modules/eslint-plugin-react-compiler": { - "version": "19.0.0-beta-b2e8e9c-20241220", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-b2e8e9c-20241220.tgz", - "integrity": "sha512-STVaOQyivSBv0un6/ujYOPntKcCaD0qXIG8siBEs9QcWmQ7q3J3ozuAE86SlSc7ElIZgPoL9HoSN3EONS47nqQ==", + "version": "19.0.0-beta-e552027-20250112", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-e552027-20250112.tgz", + "integrity": "sha512-VjkIXHouCYyJHgk5HmZ1LH+fAK5CX+ULRX9iNYtwYJ+ljbivFhIT+JJyxNT/USQpCeS2Dt5ahjFeeMv0RRwTww==", "dev": true, "license": "MIT", "dependencies": { @@ -4407,16 +4464,16 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -5048,6 +5105,13 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/hookified": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.7.0.tgz", + "integrity": "sha512-XQdMjqC1AyeOzfs+17cnIk7Wdfu1hh2JtcyNfBf5u9jHrT3iZUlGHxLTntFBuk5lwkqJ6l3+daeQdHK5yByHVA==", + "dev": true, + "license": "MIT" + }, "node_modules/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -5093,6 +5157,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5835,9 +5920,9 @@ "license": "MIT" }, "node_modules/localized-strings": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/localized-strings/-/localized-strings-1.0.0.tgz", - "integrity": "sha512-oji1Y+W/Ll/T2B74oy2Vogjo41OMAxlZymhEqtW+Zk5eu83NPjdwp+rp9yrDQgpjQ3CMYuGbHnqDnPkHBLrTpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/localized-strings/-/localized-strings-2.0.3.tgz", + "integrity": "sha512-eCSC9qK+dRrU9f9xA2glwooQCM5alK//Zj0DyKswZCBAbeefyrdplp6KQiKFeeuFu9K3QbfCQ+0Kdo0cABP6Ww==", "license": "MIT" }, "node_modules/locate-path": { @@ -6075,9 +6160,9 @@ } }, "node_modules/npm-check-updates": { - "version": "17.1.13", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.13.tgz", - "integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==", + "version": "17.1.14", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.14.tgz", + "integrity": "sha512-dr4bXIxETubLI1tFGeock5hN8yVjahvaVpx+lPO4/O2md3zJuxB7FgH3MIoTvQSCgsgkIRpe0skti01IEAA5tA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6591,9 +6676,9 @@ } }, "node_modules/react-router": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", - "integrity": "sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.3.tgz", + "integrity": "sha512-EezYymLY6Guk/zLQ2vRA8WvdUhWFEj5fcE3RfWihhxXBW7+cd1LsIiA3lmx+KCmneAGQuyBv820o44L2+TtkSA==", "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", @@ -6615,12 +6700,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.1.tgz", - "integrity": "sha512-vSrQHWlJ5DCfyrhgo0k6zViOe9ToK8uT5XGSmnuC2R3/g261IdIMpZVqfjD6vWSXdnf5Czs4VA/V60oVR6/jnA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.3.tgz", + "integrity": "sha512-qQGTE+77hleBzv9SIUIkGRvuFBQGagW+TQKy53UTZAO/3+YFNBYvRsNIZ1GT17yHbc63FylMOdS+m3oUriF1GA==", "license": "MIT", "dependencies": { - "react-router": "7.1.1" + "react-router": "7.1.3" }, "engines": { "node": ">=20.0.0" @@ -6631,9 +6716,9 @@ } }, "node_modules/react-toastify": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.2.tgz", - "integrity": "sha512-GjHuGaiXMvbls3ywqv8XdWONwrcO4DXCJIY1zVLkHU73gEElKvTTXNI5Vom3s/k/M8hnkrfsqgBSX3OwmlonbA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.3.tgz", + "integrity": "sha512-cbPtHJPfc0sGqVwozBwaTrTu1ogB9+BLLjd4dDXd863qYLj7DGrQ2sg5RAChjFUB4yc3w8iXOtWcJqPK/6xqRQ==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" @@ -7235,9 +7320,9 @@ } }, "node_modules/stylelint": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.12.0.tgz", - "integrity": "sha512-F8zZ3L/rBpuoBZRvI4JVT20ZanPLXfQLzMOZg1tzPflRVh9mKpOZ8qcSIhh1my3FjAjZWG4T2POwGnmn6a6hbg==", + "version": "16.13.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.13.2.tgz", + "integrity": "sha512-wDlgh0mRO9RtSa3TdidqHd0nOG8MmUyVKl+dxA6C1j8aZRzpNeEgdhFmU5y4sZx4Fc6r46p0fI7p1vR5O2DZqA==", "dev": true, "funding": [ { @@ -7260,16 +7345,16 @@ "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.3", - "css-tree": "^3.0.1", + "css-tree": "^3.1.0", "debug": "^4.3.7", - "fast-glob": "^3.3.2", + "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^9.1.0", + "file-entry-cache": "^10.0.5", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^6.0.2", + "ignore": "^7.0.1", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", @@ -7298,9 +7383,9 @@ } }, "node_modules/stylelint-config-recommended": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", - "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-15.0.0.tgz", + "integrity": "sha512-9LejMFsat7L+NXttdHdTq94byn25TD+82bzGRiV1Pgasl99pWnwipXS5DguTpp3nP1XjvLXVnEJIuYBfsRjRkA==", "dev": true, "funding": [ { @@ -7317,13 +7402,13 @@ "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.1.0" + "stylelint": "^16.13.0" } }, "node_modules/stylelint-config-standard": { - "version": "36.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz", - "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==", + "version": "37.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-37.0.0.tgz", + "integrity": "sha512-+6eBlbSTrOn/il2RlV0zYGQwRTkr+WtzuVSs1reaWGObxnxLpbcspCUYajVQHonVfxVw2U+h42azGhrBvcg8OA==", "dev": true, "funding": [ { @@ -7337,13 +7422,13 @@ ], "license": "MIT", "dependencies": { - "stylelint-config-recommended": "^14.0.1" + "stylelint-config-recommended": "^15.0.0" }, "engines": { "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.1.0" + "stylelint": "^16.13.0" } }, "node_modules/stylelint/node_modules/balanced-match": { @@ -7381,36 +7466,31 @@ } }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", - "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.0.5.tgz", + "integrity": "sha512-umpQsJrBNsdMDgreSryMEXvJh66XeLtZUwA8Gj7rHGearGufUFv6rB/bcXRFsiGWw/VeSUgUofF4Rf2UKEOrTA==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" + "flat-cache": "^6.1.5" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", - "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.5.tgz", + "integrity": "sha512-QR+2kN38f8nMfiIQ1LHYjuDEmZNZVjxuxY+HufbS3BW0EX01Q5OnH7iduOYRutmgiXb797HAKcXUeXrvRjjgSQ==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=18" + "cacheable": "^1.8.7", + "flatted": "^3.3.2", + "hookified": "^1.6.0" } }, "node_modules/stylelint/node_modules/ignore": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", - "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", "dev": true, "license": "MIT", "engines": { @@ -7559,15 +7639,15 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/tsconfig-paths": { diff --git a/backend/package.json b/backend/package.json index dd47b2e6a..b7155c836 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,16 +18,16 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.3.1", - "@mui/material": "^6.3.1", - "@mui/x-data-grid": "^7.23.5", - "@mui/x-date-pickers": "^7.23.3", - "@types/node": "^22.10.5", - "@types/react": "^19.0.2", - "@types/react-dom": "^19.0.2", + "@mui/icons-material": "^6.4.0", + "@mui/material": "^6.4.0", + "@mui/x-data-grid": "^7.24.0", + "@mui/x-date-pickers": "^7.24.0", + "@types/node": "^22.10.7", + "@types/react": "^19.0.7", + "@types/react-dom": "^19.0.3", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.19.0", - "@typescript-eslint/parser": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -36,23 +36,23 @@ "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", "history": "^5.3.0", - "localized-strings": "^1.0.0", + "localized-strings": "^2.0.3", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router-dom": "^7.1.1", - "react-toastify": "^11.0.2", + "react-router-dom": "^7.1.3", + "react-toastify": "^11.0.3", "typescript": "^5.2.2", "validator": "^13.12.0", "vite": "^6.0.7" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", - "babel-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", + "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "eslint-config-airbnb": "^19.0.4", - "eslint-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", - "npm-check-updates": "^17.1.13", - "stylelint": "^16.12.0", - "stylelint-config-standard": "^36.0.1", + "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", + "npm-check-updates": "^17.1.14", + "stylelint": "^16.13.2", + "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" } diff --git a/backend/src/common/langHelper.ts b/backend/src/common/langHelper.ts index 2d8f524b4..82bf78362 100644 --- a/backend/src/common/langHelper.ts +++ b/backend/src/common/langHelper.ts @@ -1,4 +1,4 @@ -import { LocalizedStrings } from 'localized-strings' +import LocalizedStrings from 'localized-strings' import env from '@/config/env.config' import * as UserService from '@/services/UserService' @@ -20,10 +20,10 @@ export const getLanguage = () => { /** * Set LocalizedStrings language. * - * @param {LocalizedStrings} strings + * @param {LocalizedStrings} strings * @param {?string} [language] */ -export const setLanguage = (strings: LocalizedStrings, language?: string) => { +export const setLanguage = (strings: LocalizedStrings, language?: string) => { const lang = language || getLanguage() strings.setLanguage(lang) } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4ccf0dd13..97108569c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,23 +10,23 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.3.1", - "@mui/material": "^6.3.1", - "@mui/x-data-grid": "^7.23.5", - "@mui/x-date-pickers": "^7.23.3", + "@mui/icons-material": "^6.4.0", + "@mui/material": "^6.4.0", + "@mui/x-data-grid": "^7.24.0", + "@mui/x-date-pickers": "^7.24.0", "@stripe/react-stripe-js": "^3.1.1", - "@stripe/stripe-js": "^5.4.0", - "@types/leaflet": "^1.9.15", + "@stripe/stripe-js": "^5.5.0", + "@types/leaflet": "^1.9.16", "@types/leaflet-boundary-canvas": "^1.0.3", - "@types/node": "^22.10.5", + "@types/node": "^22.10.7", "@types/nprogress": "^0.2.3", - "@types/react": "^19.0.2", - "@types/react-dom": "^19.0.2", + "@types/react": "^19.0.7", + "@types/react-dom": "^19.0.3", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.19.0", - "@typescript-eslint/parser": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -38,16 +38,16 @@ "history": "^5.3.0", "leaflet": "^1.9.4", "leaflet-boundary-canvas": "^1.0.0", - "localized-strings": "^1.0.0", + "localized-strings": "^2.0.3", "nprogress": "^0.2.0", "react": "^19.0.0", "react-circle-flags": "^0.0.23", "react-dom": "^19.0.0", "react-ga4": "^2.1.0", "react-leaflet": "^5.0.0", - "react-router-dom": "^7.1.1", + "react-router-dom": "^7.1.3", "react-slick": "^0.30.3", - "react-toastify": "^11.0.2", + "react-toastify": "^11.0.3", "slick-carousel": "^1.8.1", "typescript": "^5.2.2", "validator": "^13.12.0", @@ -55,12 +55,12 @@ }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", - "babel-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", + "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "eslint-config-airbnb": "^19.0.4", - "eslint-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", - "npm-check-updates": "^17.1.13", - "stylelint": "^16.12.0", - "stylelint-config-standard": "^36.0.1", + "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", + "npm-check-updates": "^17.1.14", + "stylelint": "^16.13.2", + "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" } @@ -1397,10 +1397,20 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-+E/LyaAeuABniD/RvUezWVXKpeuvwLEA9//nE9952zBaOdBd2mQ3pPoM8cUe2X6IcMByfuSLzmYqnYshG60+HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3" + } + }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.3.1.tgz", - "integrity": "sha512-2OmnEyoHpj5//dJJpMuxOeLItCCHdf99pjMFfUFdBteCunAK9jW+PwEo4mtdGcLs7P+IgZ+85ypd52eY4AigoQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.0.tgz", + "integrity": "sha512-6u74wi+9zeNlukrCtYYET8Ed/n9AS27DiaXCZKAD3TRGFaqiyYSsQgN2disW83pI/cM1Q2lJY1JX4YfwvNtlNw==", "license": "MIT", "funding": { "type": "opencollective", @@ -1408,9 +1418,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.3.1.tgz", - "integrity": "sha512-nJmWj1PBlwS3t1PnoqcixIsftE+7xrW3Su7f0yrjPw4tVjYrgkhU0hrRp+OlURfZ3ptdSkoBkalee9Bhf1Erfw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.0.tgz", + "integrity": "sha512-zF0Vqt8a+Zp2Oz8P+WvJflba6lLe3PhxIz1NNqn+n4A+wKLPbkeqY8ShmKjPyiCTg0RMbPrp993oUDl9xGsDlQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -1423,7 +1433,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.3.1", + "@mui/material": "^6.4.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1434,16 +1444,16 @@ } }, "node_modules/@mui/material": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.3.1.tgz", - "integrity": "sha512-ynG9ayhxgCsHJ/dtDcT1v78/r2GwQyP3E0hPz3GdPRl0uFJz/uUTtI5KFYwadXmbC+Uv3bfB8laZ6+Cpzh03gA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.0.tgz", + "integrity": "sha512-hNIgwdM9U3DNmowZ8mU59oFmWoDKjc92FqQnQva3Pxh6xRKWtD2Ej7POUHMX8Dwr1OpcSUlT2+tEMeLb7WYsIg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.3.1", - "@mui/system": "^6.3.1", + "@mui/core-downloads-tracker": "^6.4.0", + "@mui/system": "^6.4.0", "@mui/types": "^7.2.21", - "@mui/utils": "^6.3.1", + "@mui/utils": "^6.4.0", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", @@ -1462,7 +1472,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.3.1", + "@mui/material-pigment-css": "^6.4.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1483,13 +1493,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.3.1.tgz", - "integrity": "sha512-g0u7hIUkmXmmrmmf5gdDYv9zdAig0KoxhIQn1JN8IVqApzf/AyRhH3uDGx5mSvs8+a1zb4+0W6LC260SyTTtdQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.0.tgz", + "integrity": "sha512-rNHci8MP6NOdEWAfZ/RBMO5Rhtp1T6fUDMSmingg9F1T6wiUeodIQ+NuTHh2/pMoUSeP9GdHdgMhMmfsXxOMuw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.3.1", + "@mui/utils": "^6.4.0", "prop-types": "^15.8.1" }, "engines": { @@ -1510,9 +1520,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.3.1.tgz", - "integrity": "sha512-/7CC0d2fIeiUxN5kCCwYu4AWUDd9cCTxWCyo0v/Rnv6s8uk6hWgJC3VLZBoDENBHf/KjqDZuYJ2CR+7hD6QYww==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz", + "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1544,16 +1554,16 @@ } }, "node_modules/@mui/system": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.3.1.tgz", - "integrity": "sha512-AwqQ3EAIT2np85ki+N15fF0lFXX1iFPqenCzVOSl3QXKy2eifZeGd9dGtt7pGMoFw5dzW4dRGGzRpLAq9rkl7A==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.0.tgz", + "integrity": "sha512-wTDyfRlaZCo2sW2IuOsrjeE5dl0Usrs6J7DxE3GwNCVFqS5wMplM2YeNiV3DO7s53RfCqbho+gJY6xaB9KThUA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.3.1", - "@mui/styled-engine": "^6.3.1", + "@mui/private-theming": "^6.4.0", + "@mui/styled-engine": "^6.4.0", "@mui/types": "^7.2.21", - "@mui/utils": "^6.3.1", + "@mui/utils": "^6.4.0", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1598,9 +1608,9 @@ } }, "node_modules/@mui/utils": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.3.1.tgz", - "integrity": "sha512-sjGjXAngoio6lniQZKJ5zGfjm+LD2wvLwco7FbKe1fu8A7VIFmz2SwkLb+MDPLNX1lE7IscvNNyh1pobtZg2tw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1628,14 +1638,14 @@ } }, "node_modules/@mui/x-data-grid": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.23.5.tgz", - "integrity": "sha512-JmwdfaegpwO9Ei3PYCKy1FFip9AcdMGzZ0VTqzWE93pvDBVGxs/MZKT0g/8PYHJ6yzA5sBHHBxFN8sKfs7kVsg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.0.tgz", + "integrity": "sha512-goYTKDp+e+dXw7E+WndWUhWXTjX3aTqN8W2dCKhXnmE9Gu8dFwG6Azl7GK9l2m5YHGuqYmpWqcSG9etLdwYaVg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.23.5", + "@mui/x-internals": "7.24.0", "clsx": "^2.1.1", "prop-types": "^15.8.1", "reselect": "^5.1.1" @@ -1664,35 +1674,15 @@ } } }, - "node_modules/@mui/x-data-grid/node_modules/@mui/x-internals": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.23.5.tgz", - "integrity": "sha512-PS6p9qL7otbQ2edSF83GgTicssE0Q84Ta+X/5tSwoCnToEKClka1Wc/cXlsjhRVLmoqz8uTqaiNcZAgnyQWNYQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "@mui/utils": "^5.16.6 || ^6.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/@mui/x-date-pickers": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.23.3.tgz", - "integrity": "sha512-bjTYX/QzD5ZhVZNNnastMUS3j2Hy4p4IXmJgPJ0vKvQBvUdfEO+ZF42r3PJNNde0FVT1MmTzkmdTlz0JZ6ukdw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.0.tgz", + "integrity": "sha512-oBM9Yp2H3tJ7qoHB4APQJYxZG4rz6JD4CwLzbzD9o3r+E1HGpGSLhwK3rDEz9VEjbOq8893Z2TGYLLWoyjeFXQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.23.0", + "@mui/x-internals": "7.24.0", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -1711,7 +1701,7 @@ "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", - "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", "dayjs": "^1.10.7", "luxon": "^3.0.2", "moment": "^2.29.4", @@ -1751,9 +1741,9 @@ } }, "node_modules/@mui/x-internals": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.23.0.tgz", - "integrity": "sha512-bPclKpqUiJYIHqmTxSzMVZi6MH51cQsn5U+8jskaTlo3J4QiMeCYJn/gn7YbeR9GOZFp8hetyHjoQoVHKRXCig==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.0.tgz", + "integrity": "sha512-lYa/XLltxNMY8YAFDopIHrXda2EAoqMCilyGMuPMz+WTG+b+StlUKqtj8cgFPQ/sa5dQ2fR7R3KJdjLREKUrlQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", @@ -2110,9 +2100,9 @@ } }, "node_modules/@stripe/stripe-js": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-5.4.0.tgz", - "integrity": "sha512-3tfMbSvLGB+OsJ2MsjWjWo+7sp29dwx+3+9kG/TEnZQJt+EwbF/Nomm43cSK+6oXZA9uhspgyrB+BbrPRumx4g==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-5.5.0.tgz", + "integrity": "sha512-lkfjyAd34aeMpTKKcEVfy8IUyEsjuAT3t9EXr5yZDtdIUncnZpedl/xLV16Dkd4z+fQwixScsCCDxSMNtBOgpQ==", "license": "MIT", "engines": { "node": ">=12.16" @@ -2186,9 +2176,9 @@ "peer": true }, "node_modules/@types/leaflet": { - "version": "1.9.15", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.15.tgz", - "integrity": "sha512-7UuggAuAs+mva66gtf2OTB1nEhzU/9JED93TIaOEgvFMvG/dIGQaukHE7izHo1Zd+Ko1L4ETUw7TBc8yUxevpg==", + "version": "1.9.16", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.16.tgz", + "integrity": "sha512-wzZoyySUxkgMZ0ihJ7IaUIblG8Rdc8AbbZKLneyn+QjYsj5q1QU7TEKYqwTr10BGSzY5LI7tJk9Ifo+mEjdFRw==", "license": "MIT", "dependencies": { "@types/geojson": "*" @@ -2205,9 +2195,9 @@ } }, "node_modules/@types/node": { - "version": "22.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", - "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2232,18 +2222,18 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.2.tgz", - "integrity": "sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", + "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", "license": "MIT", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", - "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", + "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -2283,20 +2273,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", - "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/type-utils": "8.19.0", - "@typescript-eslint/utils": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2312,15 +2302,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -2336,13 +2326,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2353,15 +2343,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", - "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2376,9 +2366,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2389,19 +2379,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2415,15 +2405,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", - "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2438,12 +2428,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2894,9 +2884,9 @@ } }, "node_modules/babel-plugin-react-compiler": { - "version": "19.0.0-beta-b2e8e9c-20241220", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-b2e8e9c-20241220.tgz", - "integrity": "sha512-aigv5VrOTLUOCeq/t1fuZvvs9Ze1GUfxnleWfiGoZcR0Lo34w3JcGgNiJBoxqlWfFiVnZhzaja9Aa1p6PWAtPg==", + "version": "19.0.0-beta-e552027-20250112", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-e552027-20250112.tgz", + "integrity": "sha512-pUTT0mAZ4XLewC6bvqVeX015nVRLVultcSQlkzGdC10G6YV6K2h4E7cwGlLAuLKWTj3Z08mTO9uTnPP/opUBsg==", "dev": true, "license": "MIT", "dependencies": { @@ -2909,6 +2899,27 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -2969,6 +2980,31 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2976,6 +3012,27 @@ "devOptional": true, "license": "MIT" }, + "node_modules/cacheable": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.8.7.tgz", + "integrity": "sha512-AbfG7dAuYNjYxFUtL1lAqmlWdxczCJ47w7cFjhGcnGnUdwSo6VgmSojfoW3tUI12HUkgTJ5kqj78yyq6TsFtlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.6.0", + "keyv": "^5.2.3" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.2.3.tgz", + "integrity": "sha512-AGKecUfzrowabUv0bH1RIR5Vf7w+l4S3xtQAypKaUpTdIR1EbrAcTxHCrpo9Q+IWeUlFE2palRtgIQcgm+PQJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.0.2" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -4269,9 +4326,9 @@ } }, "node_modules/eslint-plugin-react-compiler": { - "version": "19.0.0-beta-b2e8e9c-20241220", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-b2e8e9c-20241220.tgz", - "integrity": "sha512-STVaOQyivSBv0un6/ujYOPntKcCaD0qXIG8siBEs9QcWmQ7q3J3ozuAE86SlSc7ElIZgPoL9HoSN3EONS47nqQ==", + "version": "19.0.0-beta-e552027-20250112", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-e552027-20250112.tgz", + "integrity": "sha512-VjkIXHouCYyJHgk5HmZ1LH+fAK5CX+ULRX9iNYtwYJ+ljbivFhIT+JJyxNT/USQpCeS2Dt5ahjFeeMv0RRwTww==", "dev": true, "license": "MIT", "dependencies": { @@ -4518,16 +4575,16 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -5165,6 +5222,13 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/hookified": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.7.0.tgz", + "integrity": "sha512-XQdMjqC1AyeOzfs+17cnIk7Wdfu1hh2JtcyNfBf5u9jHrT3iZUlGHxLTntFBuk5lwkqJ6l3+daeQdHK5yByHVA==", + "dev": true, + "license": "MIT" + }, "node_modules/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -5210,6 +5274,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5980,9 +6065,9 @@ "license": "MIT" }, "node_modules/localized-strings": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/localized-strings/-/localized-strings-1.0.0.tgz", - "integrity": "sha512-oji1Y+W/Ll/T2B74oy2Vogjo41OMAxlZymhEqtW+Zk5eu83NPjdwp+rp9yrDQgpjQ3CMYuGbHnqDnPkHBLrTpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/localized-strings/-/localized-strings-2.0.3.tgz", + "integrity": "sha512-eCSC9qK+dRrU9f9xA2glwooQCM5alK//Zj0DyKswZCBAbeefyrdplp6KQiKFeeuFu9K3QbfCQ+0Kdo0cABP6Ww==", "license": "MIT" }, "node_modules/locate-path": { @@ -6219,9 +6304,9 @@ } }, "node_modules/npm-check-updates": { - "version": "17.1.13", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.13.tgz", - "integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==", + "version": "17.1.14", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.14.tgz", + "integrity": "sha512-dr4bXIxETubLI1tFGeock5hN8yVjahvaVpx+lPO4/O2md3zJuxB7FgH3MIoTvQSCgsgkIRpe0skti01IEAA5tA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6773,9 +6858,9 @@ } }, "node_modules/react-router": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", - "integrity": "sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.3.tgz", + "integrity": "sha512-EezYymLY6Guk/zLQ2vRA8WvdUhWFEj5fcE3RfWihhxXBW7+cd1LsIiA3lmx+KCmneAGQuyBv820o44L2+TtkSA==", "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", @@ -6797,12 +6882,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.1.tgz", - "integrity": "sha512-vSrQHWlJ5DCfyrhgo0k6zViOe9ToK8uT5XGSmnuC2R3/g261IdIMpZVqfjD6vWSXdnf5Czs4VA/V60oVR6/jnA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.3.tgz", + "integrity": "sha512-qQGTE+77hleBzv9SIUIkGRvuFBQGagW+TQKy53UTZAO/3+YFNBYvRsNIZ1GT17yHbc63FylMOdS+m3oUriF1GA==", "license": "MIT", "dependencies": { - "react-router": "7.1.1" + "react-router": "7.1.3" }, "engines": { "node": ">=20.0.0" @@ -6830,9 +6915,9 @@ } }, "node_modules/react-toastify": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.2.tgz", - "integrity": "sha512-GjHuGaiXMvbls3ywqv8XdWONwrcO4DXCJIY1zVLkHU73gEElKvTTXNI5Vom3s/k/M8hnkrfsqgBSX3OwmlonbA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.3.tgz", + "integrity": "sha512-cbPtHJPfc0sGqVwozBwaTrTu1ogB9+BLLjd4dDXd863qYLj7DGrQ2sg5RAChjFUB4yc3w8iXOtWcJqPK/6xqRQ==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" @@ -7455,9 +7540,9 @@ } }, "node_modules/stylelint": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.12.0.tgz", - "integrity": "sha512-F8zZ3L/rBpuoBZRvI4JVT20ZanPLXfQLzMOZg1tzPflRVh9mKpOZ8qcSIhh1my3FjAjZWG4T2POwGnmn6a6hbg==", + "version": "16.13.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.13.2.tgz", + "integrity": "sha512-wDlgh0mRO9RtSa3TdidqHd0nOG8MmUyVKl+dxA6C1j8aZRzpNeEgdhFmU5y4sZx4Fc6r46p0fI7p1vR5O2DZqA==", "dev": true, "funding": [ { @@ -7480,16 +7565,16 @@ "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.3", - "css-tree": "^3.0.1", + "css-tree": "^3.1.0", "debug": "^4.3.7", - "fast-glob": "^3.3.2", + "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^9.1.0", + "file-entry-cache": "^10.0.5", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^6.0.2", + "ignore": "^7.0.1", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", @@ -7518,9 +7603,9 @@ } }, "node_modules/stylelint-config-recommended": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", - "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-15.0.0.tgz", + "integrity": "sha512-9LejMFsat7L+NXttdHdTq94byn25TD+82bzGRiV1Pgasl99pWnwipXS5DguTpp3nP1XjvLXVnEJIuYBfsRjRkA==", "dev": true, "funding": [ { @@ -7537,13 +7622,13 @@ "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.1.0" + "stylelint": "^16.13.0" } }, "node_modules/stylelint-config-standard": { - "version": "36.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz", - "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==", + "version": "37.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-37.0.0.tgz", + "integrity": "sha512-+6eBlbSTrOn/il2RlV0zYGQwRTkr+WtzuVSs1reaWGObxnxLpbcspCUYajVQHonVfxVw2U+h42azGhrBvcg8OA==", "dev": true, "funding": [ { @@ -7557,13 +7642,13 @@ ], "license": "MIT", "dependencies": { - "stylelint-config-recommended": "^14.0.1" + "stylelint-config-recommended": "^15.0.0" }, "engines": { "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.1.0" + "stylelint": "^16.13.0" } }, "node_modules/stylelint/node_modules/balanced-match": { @@ -7601,36 +7686,31 @@ } }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", - "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.0.5.tgz", + "integrity": "sha512-umpQsJrBNsdMDgreSryMEXvJh66XeLtZUwA8Gj7rHGearGufUFv6rB/bcXRFsiGWw/VeSUgUofF4Rf2UKEOrTA==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" + "flat-cache": "^6.1.5" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", - "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.5.tgz", + "integrity": "sha512-QR+2kN38f8nMfiIQ1LHYjuDEmZNZVjxuxY+HufbS3BW0EX01Q5OnH7iduOYRutmgiXb797HAKcXUeXrvRjjgSQ==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=18" + "cacheable": "^1.8.7", + "flatted": "^3.3.2", + "hookified": "^1.6.0" } }, "node_modules/stylelint/node_modules/ignore": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", - "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", "dev": true, "license": "MIT", "engines": { @@ -7779,15 +7859,15 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/tsconfig-paths": { diff --git a/frontend/package.json b/frontend/package.json index df20ff3d6..aac3ce943 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,23 +20,23 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.3.1", - "@mui/material": "^6.3.1", - "@mui/x-data-grid": "^7.23.5", - "@mui/x-date-pickers": "^7.23.3", + "@mui/icons-material": "^6.4.0", + "@mui/material": "^6.4.0", + "@mui/x-data-grid": "^7.24.0", + "@mui/x-date-pickers": "^7.24.0", "@stripe/react-stripe-js": "^3.1.1", - "@stripe/stripe-js": "^5.4.0", - "@types/leaflet": "^1.9.15", + "@stripe/stripe-js": "^5.5.0", + "@types/leaflet": "^1.9.16", "@types/leaflet-boundary-canvas": "^1.0.3", - "@types/node": "^22.10.5", + "@types/node": "^22.10.7", "@types/nprogress": "^0.2.3", - "@types/react": "^19.0.2", - "@types/react-dom": "^19.0.2", + "@types/react": "^19.0.7", + "@types/react-dom": "^19.0.3", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.19.0", - "@typescript-eslint/parser": "^8.19.0", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -48,16 +48,16 @@ "history": "^5.3.0", "leaflet": "^1.9.4", "leaflet-boundary-canvas": "^1.0.0", - "localized-strings": "^1.0.0", + "localized-strings": "^2.0.3", "nprogress": "^0.2.0", "react": "^19.0.0", "react-circle-flags": "^0.0.23", "react-dom": "^19.0.0", "react-ga4": "^2.1.0", "react-leaflet": "^5.0.0", - "react-router-dom": "^7.1.1", + "react-router-dom": "^7.1.3", "react-slick": "^0.30.3", - "react-toastify": "^11.0.2", + "react-toastify": "^11.0.3", "slick-carousel": "^1.8.1", "typescript": "^5.2.2", "validator": "^13.12.0", @@ -65,12 +65,12 @@ }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", - "babel-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", + "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "eslint-config-airbnb": "^19.0.4", - "eslint-plugin-react-compiler": "^19.0.0-beta-b2e8e9c-20241220", - "npm-check-updates": "^17.1.13", - "stylelint": "^16.12.0", - "stylelint-config-standard": "^36.0.1", + "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", + "npm-check-updates": "^17.1.14", + "stylelint": "^16.13.2", + "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" } diff --git a/frontend/src/common/langHelper.ts b/frontend/src/common/langHelper.ts index 2d8f524b4..82bf78362 100644 --- a/frontend/src/common/langHelper.ts +++ b/frontend/src/common/langHelper.ts @@ -1,4 +1,4 @@ -import { LocalizedStrings } from 'localized-strings' +import LocalizedStrings from 'localized-strings' import env from '@/config/env.config' import * as UserService from '@/services/UserService' @@ -20,10 +20,10 @@ export const getLanguage = () => { /** * Set LocalizedStrings language. * - * @param {LocalizedStrings} strings + * @param {LocalizedStrings} strings * @param {?string} [language] */ -export const setLanguage = (strings: LocalizedStrings, language?: string) => { +export const setLanguage = (strings: LocalizedStrings, language?: string) => { const lang = language || getLanguage() strings.setLanguage(lang) } diff --git a/mobile/config/env.config.ts b/mobile/config/env.config.ts index 74d45af10..22ea1009c 100644 --- a/mobile/config/env.config.ts +++ b/mobile/config/env.config.ts @@ -63,7 +63,11 @@ export const CURRENCIES: Currency[] = [ { code: 'GBP', symbol: '£', - } + }, + { + code: 'AUD', + symbol: '$', + }, ] /** diff --git a/mobile/package-lock.json b/mobile/package-lock.json index cc6b1d998..04e7a99b8 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -25,32 +25,32 @@ "axios-retry": "^4.5.0", "babel-plugin-module-resolver": "^5.0.2", "date-fns": "^4.1.0", - "expo": "~52.0.23", - "expo-asset": "~11.0.1", - "expo-constants": "~17.0.3", - "expo-device": "~7.0.1", - "expo-document-picker": "~13.0.1", - "expo-image-picker": "~16.0.3", - "expo-linking": "^7.0.3", - "expo-localization": "~16.0.0", - "expo-location": "~18.0.4", - "expo-notifications": "~0.29.11", - "expo-splash-screen": "~0.29.18", - "expo-status-bar": "~2.0.0", - "expo-updates": "~0.26.10", + "expo": "~52.0.25", + "expo-asset": "~11.0.2", + "expo-constants": "~17.0.4", + "expo-device": "~7.0.2", + "expo-document-picker": "~13.0.2", + "expo-image-picker": "~16.0.4", + "expo-linking": "^7.0.4", + "expo-localization": "~16.0.1", + "expo-location": "~18.0.5", + "expo-notifications": "~0.29.12", + "expo-splash-screen": "~0.29.20", + "expo-status-bar": "~2.0.1", + "expo-updates": "~0.26.12", "i18n-js": "^4.5.1", "lodash.debounce": "^4.0.8", "mime": "^4.0.6", "prop-types": "^15.8.1", "react": "18.3.1", - "react-native": "0.76.5", + "react-native": "0.76.6", "react-native-animatable": "^1.4.0", "react-native-dotenv": "^3.4.11", "react-native-feather": "^1.1.2", "react-native-gesture-handler": "~2.20.2", "react-native-keyboard-aware-scroll-view": "^0.9.5", - "react-native-paper": "^5.12.5", - "react-native-reanimated": "~3.16.6", + "react-native-paper": "^5.13.1", + "react-native-reanimated": "~3.16.7", "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", "react-native-size-matters": "^0.4.2", @@ -61,15 +61,15 @@ "devDependencies": { "@babel/core": "^7.26.0", "@types/react": "~18.3.12", - "@typescript-eslint/eslint-plugin": "^8.19.1", - "@typescript-eslint/parser": "^8.19.1", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "cross-env": "^7.0.3", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.30.0", "eslint-plugin-react": "^7.35.2", "eslint-plugin-react-hooks": "^4.6.2", - "npm-check-updates": "^17.1.13", + "npm-check-updates": "^17.1.14", "typescript": "~5.3.3" } }, @@ -86,9 +86,9 @@ } }, "node_modules/@0no-co/graphql.web": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.12.tgz", - "integrity": "sha512-BTDjjsV/zSPy5fqItwm+KWUfh9CSe9tTtR6rCB72ddtkAxdcHbi4Ir4r/L1Et4lyxmL+i7Rb3m9sjLLi9tYrzA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.13.tgz", + "integrity": "sha512-jqYxOevheVTU1S36ZdzAkJIdvRp2m3OYIG5SEoKDw5NI8eVwkoI0D/Q3DYNGmXCxkA6CQuoa7zvMiDPTLqUNuw==", "license": "MIT", "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" @@ -174,13 +174,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -489,6 +489,18 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -534,12 +546,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -2156,16 +2168,16 @@ }, "node_modules/@babel/traverse--for-generate-function-map": { "name": "@babel/traverse", - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2174,9 +2186,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -2313,29 +2325,29 @@ } }, "node_modules/@expo/cli": { - "version": "0.22.7", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.22.7.tgz", - "integrity": "sha512-aNrUPVFPdIX42Q6UM6qygrN4DUqnXMDS1CnkTfNFVIZWRiJ1TUA05Zk6aF35M674CKd/c/dWHFjmbgjsyN/hEA==", + "version": "0.22.9", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.22.9.tgz", + "integrity": "sha512-GFW1+InbgTz0+10qWfoo5fyBU2DhhPuJkL4TUnG7GTq8lDlim88JLghJVbq0uAX/xDLcd326QnI0XONsUGSWrw==", "license": "MIT", "dependencies": { "@0no-co/graphql.web": "^1.0.8", "@babel/runtime": "^7.20.0", "@expo/code-signing-certificates": "^0.0.5", - "@expo/config": "~10.0.4", - "@expo/config-plugins": "~9.0.10", + "@expo/config": "~10.0.8", + "@expo/config-plugins": "~9.0.14", "@expo/devcert": "^1.1.2", - "@expo/env": "~0.4.0", - "@expo/image-utils": "^0.6.0", - "@expo/json-file": "^9.0.0", - "@expo/metro-config": "~0.19.8", - "@expo/osascript": "^2.0.31", - "@expo/package-manager": "^1.5.0", - "@expo/plist": "^0.2.0", - "@expo/prebuild-config": "^8.0.23", + "@expo/env": "~0.4.1", + "@expo/image-utils": "^0.6.4", + "@expo/json-file": "^9.0.1", + "@expo/metro-config": "~0.19.9", + "@expo/osascript": "^2.1.5", + "@expo/package-manager": "^1.7.1", + "@expo/plist": "^0.2.1", + "@expo/prebuild-config": "^8.0.25", "@expo/rudder-sdk-node": "^1.1.1", "@expo/spawn-async": "^1.7.2", "@expo/xcpretty": "^4.3.0", - "@react-native/dev-middleware": "0.76.5", + "@react-native/dev-middleware": "0.76.6", "@urql/core": "^5.0.6", "@urql/exchange-retry": "^1.3.0", "accepts": "^1.3.8", @@ -2579,15 +2591,15 @@ } }, "node_modules/@expo/config": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-10.0.6.tgz", - "integrity": "sha512-xXkfPElrtxznkOZxFASJ7OPa6E9IHSjcZwj5BQ6XUF2dz5M7AFa2h5sXM8AalSaDU5tEBSgoUOjTh5957TlR8g==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-10.0.8.tgz", + "integrity": "sha512-RaKwi8e6PbkMilRexdsxObLMdQwxhY6mlgel+l/eW+IfIw8HEydSU0ERlzYUjlGJxHLHUXe4rC2vw8FEvaowyQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", - "@expo/config-plugins": "~9.0.10", - "@expo/config-types": "^52.0.0", - "@expo/json-file": "^9.0.0", + "@expo/config-plugins": "~9.0.14", + "@expo/config-types": "^52.0.3", + "@expo/json-file": "^9.0.1", "deepmerge": "^4.3.1", "getenv": "^1.0.0", "glob": "^10.4.2", @@ -2600,14 +2612,14 @@ } }, "node_modules/@expo/config-plugins": { - "version": "9.0.12", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.12.tgz", - "integrity": "sha512-/Ko/NM+GzvJyRkq8PITm8ms0KY5v0wmN1OQFYRMkcJqOi3PjlhndW+G6bHpJI9mkQXBaUnHwAiGLqIC3+MQ5Wg==", + "version": "9.0.14", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.14.tgz", + "integrity": "sha512-Lx1ebV95rTFKKQmbu4wMPLz65rKn7mqSpfANdCx+KwRxuLY2JQls8V4h3lQjG6dW8NWf9qV5QaEFAgNB6VMyOQ==", "license": "MIT", "dependencies": { - "@expo/config-types": "^52.0.0", - "@expo/json-file": "~9.0.0", - "@expo/plist": "^0.2.0", + "@expo/config-types": "^52.0.3", + "@expo/json-file": "~9.0.1", + "@expo/plist": "^0.2.1", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", @@ -2631,9 +2643,9 @@ } }, "node_modules/@expo/config-plugins/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2701,9 +2713,9 @@ } }, "node_modules/@expo/config-types": { - "version": "52.0.1", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.1.tgz", - "integrity": "sha512-vD8ZetyKV7U29lR6+NJohYeoLYTH+eNYXJeNiSOrWCz0witJYY11meMmEnpEaVbN89EfC6uauSUOa6wihtbyPQ==", + "version": "52.0.3", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.3.tgz", + "integrity": "sha512-muxvuARmbysH5OGaiBRlh1Y6vfdmL56JtpXxB+y2Hfhu0ezG1U4FjZYBIacthckZPvnDCcP3xIu1R+eTo7/QFA==", "license": "MIT" }, "node_modules/@expo/config/node_modules/@babel/code-frame": { @@ -2845,9 +2857,9 @@ } }, "node_modules/@expo/env": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@expo/env/-/env-0.4.0.tgz", - "integrity": "sha512-g2JYFqck3xKIwJyK+8LxZ2ENZPWtRgjFWpeht9abnKgzXVXBeSNECFBkg+WQjQocSIdxXhEWM6hz4ZAe7Tc4ng==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-0.4.1.tgz", + "integrity": "sha512-oDtbO3i9yXD1nx93acWiPTWGljJ3vABn35x1NAbqtQ2JL6mFOcRcArt1dwi4imZyLnG4VCcjabT9irj+LgYntw==", "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -2858,9 +2870,9 @@ } }, "node_modules/@expo/fingerprint": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.11.6.tgz", - "integrity": "sha512-hlVIfMEJYZIqIFMjeGRN5GhK/h6vJ3M4QVc1ZD8F0Bh7gMeI+jZkEyZdL5XT29jergQrksP638e2qFwgrGTw/w==", + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.11.7.tgz", + "integrity": "sha512-2rfYVS4nqWmOPQk+AL5GPfPSawbqqmI5mL++bxAhWADt+d+fjoQYfIrGtjZxQ30f9o/a1PrRPVSuh2j09+diVg==", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", @@ -2897,9 +2909,9 @@ } }, "node_modules/@expo/image-utils": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.6.3.tgz", - "integrity": "sha512-v/JbCKBrHeudxn1gN1TgfPE/pWJSlLPrl29uXJBgrJFQVkViQvUHQNDhaS+UEa9wYI5HHh7XYmtzAehyG4L+GA==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.6.4.tgz", + "integrity": "sha512-L++1PBzSvf5iYc6UHJ8Db8GcYNkfLDw+a+zqEFBQ3xqRXP/muxb/O7wuiMFlXrj/cfkx4e0U+z1a4ceV0A7S7Q==", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", @@ -2972,9 +2984,9 @@ } }, "node_modules/@expo/json-file": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.0.tgz", - "integrity": "sha512-M+55xFVrFzDcgMDf+52lPDLjKB5xwRfStWlv/b/Vu2OLgxGZLWpxoPYjlRoHqxjPbCQIi2ZCbobK+0KuNhsELg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.1.tgz", + "integrity": "sha512-ZVPhbbEBEwafPCJ0+kI25O2Iivt3XKHEKAADCml1q2cmOIbQnKgLyn8DpOJXqWEyRQr/VWS+hflBh8DU2YFSqg==", "license": "MIT", "dependencies": { "@babel/code-frame": "~7.10.4", @@ -2992,18 +3004,18 @@ } }, "node_modules/@expo/metro-config": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.19.8.tgz", - "integrity": "sha512-dVAOetouQYuOTEJ2zR0OTLNPOH6zPkeEt5fY53TK0Wxi1QmtsmH6vEWg05U4zkSJ6f1aXmQ0Za77R8QxuukESA==", + "version": "0.19.9", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.19.9.tgz", + "integrity": "sha512-JAsLWhFQqwLH0KsI4OMbPXsKFji5KJEmsi+/02Sz1GCT17YrjRmv1fZ91regUS/FUH2Y/PDAE/+2ulrTgMeG7A==", "license": "MIT", "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", "@babel/parser": "^7.20.0", "@babel/types": "^7.20.0", - "@expo/config": "~10.0.4", - "@expo/env": "~0.4.0", - "@expo/json-file": "~9.0.0", + "@expo/config": "~10.0.8", + "@expo/env": "~0.4.1", + "@expo/json-file": "~9.0.1", "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", "debug": "^4.3.2", @@ -3098,9 +3110,9 @@ } }, "node_modules/@expo/osascript": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.4.tgz", - "integrity": "sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.5.tgz", + "integrity": "sha512-Cp7YF7msGiTAIbFdzNovwHBfecdMLVL5XzSqq4xQz72ALFCQ3uSIUXRph1QV2r61ugH7Yem0gY8yi7RcDlI4qg==", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", @@ -3111,12 +3123,12 @@ } }, "node_modules/@expo/package-manager": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.6.1.tgz", - "integrity": "sha512-4rT46wP/94Ll+CWXtFKok1Lbo9XncSUtErFOo/9/3FVughGbIfdG4SKZOAWIpr9wxwEfkyhHfAP9q71ONlWODw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.7.1.tgz", + "integrity": "sha512-DKbELrTOdl7U3KT0C07Aka9P+sUP3LL+1UTKf1KmLx2x2gPH1IC+c68N7iQlwNt+yA37qIw6/vKoqyTGu5EL9g==", "license": "MIT", "dependencies": { - "@expo/json-file": "^9.0.0", + "@expo/json-file": "^9.0.1", "@expo/spawn-async": "^1.7.2", "ansi-regex": "^5.0.0", "chalk": "^4.0.0", @@ -3160,9 +3172,9 @@ "license": "MIT" }, "node_modules/@expo/plist": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.2.0.tgz", - "integrity": "sha512-F/IZJQaf8OIVnVA6XWUeMPC3OH6MV00Wxf0WC0JhTQht2QgjyHUa3U5Gs3vRtDq8tXNsZneOQRDVwpaOnd4zTQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.2.1.tgz", + "integrity": "sha512-9TaXGuNxa0LQwHQn4rYiU6YaERv6dPnQgsdKWq2rKKTr6LWOtGNQCi/yOk/HBLeZSxBm59APT5/6x60uRvr0Mg==", "license": "MIT", "dependencies": { "@xmldom/xmldom": "~0.7.7", @@ -3171,17 +3183,17 @@ } }, "node_modules/@expo/prebuild-config": { - "version": "8.0.23", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-8.0.23.tgz", - "integrity": "sha512-Zf01kFiN2PISmLb0DhIAJh76v3J2oYUKSjiAtGZLOH0HUz59by/qdyU4mGHWdeyRdCCrLUA21Rct2MBykvRMsg==", + "version": "8.0.25", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-8.0.25.tgz", + "integrity": "sha512-xYHV8eiydZEDedf2AGaOFRFwcGlaSzrqQH94dwX42urNCU03FO0RUb7yPp4nkb7WNFg5Ov6PDsV7ES+YwzNgYQ==", "license": "MIT", "dependencies": { - "@expo/config": "~10.0.4", - "@expo/config-plugins": "~9.0.10", - "@expo/config-types": "^52.0.0", - "@expo/image-utils": "^0.6.0", - "@expo/json-file": "^9.0.0", - "@react-native/normalize-colors": "0.76.5", + "@expo/config": "~10.0.8", + "@expo/config-plugins": "~9.0.14", + "@expo/config-types": "^52.0.3", + "@expo/image-utils": "^0.6.4", + "@expo/json-file": "^9.0.1", + "@react-native/normalize-colors": "0.76.6", "debug": "^4.3.1", "fs-extra": "^9.0.0", "resolve-from": "^5.0.0", @@ -3809,30 +3821,30 @@ } }, "node_modules/@react-native/assets-registry": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.5.tgz", - "integrity": "sha512-MN5dasWo37MirVcKWuysRkRr4BjNc81SXwUtJYstwbn8oEkfnwR9DaqdDTo/hHOnTdhafffLIa2xOOHcjDIGEw==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.6.tgz", + "integrity": "sha512-YI8HoReYiIwdFQs+k9Q9qpFTnsyYikZxgs/UVtVbhKixXDQF6F9LLvj2naOx4cfV+RGybNKxwmDl1vUok/dRFQ==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.5.tgz", - "integrity": "sha512-xe7HSQGop4bnOLMaXt0aU+rIatMNEQbz242SDl8V9vx5oOTI0VbZV9yLy6yBc6poUlYbcboF20YVjoRsxX4yww==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.6.tgz", + "integrity": "sha512-yFC9I/aDBOBz3ZMlqKn2NY/mDUtCksUNZ7AQmBiTAeVTUP0ujEjE0hTOx5Qd+kok7A7hwZEX87HdSgjiJZfr5g==", "license": "MIT", "dependencies": { - "@react-native/codegen": "0.76.5" + "@react-native/codegen": "0.76.6" }, "engines": { "node": ">=18" } }, "node_modules/@react-native/babel-preset": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.76.5.tgz", - "integrity": "sha512-1Nu5Um4EogOdppBLI4pfupkteTjWfmI0hqW8ezWTg7Bezw0FtBj8yS8UYVd3wTnDFT9A5mA2VNoNUqomJnvj2A==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.76.6.tgz", + "integrity": "sha512-ojlVWY6S/VE/nb9hIRetPMTsW9ZmGb2R3dnToEXAtQQDz41eHMHXbkw/k2h0THp6qhas25ruNvn3N5n2o+lBzg==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -3876,7 +3888,7 @@ "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@babel/template": "^7.25.0", - "@react-native/babel-plugin-codegen": "0.76.5", + "@react-native/babel-plugin-codegen": "0.76.6", "babel-plugin-syntax-hermes-parser": "^0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" @@ -3889,9 +3901,9 @@ } }, "node_modules/@react-native/codegen": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.76.5.tgz", - "integrity": "sha512-FoZ9VRQ5MpgtDAnVo1rT9nNRfjnWpE40o1GeJSDlpUMttd36bVXvsDm8W/NhX8BKTWXSX+CPQJsRcvN1UPYGKg==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.76.6.tgz", + "integrity": "sha512-BABb3e5G/+hyQYEYi0AODWh2km2d8ERoASZr6Hv90pVXdUHRYR+yxCatX7vSd9rnDUYndqRTzD0hZWAucPNAKg==", "license": "MIT", "dependencies": { "@babel/parser": "^7.25.3", @@ -3911,13 +3923,13 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.5.tgz", - "integrity": "sha512-3MKMnlU0cZOWlMhz5UG6WqACJiWUrE3XwBEumzbMmZw3Iw3h+fIsn+7kLLE5EhzqLt0hg5Y4cgYFi4kOaNgq+g==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.6.tgz", + "integrity": "sha512-nETlc/+U5cESVluzzgN0OcVfcoMijGBaDWzOaJhoYUodcuqnqtu75XsSEc7yzlYjwNQG+vF83mu9CQGezruNMA==", "license": "MIT", "dependencies": { - "@react-native/dev-middleware": "0.76.5", - "@react-native/metro-babel-transformer": "0.76.5", + "@react-native/dev-middleware": "0.76.6", + "@react-native/metro-babel-transformer": "0.76.6", "chalk": "^4.0.0", "execa": "^5.1.1", "invariant": "^2.2.4", @@ -4036,22 +4048,22 @@ } }, "node_modules/@react-native/debugger-frontend": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.76.5.tgz", - "integrity": "sha512-5gtsLfBaSoa9WP8ToDb/8NnDBLZjv4sybQQj7rDKytKOdsXm3Pr2y4D7x7GQQtP1ZQRqzU0X0OZrhRz9xNnOqA==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.76.6.tgz", + "integrity": "sha512-kP97xMQjiANi5/lmf8MakS7d8FTJl+BqYHQMqyvNiY+eeWyKnhqW2GL2v3eEUBAuyPBgJGivuuO4RvjZujduJg==", "license": "BSD-3-Clause", "engines": { "node": ">=18" } }, "node_modules/@react-native/dev-middleware": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.76.5.tgz", - "integrity": "sha512-f8eimsxpkvMgJia7POKoUu9uqjGF6KgkxX4zqr/a6eoR1qdEAWUd6PonSAqtag3PAqvEaJpB99gLH2ZJI1nDGg==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.76.6.tgz", + "integrity": "sha512-1bAyd2/X48Nzb45s5l2omM75vy764odx/UnDs4sJfFCuK+cupU4nRPgl0XWIqgdM/2+fbQ3E4QsVS/WIKTFxvQ==", "license": "MIT", "dependencies": { "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.76.5", + "@react-native/debugger-frontend": "0.76.6", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", @@ -4091,31 +4103,31 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.76.5.tgz", - "integrity": "sha512-7KSyD0g0KhbngITduC8OABn0MAlJfwjIdze7nA4Oe1q3R7qmAv+wQzW+UEXvPah8m1WqFjYTkQwz/4mK3XrQGw==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.76.6.tgz", + "integrity": "sha512-sDzpf4eiynryoS6bpYCweGoxSmWgCSx9lzBoxIIW+S6siyGiTaffzZHWCm8mIn9UZsSPlEO37q62ggnR9Zu/OA==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.5.tgz", - "integrity": "sha512-ggM8tcKTcaqyKQcXMIvcB0vVfqr9ZRhWVxWIdiFO1mPvJyS6n+a+lLGkgQAyO8pfH0R1qw6K9D0nqbbDo865WQ==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.6.tgz", + "integrity": "sha512-cDD7FynxWYxHkErZzAJtzPGhJ13JdOgL+R0riTh0hCovOfIUz9ItffdLQv2nx48lnvMTQ+HZXMnGOZnsFCNzQw==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.5.tgz", - "integrity": "sha512-Cm9G5Sg5BDty3/MKa3vbCAJtT3YHhlEaPlQALLykju7qBS+pHZV9bE9hocfyyvc5N/osTIGWxG5YOfqTeMu1oQ==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.6.tgz", + "integrity": "sha512-xSBi9jPliThu5HRSJvluqUlDOLLEmf34zY/U7RDDjEbZqC0ufPcPS7c5XsSg0GDPiXc7lgjBVesPZsKFkoIBgA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@react-native/babel-preset": "0.76.5", + "@react-native/babel-preset": "0.76.6", "hermes-parser": "0.23.1", "nullthrows": "^1.1.1" }, @@ -4127,11 +4139,34 @@ } }, "node_modules/@react-native/normalize-colors": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.5.tgz", - "integrity": "sha512-6QRLEok1r55gLqj+94mEWUENuU5A6wsr2OoXpyq/CgQ7THWowbHtru/kRGRr6o3AQXrVnZheR60JNgFcpNYIug==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.6.tgz", + "integrity": "sha512-1n4udXH2Cla31iA/8eLRdhFHpYUYK1NKWCn4m1Sr9L4SarWKAYuRFliK1fcLvPPALCFoFlWvn8I0ekdUOHMzDQ==", "license": "MIT" }, + "node_modules/@react-native/virtualized-lists": { + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.6.tgz", + "integrity": "sha512-0HUWVwJbRq1BWFOu11eOWGTSmK9nMHhoMPyoI27wyWcl/nqUx7HOxMbRVq0DsTCyATSMPeF+vZ6o1REapcNWKw==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": "^18.2.6", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@react-navigation/core": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.3.1.tgz", @@ -4520,17 +4555,17 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", - "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/type-utils": "8.19.1", - "@typescript-eslint/utils": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -4550,16 +4585,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.1.tgz", - "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -4575,14 +4610,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz", - "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4593,14 +4628,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz", - "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.1", - "@typescript-eslint/utils": "8.19.1", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -4617,9 +4652,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.1.tgz", - "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "dev": true, "license": "MIT", "engines": { @@ -4631,14 +4666,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz", - "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/visitor-keys": "8.19.1", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4697,16 +4732,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.1.tgz", - "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.1", - "@typescript-eslint/types": "8.19.1", - "@typescript-eslint/typescript-estree": "8.19.1" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4721,13 +4756,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz", - "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/types": "8.20.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -4891,16 +4926,38 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -5422,9 +5479,9 @@ } }, "node_modules/babel-preset-expo": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-12.0.4.tgz", - "integrity": "sha512-SAzAwqpyjA+/OFrU95OOioj6oTeCv4+rRfrNmBTy5S/gJswrZKBSPJioFudIaJBy43W+BL7HA5AspBIF6tO/aA==", + "version": "12.0.6", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-12.0.6.tgz", + "integrity": "sha512-az3H7gDVo0wxNBAFES8h5vLLWE8NPGkD9g5P962hDEOqZUdyPacb9MOzicypeLmcq9zQWr6E3iVtEHoNagCTTQ==", "license": "MIT", "dependencies": { "@babel/plugin-proposal-decorators": "^7.12.9", @@ -5433,7 +5490,7 @@ "@babel/plugin-transform-parameters": "^7.22.15", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.76.5", + "@react-native/babel-preset": "0.76.6", "babel-plugin-react-native-web": "~0.19.13", "react-refresh": "^0.14.2" }, @@ -5868,39 +5925,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -7593,26 +7617,26 @@ } }, "node_modules/expo": { - "version": "52.0.23", - "resolved": "https://registry.npmjs.org/expo/-/expo-52.0.23.tgz", - "integrity": "sha512-DR36Vkpz/ZLPci4fxDBG/pLk26nGK63vcZ+X4RZJfNBzi14DXZ939loP8YzWGV78Qp23qdPINczpo2727tqLxg==", + "version": "52.0.25", + "resolved": "https://registry.npmjs.org/expo/-/expo-52.0.25.tgz", + "integrity": "sha512-BWHveMyDSST7vuGNn8zbrSGboJdvXDE9auUEkFa14ETAWRtsghYnZ0KmjOEQNxNmrBHzct/JgZ8efh5sJGd0xA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "0.22.7", - "@expo/config": "~10.0.6", - "@expo/config-plugins": "~9.0.12", - "@expo/fingerprint": "0.11.6", - "@expo/metro-config": "0.19.8", + "@expo/cli": "0.22.9", + "@expo/config": "~10.0.8", + "@expo/config-plugins": "~9.0.14", + "@expo/fingerprint": "0.11.7", + "@expo/metro-config": "0.19.9", "@expo/vector-icons": "^14.0.0", - "babel-preset-expo": "~12.0.4", - "expo-asset": "~11.0.1", - "expo-constants": "~17.0.3", - "expo-file-system": "~18.0.6", - "expo-font": "~13.0.2", - "expo-keep-awake": "~14.0.1", - "expo-modules-autolinking": "2.0.4", - "expo-modules-core": "2.1.2", + "babel-preset-expo": "~12.0.6", + "expo-asset": "~11.0.2", + "expo-constants": "~17.0.4", + "expo-file-system": "~18.0.7", + "expo-font": "~13.0.3", + "expo-keep-awake": "~14.0.2", + "expo-modules-autolinking": "2.0.5", + "expo-modules-core": "2.1.3", "fbemitter": "^3.0.0", "web-streams-polyfill": "^3.3.2", "whatwg-url-without-unicode": "8.0.0-3" @@ -7640,22 +7664,22 @@ } }, "node_modules/expo-application": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-6.0.1.tgz", - "integrity": "sha512-w+1quSmKp8SYKT+GAFHSN5c6u+PqoVRIfpsLyRQrQdOnBA9dA8Hw6JT9sHNFmA30A2v1b/sdYZE3qKuRJFNSWQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-6.0.2.tgz", + "integrity": "sha512-qcj6kGq3mc7x5yIb5KxESurFTJCoEKwNEL34RdPEvTB/xhl7SeVZlu05sZBqxB1V4Ryzq/LsCb7NHNfBbb3L7A==", "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-asset": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.1.tgz", - "integrity": "sha512-WatvD7JVC89EsllXFYcS/rji3ajVzE2B/USo0TqedsETixwyVCQfrrvCdCPQyuKghrxVNEj8bQ/Qbea/RZLYjg==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.2.tgz", + "integrity": "sha512-We3Td5WsNsNQyXoheLnuwic6JCOt/pqXqIIyWaZ3z/PeHrA+SwoQdI18MjDhkudLK08tbIVyDSUW8IJHXa04eg==", "license": "MIT", "dependencies": { - "@expo/image-utils": "^0.6.0", - "expo-constants": "~17.0.0", + "@expo/image-utils": "^0.6.4", + "expo-constants": "~17.0.4", "invariant": "^2.2.4", "md5-file": "^3.2.3" }, @@ -7666,13 +7690,13 @@ } }, "node_modules/expo-constants": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.0.3.tgz", - "integrity": "sha512-lnbcX2sAu8SucHXEXxSkhiEpqH+jGrf+TF+MO6sHWIESjwOUVVYlT8qYdjR9xbxWmqFtrI4KV44FkeJf2DaFjQ==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.0.4.tgz", + "integrity": "sha512-5c0VlZycmDyQUCMCr3Na3cpHAsVJJ+5o6KkkD4rmATQZ0++Xp/S2gpnjWyEo2riRmO91vxoyHwmAySXuktJddQ==", "license": "MIT", "dependencies": { - "@expo/config": "~10.0.4", - "@expo/env": "~0.4.0" + "@expo/config": "~10.0.8", + "@expo/env": "~0.4.1" }, "peerDependencies": { "expo": "*", @@ -7680,9 +7704,9 @@ } }, "node_modules/expo-device": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-7.0.1.tgz", - "integrity": "sha512-/3lk0f9wvle+6svHqWSCBC1B5NYFmXp1D7hmIyecJJVYRLwzrwwTDyNs76oG/UDU5Appdu8QyDKycsx2hqv71w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-7.0.2.tgz", + "integrity": "sha512-0PkTixE4Qi8VQBjixnj4aw2f6vE4tUZH7GK8zHROGKlBypZKcWmsA+W/Vp3RC5AyREjX71pO/hjKTSo/vF0E2w==", "license": "MIT", "dependencies": { "ua-parser-js": "^0.7.33" @@ -7692,24 +7716,24 @@ } }, "node_modules/expo-document-picker": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-13.0.1.tgz", - "integrity": "sha512-M3O3SDqubsRbVyY+Xu6V45K0/G1S1IqEdmVAnPkOiUU2eIEFfF5oP4KON4CsvEhO9IIunnpRr/oq9NQxb3SrEA==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-13.0.2.tgz", + "integrity": "sha512-Ssnmgx6OTsFEBOx5ktVyJmD5q+7pnGvPRrBHppiJYwX65cREVZuuJ8xAPhoqPHYn65+4WjaxS1lP2rDkSsMo8w==", "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-eas-client": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-0.13.1.tgz", - "integrity": "sha512-IyeDiM6YSJG0c45kbuEo0qt76z0KTEZtisEFEtle+b+vfn9I3N+r3jbPscaI4yS3P6gpuoDyHv81YDVC6Dmkhw==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-0.13.2.tgz", + "integrity": "sha512-2RAAGtkO9vseoJZuW4mhJkiNQ6+FfLrX66OTMq4Qj9mRKZV2Uq/ZquxUGIeJyYqBy4vNYeKbuPd2oJtsV9LBGQ==", "license": "MIT" }, "node_modules/expo-file-system": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.6.tgz", - "integrity": "sha512-gGEwIJCXV3/wpIJ/wRyhmieLOSAY7HeFFjb+wEfHs04aE63JYR+rXXV4b7rBpEh1ZgNV9U91zfet/iQG7J8HBQ==", + "version": "18.0.7", + "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.7.tgz", + "integrity": "sha512-6PpbQfogMXdzOsJzlJayy5qf40IfIHhudtAOzr32RlRYL4Hkmk3YcR9jG0PWQ0rklJfAhbAdP63yOcN+wDgzaA==", "license": "MIT", "dependencies": { "web-streams-polyfill": "^3.3.2" @@ -7720,9 +7744,9 @@ } }, "node_modules/expo-font": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-13.0.2.tgz", - "integrity": "sha512-H9FaXM7ZW5+EfV38w80BgJG3H17kB7CuVXwHoiszIYyoPfWz9bWesFe4QwNZjTq3pzKes28sSd8irFuflIrSIA==", + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-13.0.3.tgz", + "integrity": "sha512-9IdYz+A+b3KvuCYP7DUUXF4VMZjPU+IsvAnLSVJ2TfP6zUD2JjZFx3jeo/cxWRkYk/aLj5+53Te7elTAScNl4Q==", "license": "MIT", "dependencies": { "fontfaceobserver": "^2.1.0" @@ -7742,9 +7766,9 @@ } }, "node_modules/expo-image-picker": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-16.0.3.tgz", - "integrity": "sha512-c4IOqIQOtx8puWWU4fVsJhuGiAhH6gAIdrVzhimOXSEUHnfxCckRYzvznbd/0cuvaA5y9H0CSYrxpTUc/0WNVw==", + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-16.0.4.tgz", + "integrity": "sha512-LeTdQxL+kS8wiFOf+mTxDZmr0Ok3KwcpvCaDnXwCdCQuszVd4sSml7lg+oaJfkWj793u74M+8j0hQaEDHCgkxg==", "license": "MIT", "dependencies": { "expo-image-loader": "~5.0.0" @@ -7760,9 +7784,9 @@ "license": "MIT" }, "node_modules/expo-keep-awake": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.0.1.tgz", - "integrity": "sha512-c5mGCAIk2YM+Vsdy90BlEJ4ZX+KG5Au9EkJUIxXWlpnuKmDAJ3N+5nEZ7EUO1ZTheqoSBeAo4jJ8rTWPU+JXdw==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.0.2.tgz", + "integrity": "sha512-71XAMnoWjKZrN8J7Q3+u0l9Ytp4OfhNAYz8BCWF1/9aFUw09J3I7Z5DuI3MUsVMa/KWi+XhG+eDUFP8cVA19Uw==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -7770,12 +7794,12 @@ } }, "node_modules/expo-linking": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-7.0.3.tgz", - "integrity": "sha512-YiDacNzeQZd/bdOwGyi+YlawM4GGbcSRkuFCpDGIK7D1KUGqLinBHwJvxUMb9Zert2Ois5IHtmZaZ1et6g229g==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-7.0.4.tgz", + "integrity": "sha512-i+QaFc2zwOoq/ajePVWC+op3cOKC6nd6Wj/BJtukU71byTAbxDhbi+3m0ZFbh2i1/v/iIXRqrl3PvQcKNklPkw==", "license": "MIT", "dependencies": { - "expo-constants": "~17.0.0", + "expo-constants": "~17.0.4", "invariant": "^2.2.4" }, "peerDependencies": { @@ -7784,9 +7808,9 @@ } }, "node_modules/expo-localization": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-16.0.0.tgz", - "integrity": "sha512-PaWDUs6sNaEbFwQc6QKsTfYCg9GDo3bBl+cWnoG0G7pn1A623CcMwWyV7jD5jpqz0s1gHmwSRjR3vKOqhouRWg==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-16.0.1.tgz", + "integrity": "sha512-kUrXiV/Pq9r7cG+TMt+Qa49IUQ9Y/czVwen4hmiboTclTopcWdIeCzYZv6JGtufoPpjEO9vVx1QJrXYl9V2u0Q==", "license": "MIT", "dependencies": { "rtl-detect": "^1.0.2" @@ -7797,21 +7821,21 @@ } }, "node_modules/expo-location": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-18.0.4.tgz", - "integrity": "sha512-OrZTxRpfi4bCJxjW186wt3kfoe0FHwXceE6dXdbxvjxnwMP9cbKeqY6Y2M1bVwboq7PI2eV/e1rOaGq+F+fZyw==", + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-18.0.5.tgz", + "integrity": "sha512-fcTtHseRUBh3XiATs2ghycjUJOYEl78lWUuaKRKPZMt+pLhqCX7OstbsySySCHHfz2MudvWaq2yXpe5cDmENzg==", "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo-manifests": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.4.tgz", - "integrity": "sha512-Ki6+twRbm+HTX3L8larhOdDbSYPG3ojGnZepR/+TGg3JF/5yyscosDVY6c6z8xEGjKIjs1F813yq9yAfiPh8/g==", + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.5.tgz", + "integrity": "sha512-3X3eQomnTa4G0Y9GoJeyewHPTscuzWMrTB3x4CknqOyXpGOJjOuCKjhzvccHxXZAt0XswqBI94iTbqIofo9Uqw==", "license": "MIT", "dependencies": { - "@expo/config": "~10.0.4", + "@expo/config": "~10.0.8", "expo-json-utils": "~0.14.0" }, "peerDependencies": { @@ -7819,9 +7843,9 @@ } }, "node_modules/expo-modules-autolinking": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.4.tgz", - "integrity": "sha512-e0p+19NhmD50U7s7BV7kWIypWmTNC9n/VlJKlXS05hM/zX7pe6JKmXyb+BFnXJq3SLBalLCUY0tu2gEUF3XeVg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.5.tgz", + "integrity": "sha512-z1aAa7OtnAXZRFwn/CSgr9qSclW0mepGRJzcjZjyHL49u3VWmAHaPLl6S5vVGSX3sTYsFjKJ7ioCCye3tNdeUg==", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2", @@ -7874,27 +7898,27 @@ } }, "node_modules/expo-modules-core": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.1.2.tgz", - "integrity": "sha512-0OhMU5S8zf9c/CRh1MwiXfOInI9wzz6yiIh5RuR/9J7N6xHRum68hInsPbaSc1UQpo08ZZLM4MPsbpoNRUoqIg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.1.3.tgz", + "integrity": "sha512-DkSEr7q/SobjmCAo70833+xl0liShEFDHuC/YzXmHoDRxYHJaZCNc9uVBqjMeRfPVWp+4Rj9hF/gNvfad7vy0g==", "license": "MIT", "dependencies": { "invariant": "^2.2.4" } }, "node_modules/expo-notifications": { - "version": "0.29.11", - "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.29.11.tgz", - "integrity": "sha512-u/Csc3YNOPjjuyjAeyj5ne7XR/Z0ABYVquhSnyjEj2Fp8mSldOPCMvaEA01pTFj+8HTlkjX5RZDvQ7cR62ngOA==", + "version": "0.29.12", + "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.29.12.tgz", + "integrity": "sha512-E2QQ+PJR4jAg4XX1jsYobfHwjUMuR6YO/CtMLPKT1jEpzawGKLKEFowtIGu70wpOmcXWuyZst6qStJFu1gwxuQ==", "license": "MIT", "dependencies": { - "@expo/image-utils": "^0.6.0", + "@expo/image-utils": "^0.6.4", "@ide/backoff": "^1.0.0", "abort-controller": "^3.0.0", "assert": "^2.0.0", "badgin": "^1.1.5", - "expo-application": "~6.0.0", - "expo-constants": "~17.0.0" + "expo-application": "~6.0.2", + "expo-constants": "~17.0.4" }, "peerDependencies": { "expo": "*", @@ -7903,21 +7927,21 @@ } }, "node_modules/expo-splash-screen": { - "version": "0.29.18", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.29.18.tgz", - "integrity": "sha512-bTBY+LF6YtYen2j60yGNh2SX/tG4UXZAyBCMMriOSiZZ7LSCs3ARyEufaSiWk+ckWShTeMqItOnaAN/CAF8MJA==", + "version": "0.29.20", + "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-0.29.20.tgz", + "integrity": "sha512-6CkKHyfPREhTL4/NcAI1BQqAMo+qv8K5Kt1s+jQCE8LCweX203BWMDYPu5padBnlYfVDAs7O4CFAe3cyaEDSjA==", "license": "MIT", "dependencies": { - "@expo/prebuild-config": "^8.0.23" + "@expo/prebuild-config": "^8.0.25" }, "peerDependencies": { "expo": "*" } }, "node_modules/expo-status-bar": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-2.0.0.tgz", - "integrity": "sha512-vxxdpvpNDMTEc5uTiIrbTvySKKUsOACmfl8OZuUdjNle05oGqwtq3v5YObwym/njSByjoyuZX8UpXBZnxvarwQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-2.0.1.tgz", + "integrity": "sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg==", "license": "MIT", "peerDependencies": { "react": "*", @@ -7931,19 +7955,19 @@ "license": "MIT" }, "node_modules/expo-updates": { - "version": "0.26.10", - "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-0.26.10.tgz", - "integrity": "sha512-ETGUaSZRL7x72RH6MbZWRpyU9GFzCixIPNUT0kf/hcD07ojyHlW5hcwgc5ve565THSvhgiumz3yImKLbKBv2JA==", + "version": "0.26.12", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-0.26.12.tgz", + "integrity": "sha512-s3Olz3cj20A1k+nniGpLRw3oe8UW9ZZDGYqxYh3w41xOz5i05PZp4ae0iHGqlQPvdYtazghF+J6nRiFwHn1GYw==", "license": "MIT", "dependencies": { "@expo/code-signing-certificates": "0.0.5", - "@expo/config": "~10.0.4", - "@expo/config-plugins": "~9.0.10", + "@expo/config": "~10.0.8", + "@expo/config-plugins": "~9.0.14", "@expo/spawn-async": "^1.7.2", "arg": "4.1.0", "chalk": "^4.1.2", - "expo-eas-client": "~0.13.0", - "expo-manifests": "~0.15.0", + "expo-eas-client": "~0.13.2", + "expo-manifests": "~0.15.5", "expo-structured-headers": "~4.0.0", "expo-updates-interface": "~1.0.0", "fast-glob": "^3.3.2", @@ -8764,9 +8788,9 @@ } }, "node_modules/image-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", - "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.0.tgz", + "integrity": "sha512-4S8fwbO6w3GeCVN6OPtA9I5IGKkcDMPcKndtUlpJuCwu7JLjtj7JZpwqLuyY2nrmQT3AWsCJLSKPsc2mPBSl3w==", "license": "MIT", "dependencies": { "queue": "6.0.2" @@ -9080,6 +9104,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -10099,6 +10132,18 @@ "node": ">=4" } }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/log-symbols/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -10968,9 +11013,9 @@ } }, "node_modules/npm-check-updates": { - "version": "17.1.13", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.13.tgz", - "integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==", + "version": "17.1.14", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.14.tgz", + "integrity": "sha512-dr4bXIxETubLI1tFGeock5hN8yVjahvaVpx+lPO4/O2md3zJuxB7FgH3MIoTvQSCgsgkIRpe0skti01IEAA5tA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11274,6 +11319,18 @@ "node": ">=6" } }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ora/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -12030,19 +12087,19 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-native": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.5.tgz", - "integrity": "sha512-op2p2kB+lqMF1D7AdX4+wvaR0OPFbvWYs+VBE7bwsb99Cn9xISrLRLAgFflZedQsa5HvnOGrULhtnmItbIKVVw==", + "version": "0.76.6", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.6.tgz", + "integrity": "sha512-AsRi+ud6v6ADH7ZtSOY42kRB4nbM0KtSu450pGO4pDudl4AEK/AF96ai88snb2/VJJSGGa/49QyJVFXxz/qoFg==", "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.6.3", - "@react-native/assets-registry": "0.76.5", - "@react-native/codegen": "0.76.5", - "@react-native/community-cli-plugin": "0.76.5", - "@react-native/gradle-plugin": "0.76.5", - "@react-native/js-polyfills": "0.76.5", - "@react-native/normalize-colors": "0.76.5", - "@react-native/virtualized-lists": "0.76.5", + "@react-native/assets-registry": "0.76.6", + "@react-native/codegen": "0.76.6", + "@react-native/community-cli-plugin": "0.76.6", + "@react-native/gradle-plugin": "0.76.6", + "@react-native/js-polyfills": "0.76.6", + "@react-native/normalize-colors": "0.76.6", + "@react-native/virtualized-lists": "0.76.6", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -12183,9 +12240,9 @@ } }, "node_modules/react-native-paper": { - "version": "5.12.5", - "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.12.5.tgz", - "integrity": "sha512-Qpqd1g9PClmjGj/Dkr1htAwt8cTZ3SCHVmhttxRuG/QML7KzHm5ArLNgR7vz5dW1EwJqTmyl/3gd6gnrtw90mw==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.13.1.tgz", + "integrity": "sha512-8frKVKJ5JBd8WL1G3tpcYzOgK40kxkD/U+yLHGKNeLnD6v1Qc9W6DxWTHWN7lsX/DPYnhgvw1aKkYaPTmDj5pg==", "license": "MIT", "dependencies": { "@callstack/react-theme-provider": "^3.0.9", @@ -12209,9 +12266,9 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.16.6", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.16.6.tgz", - "integrity": "sha512-jPbAfLF5t8+UCKFTO+LeOY+OmAcDP5SsAfqINvNQz5GFGvoO7UebxujjtY58CmpZNH6c3SQ514FF9//mZDpo/g==", + "version": "3.16.7", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.16.7.tgz", + "integrity": "sha512-qoUUQOwE1pHlmQ9cXTJ2MX9FQ9eHllopCLiWOkDkp6CER95ZWeXhJCP4cSm6AD4jigL5jHcZf/SkWrg8ttZUsw==", "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", @@ -12353,29 +12410,6 @@ "node": ">=10" } }, - "node_modules/react-native/node_modules/@react-native/virtualized-lists": { - "version": "0.76.5", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.5.tgz", - "integrity": "sha512-M/fW1fTwxrHbcx0OiVOIxzG6rKC0j9cR9Csf80o77y1Xry0yrNPpAlf8D1ev3LvHsiAUiRNFlauoPtodrs2J1A==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/react": "^18.2.6", - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.23.1.tgz", @@ -13274,15 +13308,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -13295,14 +13320,6 @@ "node": ">=8" } }, - "node_modules/string-width/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/string-width/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -13682,6 +13699,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -14087,9 +14105,9 @@ } }, "node_modules/undici": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", - "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", "license": "MIT", "engines": { "node": ">=18.17" @@ -14517,39 +14535,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -14562,36 +14547,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", diff --git a/mobile/package.json b/mobile/package.json index d0e8f91a1..59f7f3f42 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -41,32 +41,32 @@ "axios-retry": "^4.5.0", "babel-plugin-module-resolver": "^5.0.2", "date-fns": "^4.1.0", - "expo": "~52.0.23", - "expo-asset": "~11.0.1", - "expo-constants": "~17.0.3", - "expo-device": "~7.0.1", - "expo-document-picker": "~13.0.1", - "expo-image-picker": "~16.0.3", - "expo-linking": "^7.0.3", - "expo-localization": "~16.0.0", - "expo-location": "~18.0.4", - "expo-notifications": "~0.29.11", - "expo-splash-screen": "~0.29.18", - "expo-status-bar": "~2.0.0", - "expo-updates": "~0.26.10", + "expo": "~52.0.25", + "expo-asset": "~11.0.2", + "expo-constants": "~17.0.4", + "expo-device": "~7.0.2", + "expo-document-picker": "~13.0.2", + "expo-image-picker": "~16.0.4", + "expo-linking": "^7.0.4", + "expo-localization": "~16.0.1", + "expo-location": "~18.0.5", + "expo-notifications": "~0.29.12", + "expo-splash-screen": "~0.29.20", + "expo-status-bar": "~2.0.1", + "expo-updates": "~0.26.12", "i18n-js": "^4.5.1", "lodash.debounce": "^4.0.8", "mime": "^4.0.6", "prop-types": "^15.8.1", "react": "18.3.1", - "react-native": "0.76.5", + "react-native": "0.76.6", "react-native-animatable": "^1.4.0", "react-native-dotenv": "^3.4.11", "react-native-feather": "^1.1.2", "react-native-gesture-handler": "~2.20.2", "react-native-keyboard-aware-scroll-view": "^0.9.5", - "react-native-paper": "^5.12.5", - "react-native-reanimated": "~3.16.6", + "react-native-paper": "^5.13.1", + "react-native-reanimated": "~3.16.7", "react-native-safe-area-context": "4.12.0", "react-native-screens": "~4.4.0", "react-native-size-matters": "^0.4.2", @@ -77,15 +77,15 @@ "devDependencies": { "@babel/core": "^7.26.0", "@types/react": "~18.3.12", - "@typescript-eslint/eslint-plugin": "^8.19.1", - "@typescript-eslint/parser": "^8.19.1", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "cross-env": "^7.0.3", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.30.0", "eslint-plugin-react": "^7.35.2", "eslint-plugin-react-hooks": "^4.6.2", - "npm-check-updates": "^17.1.13", + "npm-check-updates": "^17.1.14", "typescript": "~5.3.3" }, "private": true From f7427f55d3d7ab512c1ecf6d79c11b82ff57d129 Mon Sep 17 00:00:00 2001 From: aelassas Date: Sun, 19 Jan 2025 10:59:31 +0100 Subject: [PATCH 02/42] Remove loc.yml --- .github/workflows/loc.yml | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .github/workflows/loc.yml diff --git a/.github/workflows/loc.yml b/.github/workflows/loc.yml deleted file mode 100644 index 4837db137..000000000 --- a/.github/workflows/loc.yml +++ /dev/null @@ -1,33 +0,0 @@ -on: [push] -name: Lines of Code - -jobs: - loc_job: - runs-on: ubuntu-latest - name: Run - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Create the badge - uses: shadowmoose/GHA-LoC-Badge@1.0.0 - id: badge - with: - debug: true - directory: ./ - badge: ./output/badge.svg - ignore: 'node_modules|coverage|package-lock.json|README.md|LICENSE|.github|.vscode|__scripts|__services|.env|tsconfig.tsbuildinfo|dist' - - - name: Print the output - run: | - echo "Scanned: ${{ steps.badge.outputs.counted_files }}"; - echo "Line Count: ${{ steps.badge.outputs.total_lines }}"; - - - name: Deploy to loc branch - uses: peaceiris/actions-gh-pages@v3 - with: - publish_dir: ./output - publish_branch: loc - github_token: ${{ secrets.GITHUB_TOKEN }} - user_name: 'github-actions[bot]' - user_email: 'github-actions[bot]@users.noreply.github.com' From 5b8574e81f203bc714f0b523a555f61188001068 Mon Sep 17 00:00:00 2001 From: aelassas Date: Sun, 19 Jan 2025 11:56:53 +0100 Subject: [PATCH 03/42] Bump version to 5.5.0 --- api/package-lock.json | 4 ++-- api/package.json | 2 +- backend/package-lock.json | 4 ++-- backend/package.json | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- mobile/app.json | 4 ++-- mobile/package-lock.json | 4 ++-- mobile/package.json | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index 64a901b74..880607753 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,12 +1,12 @@ { "name": "api", - "version": "5.4.0", + "version": "5.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "api", - "version": "5.4.0", + "version": "5.5.0", "license": "ISC", "dependencies": { "@babel/cli": "^7.26.4", diff --git a/api/package.json b/api/package.json index 991a031db..52daadef9 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "api", - "version": "5.4.0", + "version": "5.5.0", "description": "", "main": "index.js", "type": "module", diff --git a/backend/package-lock.json b/backend/package-lock.json index 690d26c73..ca047ae69 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "backend", - "version": "5.4.0", + "version": "5.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "backend", - "version": "5.4.0", + "version": "5.5.0", "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", diff --git a/backend/package.json b/backend/package.json index b7155c836..adf006506 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,7 +1,7 @@ { "name": "backend", "private": true, - "version": "5.4.0", + "version": "5.5.0", "type": "module", "scripts": { "install:dependencies": "cd ../packages/currency-converter && npm i && cd ../bookcars-helper && npm i && cd ../../backend", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 97108569c..0c1864c5b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "5.4.0", + "version": "5.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "5.4.0", + "version": "5.5.0", "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", diff --git a/frontend/package.json b/frontend/package.json index aac3ce943..44fed2b7c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "5.4.0", + "version": "5.5.0", "private": true, "type": "module", "scripts": { diff --git a/mobile/app.json b/mobile/app.json index 06f04ed6d..059d3dddb 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -3,7 +3,7 @@ "jsEngine": "hermes", "newArchEnabled": true, "name": "BookCars", - "version": "5.4.0", + "version": "5.5.0", "slug": "bookcars", "icon": "./assets/icon.png", "assetBundlePatterns": [ @@ -28,7 +28,7 @@ "package": "com.bookcars" }, "ios": { - "buildNumber": "5.4.0", + "buildNumber": "5.5.0", "supportsTablet": true, "icon": "./assets/icon.png", // "splash": { diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 04e7a99b8..bb8bf0ae2 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "bookcars", - "version": "5.4.0", + "version": "5.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bookcars", - "version": "5.4.0", + "version": "5.5.0", "dependencies": { "@babel/runtime": "^7.26.0", "@react-native-async-storage/async-storage": "1.23.1", diff --git a/mobile/package.json b/mobile/package.json index 59f7f3f42..e38b9d8b2 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,6 +1,6 @@ { "name": "bookcars", - "version": "5.4.0", + "version": "5.5.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "install:dependencies": "cd ../packages/currency-converter && npm i && cd ../bookcars-helper && npm i && cd ../../mobile", From 8ec7a0685944beb7f7667f9d15af3bab6ae5ca90 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 14:37:33 +0100 Subject: [PATCH 04/42] Add deposit payment option to checkout --- frontend/src/lang/checkout.ts | 18 ++++++++++------ frontend/src/pages/Checkout.tsx | 38 +++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/frontend/src/lang/checkout.ts b/frontend/src/lang/checkout.ts index f07bea4c2..da28e59cb 100644 --- a/frontend/src/lang/checkout.ts +++ b/frontend/src/lang/checkout.ts @@ -24,9 +24,11 @@ const strings = new LocalizedStrings({ SUCCESS: 'Votre paiement a été effectué avec succès. Nous vous avons envoyé un e-mail de confirmation.', PAY_LATER_SUCCESS: 'Votre réservation a été effectuée avec succès. Nous vous avons envoyé un e-mail de confirmation.', PAYMENT_OPTIONS: 'Options de paiement', - PAY_LATER: 'Payer plus tard', + PAY_LATER: 'Payer au comptoir', PAY_LATER_INFO: 'Modification et annulation gratuites', - PAY_ONLINE: 'Payer en ligne', + PAY_DEPOSIT: "Payer l'acompte et confirmer la réservation", + DEPOSIT: 'Acompte', + PAY_ONLINE: 'Payer la totalité en ligne et réserver la voiture', PAY_ONLINE_INFO: 'Modification et annulation sous conditions', PAYMENT_FAILED: 'Paiement échoué.', CHECKING: 'Vérification en cours...', @@ -61,9 +63,11 @@ const strings = new LocalizedStrings({ SUCCESS: 'Your payment was successfully done. We sent you a confirmation email.', PAY_LATER_SUCCESS: 'Your booking was successfully done. We sent you a confirmation email.', PAYMENT_OPTIONS: 'Payment options', - PAY_LATER: 'Pay later', + PAY_LATER: 'Pay at the counter', PAY_LATER_INFO: 'Free amendments and cancellation', - PAY_ONLINE: 'Pay online', + PAY_DEPOSIT: 'Pay the deposit and confirm the booking', + DEPOSIT: 'Deposit', + PAY_ONLINE: 'Pay in full online and book the car', PAY_ONLINE_INFO: 'Amendments and cancellation under conditions', PAYMENT_FAILED: 'Payment failed.', CHECKING: 'Checking in progress...', @@ -98,9 +102,11 @@ const strings = new LocalizedStrings({ SUCCESS: 'Tu pago se realizó con éxito. Te hemos enviado un correo de confirmación.', PAY_LATER_SUCCESS: 'Tu reserva se ha realizado con éxito. Te hemos enviado un correo de confirmación.', PAYMENT_OPTIONS: 'Opciones de pago', - PAY_LATER: 'Pagar más tarde', + PAY_LATER: 'Pagar en el mostrador', PAY_LATER_INFO: 'Modificaciones y cancelación gratuitas', - PAY_ONLINE: 'Pagar en línea', + PAY_DEPOSIT: 'Pagar el depósito y confirmar la reserva', + DEPOSIT: 'Depósito', + PAY_ONLINE: 'Pagar el importe total en línea y reservar el vehículo', PAY_ONLINE_INFO: 'Modificaciones y cancelación bajo condiciones', PAYMENT_FAILED: 'El pago falló.', CHECKING: 'Verificación en curso...', diff --git a/frontend/src/pages/Checkout.tsx b/frontend/src/pages/Checkout.tsx index e05e5185c..3b0506a82 100644 --- a/frontend/src/pages/Checkout.tsx +++ b/frontend/src/pages/Checkout.tsx @@ -92,6 +92,7 @@ const Checkout = () => { const [tosError, setTosError] = useState(false) const [error, setError] = useState(false) const [price, setPrice] = useState(0) + const [depositPrice, setDepositPrice] = useState(0) const [emailInfo, setEmailInfo] = useState(true) const [phoneInfo, setPhoneInfo] = useState(true) @@ -113,6 +114,7 @@ const Checkout = () => { const [addiontalDriverPhoneValid, setAddiontalDriverPhoneValid] = useState(true) const [addiontalDriverBirthDateValid, setAddiontalDriverBirthDateValid] = useState(true) const [payLater, setPayLater] = useState(false) + const [payDeposit, setPayDeposit] = useState(false) const [recaptchaError, setRecaptchaError] = useState(false) const [adManuallyChecked, setAdManuallyChecked] = useState(false) @@ -399,7 +401,7 @@ const Checkout = () => { let _sessionId: string | undefined if (!payLater) { const payload: bookcarsTypes.CreatePaymentPayload = { - amount: price, + amount: payDeposit ? depositPrice : price, currency: StripeService.getCurrency(), locale: language, receiptEmail: (!authenticated ? driver?.email : user?.email) as string, @@ -504,11 +506,13 @@ const Checkout = () => { } const _price = await StripeService.convertPrice(bookcarsHelper.calculateTotalPrice(_car, _from, _to)) + const _depositPrice = _car.deposit > 0 ? await StripeService.convertPrice(_car.deposit) : 0 const included = (val: number) => val === 0 setCar(_car) setPrice(_price) + setDepositPrice(_depositPrice) setPickupLocation(_pickupLocation) setDropOffLocation(_dropOffLocation) setFrom(_from) @@ -543,7 +547,7 @@ const Checkout = () => { {((pickupLocation.latitude && pickupLocation.longitude) || (pickupLocation.parkingSpots && pickupLocation.parkingSpots.length > 0)) && ( { hidePrice sizeAuto onLoad={() => setLoadingPage(false)} + hideSupplier /> { defaultValue="payOnline" onChange={(event) => { setPayLater(event.target.value === 'payLater') + setPayDeposit(event.target.value === 'payDeposit') }} > { )} /> + { + car.deposit > 0 && ( + } + disabled={!!clientSecret} + className={clientSecret ? 'payment-radio-disabled' : ''} + label={( + + {strings.PAY_DEPOSIT} + {`(${strings.PAY_ONLINE_INFO})`} + + )} + /> + ) + } } @@ -876,8 +898,16 @@ const Checkout = () => {
-
{`${strings.PRICE_FOR} ${days} ${days > 1 ? strings.DAYS : strings.DAY}`}
-
{bookcarsHelper.formatPrice(price, commonStrings.CURRENCY, language)}
+
+ { + payDeposit ? strings.DEPOSIT : `${strings.PRICE_FOR} ${days} ${days > 1 ? strings.DAYS : strings.DAY}` + } +
+
+ { + bookcarsHelper.formatPrice(payDeposit ? depositPrice : price, commonStrings.CURRENCY, language) + } +
{(!car.supplier.payLater || !payLater) && ( From ca0e964526ec78862eccfaf52e01b6d686592ed8 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 14:44:00 +0100 Subject: [PATCH 05/42] Update checkout.ts --- frontend/src/lang/checkout.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/lang/checkout.ts b/frontend/src/lang/checkout.ts index da28e59cb..6300afd1e 100644 --- a/frontend/src/lang/checkout.ts +++ b/frontend/src/lang/checkout.ts @@ -26,7 +26,7 @@ const strings = new LocalizedStrings({ PAYMENT_OPTIONS: 'Options de paiement', PAY_LATER: 'Payer au comptoir', PAY_LATER_INFO: 'Modification et annulation gratuites', - PAY_DEPOSIT: "Payer l'acompte et confirmer la réservation", + PAY_DEPOSIT: "Payer l'acompte en ligne et confirmer la réservation", DEPOSIT: 'Acompte', PAY_ONLINE: 'Payer la totalité en ligne et réserver la voiture', PAY_ONLINE_INFO: 'Modification et annulation sous conditions', @@ -65,7 +65,7 @@ const strings = new LocalizedStrings({ PAYMENT_OPTIONS: 'Payment options', PAY_LATER: 'Pay at the counter', PAY_LATER_INFO: 'Free amendments and cancellation', - PAY_DEPOSIT: 'Pay the deposit and confirm the booking', + PAY_DEPOSIT: 'Pay the deposit online and confirm the booking', DEPOSIT: 'Deposit', PAY_ONLINE: 'Pay in full online and book the car', PAY_ONLINE_INFO: 'Amendments and cancellation under conditions', @@ -104,7 +104,7 @@ const strings = new LocalizedStrings({ PAYMENT_OPTIONS: 'Opciones de pago', PAY_LATER: 'Pagar en el mostrador', PAY_LATER_INFO: 'Modificaciones y cancelación gratuitas', - PAY_DEPOSIT: 'Pagar el depósito y confirmar la reserva', + PAY_DEPOSIT: 'Pagar el depósito en línea y confirmar la reserva', DEPOSIT: 'Depósito', PAY_ONLINE: 'Pagar el importe total en línea y reservar el vehículo', PAY_ONLINE_INFO: 'Modificaciones y cancelación bajo condiciones', From eddc446a40c67b78cd54bd50c33d8e0e618dac4e Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 15:04:35 +0100 Subject: [PATCH 06/42] Rename db:script to script:db --- api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/package.json b/api/package.json index 52daadef9..d464ea3f6 100644 --- a/api/package.json +++ b/api/package.json @@ -11,7 +11,7 @@ "test": "rimraf coverage && npm run build && cross-env NODE_OPTIONS=\"--experimental-vm-modules\" jest --coverage --no-cache", "lint": "eslint --ext .ts .", "ncu": "ncu -u -x typescript,eslint,@types/express", - "db:script": "npm run build && node dist/scripts/db.js" + "script:db": "npm run build && node dist/scripts/db.js" }, "keywords": [], "author": "", From fe2a535736d455c5d0f3e032e710509c14969ac0 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 17:20:30 +0100 Subject: [PATCH 07/42] Add isDeposit field to Booking model and update related controllers --- api/src/config/env.config.ts | 5 +++-- api/src/controllers/bookingController.ts | 2 ++ api/src/controllers/stripeController.ts | 2 +- api/src/models/Booking.ts | 4 ++++ frontend/src/pages/Checkout.tsx | 2 ++ packages/bookcars-types/index.ts | 1 + 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/api/src/config/env.config.ts b/api/src/config/env.config.ts index 81619c2e8..150b92417 100644 --- a/api/src/config/env.config.ts +++ b/api/src/config/env.config.ts @@ -36,11 +36,11 @@ export const LANGUAGES = [ ] /** - * Website Name. + * Website Name * * @type {string} */ -export const WEBSITE_NAME = __env__('BC_WEBSITE_NAME', false, 'BookCars') +export const WEBSITE_NAME = __env__('BC_WEBSITE_NAME', false, 'bookcars') /** * Server Port. Default is 4002. @@ -475,6 +475,7 @@ export interface Booking extends Document { paymentIntentId?: string customerId?: string expireAt?: Date + isDeposit: boolean } /** diff --git a/api/src/controllers/bookingController.ts b/api/src/controllers/bookingController.ts index b4ad20232..5ed2c84f5 100644 --- a/api/src/controllers/bookingController.ts +++ b/api/src/controllers/bookingController.ts @@ -542,6 +542,7 @@ export const update = async (req: Request, res: Response) => { fullInsurance, additionalDriver, price, + isDeposit, } = body.booking const previousStatus = booking.status @@ -561,6 +562,7 @@ export const update = async (req: Request, res: Response) => { booking.fullInsurance = fullInsurance booking.additionalDriver = additionalDriver booking.price = price as number + booking.isDeposit = isDeposit || false if (!additionalDriver && booking._additionalDriver) { booking._additionalDriver = undefined diff --git a/api/src/controllers/stripeController.ts b/api/src/controllers/stripeController.ts index 51910dac6..49e73a48d 100644 --- a/api/src/controllers/stripeController.ts +++ b/api/src/controllers/stripeController.ts @@ -133,7 +133,7 @@ export const checkCheckoutSession = async (req: Request, res: Response) => { } booking.expireAt = undefined - booking.status = bookcarsTypes.BookingStatus.Paid + booking.status = booking.isDeposit ? bookcarsTypes.BookingStatus.Deposit : bookcarsTypes.BookingStatus.Paid await booking.save() // Mark car as unavailable diff --git a/api/src/models/Booking.ts b/api/src/models/Booking.ts index e98cc0080..d6549068b 100644 --- a/api/src/models/Booking.ts +++ b/api/src/models/Booking.ts @@ -99,6 +99,10 @@ const bookingSchema = new Schema( customerId: { type: String, }, + isDeposit: { + type: Boolean, + default: false, + }, expireAt: { // // Bookings created from checkout with Stripe are temporary and diff --git a/frontend/src/pages/Checkout.tsx b/frontend/src/pages/Checkout.tsx index 3b0506a82..9be19528f 100644 --- a/frontend/src/pages/Checkout.tsx +++ b/frontend/src/pages/Checkout.tsx @@ -417,6 +417,8 @@ const Checkout = () => { _customerId = res.customerId } + booking.isDeposit = payDeposit + const payload: bookcarsTypes.CheckoutPayload = { driver, booking, diff --git a/packages/bookcars-types/index.ts b/packages/bookcars-types/index.ts index 6959257cc..1151ed1c5 100644 --- a/packages/bookcars-types/index.ts +++ b/packages/bookcars-types/index.ts @@ -93,6 +93,7 @@ export interface Booking { paymentIntentId?: string customerId?: string expireAt?: Date + isDeposit?: boolean } export interface CheckoutPayload { From d71b204a104c6fff95e891a068d698e48ab717b0 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 18:29:25 +0100 Subject: [PATCH 08/42] Add coming soon and already booked tags to search results --- api/__tests__/car.test.ts | 2 ++ api/src/config/env.config.ts | 1 + api/src/controllers/carController.ts | 14 +++++++++- api/src/models/Car.ts | 4 +++ backend/src/assets/css/car-list.css | 10 ++++++- backend/src/components/CarList.tsx | 20 +++++++++---- backend/src/lang/cars.ts | 3 ++ backend/src/lang/create-car.ts | 3 ++ backend/src/pages/CreateCar.tsx | 22 ++++++++++++++- backend/src/pages/UpdateCar.tsx | 21 ++++++++++++++ frontend/src/assets/css/car.css | 19 +++++++++++++ frontend/src/components/Car.tsx | 42 +++++++++++++++++----------- frontend/src/components/CarList.tsx | 6 ++++ frontend/src/lang/cars.ts | 6 ++++ packages/bookcars-types/index.ts | 4 +++ 15 files changed, 151 insertions(+), 26 deletions(-) diff --git a/api/__tests__/car.test.ts b/api/__tests__/car.test.ts index 1de1c964f..e09ad55bf 100644 --- a/api/__tests__/car.test.ts +++ b/api/__tests__/car.test.ts @@ -207,6 +207,7 @@ describe('PUT /api/update-car', () => { range: bookcarsTypes.CarRange.Midi, multimedia: [bookcarsTypes.CarMultimedia.AndroidAuto], rating: 4, + comingSoon: true, } let res = await request(app) .put('/api/update-car') @@ -244,6 +245,7 @@ describe('PUT /api/update-car', () => { expect(car.range).toBe(payload.range) expect(car.multimedia).toStrictEqual(payload.multimedia) expect(car.rating).toBe(payload.rating) + expect(car.comingSoon).toBe(payload.comingSoon) // test success (booking not found) payload._id = testHelper.GetRandromObjectIdAsString() diff --git a/api/src/config/env.config.ts b/api/src/config/env.config.ts index 150b92417..a736a8dce 100644 --- a/api/src/config/env.config.ts +++ b/api/src/config/env.config.ts @@ -522,6 +522,7 @@ export interface Car extends Document { rating?: number trips: number co2?: number + comingSoon?: boolean } /** diff --git a/api/src/controllers/carController.ts b/api/src/controllers/carController.ts index 8e9488fb5..07e277e0b 100644 --- a/api/src/controllers/carController.ts +++ b/api/src/controllers/carController.ts @@ -105,6 +105,7 @@ export const update = async (req: Request, res: Response) => { multimedia, rating, co2, + comingSoon, } = body car.supplier = new mongoose.Types.ObjectId(supplier) @@ -138,6 +139,7 @@ export const update = async (req: Request, res: Response) => { car.multimedia = multimedia car.rating = rating car.co2 = co2 + car.comingSoon = comingSoon await car.save() return res.json(car) @@ -647,17 +649,27 @@ export const getFrontendCars = async (req: Request, res: Response) => { rating, seats, days, + includeAlreadyBookedCars, + includeComingSoonCars, } = body const $match: mongoose.FilterQuery = { $and: [ { supplier: { $in: suppliers } }, { locations: pickupLocation }, - { available: true }, { type: { $in: carType } }, + { type: { $in: carType } }, { gearbox: { $in: gearbox } }, ], } + if (!includeAlreadyBookedCars) { + $match.$and!.push({ available: true }) + } + + if (!includeComingSoonCars) { + $match.$and!.push({ $or: [{ comingSoon: false }, { comingSoon: null }] }) + } + if (fuelPolicy) { $match.$and!.push({ fuelPolicy: { $in: fuelPolicy } }) } diff --git a/api/src/models/Car.ts b/api/src/models/Car.ts index 3681869d8..d12a50426 100644 --- a/api/src/models/Car.ts +++ b/api/src/models/Car.ts @@ -65,6 +65,10 @@ const carSchema = new Schema( required: [true, "can't be blank"], index: true, }, + comingSoon: { + type: Boolean, + default: false, + }, type: { type: String, enum: [ diff --git a/backend/src/assets/css/car-list.css b/backend/src/assets/css/car-list.css index 93880d0d5..e4368052b 100644 --- a/backend/src/assets/css/car-list.css +++ b/backend/src/assets/css/car-list.css @@ -72,7 +72,7 @@ section.car-list .empty-list { max-height: 75%; } -section.car-list article div.car div.car-footer { + section.car-list article div.car div.car-footer { display: flex; flex-direction: column; position: absolute; @@ -170,6 +170,10 @@ section.car-list article div.car div.car-footer { color: #1f9201; } + section.car-list article div.car-info ul.extras-list li.car-coming-soon { + color: #0064c8; + } + section.car-list article div.car-info ul.extras-list li.car-unavailable, section.car-list article div.car-info ul.extras-list .unavailable { color: #f44336; @@ -458,6 +462,10 @@ section.car-list article div.car div.car-footer { color: #1f9201; } + section.car-list article div.car-info ul.extras-list li.car-coming-soon { + color: #0064c8; + } + section.car-list article div.car-info ul.extras-list li.car-unavailable, section.car-list article div.car-info ul.extras-list .unavailable { color: #f44336; diff --git a/backend/src/components/CarList.tsx b/backend/src/components/CarList.tsx index 0b4900d27..c636df51a 100644 --- a/backend/src/components/CarList.tsx +++ b/backend/src/components/CarList.tsx @@ -515,14 +515,22 @@ const CarList = ({
    {edit && ( -
  • - + <> +
  • + +
    + {car.available ? : } + {car.available ? {strings.CAR_AVAILABLE} : {strings.CAR_UNAVAILABLE}} +
    +
    +
  • +
  • - {car.available ? : } - {car.available ? {strings.CAR_AVAILABLE} : {strings.CAR_UNAVAILABLE}} + + {strings.COMING_SOON}
    - -
  • + + )} {car.cancellation > -1 && (
  • diff --git a/backend/src/lang/cars.ts b/backend/src/lang/cars.ts index 637891f0d..4399e75de 100644 --- a/backend/src/lang/cars.ts +++ b/backend/src/lang/cars.ts @@ -81,6 +81,7 @@ const strings = new LocalizedStrings({ PRICE_PER_DAY: 'Prix par jour :', TRIPS: 'locations', CO2: 'Effet CO2', + COMING_SOON: 'Bientôt Disponible', }, en: { NEW_CAR: 'New car', @@ -155,6 +156,7 @@ const strings = new LocalizedStrings({ PRICE_PER_DAY: 'Price per day:', TRIPS: 'trips', CO2: 'CO2 effect', + COMING_SOON: 'Coming Soon', }, es: { NEW_CAR: 'Nuevo coche', @@ -229,6 +231,7 @@ const strings = new LocalizedStrings({ PRICE_PER_DAY: 'Precio por día:', TRIPS: 'alquileres', CO2: 'Efecto CO2', + COMING_SOON: 'Próximamente', }, }) diff --git a/backend/src/lang/create-car.ts b/backend/src/lang/create-car.ts index fa059dbc8..05800a9df 100644 --- a/backend/src/lang/create-car.ts +++ b/backend/src/lang/create-car.ts @@ -30,6 +30,7 @@ const strings = new LocalizedStrings({ MULTIMEDIA: 'Multimédia', RATING: 'Notation', CO2: 'CO2 (g/km)', + COMING_SOON: 'Bientôt Disponible', }, en: { NEW_CAR_HEADING: 'New car', @@ -58,6 +59,7 @@ const strings = new LocalizedStrings({ MULTIMEDIA: 'Multimedia', RATING: 'Rating', CO2: 'CO2 (g/km)', + COMING_SOON: 'Coming Soon', }, es: { NEW_CAR_HEADING: 'Nuevo coche', @@ -86,6 +88,7 @@ const strings = new LocalizedStrings({ MULTIMEDIA: 'Multimedia', RATING: 'Calificación', CO2: 'CO2 (g/km)', + COMING_SOON: 'Próximamente', }, }) diff --git a/backend/src/pages/CreateCar.tsx b/backend/src/pages/CreateCar.tsx index f5c27f3ac..0fa5ee7c8 100644 --- a/backend/src/pages/CreateCar.tsx +++ b/backend/src/pages/CreateCar.tsx @@ -51,6 +51,7 @@ const CreateCar = () => { const [rating, setRating] = useState('') const [co2, setCo2] = useState('') const [available, setAvailable] = useState(false) + const [comingSoon, setComingSoon] = useState(false) const [type, setType] = useState('') const [gearbox, setGearbox] = useState('') const [dailyPrice, setDailyPrice] = useState('') @@ -162,6 +163,10 @@ const CreateCar = () => { setAvailable(e.target.checked) } + const handleComingSoonChange = (e: React.ChangeEvent) => { + setComingSoon(e.target.checked) + } + const handleCarTypeChange = (value: string) => { setType(value) } @@ -274,6 +279,7 @@ const CreateCar = () => { multimedia, rating: Number(rating) || undefined, co2: Number(co2) || undefined, + comingSoon, } const car = await CarService.create(data) @@ -566,6 +572,20 @@ const CreateCar = () => { } label={strings.AVAILABLE} className="checkbox-fcl" /> + + + )} + label={strings.COMING_SOON} + className="checkbox-fcl" + /> + + @@ -683,8 +703,8 @@ const CreateCar = () => { onClick={async () => { if (image) { await CarService.deleteTempImage(image) - navigate('/cars') } + navigate('/cars') }} > {commonStrings.CANCEL} diff --git a/backend/src/pages/UpdateCar.tsx b/backend/src/pages/UpdateCar.tsx index cca983a8d..2b9ff3de8 100644 --- a/backend/src/pages/UpdateCar.tsx +++ b/backend/src/pages/UpdateCar.tsx @@ -54,6 +54,7 @@ const UpdateCar = () => { const [rating, setRating] = useState('') const [co2, setCo2] = useState('') const [available, setAvailable] = useState(false) + const [comingSoon, setComingSoon] = useState(false) const [type, setType] = useState('') const [gearbox, setGearbox] = useState('') const [dailyPrice, setDailyPrice] = useState('') @@ -164,6 +165,10 @@ const UpdateCar = () => { setAvailable(e.target.checked) } + const handleComingSoonChange = (e: React.ChangeEvent) => { + setComingSoon(e.target.checked) + } + const handleCarTypeChange = (value: string) => { setType(value) } @@ -277,6 +282,7 @@ const UpdateCar = () => { multimedia, rating: Number(rating) || undefined, co2: Number(co2) || undefined, + comingSoon, } const status = await CarService.update(data) @@ -345,6 +351,7 @@ const UpdateCar = () => { setCo2(_car.co2.toString()) } setAvailable(_car.available) + setComingSoon(_car.comingSoon || false) setType(_car.type) setGearbox(_car.gearbox) setAircon(_car.aircon) @@ -646,6 +653,20 @@ const UpdateCar = () => { } label={strings.AVAILABLE} className="checkbox-fcl" /> + + + )} + label={strings.COMING_SOON} + className="checkbox-fcl" + /> + + diff --git a/frontend/src/assets/css/car.css b/frontend/src/assets/css/car.css index ebf03edc1..28d5d469b 100644 --- a/frontend/src/assets/css/car.css +++ b/frontend/src/assets/css/car.css @@ -16,6 +16,25 @@ div.car-container article .btn-book { padding: 0 20px; } +div.car-container article span.coming-soon, +div.car-container article span.already-booked { + font-size: 13px; + padding: 4px; + border-radius: 4px; + min-width: 160px; + text-align: center; +} + +div.car-container article span.coming-soon { + background-color: #0078D4; + color: #fff; +} + +div.car-container article span.already-booked { + background-color: #FD3446; + color: #fff; +} + div.car-container div.car-header { padding: 10px 0; border-bottom: #d9d8d9 2px solid; diff --git a/frontend/src/components/Car.tsx b/frontend/src/components/Car.tsx index dbbe67cd3..e49ce117c 100644 --- a/frontend/src/components/Car.tsx +++ b/frontend/src/components/Car.tsx @@ -367,23 +367,31 @@ const Car = ({ {!hidePrice && (
    - + { + car.comingSoon ? ( + {strings.COMING_SOON} + ) : car.available ? ( + + ) : ( + {strings.ALREADY_BOOKED} + ) + }
    )} diff --git a/frontend/src/components/CarList.tsx b/frontend/src/components/CarList.tsx index 99e52e4c4..cd2f84745 100644 --- a/frontend/src/components/CarList.tsx +++ b/frontend/src/components/CarList.tsx @@ -43,6 +43,8 @@ interface CarListProps { rating?: number seats?: number distance?: string + includeAlreadyBookedCars?: boolean + includeComingSoonCars?: boolean onLoad?: bookcarsTypes.DataEvent } @@ -72,6 +74,8 @@ const CarList = ({ rating, seats, distance, + includeAlreadyBookedCars, + includeComingSoonCars, onLoad, }: CarListProps) => { const [init, setInit] = useState(true) @@ -132,6 +136,8 @@ const CarList = ({ rating: _rating, seats: _seats, days: bookcarsHelper.days(from, to), + includeAlreadyBookedCars, + includeComingSoonCars, } const data = await CarService.getCars(payload, _page, env.CARS_PAGE_SIZE) diff --git a/frontend/src/lang/cars.ts b/frontend/src/lang/cars.ts index 5561f2f86..8c80ef6ed 100644 --- a/frontend/src/lang/cars.ts +++ b/frontend/src/lang/cars.ts @@ -86,6 +86,8 @@ const strings = new LocalizedStrings({ TITLE_CAR_AVAILABLE: 'voiture disponible', TITLE_CARS_AVAILABLE: 'voitures disponibles', DETAILS: 'Détails', + ALREADY_BOOKED: 'Déjà Réservée', + COMING_SOON: 'Bientôt Disponible', }, en: { NEW_CAR: 'New car', @@ -163,6 +165,8 @@ const strings = new LocalizedStrings({ TITLE_CAR_AVAILABLE: 'car available', TITLE_CARS_AVAILABLE: 'cars available', DETAILS: 'Details', + ALREADY_BOOKED: 'Already Booked', + COMING_SOON: 'Coming Soon', }, es: { NEW_CAR: 'Coche nuevo', @@ -240,6 +244,8 @@ const strings = new LocalizedStrings({ TITLE_CAR_AVAILABLE: 'coche disponible', TITLE_CARS_AVAILABLE: 'coches disponibles', DETAILS: 'Detalles', + ALREADY_BOOKED: 'Ya Reservado', + COMING_SOON: 'Próximamente', } }) diff --git a/packages/bookcars-types/index.ts b/packages/bookcars-types/index.ts index 1151ed1c5..a2f9b7a52 100644 --- a/packages/bookcars-types/index.ts +++ b/packages/bookcars-types/index.ts @@ -199,6 +199,7 @@ export interface CreateCarPayload { multimedia: string[] rating?: number co2?: number + comingSoon?: boolean } export interface UpdateCarPayload extends CreateCarPayload { @@ -226,6 +227,8 @@ export interface GetCarsPayload { rating?: number seats?: number days?: number + includeAlreadyBookedCars?: boolean + includeComingSoonCars?: boolean } export interface SignUpPayload { @@ -444,6 +447,7 @@ export interface Car { rating?: number trips: number co2?: number + comingSoon?: boolean [propKey: string]: any } From 06a454854637fb8c11933b9d6e12227f3fbd73f6 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 18:44:23 +0100 Subject: [PATCH 09/42] Fix CarList.tsx --- backend/src/components/CarList.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/src/components/CarList.tsx b/backend/src/components/CarList.tsx index c636df51a..89a9a0043 100644 --- a/backend/src/components/CarList.tsx +++ b/backend/src/components/CarList.tsx @@ -524,12 +524,14 @@ const CarList = ({
  • -
  • -
    - - {strings.COMING_SOON} -
    -
  • + {car.comingSoon && ( +
  • +
    + + {strings.COMING_SOON} +
    +
  • + )} )} {car.cancellation > -1 && ( From 8ad35cea1084ef3bed66e9654ccd936d4e978308 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 18:48:38 +0100 Subject: [PATCH 10/42] Fix car.test.ts --- api/__tests__/car.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/__tests__/car.test.ts b/api/__tests__/car.test.ts index e09ad55bf..2e0117b90 100644 --- a/api/__tests__/car.test.ts +++ b/api/__tests__/car.test.ts @@ -671,6 +671,8 @@ describe('POST /api/frontend-cars/:page/:size', () => { }, fuelPolicy: [bookcarsTypes.FuelPolicy.LikeForLike], days: 3, + includeAlreadyBookedCars: true, + includeComingSoonCars: true, } let res = await request(app) .post(`/api/frontend-cars/${testHelper.PAGE}/${testHelper.SIZE}`) From de0b9c41735b4c6dd313fead5e524e924a9da096 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 18:53:39 +0100 Subject: [PATCH 11/42] Update car.css --- frontend/src/assets/css/car.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/assets/css/car.css b/frontend/src/assets/css/car.css index 28d5d469b..352f1b902 100644 --- a/frontend/src/assets/css/car.css +++ b/frontend/src/assets/css/car.css @@ -23,9 +23,11 @@ div.car-container article span.already-booked { border-radius: 4px; min-width: 160px; text-align: center; + background-color: #1a1a1a; + color: #fff; } -div.car-container article span.coming-soon { +/* div.car-container article span.coming-soon { background-color: #0078D4; color: #fff; } @@ -33,7 +35,7 @@ div.car-container article span.coming-soon { div.car-container article span.already-booked { background-color: #FD3446; color: #fff; -} +} */ div.car-container div.car-header { padding: 10px 0; From 02e857fb0edb702bc20b14fcce79e068133988d9 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 21 Jan 2025 19:31:15 +0100 Subject: [PATCH 12/42] Fix car.test.ts --- api/__tests__/car.test.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/api/__tests__/car.test.ts b/api/__tests__/car.test.ts index 2e0117b90..84dc3db7d 100644 --- a/api/__tests__/car.test.ts +++ b/api/__tests__/car.test.ts @@ -111,6 +111,7 @@ describe('POST /api/create-car', () => { range: bookcarsTypes.CarRange.Mini, multimedia: [bookcarsTypes.CarMultimedia.Bluetooth], rating: 3, + comingSoon: true, } let res = await request(app) .post('/api/create-car') @@ -207,7 +208,7 @@ describe('PUT /api/update-car', () => { range: bookcarsTypes.CarRange.Midi, multimedia: [bookcarsTypes.CarMultimedia.AndroidAuto], rating: 4, - comingSoon: true, + comingSoon: false, } let res = await request(app) .put('/api/update-car') @@ -671,8 +672,6 @@ describe('POST /api/frontend-cars/:page/:size', () => { }, fuelPolicy: [bookcarsTypes.FuelPolicy.LikeForLike], days: 3, - includeAlreadyBookedCars: true, - includeComingSoonCars: true, } let res = await request(app) .post(`/api/frontend-cars/${testHelper.PAGE}/${testHelper.SIZE}`) @@ -680,6 +679,16 @@ describe('POST /api/frontend-cars/:page/:size', () => { expect(res.statusCode).toBe(200) expect(res.body[0].resultData.length).toBeGreaterThan(0) + payload.includeAlreadyBookedCars = true + payload.includeComingSoonCars = true + res = await request(app) + .post(`/api/frontend-cars/${testHelper.PAGE}/${testHelper.SIZE}`) + .send(payload) + expect(res.statusCode).toBe(200) + expect(res.body[0].resultData.length).toBeGreaterThan(0) + payload.includeAlreadyBookedCars = false + payload.includeComingSoonCars = false + payload.days = undefined res = await request(app) .post(`/api/frontend-cars/${testHelper.PAGE}/${testHelper.SIZE}`) From d79537398f20744f61f159d35f934f2d22c24bd1 Mon Sep 17 00:00:00 2001 From: aelassas Date: Wed, 22 Jan 2025 15:17:28 +0100 Subject: [PATCH 13/42] Add fully booked flag to cars in backend ; Add full to full and full to empty fuel policies --- api/__tests__/car.test.ts | 3 + api/src/config/env.config.ts | 3 +- api/src/controllers/carController.ts | 20 ++- api/src/models/Car.ts | 11 +- backend/src/assets/css/car-list.css | 4 +- backend/src/common/helper.ts | 12 ++ backend/src/components/CarList.tsx | 10 +- backend/src/components/FuelPolicyFilter.tsx | 132 ++++++++++++++++--- backend/src/components/FuelPolicyList.tsx | 2 + backend/src/lang/cars.ts | 54 +++++--- backend/src/lang/create-car.ts | 9 +- backend/src/pages/Cars.tsx | 2 +- backend/src/pages/CreateCar.tsx | 22 +++- backend/src/pages/UpdateCar.tsx | 21 +++ frontend/src/assets/css/car.css | 26 ++-- frontend/src/common/helper.ts | 12 ++ frontend/src/components/Car.tsx | 13 +- frontend/src/components/FuelPolicyFilter.tsx | 132 ++++++++++++++++--- frontend/src/lang/cars.ts | 36 +++-- frontend/src/pages/Search.tsx | 9 +- packages/bookcars-helper/index.ts | 2 + packages/bookcars-types/index.ts | 10 +- 22 files changed, 440 insertions(+), 105 deletions(-) diff --git a/api/__tests__/car.test.ts b/api/__tests__/car.test.ts index 84dc3db7d..bed4c5980 100644 --- a/api/__tests__/car.test.ts +++ b/api/__tests__/car.test.ts @@ -112,6 +112,7 @@ describe('POST /api/create-car', () => { multimedia: [bookcarsTypes.CarMultimedia.Bluetooth], rating: 3, comingSoon: true, + fullyBooked: true, } let res = await request(app) .post('/api/create-car') @@ -209,6 +210,7 @@ describe('PUT /api/update-car', () => { multimedia: [bookcarsTypes.CarMultimedia.AndroidAuto], rating: 4, comingSoon: false, + fullyBooked: false, } let res = await request(app) .put('/api/update-car') @@ -247,6 +249,7 @@ describe('PUT /api/update-car', () => { expect(car.multimedia).toStrictEqual(payload.multimedia) expect(car.rating).toBe(payload.rating) expect(car.comingSoon).toBe(payload.comingSoon) + expect(car.fullyBooked).toBe(payload.fullyBooked) // test success (booking not found) payload._id = testHelper.GetRandromObjectIdAsString() diff --git a/api/src/config/env.config.ts b/api/src/config/env.config.ts index a736a8dce..cd3477a8e 100644 --- a/api/src/config/env.config.ts +++ b/api/src/config/env.config.ts @@ -503,6 +503,8 @@ export interface Car extends Document { deposit: number available: boolean + fullyBooked?: boolean + comingSoon?: boolean type: bookcarsTypes.CarType gearbox: bookcarsTypes.GearboxType aircon: boolean @@ -522,7 +524,6 @@ export interface Car extends Document { rating?: number trips: number co2?: number - comingSoon?: boolean } /** diff --git a/api/src/controllers/carController.ts b/api/src/controllers/carController.ts index 07e277e0b..e64c73c01 100644 --- a/api/src/controllers/carController.ts +++ b/api/src/controllers/carController.ts @@ -78,6 +78,8 @@ export const update = async (req: Request, res: Response) => { name, minimumAge, available, + fullyBooked, + comingSoon, type, locations, dailyPrice, @@ -105,7 +107,6 @@ export const update = async (req: Request, res: Response) => { multimedia, rating, co2, - comingSoon, } = body car.supplier = new mongoose.Types.ObjectId(supplier) @@ -113,6 +114,8 @@ export const update = async (req: Request, res: Response) => { car.locations = locations.map((l) => new mongoose.Types.ObjectId(l)) car.name = name car.available = available + car.fullyBooked = fullyBooked + car.comingSoon = comingSoon car.type = type as bookcarsTypes.CarType car.dailyPrice = dailyPrice car.discountedDailyPrice = discountedDailyPrice @@ -139,7 +142,6 @@ export const update = async (req: Request, res: Response) => { car.multimedia = multimedia car.rating = rating car.co2 = co2 - car.comingSoon = comingSoon await car.save() return res.json(car) @@ -663,11 +665,21 @@ export const getFrontendCars = async (req: Request, res: Response) => { } if (!includeAlreadyBookedCars) { - $match.$and!.push({ available: true }) + $match.$and!.push({ + $or: [ + { $and: [{ $or: [{ fullyBooked: false }, { fullyBooked: null }] }, { available: true }] }, + { $and: [{ $or: [{ fullyBooked: false }, { fullyBooked: null }] }, { available: false }] }, + ], + }) } if (!includeComingSoonCars) { - $match.$and!.push({ $or: [{ comingSoon: false }, { comingSoon: null }] }) + $match.$and!.push({ + $or: [ + { $and: [{ $or: [{ comingSoon: false }, { comingSoon: null }] }, { available: true }] }, + { $and: [{ $or: [{ comingSoon: false }, { comingSoon: null }] }, { available: false }] }, + ], + }) } if (fuelPolicy) { diff --git a/api/src/models/Car.ts b/api/src/models/Car.ts index d12a50426..c2608d503 100644 --- a/api/src/models/Car.ts +++ b/api/src/models/Car.ts @@ -65,6 +65,10 @@ const carSchema = new Schema( required: [true, "can't be blank"], index: true, }, + fullyBooked: { + type: Boolean, + default: false, + }, comingSoon: { type: Boolean, default: false, @@ -111,7 +115,12 @@ const carSchema = new Schema( }, fuelPolicy: { type: String, - enum: [bookcarsTypes.FuelPolicy.LikeForLike, bookcarsTypes.FuelPolicy.FreeTank], + enum: [ + bookcarsTypes.FuelPolicy.LikeForLike, + bookcarsTypes.FuelPolicy.FreeTank, + bookcarsTypes.FuelPolicy.FullToFull, + bookcarsTypes.FuelPolicy.FullToEmpty, + ], required: [true, "can't be blank"], }, mileage: { diff --git a/backend/src/assets/css/car-list.css b/backend/src/assets/css/car-list.css index e4368052b..fe23d725e 100644 --- a/backend/src/assets/css/car-list.css +++ b/backend/src/assets/css/car-list.css @@ -171,7 +171,7 @@ section.car-list .empty-list { } section.car-list article div.car-info ul.extras-list li.car-coming-soon { - color: #0064c8; + color: #1f9201; } section.car-list article div.car-info ul.extras-list li.car-unavailable, @@ -463,7 +463,7 @@ section.car-list .empty-list { } section.car-list article div.car-info ul.extras-list li.car-coming-soon { - color: #0064c8; + color: #1f9201; } section.car-list article div.car-info ul.extras-list li.car-unavailable, diff --git a/backend/src/common/helper.ts b/backend/src/common/helper.ts index a69292632..ecb53ac01 100644 --- a/backend/src/common/helper.ts +++ b/backend/src/common/helper.ts @@ -140,6 +140,12 @@ export const getFuelPolicy = (type: string) => { case bookcarsTypes.FuelPolicy.FreeTank: return strings.FUEL_POLICY_FREE_TANK + case bookcarsTypes.FuelPolicy.FullToFull: + return strings.FUEL_POLICY_FULL_TO_FULL + + case bookcarsTypes.FuelPolicy.FullToEmpty: + return strings.FUEL_POLICY_FULL_TO_EMPTY + default: return '' } @@ -222,6 +228,12 @@ export const getFuelPolicyTooltip = (fuelPolicy: string) => { case bookcarsTypes.FuelPolicy.FreeTank: return strings.FUEL_POLICY_FREE_TANK_TOOLTIP + case bookcarsTypes.FuelPolicy.FullToFull: + return strings.FUEL_POLICY_FULL_TO_FULL_TOOLTIP + + case bookcarsTypes.FuelPolicy.FullToEmpty: + return strings.FUEL_POLICY_FULL_TO_EMPTY_TOOLTIP + default: return '' } diff --git a/backend/src/components/CarList.tsx b/backend/src/components/CarList.tsx index 89a9a0043..44dd672dd 100644 --- a/backend/src/components/CarList.tsx +++ b/backend/src/components/CarList.tsx @@ -397,7 +397,7 @@ const CarList = ({ ) - : rows.map((car) => { + : rows.map((car, index) => { const edit = admin || car.supplier._id === user._id return (
    @@ -524,6 +524,14 @@ const CarList = ({ + {car.fullyBooked && ( +
  • +
    + + {strings.FULLY_BOOKED} +
    +
  • + )} {car.comingSoon && (
  • diff --git a/backend/src/components/FuelPolicyFilter.tsx b/backend/src/components/FuelPolicyFilter.tsx index 853030988..b39de3723 100644 --- a/backend/src/components/FuelPolicyFilter.tsx +++ b/backend/src/components/FuelPolicyFilter.tsx @@ -23,13 +23,17 @@ const FuelPolicyFilter = ({ const [allChecked, setAllChecked] = useState(false) const [values, setValues] = useState([]) - const automaticRef = useRef(null) - const manualRef = useRef(null) + const freeTankRef = useRef(null) + const likeForLikeRef = useRef(null) + const fullToFullRef = useRef(null) + const fullToEmptyRef = useRef(null) useEffect(() => { - if (allChecked && automaticRef.current && manualRef.current) { - automaticRef.current.checked = true - manualRef.current.checked = true + if (allChecked && freeTankRef.current && likeForLikeRef.current && fullToFullRef.current && fullToEmptyRef.current) { + freeTankRef.current.checked = true + likeForLikeRef.current.checked = true + fullToFullRef.current.checked = true + fullToEmptyRef.current.checked = true } }, [allChecked]) @@ -43,7 +47,7 @@ const FuelPolicyFilter = ({ if ('checked' in e.currentTarget && e.currentTarget.checked) { values.push(bookcarsTypes.FuelPolicy.FreeTank) - if (values.length === 2) { + if (values.length === allTypes.length) { setAllChecked(true) } } else { @@ -74,7 +78,7 @@ const FuelPolicyFilter = ({ if ('checked' in e.currentTarget && e.currentTarget.checked) { values.push(bookcarsTypes.FuelPolicy.LikeForLike) - if (values.length === 2) { + if (values.length === allTypes.length) { setAllChecked(true) } } else { @@ -101,25 +105,99 @@ const FuelPolicyFilter = ({ handleCheckLikeForLikeChange(event) } + const handleCheckFullToFullChange = (e: React.ChangeEvent | React.MouseEvent) => { + if ('checked' in e.currentTarget && e.currentTarget.checked) { + values.push(bookcarsTypes.FuelPolicy.FullToFull) + + if (values.length === allTypes.length) { + setAllChecked(true) + } + } else { + values.splice( + values.findIndex((v) => v === bookcarsTypes.FuelPolicy.FullToFull), + 1, + ) + + if (values.length === 0) { + setAllChecked(false) + } + } + + setValues(values) + + handleOnChange(values) + } + + const handleFullToFullClick = (e: React.MouseEvent) => { + const checkbox = e.currentTarget.previousSibling as HTMLInputElement + checkbox.checked = !checkbox.checked + const event = e + event.currentTarget = checkbox + handleCheckFullToFullChange(event) + } + + const handleCheckFullToEmptyChange = (e: React.ChangeEvent | React.MouseEvent) => { + if ('checked' in e.currentTarget && e.currentTarget.checked) { + values.push(bookcarsTypes.FuelPolicy.FullToEmpty) + + if (values.length === allTypes.length) { + setAllChecked(true) + } + } else { + values.splice( + values.findIndex((v) => v === bookcarsTypes.FuelPolicy.FullToEmpty), + 1, + ) + + if (values.length === 0) { + setAllChecked(false) + } + } + + setValues(values) + + handleOnChange(values) + } + + const handleFullToEmptyClick = (e: React.MouseEvent) => { + const checkbox = e.currentTarget.previousSibling as HTMLInputElement + checkbox.checked = !checkbox.checked + const event = e + event.currentTarget = checkbox + handleCheckFullToEmptyChange(event) + } + const handleUncheckAllChange = () => { if (allChecked) { // uncheck all - if (automaticRef.current) { - automaticRef.current.checked = false + if (freeTankRef.current) { + freeTankRef.current.checked = false + } + if (likeForLikeRef.current) { + likeForLikeRef.current.checked = false } - if (manualRef.current) { - manualRef.current.checked = false + if (fullToFullRef.current) { + fullToFullRef.current.checked = false + } + if (fullToEmptyRef.current) { + fullToEmptyRef.current.checked = false } setAllChecked(false) setValues([]) } else { // check all - if (automaticRef.current) { - automaticRef.current.checked = true + if (freeTankRef.current) { + freeTankRef.current.checked = true + } + if (likeForLikeRef.current) { + likeForLikeRef.current.checked = true + } + if (fullToFullRef.current) { + fullToFullRef.current.checked = true } - if (manualRef.current) { - manualRef.current.checked = true + if (fullToEmptyRef.current) { + fullToEmptyRef.current.checked = true } const _values = allTypes @@ -137,7 +215,7 @@ const FuelPolicyFilter = ({
    - +
    - +
    +
    + + + {strings.FUEL_POLICY_FULL_TO_FULL} + +
    +
    + + + {strings.FUEL_POLICY_FULL_TO_EMPTY} + +
    {strings.FUEL_POLICY_LIKE_FOR_LIKE} {strings.FUEL_POLICY_FREE_TANK} + {strings.FUEL_POLICY_FULL_TO_FULL} + {strings.FUEL_POLICY_FULL_TO_EMPTY}
    ) diff --git a/backend/src/lang/cars.ts b/backend/src/lang/cars.ts index 4399e75de..539d27a60 100644 --- a/backend/src/lang/cars.ts +++ b/backend/src/lang/cars.ts @@ -28,8 +28,14 @@ const strings = new LocalizedStrings({ GEARBOX_AUTOMATIC: 'Automatique', GEARBOX_MANUAL_SHORT: 'M', GEARBOX_AUTOMATIC_SHORT: 'A', - FUEL_POLICY_LIKE_FOR_LIKE: 'Plein/Plein', + FUEL_POLICY_LIKE_FOR_LIKE: 'Même niveau', FUEL_POLICY_FREE_TANK: 'Plein inclus', + FUEL_POLICY_FULL_TO_FULL: 'Plein/Plein', + FUEL_POLICY_FULL_TO_EMPTY: 'Plein/Vide', + FUEL_POLICY_LIKE_FOR_LIKE_TOOLTIP: 'Cette voiture est fournie avec du carburant dans le réservoir et doit être rendu avec la même quantité de carburant.', + FUEL_POLICY_FREE_TANK_TOOLTIP: 'Le prix inclut un plein de carburant', + FUEL_POLICY_FULL_TO_FULL_TOOLTIP: 'Cette voiture est fournie avec le plein de carburant dans le réservoir et doit être rendu avec le plein de carburant.', + FUEL_POLICY_FULL_TO_EMPTY_TOOLTIP: 'Cette voiture est fournie avec le plein de carburant dans le réservoir et doit être rendu avec réservoir vide.', DIESEL_TOOLTIP: 'Cette voiture a un moteur diesel', GASOLINE_TOOLTIP: 'Cette voiture a un moteur essence', ELECTRIC_TOOLTIP: 'Cette voiture est électrique', @@ -42,8 +48,6 @@ const strings = new LocalizedStrings({ DOORS_TOOLTIP_1: 'Cette voiture a ', DOORS_TOOLTIP_2: 'portes', AIRCON_TOOLTIP: 'Cette voiture a de la climatisation', - FUEL_POLICY_LIKE_FOR_LIKE_TOOLTIP: 'Cette voiture est fournie avec du carburant dans le réservoir et doit être rendu avec la même quantité de carburant.', - FUEL_POLICY_FREE_TANK_TOOLTIP: 'Le prix inclut un plein de carburant', MILEAGE: 'Kilométrage', MILEAGE_UNIT: 'KM/jour', UNLIMITED: 'Illimité', @@ -62,10 +66,10 @@ const strings = new LocalizedStrings({ INCLUDED: 'Inclus', AVAILABLE: 'Disponile', UNAVAILABLE: 'Indisponible', - CAR_AVAILABLE: 'Disponible à la location', - CAR_AVAILABLE_TOOLTIP: 'Cette voiture est disponible à la location.', - CAR_UNAVAILABLE: 'Indisponible à la location', - CAR_UNAVAILABLE_TOOLTIP: "Cette voiture n'est pas disponible à la location.", + CAR_AVAILABLE: 'Disponible', + CAR_AVAILABLE_TOOLTIP: 'Cette voiture est disponible.', + CAR_UNAVAILABLE: 'Indisponible', + CAR_UNAVAILABLE_TOOLTIP: "Cette voiture n'est pas disponible.", VIEW_CAR: 'Voir cette voiture', EMPTY_LIST: 'Pas de voitures.', CANNOT_DELETE_CAR: 'Cette voiture ne peut pas être supprimée car elle est liée à des réservations. Vous pouvez cependant la rendre indisponible à la location en la modifiant.', @@ -82,6 +86,7 @@ const strings = new LocalizedStrings({ TRIPS: 'locations', CO2: 'Effet CO2', COMING_SOON: 'Bientôt Disponible', + FULLY_BOOKED: 'Déjà réservée', }, en: { NEW_CAR: 'New car', @@ -105,6 +110,12 @@ const strings = new LocalizedStrings({ GEARBOX_AUTOMATIC_SHORT: 'A', FUEL_POLICY_LIKE_FOR_LIKE: 'Like for Like', FUEL_POLICY_FREE_TANK: 'Free tank', + FUEL_POLICY_FULL_TO_FULL: 'Full to Full', + FUEL_POLICY_FULL_TO_EMPTY: 'Full to Empty', + FUEL_POLICY_LIKE_FOR_LIKE_TOOLTIP: 'This car is supplied with fuel and must be returned with the same amount of fuel.', + FUEL_POLICY_FREE_TANK_TOOLTIP: 'The price includes a full tank of fuel.', + FUEL_POLICY_FULL_TO_FULL_TOOLTIP: 'This car is supplied with a full tank of fuel and must be returned with a full tank of fuel.', + FUEL_POLICY_FULL_TO_EMPTY_TOOLTIP: 'This car is supplied with a full tank of fuel and must be returned with an empty tank.', DIESEL_TOOLTIP: 'This car has a diesel engine', GASOLINE_TOOLTIP: 'This car has a gasoline engine', ELECTRIC_TOOLTIP: 'This car is electric', @@ -117,8 +128,7 @@ const strings = new LocalizedStrings({ DOORS_TOOLTIP_1: 'This car has ', DOORS_TOOLTIP_2: 'doors', AIRCON_TOOLTIP: 'This car has aircon', - FUEL_POLICY_LIKE_FOR_LIKE_TOOLTIP: 'This car is supplied with fuel and must be returned with the same amount of fuel.', - FUEL_POLICY_FREE_TANK_TOOLTIP: 'The price includes a full tank of fuel.', + MILEAGE: 'Mileage', MILEAGE_UNIT: 'KM/day', UNLIMITED: 'Unlimited', @@ -137,10 +147,10 @@ const strings = new LocalizedStrings({ INCLUDED: 'Included', AVAILABLE: 'Available', UNAVAILABLE: 'Unavailable', - CAR_AVAILABLE: 'Available for rental', - CAR_AVAILABLE_TOOLTIP: 'This car is available for rental.', - CAR_UNAVAILABLE: 'Unavailable for rental', - CAR_UNAVAILABLE_TOOLTIP: 'This car is unavailable for rental.', + CAR_AVAILABLE: 'Available', + CAR_AVAILABLE_TOOLTIP: 'This car is available.', + CAR_UNAVAILABLE: 'Unavailable', + CAR_UNAVAILABLE_TOOLTIP: 'This car is unavailable.', VIEW_CAR: 'View this car', EMPTY_LIST: 'No cars.', CANNOT_DELETE_CAR: 'This car cannot be deleted because it is linked to bookings. However, you can make it unavailable for rental by modifying it.', @@ -157,6 +167,7 @@ const strings = new LocalizedStrings({ TRIPS: 'trips', CO2: 'CO2 effect', COMING_SOON: 'Coming Soon', + FULLY_BOOKED: 'Fully Booked', }, es: { NEW_CAR: 'Nuevo coche', @@ -180,6 +191,12 @@ const strings = new LocalizedStrings({ GEARBOX_AUTOMATIC_SHORT: 'A', FUEL_POLICY_LIKE_FOR_LIKE: 'Igual que', FUEL_POLICY_FREE_TANK: 'Tanque gratis', + FUEL_POLICY_FULL_TO_FULL: 'Lleno/Lleno', + FUEL_POLICY_FULL_TO_EMPTY: 'Lleno/Vacío', + FUEL_POLICY_LIKE_FOR_LIKE_TOOLTIP: 'Este coche se suministra con combustible y debe devolverse con la misma cantidad de combustible.', + FUEL_POLICY_FREE_TANK_TOOLTIP: 'El precio incluye un tanque lleno de combustible.', + FUEL_POLICY_FULL_TO_FULL_TOOLTIP: 'Este coche se entrega con un tanque de combustible lleno y debe devolverse con un tanque de combustible lleno.', + FUEL_POLICY_FULL_TO_EMPTY_TOOLTIP: 'Este coche se entrega con un tanque de combustible lleno y debe devolverse con un tanque de combustible lleno. un tanque vacío.', DIESEL_TOOLTIP: 'Este coche tiene un motor diésel', GASOLINE_TOOLTIP: 'Este coche tiene un motor de gasolina', ELECTRIC_TOOLTIP: 'Este coche es eléctrico', @@ -192,8 +209,6 @@ const strings = new LocalizedStrings({ DOORS_TOOLTIP_1: 'Este coche tiene ', DOORS_TOOLTIP_2: 'puertas', AIRCON_TOOLTIP: 'Este coche tiene aire acondicionado', - FUEL_POLICY_LIKE_FOR_LIKE_TOOLTIP: 'Este coche se suministra con combustible y debe devolverse con la misma cantidad de combustible.', - FUEL_POLICY_FREE_TANK_TOOLTIP: 'El precio incluye un tanque lleno de combustible.', MILEAGE: 'Kilometraje', MILEAGE_UNIT: 'KM/día', UNLIMITED: 'Ilimitado', @@ -212,10 +227,10 @@ const strings = new LocalizedStrings({ INCLUDED: 'Incluido', AVAILABLE: 'Disponible', UNAVAILABLE: 'No disponible', - CAR_AVAILABLE: 'Disponible para alquiler', - CAR_AVAILABLE_TOOLTIP: 'Este coche está disponible para alquilar.', - CAR_UNAVAILABLE: 'No disponible para alquiler', - CAR_UNAVAILABLE_TOOLTIP: 'Este coche no está disponible para alquilar.', + CAR_AVAILABLE: 'Disponible', + CAR_AVAILABLE_TOOLTIP: 'Este coche está disponible.', + CAR_UNAVAILABLE: 'No disponible', + CAR_UNAVAILABLE_TOOLTIP: 'Este coche no está disponible.', VIEW_CAR: 'Ver este coche', EMPTY_LIST: 'No hay coches.', CANNOT_DELETE_CAR: 'Este coche no se puede eliminar porque está vinculado a reservas. Sin embargo, puede hacerlo no disponible para alquilar modificándolo.', @@ -232,6 +247,7 @@ const strings = new LocalizedStrings({ TRIPS: 'alquileres', CO2: 'Efecto CO2', COMING_SOON: 'Próximamente', + FULLY_BOOKED: 'Ya Reservado', }, }) diff --git a/backend/src/lang/create-car.ts b/backend/src/lang/create-car.ts index 05800a9df..f854eddb6 100644 --- a/backend/src/lang/create-car.ts +++ b/backend/src/lang/create-car.ts @@ -10,7 +10,7 @@ const strings = new LocalizedStrings({ RECOMMENDED_IMAGE_SIZE: `Taille d'image recommandée : ${env.CAR_IMAGE_WIDTH}x${env.CAR_IMAGE_HEIGHT}`, SUPPLIER: 'Fournisseur', LOCATIONS: 'Lieux de prise en charge', - AVAILABLE: 'Disponible à la location', + AVAILABLE: 'Disponible', CAR_TYPE: 'Moteur', DAILY_PRICE: 'Prix/jour', DISCOUNTED_DAILY_PRICE: 'Prix discount/jour', @@ -31,6 +31,7 @@ const strings = new LocalizedStrings({ RATING: 'Notation', CO2: 'CO2 (g/km)', COMING_SOON: 'Bientôt Disponible', + FULLY_BOOKED: 'Déjà réservée', }, en: { NEW_CAR_HEADING: 'New car', @@ -39,7 +40,7 @@ const strings = new LocalizedStrings({ RECOMMENDED_IMAGE_SIZE: `Recommended image size: ${env.CAR_IMAGE_WIDTH}x${env.CAR_IMAGE_HEIGHT}`, SUPPLIER: 'Supplier', LOCATIONS: 'Pick-up locations', - AVAILABLE: 'Available for rental', + AVAILABLE: 'Available', CAR_TYPE: 'Engine', DAILY_PRICE: 'Daily Price', DISCOUNTED_DAILY_PRICE: 'Discounted Daily Price', @@ -60,6 +61,7 @@ const strings = new LocalizedStrings({ RATING: 'Rating', CO2: 'CO2 (g/km)', COMING_SOON: 'Coming Soon', + FULLY_BOOKED: 'Fully Booked', }, es: { NEW_CAR_HEADING: 'Nuevo coche', @@ -68,7 +70,7 @@ const strings = new LocalizedStrings({ RECOMMENDED_IMAGE_SIZE: `Tamaño de imagen recomendado: ${env.CAR_IMAGE_WIDTH}x${env.CAR_IMAGE_HEIGHT}`, SUPPLIER: 'Proveedor', LOCATIONS: 'Lugares de recogida', - AVAILABLE: 'Disponible para alquiler', + AVAILABLE: 'Disponible', CAR_TYPE: 'Motor', DAILY_PRICE: 'Precio diario', DISCOUNTED_DAILY_PRICE: 'Precio diario con descuento', @@ -89,6 +91,7 @@ const strings = new LocalizedStrings({ RATING: 'Calificación', CO2: 'CO2 (g/km)', COMING_SOON: 'Próximamente', + FULLY_BOOKED: 'Ya Reservado', }, }) diff --git a/backend/src/pages/Cars.tsx b/backend/src/pages/Cars.tsx index ab543f511..300b55995 100644 --- a/backend/src/pages/Cars.tsx +++ b/backend/src/pages/Cars.tsx @@ -40,7 +40,7 @@ const Cars = () => { const [gearbox, setGearbox] = useState([bookcarsTypes.GearboxType.Automatic, bookcarsTypes.GearboxType.Manual]) const [mileage, setMileage] = useState([bookcarsTypes.Mileage.Limited, bookcarsTypes.Mileage.Unlimited]) const [availability, setAvailability] = useState([bookcarsTypes.Availablity.Available, bookcarsTypes.Availablity.Unavailable]) - const [fuelPolicy, setFuelPolicy] = useState([bookcarsTypes.FuelPolicy.FreeTank, bookcarsTypes.FuelPolicy.LikeForLike]) + const [fuelPolicy, setFuelPolicy] = useState(bookcarsHelper.getAllFuelPolicies()) const [deposit, setDeposit] = useState(-1) const [language, setLanguage] = useState(env.DEFAULT_LANGUAGE) diff --git a/backend/src/pages/CreateCar.tsx b/backend/src/pages/CreateCar.tsx index 0fa5ee7c8..b91e58ee9 100644 --- a/backend/src/pages/CreateCar.tsx +++ b/backend/src/pages/CreateCar.tsx @@ -51,6 +51,7 @@ const CreateCar = () => { const [rating, setRating] = useState('') const [co2, setCo2] = useState('') const [available, setAvailable] = useState(false) + const [fullyBooked, setFullyBooked] = useState(false) const [comingSoon, setComingSoon] = useState(false) const [type, setType] = useState('') const [gearbox, setGearbox] = useState('') @@ -163,6 +164,10 @@ const CreateCar = () => { setAvailable(e.target.checked) } + const handleFullyBookedChange = (e: React.ChangeEvent) => { + setFullyBooked(e.target.checked) + } + const handleComingSoonChange = (e: React.ChangeEvent) => { setComingSoon(e.target.checked) } @@ -261,6 +266,8 @@ const CreateCar = () => { discountedMonthlyPrice: getPrice(discountedMonthlyPrice), deposit: Number(deposit), available, + fullyBooked, + comingSoon, type, gearbox, aircon, @@ -279,7 +286,6 @@ const CreateCar = () => { multimedia, rating: Number(rating) || undefined, co2: Number(co2) || undefined, - comingSoon, } const car = await CarService.create(data) @@ -572,6 +578,20 @@ const CreateCar = () => { } label={strings.AVAILABLE} className="checkbox-fcl" /> + + + )} + label={strings.FULLY_BOOKED} + className="checkbox-fcl" + /> + + { const [rating, setRating] = useState('') const [co2, setCo2] = useState('') const [available, setAvailable] = useState(false) + const [fullyBooked, setFullyBooked] = useState(false) const [comingSoon, setComingSoon] = useState(false) const [type, setType] = useState('') const [gearbox, setGearbox] = useState('') @@ -165,6 +166,10 @@ const UpdateCar = () => { setAvailable(e.target.checked) } + const handleFullyBookedChange = (e: React.ChangeEvent) => { + setFullyBooked(e.target.checked) + } + const handleComingSoonChange = (e: React.ChangeEvent) => { setComingSoon(e.target.checked) } @@ -283,6 +288,7 @@ const UpdateCar = () => { rating: Number(rating) || undefined, co2: Number(co2) || undefined, comingSoon, + fullyBooked, } const status = await CarService.update(data) @@ -351,6 +357,7 @@ const UpdateCar = () => { setCo2(_car.co2.toString()) } setAvailable(_car.available) + setFullyBooked(_car.fullyBooked || false) setComingSoon(_car.comingSoon || false) setType(_car.type) setGearbox(_car.gearbox) @@ -653,6 +660,20 @@ const UpdateCar = () => { } label={strings.AVAILABLE} className="checkbox-fcl" /> + + + )} + label={strings.FULLY_BOOKED} + className="checkbox-fcl" + /> + + { case bookcarsTypes.FuelPolicy.FreeTank: return strings.FUEL_POLICY_FREE_TANK + case bookcarsTypes.FuelPolicy.FullToFull: + return strings.FUEL_POLICY_FULL_TO_FULL + + case bookcarsTypes.FuelPolicy.FullToEmpty: + return strings.FUEL_POLICY_FULL_TO_EMPTY + default: return '' } @@ -222,6 +228,12 @@ export const getFuelPolicyTooltip = (fuelPolicy: string) => { case bookcarsTypes.FuelPolicy.FreeTank: return strings.FUEL_POLICY_FREE_TANK_TOOLTIP + case bookcarsTypes.FuelPolicy.FullToFull: + return strings.FUEL_POLICY_FULL_TO_FULL_TOOLTIP + + case bookcarsTypes.FuelPolicy.FullToEmpty: + return strings.FUEL_POLICY_FULL_TO_EMPTY_TOOLTIP + default: return '' } diff --git a/frontend/src/components/Car.tsx b/frontend/src/components/Car.tsx index e49ce117c..0fc91efec 100644 --- a/frontend/src/components/Car.tsx +++ b/frontend/src/components/Car.tsx @@ -223,6 +223,13 @@ const Car = ({ {`${strings.PRICE_PER_DAY} `} {bookcarsHelper.formatPrice(totalPrice / days, commonStrings.CURRENCY, language)} + { + car.comingSoon ? ( + {strings.COMING_SOON} + ) : car.fullyBooked ? ( + {strings.FULLY_BOOKED} + ) : null + }
    )} @@ -368,9 +375,7 @@ const Car = ({ {!hidePrice && (
    { - car.comingSoon ? ( - {strings.COMING_SOON} - ) : car.available ? ( + car.available && !car.comingSoon && !car.fullyBooked && ( - ) : ( - {strings.ALREADY_BOOKED} ) }
    diff --git a/frontend/src/components/FuelPolicyFilter.tsx b/frontend/src/components/FuelPolicyFilter.tsx index 853030988..b39de3723 100644 --- a/frontend/src/components/FuelPolicyFilter.tsx +++ b/frontend/src/components/FuelPolicyFilter.tsx @@ -23,13 +23,17 @@ const FuelPolicyFilter = ({ const [allChecked, setAllChecked] = useState(false) const [values, setValues] = useState([]) - const automaticRef = useRef(null) - const manualRef = useRef(null) + const freeTankRef = useRef(null) + const likeForLikeRef = useRef(null) + const fullToFullRef = useRef(null) + const fullToEmptyRef = useRef(null) useEffect(() => { - if (allChecked && automaticRef.current && manualRef.current) { - automaticRef.current.checked = true - manualRef.current.checked = true + if (allChecked && freeTankRef.current && likeForLikeRef.current && fullToFullRef.current && fullToEmptyRef.current) { + freeTankRef.current.checked = true + likeForLikeRef.current.checked = true + fullToFullRef.current.checked = true + fullToEmptyRef.current.checked = true } }, [allChecked]) @@ -43,7 +47,7 @@ const FuelPolicyFilter = ({ if ('checked' in e.currentTarget && e.currentTarget.checked) { values.push(bookcarsTypes.FuelPolicy.FreeTank) - if (values.length === 2) { + if (values.length === allTypes.length) { setAllChecked(true) } } else { @@ -74,7 +78,7 @@ const FuelPolicyFilter = ({ if ('checked' in e.currentTarget && e.currentTarget.checked) { values.push(bookcarsTypes.FuelPolicy.LikeForLike) - if (values.length === 2) { + if (values.length === allTypes.length) { setAllChecked(true) } } else { @@ -101,25 +105,99 @@ const FuelPolicyFilter = ({ handleCheckLikeForLikeChange(event) } + const handleCheckFullToFullChange = (e: React.ChangeEvent | React.MouseEvent) => { + if ('checked' in e.currentTarget && e.currentTarget.checked) { + values.push(bookcarsTypes.FuelPolicy.FullToFull) + + if (values.length === allTypes.length) { + setAllChecked(true) + } + } else { + values.splice( + values.findIndex((v) => v === bookcarsTypes.FuelPolicy.FullToFull), + 1, + ) + + if (values.length === 0) { + setAllChecked(false) + } + } + + setValues(values) + + handleOnChange(values) + } + + const handleFullToFullClick = (e: React.MouseEvent) => { + const checkbox = e.currentTarget.previousSibling as HTMLInputElement + checkbox.checked = !checkbox.checked + const event = e + event.currentTarget = checkbox + handleCheckFullToFullChange(event) + } + + const handleCheckFullToEmptyChange = (e: React.ChangeEvent | React.MouseEvent) => { + if ('checked' in e.currentTarget && e.currentTarget.checked) { + values.push(bookcarsTypes.FuelPolicy.FullToEmpty) + + if (values.length === allTypes.length) { + setAllChecked(true) + } + } else { + values.splice( + values.findIndex((v) => v === bookcarsTypes.FuelPolicy.FullToEmpty), + 1, + ) + + if (values.length === 0) { + setAllChecked(false) + } + } + + setValues(values) + + handleOnChange(values) + } + + const handleFullToEmptyClick = (e: React.MouseEvent) => { + const checkbox = e.currentTarget.previousSibling as HTMLInputElement + checkbox.checked = !checkbox.checked + const event = e + event.currentTarget = checkbox + handleCheckFullToEmptyChange(event) + } + const handleUncheckAllChange = () => { if (allChecked) { // uncheck all - if (automaticRef.current) { - automaticRef.current.checked = false + if (freeTankRef.current) { + freeTankRef.current.checked = false + } + if (likeForLikeRef.current) { + likeForLikeRef.current.checked = false } - if (manualRef.current) { - manualRef.current.checked = false + if (fullToFullRef.current) { + fullToFullRef.current.checked = false + } + if (fullToEmptyRef.current) { + fullToEmptyRef.current.checked = false } setAllChecked(false) setValues([]) } else { // check all - if (automaticRef.current) { - automaticRef.current.checked = true + if (freeTankRef.current) { + freeTankRef.current.checked = true + } + if (likeForLikeRef.current) { + likeForLikeRef.current.checked = true + } + if (fullToFullRef.current) { + fullToFullRef.current.checked = true } - if (manualRef.current) { - manualRef.current.checked = true + if (fullToEmptyRef.current) { + fullToEmptyRef.current.checked = true } const _values = allTypes @@ -137,7 +215,7 @@ const FuelPolicyFilter = ({
    - +
    - +
    +
    + + + {strings.FUEL_POLICY_FULL_TO_FULL} + +
    +
    + + + {strings.FUEL_POLICY_FULL_TO_EMPTY} + +
    { const [carType, setCarType] = useState(bookcarsHelper.getAllCarTypes()) const [gearbox, setGearbox] = useState([bookcarsTypes.GearboxType.Automatic, bookcarsTypes.GearboxType.Manual]) const [mileage, setMileage] = useState([bookcarsTypes.Mileage.Limited, bookcarsTypes.Mileage.Unlimited]) - const [fuelPolicy, setFuelPolicy] = useState([bookcarsTypes.FuelPolicy.FreeTank, bookcarsTypes.FuelPolicy.LikeForLike]) + const [fuelPolicy, setFuelPolicy] = useState(bookcarsHelper.getAllFuelPolicies()) const [deposit, setDeposit] = useState(-1) const [ranges, setRanges] = useState(bookcarsHelper.getAllRanges()) const [multimedia, setMultimedia] = useState([]) @@ -327,8 +327,11 @@ const Search = () => { multimedia={multimedia} rating={rating} seats={seats} - // distance={distance} - // onLoad={() => setLoadingPage(false)} + // distance={distance} + // onLoad={() => setLoadingPage(false)} + hideSupplier + // includeAlreadyBookedCars + includeComingSoonCars />
    diff --git a/packages/bookcars-helper/index.ts b/packages/bookcars-helper/index.ts index 49c31dc38..a472d47dc 100644 --- a/packages/bookcars-helper/index.ts +++ b/packages/bookcars-helper/index.ts @@ -447,6 +447,8 @@ export const getAllMultimedias = () => [ export const getAllFuelPolicies = () => [ bookcarsTypes.FuelPolicy.FreeTank, bookcarsTypes.FuelPolicy.LikeForLike, + bookcarsTypes.FuelPolicy.FullToFull, + bookcarsTypes.FuelPolicy.FullToEmpty, ] /** diff --git a/packages/bookcars-types/index.ts b/packages/bookcars-types/index.ts index a2f9b7a52..580bf4b21 100644 --- a/packages/bookcars-types/index.ts +++ b/packages/bookcars-types/index.ts @@ -39,7 +39,9 @@ export enum GearboxType { export enum FuelPolicy { LikeForLike = 'likeForlike', - FreeTank = 'freeTank' + FreeTank = 'freeTank', + FullToFull = 'fullToFull', + FullToEmpty = 'FullToEmpty' } export enum BookingStatus { @@ -181,6 +183,8 @@ export interface CreateCarPayload { deposit: number available: boolean + fullyBooked?: boolean + comingSoon?: boolean type: string gearbox: string aircon: boolean @@ -199,7 +203,6 @@ export interface CreateCarPayload { multimedia: string[] rating?: number co2?: number - comingSoon?: boolean } export interface UpdateCarPayload extends CreateCarPayload { @@ -428,6 +431,8 @@ export interface Car { deposit: number available: boolean + fullyBooked?: boolean + comingSoon?: boolean type: CarType gearbox: GearboxType aircon: boolean @@ -447,7 +452,6 @@ export interface Car { rating?: number trips: number co2?: number - comingSoon?: boolean [propKey: string]: any } From 8cff70b9e06e71d1f63e29c3426f1041c329d66f Mon Sep 17 00:00:00 2001 From: Akram El Assas Date: Wed, 22 Jan 2025 15:32:42 +0100 Subject: [PATCH 14/42] Update CarList.tsx --- backend/src/components/CarList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/components/CarList.tsx b/backend/src/components/CarList.tsx index 44dd672dd..625abe6de 100644 --- a/backend/src/components/CarList.tsx +++ b/backend/src/components/CarList.tsx @@ -397,7 +397,7 @@ const CarList = ({ ) - : rows.map((car, index) => { + : rows.map((car) => { const edit = admin || car.supplier._id === user._id return (
    From 30f5d658c7bf2d4f906fb3de3decdcee93bded0b Mon Sep 17 00:00:00 2001 From: aelassas Date: Wed, 22 Jan 2025 18:58:11 +0100 Subject: [PATCH 15/42] Add fully booked and coming soon flags, full to full and full to empty fuel policies to mobile app --- api/src/controllers/carController.ts | 15 ++---- backend/src/lang/cars.ts | 24 +++++----- backend/src/lang/create-car.ts | 6 +-- mobile/common/helper.ts | 6 +++ mobile/components/Car.tsx | 15 +++++- mobile/components/CarList.tsx | 8 +++- mobile/components/FuelPolicyFilter.tsx | 66 ++++++++++++++++++++++---- mobile/lang/en.ts | 4 ++ mobile/lang/es.ts | 4 ++ mobile/lang/fr.ts | 6 ++- mobile/package-lock.json | 50 +++++++++---------- mobile/package.json | 6 +-- mobile/screens/SearchScreen.tsx | 2 + 13 files changed, 146 insertions(+), 66 deletions(-) diff --git a/api/src/controllers/carController.ts b/api/src/controllers/carController.ts index e64c73c01..251176f86 100644 --- a/api/src/controllers/carController.ts +++ b/api/src/controllers/carController.ts @@ -661,25 +661,16 @@ export const getFrontendCars = async (req: Request, res: Response) => { { locations: pickupLocation }, { type: { $in: carType } }, { gearbox: { $in: gearbox } }, + { available: true }, ], } if (!includeAlreadyBookedCars) { - $match.$and!.push({ - $or: [ - { $and: [{ $or: [{ fullyBooked: false }, { fullyBooked: null }] }, { available: true }] }, - { $and: [{ $or: [{ fullyBooked: false }, { fullyBooked: null }] }, { available: false }] }, - ], - }) + $match.$and!.push({ $or: [{ fullyBooked: false }, { fullyBooked: null }] }) } if (!includeComingSoonCars) { - $match.$and!.push({ - $or: [ - { $and: [{ $or: [{ comingSoon: false }, { comingSoon: null }] }, { available: true }] }, - { $and: [{ $or: [{ comingSoon: false }, { comingSoon: null }] }, { available: false }] }, - ], - }) + $match.$and!.push({ $or: [{ comingSoon: false }, { comingSoon: null }] }) } if (fuelPolicy) { diff --git a/backend/src/lang/cars.ts b/backend/src/lang/cars.ts index 539d27a60..8dc11b96b 100644 --- a/backend/src/lang/cars.ts +++ b/backend/src/lang/cars.ts @@ -66,10 +66,10 @@ const strings = new LocalizedStrings({ INCLUDED: 'Inclus', AVAILABLE: 'Disponile', UNAVAILABLE: 'Indisponible', - CAR_AVAILABLE: 'Disponible', - CAR_AVAILABLE_TOOLTIP: 'Cette voiture est disponible.', - CAR_UNAVAILABLE: 'Indisponible', - CAR_UNAVAILABLE_TOOLTIP: "Cette voiture n'est pas disponible.", + CAR_AVAILABLE: 'Répertoriée dans les résultats de recherche', + CAR_AVAILABLE_TOOLTIP: 'Cette voiture est eépertoriée dans les résultats de recherche.', + CAR_UNAVAILABLE: 'Non répertoriée dans les résultats de recherche', + CAR_UNAVAILABLE_TOOLTIP: "Cette voiture n'est pas répertoriée dans les résultats de recherche.", VIEW_CAR: 'Voir cette voiture', EMPTY_LIST: 'Pas de voitures.', CANNOT_DELETE_CAR: 'Cette voiture ne peut pas être supprimée car elle est liée à des réservations. Vous pouvez cependant la rendre indisponible à la location en la modifiant.', @@ -147,10 +147,10 @@ const strings = new LocalizedStrings({ INCLUDED: 'Included', AVAILABLE: 'Available', UNAVAILABLE: 'Unavailable', - CAR_AVAILABLE: 'Available', - CAR_AVAILABLE_TOOLTIP: 'This car is available.', - CAR_UNAVAILABLE: 'Unavailable', - CAR_UNAVAILABLE_TOOLTIP: 'This car is unavailable.', + CAR_AVAILABLE: 'Listed in search results', + CAR_AVAILABLE_TOOLTIP: 'This car is listed in search results.', + CAR_UNAVAILABLE: 'Not listed in search results', + CAR_UNAVAILABLE_TOOLTIP: 'This car is not listed in search results.', VIEW_CAR: 'View this car', EMPTY_LIST: 'No cars.', CANNOT_DELETE_CAR: 'This car cannot be deleted because it is linked to bookings. However, you can make it unavailable for rental by modifying it.', @@ -227,10 +227,10 @@ const strings = new LocalizedStrings({ INCLUDED: 'Incluido', AVAILABLE: 'Disponible', UNAVAILABLE: 'No disponible', - CAR_AVAILABLE: 'Disponible', - CAR_AVAILABLE_TOOLTIP: 'Este coche está disponible.', - CAR_UNAVAILABLE: 'No disponible', - CAR_UNAVAILABLE_TOOLTIP: 'Este coche no está disponible.', + CAR_AVAILABLE: 'Incluido en los resultados de búsqueda', + CAR_AVAILABLE_TOOLTIP: 'Este coche está incluido en los resultados de búsqueda.', + CAR_UNAVAILABLE: 'No incluido en los resultados de búsqueda', + CAR_UNAVAILABLE_TOOLTIP: 'Este coche no está incluido en los resultados de búsqueda.', VIEW_CAR: 'Ver este coche', EMPTY_LIST: 'No hay coches.', CANNOT_DELETE_CAR: 'Este coche no se puede eliminar porque está vinculado a reservas. Sin embargo, puede hacerlo no disponible para alquilar modificándolo.', diff --git a/backend/src/lang/create-car.ts b/backend/src/lang/create-car.ts index f854eddb6..f84ffad2a 100644 --- a/backend/src/lang/create-car.ts +++ b/backend/src/lang/create-car.ts @@ -10,7 +10,7 @@ const strings = new LocalizedStrings({ RECOMMENDED_IMAGE_SIZE: `Taille d'image recommandée : ${env.CAR_IMAGE_WIDTH}x${env.CAR_IMAGE_HEIGHT}`, SUPPLIER: 'Fournisseur', LOCATIONS: 'Lieux de prise en charge', - AVAILABLE: 'Disponible', + AVAILABLE: 'Répertoriée dans les résultats de recherche', CAR_TYPE: 'Moteur', DAILY_PRICE: 'Prix/jour', DISCOUNTED_DAILY_PRICE: 'Prix discount/jour', @@ -40,7 +40,7 @@ const strings = new LocalizedStrings({ RECOMMENDED_IMAGE_SIZE: `Recommended image size: ${env.CAR_IMAGE_WIDTH}x${env.CAR_IMAGE_HEIGHT}`, SUPPLIER: 'Supplier', LOCATIONS: 'Pick-up locations', - AVAILABLE: 'Available', + AVAILABLE: 'Listed in search results', CAR_TYPE: 'Engine', DAILY_PRICE: 'Daily Price', DISCOUNTED_DAILY_PRICE: 'Discounted Daily Price', @@ -70,7 +70,7 @@ const strings = new LocalizedStrings({ RECOMMENDED_IMAGE_SIZE: `Tamaño de imagen recomendado: ${env.CAR_IMAGE_WIDTH}x${env.CAR_IMAGE_HEIGHT}`, SUPPLIER: 'Proveedor', LOCATIONS: 'Lugares de recogida', - AVAILABLE: 'Disponible', + AVAILABLE: 'Incluido en los resultados de búsqueda', CAR_TYPE: 'Motor', DAILY_PRICE: 'Precio diario', DISCOUNTED_DAILY_PRICE: 'Precio diario con descuento', diff --git a/mobile/common/helper.ts b/mobile/common/helper.ts index d4dbd0027..3c046349e 100644 --- a/mobile/common/helper.ts +++ b/mobile/common/helper.ts @@ -230,6 +230,12 @@ export const getFuelPolicy = (type: string) => { case bookcarsTypes.FuelPolicy.FreeTank: return i18n.t('FUEL_POLICY_FREE_TANK') + case bookcarsTypes.FuelPolicy.FullToFull: + return i18n.t('FUEL_POLICY_FULL_TO_FULL') + + case bookcarsTypes.FuelPolicy.FullToEmpty: + return i18n.t('FUEL_POLICY_FULL_TO_EMPTY') + default: return '' } diff --git a/mobile/components/Car.tsx b/mobile/components/Car.tsx index 0642581e7..5ed97830f 100644 --- a/mobile/components/Car.tsx +++ b/mobile/components/Car.tsx @@ -259,6 +259,11 @@ const Car = ({ fontSize: 13, color: '#a1a1a1', }, + carInfo: { + fontSize: 13, + fontWeight: 'bold', + color: '#FD3446', + }, buttonContainer: { alignItems: 'center', justifyContent: 'center', @@ -418,11 +423,19 @@ const Car = ({ {helper.getDays(days)} {`${bookcarsHelper.formatPrice(totalPrice, currencySymbol, language)}`} {`${i18n.t('PRICE_PER_DAY')} ${bookcarsHelper.formatPrice(totalPrice / days, currencySymbol, language)}`} + + { + car.comingSoon ? ( + {i18n.t('COMING_SOON')} + ) : car.fullyBooked ? ( + {i18n.t('FULLY_BOOKED')} + ) : null + } )} - {!hidePrice && from && to && pickupLocation && dropOffLocation && ( + {!hidePrice && from && to && pickupLocation && dropOffLocation && car.available && !car.fullyBooked && !car.comingSoon && ( + + + ) +} + +export default VehicleSchedulerFilter diff --git a/backend/src/lang/header.ts b/backend/src/lang/header.ts index a78fea5a6..19f80bb4c 100644 --- a/backend/src/lang/header.ts +++ b/backend/src/lang/header.ts @@ -4,6 +4,7 @@ import * as langHelper from '@/common/langHelper' const strings = new LocalizedStrings({ fr: { DASHBOARD: 'Tableau de bord', + SCHEDULER: 'Planificateur', HOME: 'Accueil', COMPANIES: 'Fournisseurs', LOCATIONS: 'Lieux', @@ -19,6 +20,7 @@ const strings = new LocalizedStrings({ }, en: { DASHBOARD: 'Dashboard', + SCHEDULER: 'Vehicle Scheduler', HOME: 'Home', COMPANIES: 'Suppliers', LOCATIONS: 'Locations', @@ -34,6 +36,7 @@ const strings = new LocalizedStrings({ }, es: { DASHBOARD: 'Panel de control', + SCHEDULER: 'Programador', HOME: 'Inicio', COMPANIES: 'Proveedores', LOCATIONS: 'Ubicaciones', diff --git a/backend/src/pages/Scheduler.tsx b/backend/src/pages/Scheduler.tsx new file mode 100644 index 000000000..522796bc5 --- /dev/null +++ b/backend/src/pages/Scheduler.tsx @@ -0,0 +1,93 @@ +import React, { useState } from 'react' +import * as bookcarsTypes from ':bookcars-types' +import * as bookcarsHelper from ':bookcars-helper' +import env from '@/config/env.config' +import * as helper from '@/common/helper' +import * as SupplierService from '@/services/SupplierService' +import VehicleScheduler from '@/components/VehicleScheduler' +import SupplierFilter from '@/components/SupplierFilter' +import StatusFilter from '@/components/StatusFilter' +import VehicleSchedulerFilter from '@/components/VehicleSchedulerFilter' + +import Layout from '@/components/Layout' + +import '@/assets/css/scheduler.css' + +const Scheduler = () => { + const [user, setUser] = useState() + const [leftPanel, setLeftPanel] = useState(false) + const [admin, setAdmin] = useState(false) + const [allSuppliers, setAllSuppliers] = useState([]) + const [suppliers, setSuppliers] = useState() + const [statuses, setStatuses] = useState(helper.getBookingStatuses().map((status) => status.value)) + const [filter, setFilter] = useState() + + const handleSupplierFilterChange = (_suppliers: string[]) => { + setSuppliers(_suppliers) + } + + const handleStatusFilterChange = (_statuses: bookcarsTypes.BookingStatus[]) => { + setStatuses(_statuses) + } + + const handleVehicleSchedulerFilterSubmit = (_filter: bookcarsTypes.Filter | null) => { + setFilter(_filter) + } + + const onLoad = async (_user?: bookcarsTypes.User) => { + if (_user) { + const _admin = helper.admin(_user) + setUser(_user) + setAdmin(_admin) + setLeftPanel(!_admin) + + const _allSuppliers = await SupplierService.getAllSuppliers() + const _suppliers = _admin ? bookcarsHelper.flattenSuppliers(_allSuppliers) : [_user._id ?? ''] + setAllSuppliers(_allSuppliers) + setSuppliers(_suppliers) + setLeftPanel(true) + } + } + + return ( + + {user && suppliers && ( +
    +
    + {leftPanel && ( + <> + {admin + && ( + + )} + + + + )} +
    +
    + +
    +
    + )} +
    + ) +} + +export default Scheduler diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0c1864c5b..452c028aa 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "5.5.0", "dependencies": { + "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.0", @@ -30,7 +31,7 @@ "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", - "date-fns": "^2.29.3", + "date-fns": "^3.2.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", @@ -65,6 +66,25 @@ "vite-plugin-html": "^3.2.2" } }, + "node_modules/@aldabil/react-scheduler": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@aldabil/react-scheduler/-/react-scheduler-2.9.5.tgz", + "integrity": "sha512-PM8D0aFtXIj0XQWvllzwsJuqE1vu34pq33OrcInSMqLAYLgWfRrEh6rYaFJeJ1kAQuzvBGXr0GmVxYut97rIcA==", + "license": "MIT", + "peerDependencies": { + "@mui/icons-material": ">=5.0.0", + "@mui/material": ">=5.0.0", + "@mui/x-date-pickers": ">=6.19.0", + "date-fns": ">=3.2.0", + "react": ">=17.0.0", + "rrule": ">=2.8.1" + }, + "peerDependenciesMeta": { + "rrule": { + "optional": true + } + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -3466,19 +3486,13 @@ } }, "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.2.0.tgz", + "integrity": "sha512-E4KWKavANzeuusPi0jUjpuI22SURAznGkx7eZV+4i6x2A+IZxAMcajgkvuDAU1bg40+xuhW1zRdVIIM/4khuIg==", "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/debug": { diff --git a/frontend/package.json b/frontend/package.json index 44fed2b7c..be8a2a9f0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "stylelint:fix": "stylelint \"src/**/*.css\" --fix" }, "dependencies": { + "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.0", @@ -40,7 +41,7 @@ "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", - "date-fns": "^2.29.3", + "date-fns": "^3.2.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", diff --git a/frontend/src/components/DatePicker.tsx b/frontend/src/components/DatePicker.tsx index bfc524f1d..81674f333 100644 --- a/frontend/src/components/DatePicker.tsx +++ b/frontend/src/components/DatePicker.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react' -import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns' +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3' import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' import { DatePicker as MuiDatePicker } from '@mui/x-date-pickers/DatePicker' import { fr, enUS, es } from 'date-fns/locale' diff --git a/frontend/src/components/DateTimePicker.tsx b/frontend/src/components/DateTimePicker.tsx index d5ff6684d..edbc829dd 100644 --- a/frontend/src/components/DateTimePicker.tsx +++ b/frontend/src/components/DateTimePicker.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react' -import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns' +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3' import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' import { DateTimePicker as MuiDateTimePicker } from '@mui/x-date-pickers/DateTimePicker' import { fr, enUS, es } from 'date-fns/locale' diff --git a/frontend/src/components/SearchForm.tsx b/frontend/src/components/SearchForm.tsx index 9d17862c7..82b513583 100644 --- a/frontend/src/components/SearchForm.tsx +++ b/frontend/src/components/SearchForm.tsx @@ -7,7 +7,6 @@ import { Checkbox, } from '@mui/material' import { DateTimeValidationError } from '@mui/x-date-pickers' -import env from '@/config/env.config' import * as bookcarsTypes from ':bookcars-types' import * as bookcarsHelper from ':bookcars-helper' import { strings as commonStrings } from '@/lang/common' @@ -42,7 +41,6 @@ const SearchForm = ({ const [dropOffLocation, setDropOffLocation] = useState('') const [selectedDropOffLocation, setSelectedDropOffLocation] = useState(undefined) const [minDate, setMinDate] = useState(_minDate) - const [maxDate, setMaxDate] = useState() const [from, setFrom] = useState() const [to, setTo] = useState() const [sameLocation, setSameLocation] = useState(true) @@ -61,10 +59,6 @@ const SearchForm = ({ const _to = new Date(_from) _to.setDate(_to.getDate() + 3) - const _maxDate = new Date(_to) - _maxDate.setDate(_maxDate.getDate() - 1) - setMaxDate(_maxDate) - const __minDate = new Date(_from) __minDate.setDate(__minDate.getDate() + 1) @@ -185,8 +179,9 @@ const SearchForm = ({ { @@ -208,6 +203,9 @@ const SearchForm = ({ setFrom(date) setMinDate(__minDate) setFromError(false) + const _to = new Date(date) + _to.setDate(date.getDate() + 3) + setTo(_to) } else { setFrom(undefined) setMinDate(_minDate) @@ -232,14 +230,10 @@ const SearchForm = ({ required onChange={(date) => { if (date) { - const _maxDate = new Date(date) - _maxDate.setDate(_maxDate.getDate() - 1) setTo(date) - setMaxDate(_maxDate) setToError(false) } else { setTo(undefined) - setMaxDate(undefined) } }} onError={(err: DateTimeValidationError) => { @@ -272,8 +266,9 @@ const SearchForm = ({ Date: Thu, 23 Jan 2025 02:42:45 +0100 Subject: [PATCH 20/42] Remove unused state and import in VehicleScheduler and VehicleSchedulerFilter components --- backend/src/components/VehicleScheduler.tsx | 3 --- backend/src/components/VehicleSchedulerFilter.tsx | 1 - 2 files changed, 4 deletions(-) diff --git a/backend/src/components/VehicleScheduler.tsx b/backend/src/components/VehicleScheduler.tsx index 85c0adad0..232283176 100644 --- a/backend/src/components/VehicleScheduler.tsx +++ b/backend/src/components/VehicleScheduler.tsx @@ -27,7 +27,6 @@ const VehicleScheduler = ( language, }: VehicleSchedulerProps ) => { - const [remoteQuery, setRemoteQuery] = useState() const [filter, setFilter] = useState() const [init, setInit] = useState(true) @@ -38,8 +37,6 @@ const VehicleScheduler = ( }, [filterFromProps]) const fetchBookings = useCallback(async (query: RemoteQuery): Promise => { - setRemoteQuery(query) - const payload: bookcarsTypes.GetBookingsPayload = { suppliers, statuses, diff --git a/backend/src/components/VehicleSchedulerFilter.tsx b/backend/src/components/VehicleSchedulerFilter.tsx index 1bd98d78d..184e2b03b 100644 --- a/backend/src/components/VehicleSchedulerFilter.tsx +++ b/backend/src/components/VehicleSchedulerFilter.tsx @@ -11,7 +11,6 @@ import * as bookcarsHelper from ':bookcars-helper' import { strings as commonStrings } from '@/lang/common' import { strings } from '@/lang/booking-filter' import LocationSelectList from './LocationSelectList' -import DatePicker from './DatePicker' import Accordion from '@/components/Accordion' import '@/assets/css/vehicle-scheduler-filter.css' From 1a6b540f348d96caf51f3882ec8ae7eff109f96c Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 07:11:45 +0100 Subject: [PATCH 21/42] Fix VehicleScheduler for empty bookings --- backend/src/components/VehicleScheduler.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/src/components/VehicleScheduler.tsx b/backend/src/components/VehicleScheduler.tsx index 232283176..730cf6615 100644 --- a/backend/src/components/VehicleScheduler.tsx +++ b/backend/src/components/VehicleScheduler.tsx @@ -37,6 +37,14 @@ const VehicleScheduler = ( }, [filterFromProps]) const fetchBookings = useCallback(async (query: RemoteQuery): Promise => { + const emptyEvents: ProcessedEvent[] = [ + { + event_id: '1', + title: 'Dummy Event', + start: new Date(1970, 0, 1), + end: new Date(1970, 0, 2), + } + ] const payload: bookcarsTypes.GetBookingsPayload = { suppliers, statuses, @@ -55,9 +63,10 @@ const VehicleScheduler = ( const _data = data && data.length > 0 ? data[0] : { pageInfo: { totalRecord: 0 }, resultData: [] } if (!_data) { helper.error() - return [] + return emptyEvents } const bookings = _data.resultData + const events = bookings.map((booking): ProcessedEvent => ({ event_id: booking._id as string, title: `${(booking.car as bookcarsTypes.Car).name} / ${(booking.supplier as bookcarsTypes.User).fullName} / ${helper.getBookingStatus(booking.status)}`, @@ -67,6 +76,10 @@ const VehicleScheduler = ( textColor: helper.getBookingStatusTextColor(booking.status), })) setInit(false) + + if (events.length === 0) { + return emptyEvents + } return events }, [filter, statuses, suppliers, user]) From 23180a55d83ab6a4cbaf2a8cbb039763ad0c041c Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 09:05:34 +0100 Subject: [PATCH 22/42] Fix: date handling in VehicleScheduler for improved month view calculations --- backend/src/components/VehicleScheduler.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/components/VehicleScheduler.tsx b/backend/src/components/VehicleScheduler.tsx index 730cf6615..cd20b8747 100644 --- a/backend/src/components/VehicleScheduler.tsx +++ b/backend/src/components/VehicleScheduler.tsx @@ -49,9 +49,11 @@ const VehicleScheduler = ( suppliers, statuses, filter: { - from: query.view !== 'day' ? query.start : undefined, + // from: query.view !== 'day' ? query.start : undefined, + from: query.view !== 'day' ? new Date(query.start.getFullYear(), query.start.getMonth() - 1, 1) : undefined, dateBetween: query.view === 'day' ? new Date(query.end.getFullYear(), query.end.getMonth(), query.end.getDate(), 0, 0, 0) : undefined, - to: query.view === 'month' ? query.end : new Date(query.end.getFullYear(), query.end.getMonth() + 1, 0), + // to: query.view === 'month' ? query.end : new Date(query.end.getFullYear(), query.end.getMonth() + 1, 0), + to: new Date(query.end.getFullYear(), query.end.getMonth() + 1, 0), pickupLocation: filter?.pickupLocation, dropOffLocation: filter?.dropOffLocation, keyword: filter?.keyword, From 71c5be49dc28c85abeb8dad6cb69cd3fc16c7972 Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 09:14:31 +0100 Subject: [PATCH 23/42] Fix: Pick-up date not working properly in mobile app --- mobile/components/SearchForm.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/mobile/components/SearchForm.tsx b/mobile/components/SearchForm.tsx index d0dbb0cb2..79b21feb5 100644 --- a/mobile/components/SearchForm.tsx +++ b/mobile/components/SearchForm.tsx @@ -73,9 +73,6 @@ const SearchForm = ( const _minDate = new Date() _minDate.setDate(_minDate.getDate() + 2) - const _maxDate = new Date(_toDate) - _maxDate.setDate(_maxDate.getDate() - 1) - const [pickupLocationId, setPickupLocationId] = useState(pickupLocation || '') const [dropOffLocationId, setDropOffLocationId] = useState(dropOffLocation || '') const [sameLocation, setSameLocation] = useState(pickupLocation === dropOffLocation) @@ -85,7 +82,6 @@ const SearchForm = ( const [language, setLanguage] = useState(env.DEFAULT_LANGUAGE) const [blur, setBlur] = useState(false) const [minDate, setMinDate] = useState(_minDate) - const [maxDate, setMaxDate] = useState(_maxDate) const [fromDate, setFromDate] = useState(_fromDate) const [fromTime, setFromTime] = useState(_fromTime) const [toTime, setToTime] = useState(_toTime) @@ -246,7 +242,7 @@ const SearchForm = ( label={i18n.t('FROM_DATE')} value={fromDate} minDate={_fromDate} - maxDate={maxDate} + // maxDate={maxDate} hideClearButton size={size || undefined} onChange={(date) => { @@ -258,6 +254,9 @@ const SearchForm = ( const __minDate = new Date(date) __minDate.setDate(date.getDate() + 1) setMinDate(__minDate) + const _to = new Date(date) + _to.setDate(date.getDate() + 3) + setToDate(_to) } else { setMinDate(_minDate) } @@ -295,11 +294,6 @@ const SearchForm = ( onChange={(date) => { if (date) { setToDate(date) - const __maxDate = new Date(date) - __maxDate.setDate(__maxDate.getDate() - 1) - setMaxDate(__maxDate) - } else { - setMaxDate(_maxDate) } }} onPress={blurLocations} From f43b58c48d1b33762da2d0ef8c2f23775faa61f1 Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 09:54:01 +0100 Subject: [PATCH 24/42] Update car range labels and implement helper functions for consistent display --- backend/src/assets/css/car-range-filter.css | 2 +- backend/src/common/helper.ts | 25 ++++++++++++++++++++ backend/src/components/CarRangeFilter.tsx | 9 +++---- backend/src/components/CarRangeList.tsx | 10 ++++---- backend/src/lang/cars.ts | 12 ++++++++++ frontend/src/assets/css/car-range-filter.css | 2 +- frontend/src/common/helper.ts | 25 ++++++++++++++++++++ frontend/src/components/CarRangeFilter.tsx | 9 +++---- frontend/src/lang/cars.ts | 12 ++++++++++ frontend/src/pages/Home.tsx | 13 +++++----- mobile/common/helper.ts | 25 ++++++++++++++++++++ mobile/components/CarRangeFilter.tsx | 9 +++---- mobile/lang/en.ts | 8 +++---- mobile/lang/es.ts | 8 +++---- mobile/lang/fr.ts | 8 +++---- 15 files changed, 140 insertions(+), 37 deletions(-) diff --git a/backend/src/assets/css/car-range-filter.css b/backend/src/assets/css/car-range-filter.css index d25fd2d8d..5fbf3cd3c 100644 --- a/backend/src/assets/css/car-range-filter.css +++ b/backend/src/assets/css/car-range-filter.css @@ -12,7 +12,7 @@ div.range-filter div.filter-elements div.filter-element span { cursor: pointer; font-size: 12px; font-weight: 400; - text-transform: uppercase; + /* text-transform: uppercase; */ } div.range-filter div.filter-actions { diff --git a/backend/src/common/helper.ts b/backend/src/common/helper.ts index a26e239f8..49b634bfd 100644 --- a/backend/src/common/helper.ts +++ b/backend/src/common/helper.ts @@ -790,3 +790,28 @@ export const downloadURI = (uri: string, name: string = '') => { link.click() link.remove() } + +/** + * Get car range label. + * + * @param {string} range + * @returns {string} + */ +export const getCarRange = (range: bookcarsTypes.CarRange) => { + switch (range) { + case bookcarsTypes.CarRange.Mini: + return strings.CAR_RANGE_MINI + + case bookcarsTypes.CarRange.Midi: + return strings.CAR_RANGE_MIDI + + case bookcarsTypes.CarRange.Maxi: + return strings.CAR_RANGE_MAXI + + case bookcarsTypes.CarRange.Scooter: + return strings.CAR_RANGE_SCOOTER + + default: + return '' + } +} diff --git a/backend/src/components/CarRangeFilter.tsx b/backend/src/components/CarRangeFilter.tsx index a323a7978..2ecefd9d6 100644 --- a/backend/src/components/CarRangeFilter.tsx +++ b/backend/src/components/CarRangeFilter.tsx @@ -3,6 +3,7 @@ import * as bookcarsTypes from ':bookcars-types' import * as bookcarsHelper from ':bookcars-helper' import { strings as commonStrings } from '@/lang/common' import { strings } from '@/lang/car-range-filter' +import * as helper from '@/common/helper' import Accordion from './Accordion' import '@/assets/css/car-range-filter.css' @@ -219,7 +220,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.MINI} + {helper.getCarRange(bookcarsTypes.CarRange.Mini)}
    @@ -229,7 +230,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.MIDI} + {helper.getCarRange(bookcarsTypes.CarRange.Midi)}
    @@ -239,7 +240,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.MAXI} + {helper.getCarRange(bookcarsTypes.CarRange.Maxi)}
    @@ -249,7 +250,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.SCOOTER} + {helper.getCarRange(bookcarsTypes.CarRange.Scooter)}
    diff --git a/backend/src/components/CarRangeList.tsx b/backend/src/components/CarRangeList.tsx index c1ab0cae8..75a61502e 100644 --- a/backend/src/components/CarRangeList.tsx +++ b/backend/src/components/CarRangeList.tsx @@ -6,6 +6,7 @@ import { SelectChangeEvent } from '@mui/material' import * as bookcarsTypes from ':bookcars-types' +import * as helper from '@/common/helper' interface CarRangeListProps { value?: string @@ -29,7 +30,6 @@ const CarRangeList = ({ }, [carRangeValue]) const handleChange = (e: SelectChangeEvent) => { - console.log(e.target.value) const _value = e.target.value || '' setValue(_value) @@ -42,10 +42,10 @@ const CarRangeList = ({
    {label}
    ) diff --git a/backend/src/lang/cars.ts b/backend/src/lang/cars.ts index 8dc11b96b..99c6e57b8 100644 --- a/backend/src/lang/cars.ts +++ b/backend/src/lang/cars.ts @@ -87,6 +87,10 @@ const strings = new LocalizedStrings({ CO2: 'Effet CO2', COMING_SOON: 'Bientôt Disponible', FULLY_BOOKED: 'Déjà réservée', + CAR_RANGE_MINI: 'Voiture', + CAR_RANGE_MIDI: 'SUV', + CAR_RANGE_MAXI: 'Fourgon', + CAR_RANGE_SCOOTER: 'Scooter', }, en: { NEW_CAR: 'New car', @@ -168,6 +172,10 @@ const strings = new LocalizedStrings({ CO2: 'CO2 effect', COMING_SOON: 'Coming Soon', FULLY_BOOKED: 'Fully Booked', + CAR_RANGE_MINI: 'Car', + CAR_RANGE_MIDI: 'SUV', + CAR_RANGE_MAXI: 'Van', + CAR_RANGE_SCOOTER: 'Scooter', }, es: { NEW_CAR: 'Nuevo coche', @@ -248,6 +256,10 @@ const strings = new LocalizedStrings({ CO2: 'Efecto CO2', COMING_SOON: 'Próximamente', FULLY_BOOKED: 'Ya Reservado', + CAR_RANGE_MINI: 'Auto', + CAR_RANGE_MIDI: 'Todoterreno', + CAR_RANGE_MAXI: 'furgoneta', + CAR_RANGE_SCOOTER: 'Scooter', }, }) diff --git a/frontend/src/assets/css/car-range-filter.css b/frontend/src/assets/css/car-range-filter.css index d25fd2d8d..5fbf3cd3c 100644 --- a/frontend/src/assets/css/car-range-filter.css +++ b/frontend/src/assets/css/car-range-filter.css @@ -12,7 +12,7 @@ div.range-filter div.filter-elements div.filter-element span { cursor: pointer; font-size: 12px; font-weight: 400; - text-transform: uppercase; + /* text-transform: uppercase; */ } div.range-filter div.filter-actions { diff --git a/frontend/src/common/helper.ts b/frontend/src/common/helper.ts index 283e70bb5..43da02316 100644 --- a/frontend/src/common/helper.ts +++ b/frontend/src/common/helper.ts @@ -670,3 +670,28 @@ export const verifyReCaptcha = async (token: string): Promise => { return false } } + +/** + * Get car range label. + * + * @param {string} range + * @returns {string} + */ +export const getCarRange = (range: bookcarsTypes.CarRange) => { + switch (range) { + case bookcarsTypes.CarRange.Mini: + return strings.CAR_RANGE_MINI + + case bookcarsTypes.CarRange.Midi: + return strings.CAR_RANGE_MIDI + + case bookcarsTypes.CarRange.Maxi: + return strings.CAR_RANGE_MAXI + + case bookcarsTypes.CarRange.Scooter: + return strings.CAR_RANGE_SCOOTER + + default: + return '' + } +} diff --git a/frontend/src/components/CarRangeFilter.tsx b/frontend/src/components/CarRangeFilter.tsx index a323a7978..2ecefd9d6 100644 --- a/frontend/src/components/CarRangeFilter.tsx +++ b/frontend/src/components/CarRangeFilter.tsx @@ -3,6 +3,7 @@ import * as bookcarsTypes from ':bookcars-types' import * as bookcarsHelper from ':bookcars-helper' import { strings as commonStrings } from '@/lang/common' import { strings } from '@/lang/car-range-filter' +import * as helper from '@/common/helper' import Accordion from './Accordion' import '@/assets/css/car-range-filter.css' @@ -219,7 +220,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.MINI} + {helper.getCarRange(bookcarsTypes.CarRange.Mini)}
    @@ -229,7 +230,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.MIDI} + {helper.getCarRange(bookcarsTypes.CarRange.Midi)}
    @@ -239,7 +240,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.MAXI} + {helper.getCarRange(bookcarsTypes.CarRange.Maxi)}
    @@ -249,7 +250,7 @@ const CarRangeFilter = ({ role="button" tabIndex={0} > - {strings.SCOOTER} + {helper.getCarRange(bookcarsTypes.CarRange.Scooter)}
    diff --git a/frontend/src/lang/cars.ts b/frontend/src/lang/cars.ts index 5a0fe6537..8995ab557 100644 --- a/frontend/src/lang/cars.ts +++ b/frontend/src/lang/cars.ts @@ -92,6 +92,10 @@ const strings = new LocalizedStrings({ DETAILS: 'Détails', FULLY_BOOKED: 'Déjà Réservée', COMING_SOON: 'Bientôt Disponible', + CAR_RANGE_MINI: 'Voiture', + CAR_RANGE_MIDI: 'SUV', + CAR_RANGE_MAXI: 'Fourgon', + CAR_RANGE_SCOOTER: 'Scooter', }, en: { NEW_CAR: 'New car', @@ -175,6 +179,10 @@ const strings = new LocalizedStrings({ DETAILS: 'Details', FULLY_BOOKED: 'Fully Booked', COMING_SOON: 'Coming Soon', + CAR_RANGE_MINI: 'Car', + CAR_RANGE_MIDI: 'SUV', + CAR_RANGE_MAXI: 'Van', + CAR_RANGE_SCOOTER: 'Scooter', }, es: { NEW_CAR: 'Coche nuevo', @@ -258,6 +266,10 @@ const strings = new LocalizedStrings({ DETAILS: 'Detalles', FULLY_BOOKED: 'Ya Reservado', COMING_SOON: 'Próximamente', + CAR_RANGE_MINI: 'Auto', + CAR_RANGE_MIDI: 'Todoterreno', + CAR_RANGE_MAXI: 'furgoneta', + CAR_RANGE_SCOOTER: 'Scooter', } }) diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 2412bfebc..1db994c1b 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -25,6 +25,7 @@ import * as bookcarsTypes from ':bookcars-types' import * as bookcarsHelper from ':bookcars-helper' import env from '@/config/env.config' import { strings as commonStrings } from '@/lang/common' +import { strings as carsStrings } from '@/lang/cars' import { strings } from '@/lang/home' import * as UserService from '@/services/UserService' import * as SupplierService from '@/services/SupplierService' @@ -64,8 +65,8 @@ const Home = () => { const [miniPricePday, setMiniPricePday] = useState(40) const [midiPricePhr, setMidiPricePhr] = useState(3.5) const [midiPricePday, setMidiPricePday] = useState(50) - const [maxiPricePhr, setMaxiPricePhr] = useState(3.5) - const [maxiPricePday, setMaxiPricePday] = useState(50) + const [maxiPricePhr, setMaxiPricePhr] = useState(4.5) + const [maxiPricePday, setMaxiPricePday] = useState(80) useEffect(() => { const init = async () => { @@ -345,7 +346,7 @@ const Home = () => { )} label={strings.MINI} /> */} - {strings.MINI} + {carsStrings.CAR_RANGE_MINI}
    • {bookcarsHelper.formatPrice(miniPricePhr, commonStrings.CURRENCY, language)} @@ -394,7 +395,7 @@ const Home = () => { )} label={strings.MIDI} /> */} - {strings.MIDI} + {carsStrings.CAR_RANGE_MIDI}
      • {bookcarsHelper.formatPrice(midiPricePhr, commonStrings.CURRENCY, language)} @@ -442,7 +443,7 @@ const Home = () => { )} label={strings.MAXI} /> */} - {strings.MAXI} + {carsStrings.CAR_RANGE_MAXI}
        • {bookcarsHelper.formatPrice(maxiPricePhr, commonStrings.CURRENCY, language)} @@ -488,7 +489,7 @@ const Home = () => {
          { diff --git a/mobile/common/helper.ts b/mobile/common/helper.ts index 3c046349e..e79223880 100644 --- a/mobile/common/helper.ts +++ b/mobile/common/helper.ts @@ -775,3 +775,28 @@ export const getDepositFilterValue = async (language: string, value: DepositFilt return `Less than ${isCurrencyRTL ? currency : ''}${depositFilterValue}${!isCurrencyRTL ? (` ${currency}`) : ''}` } } + +/** + * Get fuel policy label. + * + * @param {string} range + * @returns {string} + */ +export const getCarRange = (range: bookcarsTypes.CarRange) => { + switch (range) { + case bookcarsTypes.CarRange.Mini: + return i18n.t('CAR_RANGE_MINI') + + case bookcarsTypes.CarRange.Midi: + return i18n.t('CAR_RANGE_MIDI') + + case bookcarsTypes.CarRange.Maxi: + return i18n.t('CAR_RANGE_MAXI') + + case bookcarsTypes.CarRange.Scooter: + return i18n.t('CAR_RANGE_SCOOTER') + + default: + return '' + } +} diff --git a/mobile/components/CarRangeFilter.tsx b/mobile/components/CarRangeFilter.tsx index 347a3622e..24d2b4f2c 100644 --- a/mobile/components/CarRangeFilter.tsx +++ b/mobile/components/CarRangeFilter.tsx @@ -3,6 +3,7 @@ import { StyleSheet, View } from 'react-native' import * as bookcarsTypes from ':bookcars-types' import * as bookcarsHelper from ':bookcars-helper' +import * as helper from '@/common/helper' import i18n from '@/lang/i18n' import Accordion from './Accordion' import Link from './Link' @@ -131,10 +132,10 @@ const CarRangeFilter = ({ - - - - + + + + Date: Thu, 23 Jan 2025 10:18:00 +0100 Subject: [PATCH 25/42] Fix: scheduler mobile layout --- backend/src/assets/css/scheduler.css | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/assets/css/scheduler.css b/backend/src/assets/css/scheduler.css index 55a8a9776..2e40554c3 100644 --- a/backend/src/assets/css/scheduler.css +++ b/backend/src/assets/css/scheduler.css @@ -11,13 +11,16 @@ div.scheduler { overflow-y: auto; } - div.scheduler div.col-1, - div.scheduler div.col-2 { + div.scheduler div.col-1 { display: flex; flex-direction: column; align-items: center; } + div.scheduler div.col-2 { + display: flex; + } + div.scheduler div.col-1 .cl-supplier-filter label.accordion, div.scheduler div.col-1 .cl-status-filter label.accordion, div.scheduler div.col-1 .cl-scheduler-filter label.accordion { From 8a4bc865a3d2affa71cc6d5b115ed9a83c0ad854 Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 17:48:15 +0100 Subject: [PATCH 26/42] Update VehicleScheduler.tsx --- backend/src/components/VehicleScheduler.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/components/VehicleScheduler.tsx b/backend/src/components/VehicleScheduler.tsx index cd20b8747..04b4ccb7d 100644 --- a/backend/src/components/VehicleScheduler.tsx +++ b/backend/src/components/VehicleScheduler.tsx @@ -45,6 +45,7 @@ const VehicleScheduler = ( end: new Date(1970, 0, 2), } ] + const payload: bookcarsTypes.GetBookingsPayload = { suppliers, statuses, @@ -77,6 +78,7 @@ const VehicleScheduler = ( color: helper.getBookingStatusBackgroundColor(booking.status), textColor: helper.getBookingStatusTextColor(booking.status), })) + setInit(false) if (events.length === 0) { @@ -93,7 +95,7 @@ const VehicleScheduler = ( if (!init && statuses.length > 0 && suppliers.length > 0) { fetchEvents() } - }, [statuses, suppliers, filter, init, fetchBookings]) + }, [statuses, suppliers, filter]) // eslint-disable-line react-hooks/exhaustive-deps const getTranslations = (_language: string) => { if (_language === 'fr') { @@ -212,6 +214,7 @@ const VehicleScheduler = ( disableViewer editable={false} draggable={false} + agenda={false} onEventClick={(event: ProcessedEvent) => { const url = `/update-booking?b=${event.event_id}` window.open(url, '_blank')!.focus() From 1229283e1111ee65d46c5250bb93f16616971582 Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 18:54:47 +0100 Subject: [PATCH 27/42] Fix: rollback to react 18 due to issues with @aldabil/react-scheduler issues and react 19 --- backend/package-lock.json | 206 +++++++++--------- backend/package.json | 20 +- backend/src/components/SuspenseRouter.tsx | 47 ---- backend/src/context/GlobalContext.tsx | 4 +- frontend/package-lock.json | 98 ++++----- frontend/package.json | 13 +- frontend/src/components/SuspenseRouter.tsx | 47 ---- frontend/src/context/GlobalContext.tsx | 4 +- frontend/src/pages/Home.tsx | 2 +- .../reactjs-social-login/package-lock.json | 40 +++- packages/reactjs-social-login/package.json | 4 +- 11 files changed, 205 insertions(+), 280 deletions(-) delete mode 100644 backend/src/components/SuspenseRouter.tsx delete mode 100644 frontend/src/components/SuspenseRouter.tsx diff --git a/backend/package-lock.json b/backend/package-lock.json index e62fd0f12..d6556d5b4 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -11,16 +11,16 @@ "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.4.0", - "@mui/material": "^6.4.0", + "@mui/icons-material": "^6.4.1", + "@mui/material": "^6.4.1", "@mui/x-data-grid": "^7.24.0", "@mui/x-date-pickers": "^7.24.0", "@types/node": "^22.10.7", - "@types/react": "^19.0.7", - "@types/react-dom": "^19.0.3", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -30,13 +30,13 @@ "eslint-plugin-react-refresh": "^0.4.11", "history": "^5.3.0", "localized-strings": "^2.0.3", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-router-dom": "^7.1.3", "react-toastify": "^11.0.3", "typescript": "^5.2.2", "validator": "^13.12.0", - "vite": "^6.0.7" + "vite": "^6.0.11" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", @@ -1412,9 +1412,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.0.tgz", - "integrity": "sha512-6u74wi+9zeNlukrCtYYET8Ed/n9AS27DiaXCZKAD3TRGFaqiyYSsQgN2disW83pI/cM1Q2lJY1JX4YfwvNtlNw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.1.tgz", + "integrity": "sha512-SfDLWMV5b5oXgDf3NTa2hCTPC1d2defhDH2WgFKmAiejC4mSfXYbyi+AFCLzpizauXhgBm8OaZy9BHKnrSpahQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -1422,9 +1422,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.0.tgz", - "integrity": "sha512-zF0Vqt8a+Zp2Oz8P+WvJflba6lLe3PhxIz1NNqn+n4A+wKLPbkeqY8ShmKjPyiCTg0RMbPrp993oUDl9xGsDlQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.1.tgz", + "integrity": "sha512-wsxFcUTQxt4s+7Bg4GgobqRjyaHLmZGNOs+HJpbwrwmLbT6mhIJxhpqsKzzWq9aDY8xIe7HCjhpH7XI5UD6teA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -1437,7 +1437,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.4.0", + "@mui/material": "^6.4.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1448,16 +1448,16 @@ } }, "node_modules/@mui/material": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.0.tgz", - "integrity": "sha512-hNIgwdM9U3DNmowZ8mU59oFmWoDKjc92FqQnQva3Pxh6xRKWtD2Ej7POUHMX8Dwr1OpcSUlT2+tEMeLb7WYsIg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.1.tgz", + "integrity": "sha512-MFBfia6UiKxyoLeGkAh8M15bkeDmfnsUTMRJd/vTQue6YQ8AQ6lw9HqDthyYghzDEWIvZO/lQQzLrZE8XwNJLA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.0", - "@mui/system": "^6.4.0", + "@mui/core-downloads-tracker": "^6.4.1", + "@mui/system": "^6.4.1", "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.1", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", @@ -1476,7 +1476,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.0", + "@mui/material-pigment-css": "^6.4.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1497,13 +1497,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.0.tgz", - "integrity": "sha512-rNHci8MP6NOdEWAfZ/RBMO5Rhtp1T6fUDMSmingg9F1T6wiUeodIQ+NuTHh2/pMoUSeP9GdHdgMhMmfsXxOMuw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.1.tgz", + "integrity": "sha512-DcT7mwK89owwgcEuiE7w458te4CIjHbYWW6Kn6PiR6eLtxBsoBYphA968uqsQAOBQDpbYxvkuFLwhgk4bxoN/Q==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.1", "prop-types": "^15.8.1" }, "engines": { @@ -1558,16 +1558,16 @@ } }, "node_modules/@mui/system": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.0.tgz", - "integrity": "sha512-wTDyfRlaZCo2sW2IuOsrjeE5dl0Usrs6J7DxE3GwNCVFqS5wMplM2YeNiV3DO7s53RfCqbho+gJY6xaB9KThUA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.1.tgz", + "integrity": "sha512-rgQzgcsHCTtzF9MZ+sL0tOhf2ZBLazpjrujClcb4Siju5lTrK0xX4PsiropActzCemNfM+mOu+0jezAVnfRK8g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.4.0", + "@mui/private-theming": "^6.4.1", "@mui/styled-engine": "^6.4.0", "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.1", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1612,9 +1612,9 @@ } }, "node_modules/@mui/utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", - "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.1.tgz", + "integrity": "sha512-iQUDUeYh87SvR4lVojaRaYnQix8BbRV51MxaV6MBmqthecQoxwSbS5e2wnbDJUeFxY2ppV505CiqPLtd0OWkqw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -2161,21 +2161,22 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.0.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", - "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "license": "MIT", "dependencies": { + "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", - "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^18.0.0" } }, "node_modules/@types/react-transition-group": { @@ -2194,16 +2195,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", - "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", + "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/type-utils": "8.20.0", - "@typescript-eslint/utils": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/type-utils": "8.21.0", + "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2223,15 +2224,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", - "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", + "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4" }, "engines": { @@ -2247,13 +2248,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", - "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", + "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0" + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2264,13 +2265,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", - "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", + "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/utils": "8.21.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -2287,9 +2288,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", - "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", + "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2300,13 +2301,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", - "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", + "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2326,15 +2327,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", - "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", + "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0" + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2349,12 +2350,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", - "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", + "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/types": "8.21.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -6654,24 +6655,28 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", "dependencies": { - "scheduler": "^0.25.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^19.0.0" + "react": "^18.3.1" } }, "node_modules/react-is": { @@ -6986,10 +6991,13 @@ } }, "node_modules/scheduler": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", - "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", - "license": "MIT" + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } }, "node_modules/semver": { "version": "7.6.3", @@ -7913,9 +7921,9 @@ } }, "node_modules/vite": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", - "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz", + "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==", "license": "MIT", "dependencies": { "esbuild": "^0.24.2", diff --git a/backend/package.json b/backend/package.json index a401c8b15..d0bdc3ec8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,7 +11,7 @@ "preview": "vite preview", "fix": "eslint --fix .", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", + "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh,react,react-dom,@types/react,@types/react-dom", "stylelint": "stylelint \"src/**/*.css\"", "stylelint:fix": "stylelint \"src/**/*.css\" --fix" }, @@ -19,16 +19,16 @@ "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.4.0", - "@mui/material": "^6.4.0", + "@mui/icons-material": "^6.4.1", + "@mui/material": "^6.4.1", "@mui/x-data-grid": "^7.24.0", "@mui/x-date-pickers": "^7.24.0", "@types/node": "^22.10.7", - "@types/react": "^19.0.7", - "@types/react-dom": "^19.0.3", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -38,13 +38,13 @@ "eslint-plugin-react-refresh": "^0.4.11", "history": "^5.3.0", "localized-strings": "^2.0.3", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-router-dom": "^7.1.3", "react-toastify": "^11.0.3", "typescript": "^5.2.2", "validator": "^13.12.0", - "vite": "^6.0.7" + "vite": "^6.0.11" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", diff --git a/backend/src/components/SuspenseRouter.tsx b/backend/src/components/SuspenseRouter.tsx deleted file mode 100644 index e924c0cfc..000000000 --- a/backend/src/components/SuspenseRouter.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable react-compiler/react-compiler */ -import React, { useLayoutEffect, useRef, useState, useTransition } from 'react' -import { Router } from 'react-router-dom' -import { BrowserHistory, createBrowserHistory, Update } from 'history' - -export interface BrowserRouterProps { - basename?: string - children?: React.ReactNode - window?: Window -} - -export const SuspenseRouter = ({ basename, children, window }: BrowserRouterProps) => { - const historyRef = useRef(null) - const [isPending, startTransition] = useTransition() - - if (historyRef.current == null) { - // const history = createBrowserHistory(startTransition, { window }); - historyRef.current = createBrowserHistory({ window }) - } - - const history = historyRef.current - const [state, setState] = useState({ - action: history.action, - location: history.location, - }) - - function setStateAsync(update: Update) { - startTransition(() => { - setState(update) - }) - } - - useLayoutEffect(() => history.listen(setStateAsync), [history]) - - return ( - - {children} - - ) -} - -export default SuspenseRouter diff --git a/backend/src/context/GlobalContext.tsx b/backend/src/context/GlobalContext.tsx index ba248eed0..0cebfcbc3 100644 --- a/backend/src/context/GlobalContext.tsx +++ b/backend/src/context/GlobalContext.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, createContext, use, useMemo, useState } from 'react' +import React, { ReactNode, createContext, useContext, useMemo, useState } from 'react' // Create context export interface GlobalContextType { @@ -24,4 +24,4 @@ export const GlobalProvider = ({ children }: GlobalProviderProps) => { // Create a custom hook to access context // eslint-disable-next-line react-refresh/only-export-components -export const useGlobalContext = () => use(GlobalContext) +export const useGlobalContext = () => useContext(GlobalContext) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 452c028aa..07c6a3955 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,7 +8,6 @@ "name": "frontend", "version": "5.5.0", "dependencies": { - "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.0", @@ -21,8 +20,8 @@ "@types/leaflet-boundary-canvas": "^1.0.3", "@types/node": "^22.10.7", "@types/nprogress": "^0.2.3", - "@types/react": "^19.0.7", - "@types/react-dom": "^19.0.3", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", @@ -41,11 +40,11 @@ "leaflet-boundary-canvas": "^1.0.0", "localized-strings": "^2.0.3", "nprogress": "^0.2.0", - "react": "^19.0.0", + "react": "^18.3.1", "react-circle-flags": "^0.0.23", - "react-dom": "^19.0.0", + "react-dom": "^18.3.1", "react-ga4": "^2.1.0", - "react-leaflet": "^5.0.0", + "react-leaflet": "^4.2.1", "react-router-dom": "^7.1.3", "react-slick": "^0.30.3", "react-toastify": "^11.0.3", @@ -66,25 +65,6 @@ "vite-plugin-html": "^3.2.2" } }, - "node_modules/@aldabil/react-scheduler": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/@aldabil/react-scheduler/-/react-scheduler-2.9.5.tgz", - "integrity": "sha512-PM8D0aFtXIj0XQWvllzwsJuqE1vu34pq33OrcInSMqLAYLgWfRrEh6rYaFJeJ1kAQuzvBGXr0GmVxYut97rIcA==", - "license": "MIT", - "peerDependencies": { - "@mui/icons-material": ">=5.0.0", - "@mui/material": ">=5.0.0", - "@mui/x-date-pickers": ">=6.19.0", - "date-fns": ">=3.2.0", - "react": ">=17.0.0", - "rrule": ">=2.8.1" - }, - "peerDependenciesMeta": { - "rrule": { - "optional": true - } - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -1826,14 +1806,14 @@ } }, "node_modules/@react-leaflet/core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", - "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", + "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", "license": "Hippocratic-2.1", "peerDependencies": { "leaflet": "^1.9.0", - "react": "^19.0.0", - "react-dom": "^19.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@rollup/pluginutils": { @@ -2242,21 +2222,22 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.0.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", - "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "license": "MIT", "dependencies": { + "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", - "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^18.0.0" } }, "node_modules/@types/react-recaptcha-v3": { @@ -6804,10 +6785,13 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } @@ -6825,15 +6809,16 @@ } }, "node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", "dependencies": { - "scheduler": "^0.25.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^19.0.0" + "react": "^18.3.1" } }, "node_modules/react-ga4": { @@ -6849,17 +6834,17 @@ "license": "MIT" }, "node_modules/react-leaflet": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", - "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", + "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", "license": "Hippocratic-2.1", "dependencies": { - "@react-leaflet/core": "^3.0.0" + "@react-leaflet/core": "^2.1.0" }, "peerDependencies": { "leaflet": "^1.9.0", - "react": "^19.0.0", - "react-dom": "^19.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/react-refresh": { @@ -7191,10 +7176,13 @@ } }, "node_modules/scheduler": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", - "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", - "license": "MIT" + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } }, "node_modules/semver": { "version": "7.6.3", diff --git a/frontend/package.json b/frontend/package.json index be8a2a9f0..4f3ceda15 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,12 +13,11 @@ "preview": "vite preview", "fix": "eslint --fix .", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", + "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh,react,react-dom,@types/react,@types/react-dom,react-leaflet", "stylelint": "stylelint \"src/**/*.css\"", "stylelint:fix": "stylelint \"src/**/*.css\" --fix" }, "dependencies": { - "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.0", @@ -31,8 +30,8 @@ "@types/leaflet-boundary-canvas": "^1.0.3", "@types/node": "^22.10.7", "@types/nprogress": "^0.2.3", - "@types/react": "^19.0.7", - "@types/react-dom": "^19.0.3", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", @@ -51,11 +50,11 @@ "leaflet-boundary-canvas": "^1.0.0", "localized-strings": "^2.0.3", "nprogress": "^0.2.0", - "react": "^19.0.0", + "react": "^18.3.1", "react-circle-flags": "^0.0.23", - "react-dom": "^19.0.0", + "react-dom": "^18.3.1", "react-ga4": "^2.1.0", - "react-leaflet": "^5.0.0", + "react-leaflet": "^4.2.1", "react-router-dom": "^7.1.3", "react-slick": "^0.30.3", "react-toastify": "^11.0.3", diff --git a/frontend/src/components/SuspenseRouter.tsx b/frontend/src/components/SuspenseRouter.tsx deleted file mode 100644 index e924c0cfc..000000000 --- a/frontend/src/components/SuspenseRouter.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable react-compiler/react-compiler */ -import React, { useLayoutEffect, useRef, useState, useTransition } from 'react' -import { Router } from 'react-router-dom' -import { BrowserHistory, createBrowserHistory, Update } from 'history' - -export interface BrowserRouterProps { - basename?: string - children?: React.ReactNode - window?: Window -} - -export const SuspenseRouter = ({ basename, children, window }: BrowserRouterProps) => { - const historyRef = useRef(null) - const [isPending, startTransition] = useTransition() - - if (historyRef.current == null) { - // const history = createBrowserHistory(startTransition, { window }); - historyRef.current = createBrowserHistory({ window }) - } - - const history = historyRef.current - const [state, setState] = useState({ - action: history.action, - location: history.location, - }) - - function setStateAsync(update: Update) { - startTransition(() => { - setState(update) - }) - } - - useLayoutEffect(() => history.listen(setStateAsync), [history]) - - return ( - - {children} - - ) -} - -export default SuspenseRouter diff --git a/frontend/src/context/GlobalContext.tsx b/frontend/src/context/GlobalContext.tsx index 18c9b2b17..9a55ee84b 100644 --- a/frontend/src/context/GlobalContext.tsx +++ b/frontend/src/context/GlobalContext.tsx @@ -1,5 +1,5 @@ /* eslint-disable react-refresh/only-export-components */ -import React, { ReactNode, createContext, use, useMemo, useState } from 'react' +import React, { ReactNode, createContext, useContext, useMemo, useState } from 'react' // Create context export interface GlobalContextType { @@ -24,4 +24,4 @@ export const GlobalProvider = ({ children }: GlobalProviderProps) => { } // Create a custom hook to access context -export const useGlobalContext = () => use(GlobalContext) +export const useGlobalContext = () => useContext(GlobalContext) diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 1db994c1b..c67a0984f 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -489,7 +489,7 @@ const Home = () => {
          { diff --git a/packages/reactjs-social-login/package-lock.json b/packages/reactjs-social-login/package-lock.json index 45b240c8e..be4daf315 100644 --- a/packages/reactjs-social-login/package-lock.json +++ b/packages/reactjs-social-login/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "devDependencies": { "@types/node": "^22.10.3", - "@types/react": "^19.0.2", + "@types/react": "^18.3.12", "microbundle-crl": "^0.13.11", - "react": "^19.0.0", + "react": "^18.3.1", "rimraf": "^6.0.1", "typescript": "^5.7.2" } @@ -2317,6 +2317,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/q": { "version": "1.5.8", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", @@ -2325,12 +2332,13 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.2.tgz", - "integrity": "sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==", + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "dev": true, "license": "MIT", "dependencies": { + "@types/prop-types": "*", "csstype": "^3.0.2" } }, @@ -5235,6 +5243,19 @@ "dev": true, "license": "MIT" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7106,11 +7127,14 @@ } }, "node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } diff --git a/packages/reactjs-social-login/package.json b/packages/reactjs-social-login/package.json index 2d54c71a6..cd7608b72 100644 --- a/packages/reactjs-social-login/package.json +++ b/packages/reactjs-social-login/package.json @@ -10,9 +10,9 @@ }, "devDependencies": { "@types/node": "^22.10.3", - "@types/react": "^19.0.2", + "@types/react": "^18.3.12", "microbundle-crl": "^0.13.11", - "react": "^19.0.0", + "react": "^18.3.1", "rimraf": "^6.0.1", "typescript": "^5.7.2" }, From e896902b2207fd034837b8e2f1a541549eade581 Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 20:05:36 +0100 Subject: [PATCH 28/42] Fix: toggle hideSupplier prop in Search component --- frontend/src/pages/Search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Search.tsx b/frontend/src/pages/Search.tsx index c84e701fe..3cbb097bf 100644 --- a/frontend/src/pages/Search.tsx +++ b/frontend/src/pages/Search.tsx @@ -329,7 +329,7 @@ const Search = () => { seats={seats} // distance={distance} // onLoad={() => setLoadingPage(false)} - hideSupplier + // hideSupplier // includeAlreadyBookedCars includeComingSoonCars /> From 46b8d484ec39649ac16e04b3876e882074b87ecf Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 20:10:34 +0100 Subject: [PATCH 29/42] Bump vite to 6.0.11 --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 4f3ceda15..1b0c4f064 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -61,7 +61,7 @@ "slick-carousel": "^1.8.1", "typescript": "^5.2.2", "validator": "^13.12.0", - "vite": "^6.0.7" + "vite": "^6.0.11" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", From 516af79ddeae28068e3159293e4465c36bb1649e Mon Sep 17 00:00:00 2001 From: aelassas Date: Thu, 23 Jan 2025 21:43:56 +0100 Subject: [PATCH 30/42] Fix: date handling in VehicleScheduler and CarFilter components --- backend/src/components/VehicleScheduler.tsx | 11 ++++--- frontend/src/components/CarFilter.tsx | 32 +++++++++------------ frontend/src/components/SearchForm.tsx | 9 ++++-- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/backend/src/components/VehicleScheduler.tsx b/backend/src/components/VehicleScheduler.tsx index 04b4ccb7d..9d119e9e5 100644 --- a/backend/src/components/VehicleScheduler.tsx +++ b/backend/src/components/VehicleScheduler.tsx @@ -46,21 +46,24 @@ const VehicleScheduler = ( } ] + const dateBetween = new Date(query.end.getTime() - Math.ceil(query.end.getTime() - query.start.getTime()) / 2) + dateBetween.setHours(23, 59, 0, 0) + const payload: bookcarsTypes.GetBookingsPayload = { suppliers, statuses, filter: { - // from: query.view !== 'day' ? query.start : undefined, from: query.view !== 'day' ? new Date(query.start.getFullYear(), query.start.getMonth() - 1, 1) : undefined, - dateBetween: query.view === 'day' ? new Date(query.end.getFullYear(), query.end.getMonth(), query.end.getDate(), 0, 0, 0) : undefined, - // to: query.view === 'month' ? query.end : new Date(query.end.getFullYear(), query.end.getMonth() + 1, 0), - to: new Date(query.end.getFullYear(), query.end.getMonth() + 1, 0), + dateBetween: query.view === 'day' ? dateBetween : undefined, + to: query.view === 'month' ? new Date(query.end.getFullYear(), query.end.getMonth() + 1, 0) : new Date(query.end.getFullYear(), query.end.getMonth() + 2, 0), pickupLocation: filter?.pickupLocation, dropOffLocation: filter?.dropOffLocation, keyword: filter?.keyword, }, user: (user && user._id) || undefined, } + console.log(payload.filter?.dateBetween) + console.log(payload.filter?.to) const data = await BookingService.getBookings(payload, 1, 10000) const _data = data && data.length > 0 ? data[0] : { pageInfo: { totalRecord: 0 }, resultData: [] } diff --git a/frontend/src/components/CarFilter.tsx b/frontend/src/components/CarFilter.tsx index 2015036f5..bc4ab9474 100644 --- a/frontend/src/components/CarFilter.tsx +++ b/frontend/src/components/CarFilter.tsx @@ -40,7 +40,6 @@ const CarFilter = ({ const [from, setFrom] = useState(filterFrom) const [to, setTo] = useState(filterTo) const [minDate, setMinDate] = useState() - const [maxDate, setMaxDate] = useState() const [pickupLocation, setPickupLocation] = useState(filterPickupLocation) const [dropOffLocation, setDropOffLocation] = useState(filterDropOffLocation) const [sameLocation, setSameLocation] = useState(filterPickupLocation === filterDropOffLocation) @@ -57,14 +56,6 @@ const CarFilter = ({ } }, [filterFrom]) - useEffect(() => { - if (filterTo) { - const __maxDate = new Date(filterTo) - __maxDate.setDate(__maxDate.getDate() - 1) - setMaxDate(__maxDate) - } - }, [filterTo]) - useEffect(() => { setPickupLocation(filterPickupLocation) }, [filterPickupLocation]) @@ -163,7 +154,7 @@ const CarFilter = ({ label={strings.PICK_UP_DATE} value={from} minDate={_minDate} - maxDate={maxDate} + // maxDate={maxDate} variant="standard" required onChange={(date) => { @@ -173,6 +164,11 @@ const CarFilter = ({ setFrom(date) setMinDate(__minDate) setFromError(false) + if (to!.getTime() - date.getTime() < 24 * 60 * 60 * 1000) { + const _to = new Date(date) + _to.setDate(_to.getDate() + 3) + setTo(_to) + } } else { setFrom(undefined) setMinDate(_minDate) @@ -197,14 +193,10 @@ const CarFilter = ({ required onChange={(date) => { if (date) { - const _maxDate = new Date(date) - _maxDate.setDate(_maxDate.getDate() - 1) setTo(date) - setMaxDate(_maxDate) setToError(false) } else { setTo(undefined) - setMaxDate(undefined) } }} onError={(err: DateTimeValidationError) => { @@ -261,7 +253,7 @@ const CarFilter = ({ label={strings.PICK_UP_DATE} value={from} minDate={_minDate} - maxDate={maxDate} + // maxDate={maxDate} variant="standard" required onChange={(date) => { @@ -271,6 +263,12 @@ const CarFilter = ({ setFrom(date) setMinDate(__minDate) setFromError(false) + + if (to!.getTime() - date.getTime() < 24 * 60 * 60 * 1000) { + const _to = new Date(date) + _to.setDate(_to.getDate() + 3) + setTo(_to) + } } else { setFrom(undefined) setMinDate(_minDate) @@ -325,14 +323,10 @@ const CarFilter = ({ required onChange={(date) => { if (date) { - const _maxDate = new Date(date) - _maxDate.setDate(_maxDate.getDate() - 1) setTo(date) - setMaxDate(_maxDate) setToError(false) } else { setTo(undefined) - setMaxDate(undefined) } }} onError={(err: DateTimeValidationError) => { diff --git a/frontend/src/components/SearchForm.tsx b/frontend/src/components/SearchForm.tsx index 82b513583..45a469c96 100644 --- a/frontend/src/components/SearchForm.tsx +++ b/frontend/src/components/SearchForm.tsx @@ -203,9 +203,12 @@ const SearchForm = ({ setFrom(date) setMinDate(__minDate) setFromError(false) - const _to = new Date(date) - _to.setDate(date.getDate() + 3) - setTo(_to) + + if (to!.getTime() - date.getTime() < 24 * 60 * 60 * 1000) { + const _to = new Date(date) + _to.setDate(_to.getDate() + 3) + setTo(_to) + } } else { setFrom(undefined) setMinDate(_minDate) From c69049c9bd11e927d462a735de105706971fb3f7 Mon Sep 17 00:00:00 2001 From: aelassas Date: Fri, 24 Jan 2025 12:25:11 +0100 Subject: [PATCH 31/42] Add timezone setting to api --- api/.env.docker.example | 1 + api/.env.example | 1 + api/src/config/env.config.ts | 9 +++ api/src/controllers/bookingController.ts | 1 + api/src/lang/en.ts | 2 +- api/src/lang/es.ts | 92 ++++++++++++------------ api/src/lang/fr.ts | 2 +- frontend/src/components/CarFilter.tsx | 2 - frontend/src/components/SearchForm.tsx | 1 - 9 files changed, 60 insertions(+), 51 deletions(-) diff --git a/api/.env.docker.example b/api/.env.docker.example index a882803f7..ee2504f66 100644 --- a/api/.env.docker.example +++ b/api/.env.docker.example @@ -38,3 +38,4 @@ BC_STRIPE_SESSION_EXPIRE_AT=82800 BC_ADMIN_EMAIL=admin@bookcars.ma BC_RECAPTCHA_SECRET=RECAPTCHA_SECRET BC_WEBSITE_NAME=BookCars +BC_TIMEZONE=UTC diff --git a/api/.env.example b/api/.env.example index ee6344aae..21245ea3b 100644 --- a/api/.env.example +++ b/api/.env.example @@ -38,3 +38,4 @@ BC_STRIPE_SESSION_EXPIRE_AT=82800 BC_ADMIN_EMAIL=admin@bookcars.ma BC_RECAPTCHA_SECRET=RECAPTCHA_SECRET BC_WEBSITE_NAME=BookCars +BC_TIMEZONE=UTC diff --git a/api/src/config/env.config.ts b/api/src/config/env.config.ts index cd3477a8e..e65edf7b7 100644 --- a/api/src/config/env.config.ts +++ b/api/src/config/env.config.ts @@ -366,6 +366,15 @@ export const ADMIN_EMAIL = __env__('BC_ADMIN_EMAIL', false) */ export const RECAPTCHA_SECRET = __env__('BC_RECAPTCHA_SECRET', false) +/** + * Timezone for cenverting dates from UTC to local time. + * Must be a valid TZ idenfidier: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + * Default is UTC. + * + * @type {string} + */ +export const TIMEZONE = __env__('BC_TIMEZONE', false, 'UTC') + /** * User Document. * diff --git a/api/src/controllers/bookingController.ts b/api/src/controllers/bookingController.ts index a99a7c4dd..4fc4c9a47 100644 --- a/api/src/controllers/bookingController.ts +++ b/api/src/controllers/bookingController.ts @@ -118,6 +118,7 @@ export const confirm = async (user: env.User, supplier: env.User, booking: env.B day: 'numeric', hour: 'numeric', minute: 'numeric', + timeZone: env.TIMEZONE, } const from = booking.from.toLocaleString(locale, options) const to = booking.to.toLocaleString(locale, options) diff --git a/api/src/lang/en.ts b/api/src/lang/en.ts index 512376df5..0f24ccf90 100644 --- a/api/src/lang/en.ts +++ b/api/src/lang/en.ts @@ -27,7 +27,7 @@ export const en = { BOOKING_CONFIRMED_PART3: ' Please present yourself to our agency ', BOOKING_CONFIRMED_PART4: ' (', BOOKING_CONFIRMED_PART5: ') on ', - BOOKING_CONFIRMED_PART6: ' (local time) to pick up your vehicle ', + BOOKING_CONFIRMED_PART6: ` (${env.TIMEZONE}) to pick up your vehicle `, BOOKING_CONFIRMED_PART7: '.', BOOKING_CONFIRMED_PART8: "Please bring your ID, driver's license and warranty check with you.", BOOKING_CONFIRMED_PART9: 'You must drop-off the vehicle to our agency ', diff --git a/api/src/lang/es.ts b/api/src/lang/es.ts index 208809680..47dfa3079 100644 --- a/api/src/lang/es.ts +++ b/api/src/lang/es.ts @@ -1,49 +1,49 @@ import * as env from '../config/env.config' export const es = { - ERROR: 'Error interno: ', - DB_ERROR: 'Error en la solicitud a la base de datos: ', - SMTP_ERROR: 'Error SMTP - Fallo en el envío del correo electrónico: ', - ACCOUNT_ACTIVATION_SUBJECT: 'Activación de su cuenta', - HELLO: 'Hola ', - ACCOUNT_ACTIVATION_LINK: 'Por favor, active su cuenta haciendo clic en el enlace:', - REGARDS: `Atentamente,
          El equipo de ${env.WEBSITE_NAME}`, - ACCOUNT_ACTIVATION_TECHNICAL_ISSUE: '¡Problema técnico! Haga clic en reenviar para validar su correo electrónico.', - ACCOUNT_ACTIVATION_LINK_EXPIRED: 'Es posible que su enlace de validación haya expirado. Haga clic en reenviar para validar su correo electrónico.', - ACCOUNT_ACTIVATION_LINK_ERROR: 'No pudimos encontrar un usuario correspondiente a esta dirección de correo electrónico. Por favor, regístrese.', - ACCOUNT_ACTIVATION_SUCCESS: 'Su cuenta ha sido validada con éxito.', - ACCOUNT_ACTIVATION_RESEND_ERROR: 'No pudimos encontrar un usuario correspondiente a esta dirección de correo electrónico. Asegúrese de que su correo electrónico sea correcto.', - ACCOUNT_ACTIVATION_ACCOUNT_VERIFIED: 'Esta cuenta ya ha sido validada. Por favor, inicie sesión.', - ACCOUNT_ACTIVATION_EMAIL_SENT_PART_1: 'Se ha enviado un correo de validación a', - ACCOUNT_ACTIVATION_EMAIL_SENT_PART_2: '. Expirará después de un día. Si no ha recibido el correo de validación, haga clic en reenviar.', - CAR_IMAGE_REQUIRED: 'El campo de imagen de Car no puede estar vacío: ', - CAR_IMAGE_NOT_FOUND: 'Archivo de imagen no encontrado: ', - PASSWORD_RESET_SUBJECT: 'Restablecimiento de contraseña', - PASSWORD_RESET_LINK: 'Por favor, restablezca su contraseña haciendo clic en el enlace:', - BOOKING_CONFIRMED_SUBJECT_PART1: 'Su reserva', - BOOKING_CONFIRMED_SUBJECT_PART2: 'ha sido confirmada.', - BOOKING_CONFIRMED_PART1: 'Su reserva', - BOOKING_CONFIRMED_PART2: 'ha sido confirmada y el pago ha sido procesado con éxito.', - BOOKING_CONFIRMED_PART3: ' Por favor, acuda a nuestra agencia ', - BOOKING_CONFIRMED_PART4: ' (', - BOOKING_CONFIRMED_PART5: ') el ', - BOOKING_CONFIRMED_PART6: ' (hora local) para recoger su vehículo ', - BOOKING_CONFIRMED_PART7: '.', - BOOKING_CONFIRMED_PART8: 'Por favor, traiga su identificación, licencia de conducir y el cheque de garantía.', - BOOKING_CONFIRMED_PART9: 'Debe devolver el vehículo en nuestra agencia ', - BOOKING_CONFIRMED_PART10: ' (', - BOOKING_CONFIRMED_PART11: ') el ', - BOOKING_CONFIRMED_PART12: ' (hora local).', - BOOKING_CONFIRMED_PART13: 'Por favor, respete las fechas y horarios de recogida y devolución del vehículo.', - BOOKING_CONFIRMED_PART14: 'Puede seguir su reserva en: ', - BOOKING_PAY_LATER_NOTIFICATION: 'ha confirmado la reserva', - BOOKING_PAID_NOTIFICATION: 'ha pagado la reserva', - CANCEL_BOOKING_NOTIFICATION: 'ha solicitado la cancelación de la reserva', - BOOKING_UPDATED_NOTIFICATION_PART1: 'El estado de la reserva', - BOOKING_UPDATED_NOTIFICATION_PART2: 'ha sido actualizado.', - CONTACT_SUBJECT: 'Nuevo Mensaje desde el formulario de Contacto', - SUBJECT: 'Asunto', - FROM: 'De', - MESSAGE: 'Mensaje', - LOCATION_IMAGE_NOT_FOUND: 'Imagen del lugar no encontrada', - } + ERROR: 'Error interno: ', + DB_ERROR: 'Error en la solicitud a la base de datos: ', + SMTP_ERROR: 'Error SMTP - Fallo en el envío del correo electrónico: ', + ACCOUNT_ACTIVATION_SUBJECT: 'Activación de su cuenta', + HELLO: 'Hola ', + ACCOUNT_ACTIVATION_LINK: 'Por favor, active su cuenta haciendo clic en el enlace:', + REGARDS: `Atentamente,
          El equipo de ${env.WEBSITE_NAME}`, + ACCOUNT_ACTIVATION_TECHNICAL_ISSUE: '¡Problema técnico! Haga clic en reenviar para validar su correo electrónico.', + ACCOUNT_ACTIVATION_LINK_EXPIRED: 'Es posible que su enlace de validación haya expirado. Haga clic en reenviar para validar su correo electrónico.', + ACCOUNT_ACTIVATION_LINK_ERROR: 'No pudimos encontrar un usuario correspondiente a esta dirección de correo electrónico. Por favor, regístrese.', + ACCOUNT_ACTIVATION_SUCCESS: 'Su cuenta ha sido validada con éxito.', + ACCOUNT_ACTIVATION_RESEND_ERROR: 'No pudimos encontrar un usuario correspondiente a esta dirección de correo electrónico. Asegúrese de que su correo electrónico sea correcto.', + ACCOUNT_ACTIVATION_ACCOUNT_VERIFIED: 'Esta cuenta ya ha sido validada. Por favor, inicie sesión.', + ACCOUNT_ACTIVATION_EMAIL_SENT_PART_1: 'Se ha enviado un correo de validación a', + ACCOUNT_ACTIVATION_EMAIL_SENT_PART_2: '. Expirará después de un día. Si no ha recibido el correo de validación, haga clic en reenviar.', + CAR_IMAGE_REQUIRED: 'El campo de imagen de Car no puede estar vacío: ', + CAR_IMAGE_NOT_FOUND: 'Archivo de imagen no encontrado: ', + PASSWORD_RESET_SUBJECT: 'Restablecimiento de contraseña', + PASSWORD_RESET_LINK: 'Por favor, restablezca su contraseña haciendo clic en el enlace:', + BOOKING_CONFIRMED_SUBJECT_PART1: 'Su reserva', + BOOKING_CONFIRMED_SUBJECT_PART2: 'ha sido confirmada.', + BOOKING_CONFIRMED_PART1: 'Su reserva', + BOOKING_CONFIRMED_PART2: 'ha sido confirmada y el pago ha sido procesado con éxito.', + BOOKING_CONFIRMED_PART3: ' Por favor, acuda a nuestra agencia ', + BOOKING_CONFIRMED_PART4: ' (', + BOOKING_CONFIRMED_PART5: ') el ', + BOOKING_CONFIRMED_PART6: ` (${env.TIMEZONE}) para recoger su vehículo `, + BOOKING_CONFIRMED_PART7: '.', + BOOKING_CONFIRMED_PART8: 'Por favor, traiga su identificación, licencia de conducir y el cheque de garantía.', + BOOKING_CONFIRMED_PART9: 'Debe devolver el vehículo en nuestra agencia ', + BOOKING_CONFIRMED_PART10: ' (', + BOOKING_CONFIRMED_PART11: ') el ', + BOOKING_CONFIRMED_PART12: ' (hora local).', + BOOKING_CONFIRMED_PART13: 'Por favor, respete las fechas y horarios de recogida y devolución del vehículo.', + BOOKING_CONFIRMED_PART14: 'Puede seguir su reserva en: ', + BOOKING_PAY_LATER_NOTIFICATION: 'ha confirmado la reserva', + BOOKING_PAID_NOTIFICATION: 'ha pagado la reserva', + CANCEL_BOOKING_NOTIFICATION: 'ha solicitado la cancelación de la reserva', + BOOKING_UPDATED_NOTIFICATION_PART1: 'El estado de la reserva', + BOOKING_UPDATED_NOTIFICATION_PART2: 'ha sido actualizado.', + CONTACT_SUBJECT: 'Nuevo Mensaje desde el formulario de Contacto', + SUBJECT: 'Asunto', + FROM: 'De', + MESSAGE: 'Mensaje', + LOCATION_IMAGE_NOT_FOUND: 'Imagen del lugar no encontrada', +} diff --git a/api/src/lang/fr.ts b/api/src/lang/fr.ts index b3321ce06..c3de13e6e 100644 --- a/api/src/lang/fr.ts +++ b/api/src/lang/fr.ts @@ -27,7 +27,7 @@ export const fr = { BOOKING_CONFIRMED_PART3: ' Veuillez vous rendre à notre agence ', BOOKING_CONFIRMED_PART4: ' (', BOOKING_CONFIRMED_PART5: ') le ', - BOOKING_CONFIRMED_PART6: ' (heure locale) pour récupérer votre véhicule ', + BOOKING_CONFIRMED_PART6: ` (${env.TIMEZONE}) pour récupérer votre véhicule `, BOOKING_CONFIRMED_PART7: '.', BOOKING_CONFIRMED_PART8: "Veuillez apporter avec vous votre pièce d'identité, votre permis de conduire et le chèque de garantie.", BOOKING_CONFIRMED_PART9: 'Vous devez rendre le véhicule à notre agence ', diff --git a/frontend/src/components/CarFilter.tsx b/frontend/src/components/CarFilter.tsx index bc4ab9474..d66bf8f10 100644 --- a/frontend/src/components/CarFilter.tsx +++ b/frontend/src/components/CarFilter.tsx @@ -154,7 +154,6 @@ const CarFilter = ({ label={strings.PICK_UP_DATE} value={from} minDate={_minDate} - // maxDate={maxDate} variant="standard" required onChange={(date) => { @@ -253,7 +252,6 @@ const CarFilter = ({ label={strings.PICK_UP_DATE} value={from} minDate={_minDate} - // maxDate={maxDate} variant="standard" required onChange={(date) => { diff --git a/frontend/src/components/SearchForm.tsx b/frontend/src/components/SearchForm.tsx index 45a469c96..2d6541b9e 100644 --- a/frontend/src/components/SearchForm.tsx +++ b/frontend/src/components/SearchForm.tsx @@ -193,7 +193,6 @@ const SearchForm = ({ label={strings.PICK_UP_DATE} value={from} minDate={_minDate} - // maxDate={maxDate} variant="outlined" required onChange={(date) => { From 6ed97330b42d50bca278cf86cbef2d8d59f069c3 Mon Sep 17 00:00:00 2001 From: aelassas Date: Fri, 24 Jan 2025 12:29:57 +0100 Subject: [PATCH 32/42] Update booking confirmation messages to include timezone variable --- api/src/lang/en.ts | 2 +- api/src/lang/es.ts | 2 +- api/src/lang/fr.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/lang/en.ts b/api/src/lang/en.ts index 0f24ccf90..4a3710c77 100644 --- a/api/src/lang/en.ts +++ b/api/src/lang/en.ts @@ -33,7 +33,7 @@ export const en = { BOOKING_CONFIRMED_PART9: 'You must drop-off the vehicle to our agency ', BOOKING_CONFIRMED_PART10: ' (', BOOKING_CONFIRMED_PART11: ') on ', - BOOKING_CONFIRMED_PART12: ' (local time).', + BOOKING_CONFIRMED_PART12: ` (${env.TIMEZONE}).`, BOOKING_CONFIRMED_PART13: 'Please respect the pick-up and drop-off dates and times.', BOOKING_CONFIRMED_PART14: 'You can follow your booking on: ', BOOKING_PAY_LATER_NOTIFICATION: 'confirmed the booking', diff --git a/api/src/lang/es.ts b/api/src/lang/es.ts index 47dfa3079..29133e683 100644 --- a/api/src/lang/es.ts +++ b/api/src/lang/es.ts @@ -33,7 +33,7 @@ export const es = { BOOKING_CONFIRMED_PART9: 'Debe devolver el vehículo en nuestra agencia ', BOOKING_CONFIRMED_PART10: ' (', BOOKING_CONFIRMED_PART11: ') el ', - BOOKING_CONFIRMED_PART12: ' (hora local).', + BOOKING_CONFIRMED_PART12: ` (${env.TIMEZONE}).`, BOOKING_CONFIRMED_PART13: 'Por favor, respete las fechas y horarios de recogida y devolución del vehículo.', BOOKING_CONFIRMED_PART14: 'Puede seguir su reserva en: ', BOOKING_PAY_LATER_NOTIFICATION: 'ha confirmado la reserva', diff --git a/api/src/lang/fr.ts b/api/src/lang/fr.ts index c3de13e6e..497d8caf2 100644 --- a/api/src/lang/fr.ts +++ b/api/src/lang/fr.ts @@ -33,7 +33,7 @@ export const fr = { BOOKING_CONFIRMED_PART9: 'Vous devez rendre le véhicule à notre agence ', BOOKING_CONFIRMED_PART10: ' (', BOOKING_CONFIRMED_PART11: ') le ', - BOOKING_CONFIRMED_PART12: ' (heure locale).', + BOOKING_CONFIRMED_PART12: ` (${env.TIMEZONE}).`, BOOKING_CONFIRMED_PART13: 'Veuillez respecter les dates et les horaires de prise en charge et de restitution du véhicule.', BOOKING_CONFIRMED_PART14: 'Vous pouvez suivre votre réservation sur : ', BOOKING_PAY_LATER_NOTIFICATION: 'a confirmé la réservation', From b145148e2efbffdbc6b85e5a168203f77b305935 Mon Sep 17 00:00:00 2001 From: aelassas Date: Fri, 24 Jan 2025 17:32:10 +0100 Subject: [PATCH 33/42] Adjust date handling in VehicleScheduler and add new booking button in Scheduler --- api/src/controllers/bookingController.ts | 14 ++++++++++++-- backend/src/assets/css/scheduler.css | 10 ++++++++++ backend/src/components/VehicleScheduler.tsx | 6 ++---- backend/src/pages/Scheduler.tsx | 5 +++++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/api/src/controllers/bookingController.ts b/api/src/controllers/bookingController.ts index 4fc4c9a47..f685efdab 100644 --- a/api/src/controllers/bookingController.ts +++ b/api/src/controllers/bookingController.ts @@ -812,9 +812,19 @@ export const getBookings = async (req: Request, res: Response) => { } if (dateBetween) { - $match.$and!.push({ $and: [{ from: { $lte: dateBetween } }, { to: { $gte: dateBetween } }] }) + const dateBetweenStart = new Date(dateBetween) + dateBetweenStart.setHours(0, 0, 0, 0) + const dateBetweenEnd = new Date(dateBetween) + dateBetweenEnd.setHours(23, 59, 59, 999) + + $match.$and!.push({ + $and: [ + { from: { $lte: dateBetweenEnd } }, + { to: { $gte: dateBetweenStart } }, + ], + }) } else if (from) { - $match.$and!.push({ from: { $gte: from } }) // $from > from + $match.$and!.push({ from: { $gte: from } }) // $from >= from } if (to) { diff --git a/backend/src/assets/css/scheduler.css b/backend/src/assets/css/scheduler.css index 2e40554c3..b1351ebb0 100644 --- a/backend/src/assets/css/scheduler.css +++ b/backend/src/assets/css/scheduler.css @@ -41,6 +41,12 @@ div.scheduler { padding-right: 15px; padding-left: 15px; } + + div.scheduler div.col-1 .cl-new-booking { + width: calc(100% - 20px); + max-width: 480px; + margin: 15px 10px 5px; + } } @media only screen and (width >=960px) { @@ -91,4 +97,8 @@ div.scheduler { margin-bottom: 10px; } + div.scheduler div.col-1 .cl-new-booking { + width: 265px; + margin-left: 5px; + } } diff --git a/backend/src/components/VehicleScheduler.tsx b/backend/src/components/VehicleScheduler.tsx index 9d119e9e5..d8a417d22 100644 --- a/backend/src/components/VehicleScheduler.tsx +++ b/backend/src/components/VehicleScheduler.tsx @@ -47,7 +47,7 @@ const VehicleScheduler = ( ] const dateBetween = new Date(query.end.getTime() - Math.ceil(query.end.getTime() - query.start.getTime()) / 2) - dateBetween.setHours(23, 59, 0, 0) + dateBetween.setHours(10, 0, 0, 0) const payload: bookcarsTypes.GetBookingsPayload = { suppliers, @@ -62,8 +62,6 @@ const VehicleScheduler = ( }, user: (user && user._id) || undefined, } - console.log(payload.filter?.dateBetween) - console.log(payload.filter?.to) const data = await BookingService.getBookings(payload, 1, 10000) const _data = data && data.length > 0 ? data[0] : { pageInfo: { totalRecord: 0 }, resultData: [] } @@ -212,8 +210,8 @@ const VehicleScheduler = ( return ( {
          {leftPanel && ( <> + {admin && ( Date: Fri, 24 Jan 2025 18:00:50 +0100 Subject: [PATCH 34/42] Remove disableElevation from buttons in multiple pages for consistent styling --- frontend/src/pages/Activate.tsx | 2 +- frontend/src/pages/ResetPassword.tsx | 2 +- frontend/src/pages/Settings.tsx | 3 +-- frontend/src/pages/SignIn.tsx | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/Activate.tsx b/frontend/src/pages/Activate.tsx index 40c9f5d49..07574e330 100644 --- a/frontend/src/pages/Activate.tsx +++ b/frontend/src/pages/Activate.tsx @@ -213,7 +213,7 @@ const Activate = () => {
          - - -
          From 2857e881025260b0a795bae7fd973244ef3d2d05 Mon Sep 17 00:00:00 2001 From: aelassas Date: Sun, 26 Jan 2025 21:42:11 +0100 Subject: [PATCH 35/42] Add scheduler component and hooks for improved event management --- backend/package-lock.json | 139 +++---- backend/package.json | 20 +- backend/src/components/VehicleScheduler.tsx | 6 +- backend/src/components/scheduler/README.md | 154 ++++++++ .../scheduler/SchedulerComponent.tsx | 75 ++++ .../scheduler/components/common/Cell.tsx | 53 +++ .../components/common/LocaleArrow.tsx | 38 ++ .../components/common/ResourceHeader.tsx | 74 ++++ .../scheduler/components/common/Tabs.tsx | 117 ++++++ .../scheduler/components/common/TodayTypo.tsx | 45 +++ .../components/common/WithResources.tsx | 95 +++++ .../scheduler/components/events/Actions.tsx | 66 ++++ .../components/events/AgendaEventsList.tsx | 109 ++++++ .../components/events/CurrentTimeBar.tsx | 49 +++ .../components/events/EmptyAgenda.tsx | 25 ++ .../scheduler/components/events/EventItem.tsx | 151 ++++++++ .../components/events/EventItemPopover.tsx | 175 +++++++++ .../components/events/MonthEvents.tsx | 141 +++++++ .../components/events/TodayEvents.tsx | 91 +++++ .../scheduler/components/hoc/DateProvider.tsx | 20 + .../components/inputs/DatePicker.tsx | 89 +++++ .../scheduler/components/inputs/Input.tsx | 108 ++++++ .../components/inputs/SelectInput.tsx | 157 ++++++++ .../scheduler/components/month/MonthTable.tsx | 201 ++++++++++ .../scheduler/components/nav/DayDateBtn.tsx | 68 ++++ .../scheduler/components/nav/MonthDateBtn.tsx | 68 ++++ .../scheduler/components/nav/Navigation.tsx | 195 ++++++++++ .../scheduler/components/nav/WeekDateBtn.tsx | 77 ++++ .../scheduler/components/week/WeekTable.tsx | 204 ++++++++++ .../components/scheduler/helpers/constants.ts | 4 + .../components/scheduler/helpers/generals.tsx | 304 +++++++++++++++ .../scheduler/hooks/useCellAttributes.ts | 67 ++++ .../scheduler/hooks/useDragAttributes.ts | 31 ++ .../scheduler/hooks/useEventPermissions.ts | 42 +++ .../components/scheduler/hooks/useStore.ts | 6 + .../scheduler/hooks/useSyncScroll.ts | 31 ++ .../scheduler/hooks/useWindowResize.ts | 37 ++ backend/src/components/scheduler/index.tsx | 12 + .../scheduler/positionManger/context.ts | 14 + .../scheduler/positionManger/provider.tsx | 101 +++++ .../scheduler/positionManger/usePosition.ts | 6 + .../src/components/scheduler/store/context.ts | 5 + .../src/components/scheduler/store/default.ts | 150 ++++++++ .../components/scheduler/store/provider.tsx | 203 ++++++++++ .../src/components/scheduler/store/types.ts | 32 ++ .../src/components/scheduler/styles/styles.ts | 240 ++++++++++++ backend/src/components/scheduler/types.ts | 356 ++++++++++++++++++ .../src/components/scheduler/views/Day.tsx | 216 +++++++++++ .../components/scheduler/views/DayAgenda.tsx | 46 +++ .../src/components/scheduler/views/Editor.tsx | 258 +++++++++++++ .../src/components/scheduler/views/Month.tsx | 92 +++++ .../scheduler/views/MonthAgenda.tsx | 79 ++++ .../src/components/scheduler/views/Week.tsx | 104 +++++ .../components/scheduler/views/WeekAgenda.tsx | 69 ++++ frontend/package-lock.json | 284 +++++++------- frontend/package.json | 28 +- .../reactjs-social-login/package-lock.json | 40 +- packages/reactjs-social-login/package.json | 4 +- 58 files changed, 5385 insertions(+), 286 deletions(-) create mode 100644 backend/src/components/scheduler/README.md create mode 100644 backend/src/components/scheduler/SchedulerComponent.tsx create mode 100644 backend/src/components/scheduler/components/common/Cell.tsx create mode 100644 backend/src/components/scheduler/components/common/LocaleArrow.tsx create mode 100644 backend/src/components/scheduler/components/common/ResourceHeader.tsx create mode 100644 backend/src/components/scheduler/components/common/Tabs.tsx create mode 100644 backend/src/components/scheduler/components/common/TodayTypo.tsx create mode 100644 backend/src/components/scheduler/components/common/WithResources.tsx create mode 100644 backend/src/components/scheduler/components/events/Actions.tsx create mode 100644 backend/src/components/scheduler/components/events/AgendaEventsList.tsx create mode 100644 backend/src/components/scheduler/components/events/CurrentTimeBar.tsx create mode 100644 backend/src/components/scheduler/components/events/EmptyAgenda.tsx create mode 100644 backend/src/components/scheduler/components/events/EventItem.tsx create mode 100644 backend/src/components/scheduler/components/events/EventItemPopover.tsx create mode 100644 backend/src/components/scheduler/components/events/MonthEvents.tsx create mode 100644 backend/src/components/scheduler/components/events/TodayEvents.tsx create mode 100644 backend/src/components/scheduler/components/hoc/DateProvider.tsx create mode 100644 backend/src/components/scheduler/components/inputs/DatePicker.tsx create mode 100644 backend/src/components/scheduler/components/inputs/Input.tsx create mode 100644 backend/src/components/scheduler/components/inputs/SelectInput.tsx create mode 100644 backend/src/components/scheduler/components/month/MonthTable.tsx create mode 100644 backend/src/components/scheduler/components/nav/DayDateBtn.tsx create mode 100644 backend/src/components/scheduler/components/nav/MonthDateBtn.tsx create mode 100644 backend/src/components/scheduler/components/nav/Navigation.tsx create mode 100644 backend/src/components/scheduler/components/nav/WeekDateBtn.tsx create mode 100644 backend/src/components/scheduler/components/week/WeekTable.tsx create mode 100644 backend/src/components/scheduler/helpers/constants.ts create mode 100644 backend/src/components/scheduler/helpers/generals.tsx create mode 100644 backend/src/components/scheduler/hooks/useCellAttributes.ts create mode 100644 backend/src/components/scheduler/hooks/useDragAttributes.ts create mode 100644 backend/src/components/scheduler/hooks/useEventPermissions.ts create mode 100644 backend/src/components/scheduler/hooks/useStore.ts create mode 100644 backend/src/components/scheduler/hooks/useSyncScroll.ts create mode 100644 backend/src/components/scheduler/hooks/useWindowResize.ts create mode 100644 backend/src/components/scheduler/index.tsx create mode 100644 backend/src/components/scheduler/positionManger/context.ts create mode 100644 backend/src/components/scheduler/positionManger/provider.tsx create mode 100644 backend/src/components/scheduler/positionManger/usePosition.ts create mode 100644 backend/src/components/scheduler/store/context.ts create mode 100644 backend/src/components/scheduler/store/default.ts create mode 100644 backend/src/components/scheduler/store/provider.tsx create mode 100644 backend/src/components/scheduler/store/types.ts create mode 100644 backend/src/components/scheduler/styles/styles.ts create mode 100644 backend/src/components/scheduler/types.ts create mode 100644 backend/src/components/scheduler/views/Day.tsx create mode 100644 backend/src/components/scheduler/views/DayAgenda.tsx create mode 100644 backend/src/components/scheduler/views/Editor.tsx create mode 100644 backend/src/components/scheduler/views/Month.tsx create mode 100644 backend/src/components/scheduler/views/MonthAgenda.tsx create mode 100644 backend/src/components/scheduler/views/Week.tsx create mode 100644 backend/src/components/scheduler/views/WeekAgenda.tsx diff --git a/backend/package-lock.json b/backend/package-lock.json index d6556d5b4..16b625322 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -8,16 +8,15 @@ "name": "backend", "version": "5.5.0", "dependencies": { - "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.1", "@mui/material": "^6.4.1", - "@mui/x-data-grid": "^7.24.0", - "@mui/x-date-pickers": "^7.24.0", - "@types/node": "^22.10.7", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", + "@mui/x-data-grid": "^7.24.1", + "@mui/x-date-pickers": "^7.24.1", + "@types/node": "^22.10.10", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", "@types/validator": "^13.12.2", "@typescript-eslint/eslint-plugin": "^8.21.0", "@typescript-eslint/parser": "^8.21.0", @@ -30,10 +29,11 @@ "eslint-plugin-react-refresh": "^0.4.11", "history": "^5.3.0", "localized-strings": "^2.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-router-dom": "^7.1.3", "react-toastify": "^11.0.3", + "rrule": "^2.8.1", "typescript": "^5.2.2", "validator": "^13.12.0", "vite": "^6.0.11" @@ -44,31 +44,12 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.13.2", + "stylelint": "^16.14.0", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" } }, - "node_modules/@aldabil/react-scheduler": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/@aldabil/react-scheduler/-/react-scheduler-2.9.5.tgz", - "integrity": "sha512-PM8D0aFtXIj0XQWvllzwsJuqE1vu34pq33OrcInSMqLAYLgWfRrEh6rYaFJeJ1kAQuzvBGXr0GmVxYut97rIcA==", - "license": "MIT", - "peerDependencies": { - "@mui/icons-material": ">=5.0.0", - "@mui/material": ">=5.0.0", - "@mui/x-date-pickers": ">=6.19.0", - "date-fns": ">=3.2.0", - "react": ">=17.0.0", - "rrule": ">=2.8.1" - }, - "peerDependenciesMeta": { - "rrule": { - "optional": true - } - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -1642,14 +1623,14 @@ } }, "node_modules/@mui/x-data-grid": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.0.tgz", - "integrity": "sha512-goYTKDp+e+dXw7E+WndWUhWXTjX3aTqN8W2dCKhXnmE9Gu8dFwG6Azl7GK9l2m5YHGuqYmpWqcSG9etLdwYaVg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.1.tgz", + "integrity": "sha512-4sYTbMwsDotuTd2Cwa2JGTPXPWQs8RGJvocAKnIsNOzNdZNMrikE//HO35snriK8s4dauAApY7RVbeisjpVT+A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.24.0", + "@mui/x-internals": "7.24.1", "clsx": "^2.1.1", "prop-types": "^15.8.1", "reselect": "^5.1.1" @@ -1679,14 +1660,14 @@ } }, "node_modules/@mui/x-date-pickers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.0.tgz", - "integrity": "sha512-oBM9Yp2H3tJ7qoHB4APQJYxZG4rz6JD4CwLzbzD9o3r+E1HGpGSLhwK3rDEz9VEjbOq8893Z2TGYLLWoyjeFXQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.1.tgz", + "integrity": "sha512-ykQugMQHuQKBk3kViW/r0PpubtHQOlrd54bgbdafgTMCeM2VpXvv4zimzOu5IGnM6wEN8hupC7EXZbkrT6x46w==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.24.0", + "@mui/x-internals": "7.24.1", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -1745,9 +1726,9 @@ } }, "node_modules/@mui/x-internals": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.0.tgz", - "integrity": "sha512-lYa/XLltxNMY8YAFDopIHrXda2EAoqMCilyGMuPMz+WTG+b+StlUKqtj8cgFPQ/sa5dQ2fR7R3KJdjLREKUrlQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.1.tgz", + "integrity": "sha512-9BvJzpLJnS9BDphvkiv6v0QOLxbnu8jhwcexFjtCQ2ZyxtVuVsWzGZ2npT9sGOil7+eaFDmWnJtea/tgrPvSwQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", @@ -2140,9 +2121,9 @@ "peer": true }, "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2161,22 +2142,21 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "version": "19.0.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", + "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", + "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" + "@types/react": "^19.0.0" } }, "node_modules/@types/react-transition-group": { @@ -6511,9 +6491,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -6530,7 +6510,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6655,28 +6635,24 @@ "license": "MIT" }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" } }, "node_modules/react-is": { @@ -6928,6 +6904,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrule": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.8.1.tgz", + "integrity": "sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6991,13 +6976,10 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" }, "node_modules/semver": { "version": "7.6.3", @@ -7342,9 +7324,9 @@ } }, "node_modules/stylelint": { - "version": "16.13.2", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.13.2.tgz", - "integrity": "sha512-wDlgh0mRO9RtSa3TdidqHd0nOG8MmUyVKl+dxA6C1j8aZRzpNeEgdhFmU5y4sZx4Fc6r46p0fI7p1vR5O2DZqA==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.0.tgz", + "integrity": "sha512-orePw2dKxzXC0hd1VmxrDBqgf1KUV9DYsZY4guKLE9XcQD7m0BxVnWMaoQqMNsQIG14MyyTHf6zoajvOnDra8g==", "dev": true, "funding": [ { @@ -7376,7 +7358,7 @@ "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^7.0.1", + "ignore": "^7.0.3", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", @@ -7385,7 +7367,7 @@ "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.4.49", + "postcss": "^8.5.1", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.0.0", @@ -7704,7 +7686,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/turbo-stream": { diff --git a/backend/package.json b/backend/package.json index d0bdc3ec8..d67d19559 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,21 +11,20 @@ "preview": "vite preview", "fix": "eslint --fix .", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh,react,react-dom,@types/react,@types/react-dom", + "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", "stylelint": "stylelint \"src/**/*.css\"", "stylelint:fix": "stylelint \"src/**/*.css\" --fix" }, "dependencies": { - "@aldabil/react-scheduler": "^2.9.5", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.1", "@mui/material": "^6.4.1", - "@mui/x-data-grid": "^7.24.0", - "@mui/x-date-pickers": "^7.24.0", - "@types/node": "^22.10.7", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", + "@mui/x-data-grid": "^7.24.1", + "@mui/x-date-pickers": "^7.24.1", + "@types/node": "^22.10.10", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", "@types/validator": "^13.12.2", "@typescript-eslint/eslint-plugin": "^8.21.0", "@typescript-eslint/parser": "^8.21.0", @@ -38,10 +37,11 @@ "eslint-plugin-react-refresh": "^0.4.11", "history": "^5.3.0", "localized-strings": "^2.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-router-dom": "^7.1.3", "react-toastify": "^11.0.3", + "rrule": "^2.8.1", "typescript": "^5.2.2", "validator": "^13.12.0", "vite": "^6.0.11" @@ -52,7 +52,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.13.2", + "stylelint": "^16.14.0", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" diff --git a/backend/src/components/VehicleScheduler.tsx b/backend/src/components/VehicleScheduler.tsx index d8a417d22..00e6aca81 100644 --- a/backend/src/components/VehicleScheduler.tsx +++ b/backend/src/components/VehicleScheduler.tsx @@ -1,11 +1,11 @@ import React, { useCallback, useEffect, useState } from 'react' import { fr, enUS, es } from 'date-fns/locale' -import { Scheduler as ReactScheduler } from '@aldabil/react-scheduler' +import { Scheduler } from '@/components/scheduler/index' import { ProcessedEvent, RemoteQuery, SchedulerRef, -} from '@aldabil/react-scheduler/types' +} from '@/components/scheduler/types' import * as bookcarsTypes from ':bookcars-types' import * as helper from '@/common/helper' import * as BookingService from '@/services/BookingService' @@ -208,7 +208,7 @@ const VehicleScheduler = ( } return ( - :warning: **Notice**: This component uses `mui`/`emotion`/`date-fns`. if your project is not already using these libs, this component may not be suitable. + +## Installation + +If you plan to use `recurring` events in your scheduler, install `rrule` [package](https://www.npmjs.com/package/rrule) + +## Usage + +```jsx +import { Scheduler } from "@components/schduler/index"; +``` + +## Example + +```jsx + +``` + +### Scheduler Props + +All props are _optional_ +| Prop | Value | +|----------|-------------| +| height | number. Min height of table.
          _Default_: 600 +| view | string. Initial view to load. options: "week", "month", "day".
          _Default_: "week" (if it's not null) +| agenda | boolean. Activate agenda view +| alwaysShowAgendaDays | boolean. if true, day rows without events will be shown +| month | Object. Month view props.
          _default_:
          {
          weekDays: [0, 1, 2, 3, 4, 5],
          weekStartOn: 6,
          startHour: 9,
          endHour: 17,
          cellRenderer?:(props: CellProps) => JSX.Element,
          navigation: true,
          disableGoToDay: false
          }
          +| week | Object. Week view props.
          _default_:
          { 
          weekDays: [0, 1, 2, 3, 4, 5],
          weekStartOn: 6,
          startHour: 9,
          endHour: 17,
          step: 60,
          cellRenderer?:(props: CellProps) => JSX.Element,
          navigation: true,
          disableGoToDay: false
          }
          +| day | Object. Day view props.
          _default_:
          {
          startHour: 9,
          endHour: 17,
          step: 60,
          cellRenderer?:(props: CellProps) => JSX.Element,
          hourRenderer?:(hour: string) => JSX.Element,
          navigation: true
          }
          +| selectedDate | Date. Initial selected date.
          _Default_: `new Date()` +| navigation | boolean. Show/Hide top bar date navigation.
          _Default_: `true` +| navigationPickerProps | CalendarPickerProps for top bar date navigation. Ref [CalendarPicker API](https://mui.com/x/api/date-pickers/calendar-picker/#main-content) +| disableViewNavigator | boolean. Show/Hide top bar date View navigator.
          _Default_: `false` +| events | Array of ProcessedEvent.
          _Default_: []
          type ProcessedEvent = {
          event*id: number or string;
          title: string;
          subtitle?: string;
          start: Date;
          end: Date;
          disabled?: boolean;
          recurring: RRule;
          color?: string or "palette.path";
          textColor?: string or "palette.path";
          editable?: boolean;
          deletable?: boolean;
          draggable?: boolean;
          allDay?: boolean;
          agendaAvatar?: React.ReactElement \| string
          sx?: Mui sx prop
          }
          +| eventRenderer | Function(event:ProcessedEvent): JSX.Element.
          A function that overrides the event item render function, see demo \_Custom Event Renderer* below +| editable | boolean. If `true`, the scheduler cell click will not open the editor, and the event item will not show the edit button, this is applied to all events, and can be overridden in each event property, see `ProcessedEvent` type. +| deletable | boolean. Whether the event item will show the delete button, this is applied to all events, and can be overridden in each event property, see `ProcessedEvent` type. +| draggable | boolean. Whether activate drag&drop for the events, this is applied to all events, and can be overridden in each event property, see `ProcessedEvent` type. +| getRemoteEvents | Function(RemoteQuery). Return promise of array of events. Can be used as a callback to fetch events by parent component or fetch.
          type RemoteQuery = { 
          start: Date;
          end: Date;
          view: "day" \| "week" \| "month";
          }
          +| fields | Array of extra fields with configurations.
          Example:
           { 
          name: "description",
          type: "input" ,
          config: { label: "Description", required: true, min: 3, email: true, variant: "outlined", ....
          }
          +| loading | boolean. Loading state of the calendar table +| loadingComponent | Custom component to override the default `CircularProgress` +| onConfirm | Function(event, action). Return promise with the new added/edited event use with remote data.
          _action_: `add` | `edit` +| onDelete | Function(id) Return promise with the deleted event id to use with remote data. +| customEditor | Function(scheduler). Override editor modal.
          Provided prop _scheduler_ object with helper props:
          {
          state: state obj,
          close(): void
          loading(status: boolean): void
          edited?: ProcessedEvent
          onConfirm(event: ProcessedEvent, action:EventActions): void
          }
          +| customViewer | Function(event: ProcessedEvent, close: () => void). Used to render fully customized content of the event popper. If used, `viewerExtraComponent` & `viewerTitleComponent` will be ignored +| viewerExtraComponent | Function(fields, event) OR Component. Additional component in event viewer popper +| viewerTitleComponent | Function(event). Helper function to render custom title in event popper +| viewerSubtitleComponent | Function(event). Helper function to render custom subtitle in event popper +| disableViewer | boolean. If true, the viewer popover will be disabled globally +| resources | Array. Resources array to split event views with resources
          _Example_
          {
          assignee: 1,
          text: "User One",
          subtext: "Sales Manager",
          avatar: "https://picsum.photos/200/300",
          color: "#ab2d2d",
          }
          +| resourceFields | Object. Map the resources correct fields.
          _Example_:
          {
          idField: "admin*id",
          textField: "title",
          subTextField: "mobile",
          avatarField: "title",
          colorField: "background",
          }
          +| resourceHeaderComponent | Function(resource). Override header component of resource +| resourceViewMode | Display resources mode.
          \_Options*: `default` | `vertical` | `tabs` +| onResourceChange | Function(resource: Resource): void. Triggered when the resource tabs changes, only applicable when `resourceViewMode="tabs"` +| direction | string. Table direction. `rtl` | `ltr` +| dialogMaxWidth | Edito dialog maxWith. Ex: `lg` | `md` | `sm`... _Default_:`md` +| locale | Locale of date-fns. _Default_: `enUS` +| hourFormat | Hour format.
          _Options_: `12` | `24`. _Default_: `12` +| timeZone| String, time zone [IANA ID](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) +| translations | Object. Translations view props.
          _default_:
          {
          navigation: {
          month: "Month",
          week: "Week",
          day: "Day",
          today: "Today"
          agenda: "Agenda"
          },
          form: {
          addTitle: "Add Event",
          editTitle: "Edit Event",
          confirm: "Confirm",
          delete: "Delete",
          cancel: "Cancel"
          },
          event: {
          title: "Title",
          subtitle: "Subtitle",
          start: "Start",
          end: "End",
          allDay: "All Day"
          },
          validation: {
          required: "Required",
          invalidEmail: "Invalid Email",
          onlyNumbers: "Only Numbers Allowed",
          min: "Minimum {{min}} letters",
          max: "Maximum {{max}} letters"
          },
          moreEvents: "More...",
          noDataToDisplay: "No data to display",
          loading: "Loading..."
          }
          +| onEventDrop | Function(event: DragEvent, droppedOn: Date, updatedEvent: ProcessedEvent, originalEvent: ProcessedEvent). Return a promise, used to update remote data of the dropped event. Return an event to update state internally, or void if event state is managed within component +| onEventClick | Function(event: ProcessedEvent): void. Triggered when an event item is clicked +| onEventEdit | Function(event: ProcessedEvent): void. Triggered when an event item is being edited from the popover +| onCellClick | Function(start: Date, end: Date, resourceKey?: string, resourceVal?: string | number): void. Triggered when a cell in the grid is clicked +| onSelectedDateChange | Function(date: Date): void. Triggered when the `selectedDate` prop changes by navigation date picker or `today` button +| onViewChange | Function(view: View, agenda?: boolean): void. Triggered when navigation view changes +| stickyNavigation | If `true`, the navigation controller bar will be sticky +| onClickMore | Function(date: Date, goToDay: Function(date: Date): void): void. Triggered when the "More..." button is clicked, it receives the date and a `goToDay` function that shows a day view for a specfic date. + +### SchedulerRef + +Used to help manage and control the internal state of the `Scheduler` component from outside of `Scheduler` props, Example: + +```js +import { Scheduler } from "@aldabil/react-scheduler"; +import type { SchedulerRef } from "@aldabil/react-scheduler/types" + +const SomeComponent = () => { + const calendarRef = useRef(null); + + return +
          + + +
          + + +
          +}; +``` + +The `calendarRef` holds the entire internal state of the Scheduler component. Perhaps the most useful method inside the `calendarRef` is `handleState`, example: + +``` +calendarRef.current.scheduler.handleState(value, key); +``` + +consider looking inside `SchedulerRef` type to see all fields & methods available. + +### Demos + +- [Basic](https://codesandbox.io/p/sandbox/standard-x24pqk) +- [Remote Data](https://codesandbox.io/s/remote-data-j13ei) +- [Custom Fields](https://codesandbox.io/s/custom-fields-b2kbv) +- [Editor/Viewer Override](https://codesandbox.io/s/customeditor-tt2pf) +- [Resources/View Mode](https://codesandbox.io/s/resources-7wlcy) +- [Custom Cell Action](https://codesandbox.io/s/custom-cell-action-n02dv) +- [Custom Event Renderer](https://codesandbox.io/s/custom-event-renderer-rkf4xw) + +### Todos + +- [ ] Tests +- [x] Drag&Drop - partially +- [ ] Resizable +- [x] Recurring events - partially +- [x] Localization +- [x] Hour format 12 | 24 diff --git a/backend/src/components/scheduler/SchedulerComponent.tsx b/backend/src/components/scheduler/SchedulerComponent.tsx new file mode 100644 index 000000000..23aeccebc --- /dev/null +++ b/backend/src/components/scheduler/SchedulerComponent.tsx @@ -0,0 +1,75 @@ +import React, { forwardRef, useMemo } from 'react' +import { CircularProgress, Typography } from '@mui/material' +import { Week } from './views/Week' +import { Navigation } from './components/nav/Navigation' +import Editor from './views/Editor' +import { Month } from './views/Month' +import { Day } from './views/Day' +import { Table, Wrapper } from './styles/styles' +import useStore from './hooks/useStore' +import { SchedulerRef } from './types' +import { PositionProvider } from './positionManger/provider' + +const SchedulerComponent = forwardRef((_, ref) => { + const store = useStore() + const { view, dialog, loading, loadingComponent, resourceViewMode, resources, translations } = store + + const Views = useMemo(() => { + switch (view) { + case 'month': + return + case 'week': + return + case 'day': + return + default: + return '' + } + }, [view]) + + const LoadingComp = useMemo(() => ( +
          + {loadingComponent || ( +
          + + + {translations.loading} + +
          + )} +
          + ), [loadingComponent, translations.loading]) + + return ( + { + const calendarRef = ref as any + if (calendarRef) { + calendarRef.current = { + el, + scheduler: store, + } + } + }} + > + {loading ? LoadingComp : null} + + 1 ? 'auto' : undefined, + flexDirection: resourceViewMode === 'vertical' ? 'column' : undefined, + }} + data-testid="grid" + > + {Views} +
          + {dialog && } +
          + ) +}) + +export default SchedulerComponent diff --git a/backend/src/components/scheduler/components/common/Cell.tsx b/backend/src/components/scheduler/components/common/Cell.tsx new file mode 100644 index 000000000..82613a603 --- /dev/null +++ b/backend/src/components/scheduler/components/common/Cell.tsx @@ -0,0 +1,53 @@ +import React, { ReactNode } from 'react' +import { Button } from '@mui/material' +import { useCellAttributes } from '../../hooks/useCellAttributes' +import { CellRenderedProps } from '../../types' + +interface CellProps { + day: Date; + start: Date; + height: number; + end: Date; + resourceKey: string; + resourceVal: string | number; + cellRenderer?(props: CellRenderedProps): ReactNode; + children?: ReactNode; +} + +const Cell = ({ + day, + start, + end, + resourceKey, + resourceVal, + cellRenderer, + height, + children, +}: CellProps) => { + const props = useCellAttributes({ start, end, resourceKey, resourceVal }) + + if (cellRenderer) { + return cellRenderer({ + day, + start, + end, + height, + ...props, + }) + } + + return ( + + ) +} + +export default Cell diff --git a/backend/src/components/scheduler/components/common/LocaleArrow.tsx b/backend/src/components/scheduler/components/common/LocaleArrow.tsx new file mode 100644 index 000000000..d04c5be29 --- /dev/null +++ b/backend/src/components/scheduler/components/common/LocaleArrow.tsx @@ -0,0 +1,38 @@ +import React, { MouseEvent } from 'react' +import NavigateBeforeRoundedIcon from '@mui/icons-material/NavigateBeforeRounded' +import NavigateNextRoundedIcon from '@mui/icons-material/NavigateNextRounded' +import { IconButton, IconButtonProps } from '@mui/material' +import useStore from '../../hooks/useStore' + +interface LocaleArrowProps extends Omit { + type: 'prev' | 'next'; + onClick?(e?: MouseEvent): void; +} +const LocaleArrow = ({ type, onClick, ...props }: LocaleArrowProps) => { + const { direction } = useStore() + + let Arrow = NavigateNextRoundedIcon + if (type === 'prev') { + Arrow = direction === 'rtl' ? NavigateNextRoundedIcon : NavigateBeforeRoundedIcon + } else if (type === 'next') { + Arrow = direction === 'rtl' ? NavigateBeforeRoundedIcon : NavigateNextRoundedIcon + } + + return ( + { + e.preventDefault() + if (onClick) { + onClick() + } + }} + {...props} + > + + + ) +} + +export { LocaleArrow } diff --git a/backend/src/components/scheduler/components/common/ResourceHeader.tsx b/backend/src/components/scheduler/components/common/ResourceHeader.tsx new file mode 100644 index 000000000..0500c301e --- /dev/null +++ b/backend/src/components/scheduler/components/common/ResourceHeader.tsx @@ -0,0 +1,74 @@ +import React from 'react' +import { + Avatar, + ListItem, + ListItemAvatar, + ListItemText, + Typography, + useTheme, +} from '@mui/material' +import { DefaultResource } from '../../types' +import useStore from '../../hooks/useStore' + +interface ResourceHeaderProps { + resource: DefaultResource; +} +const ResourceHeader = ({ resource }: ResourceHeaderProps) => { + const { resourceHeaderComponent, resourceFields, direction, resourceViewMode } = useStore() + const theme = useTheme() + + const text = resource[resourceFields.textField] + const subtext = resource[resourceFields.subTextField || ''] + const avatar = resource[resourceFields.avatarField || ''] + const color = resource[resourceFields.colorField || ''] + + if (resourceHeaderComponent instanceof Function) { + return resourceHeaderComponent(resource) + } + + return ( + + + + + + {text} + + )} + secondary={( + + {subtext} + + )} + /> + + ) +} + +export { ResourceHeader } diff --git a/backend/src/components/scheduler/components/common/Tabs.tsx b/backend/src/components/scheduler/components/common/Tabs.tsx new file mode 100644 index 000000000..41d0fda99 --- /dev/null +++ b/backend/src/components/scheduler/components/common/Tabs.tsx @@ -0,0 +1,117 @@ +import React, { CSSProperties, ReactNode } from 'react' +import { Tabs, Tab } from '@mui/material' +import { styled } from '@mui/material/styles' +import { Theme } from '@mui/system' + +interface TabPanelProps { + value: string | number; + index: string | number; + children: React.ReactNode; +} +const TabPanel = (props: TabPanelProps) => { + const { children, value, index } = props + return value === index ? <>{children} : <> +} + +function a11yProps(index: string | number) { + return { + id: `scrollable-auto-tab-${index}`, + 'aria-controls': `scrollable-auto-tabpanel-${index}`, + } +} + +const StyledTaps = styled('div')(({ theme }: { theme: Theme }) => ({ + flexGrow: 1, + width: '100%', + backgroundColor: theme.palette.background.paper, + alignSelf: 'center', + '& .tabs': { + borderColor: theme.palette.grey[300], + borderStyle: 'solid', + borderWidth: 1, + '& button.MuiTab-root': { + borderColor: theme.palette.grey[300], + borderRightStyle: 'solid', + borderWidth: 1, + }, + }, + '& .primary': { + background: theme.palette.primary.main, + }, + '& .secondary': { + background: theme.palette.secondary.main, + }, + '& .error': { + background: theme.palette.error.main, + }, + '& .info': { + background: theme.palette.info.dark, + }, + '& .text_primary': { + color: theme.palette.primary.main, + }, + '& .text_secondary': { + color: theme.palette.secondary.main, + }, + '& .text_error': { + color: theme.palette.error.main, + }, + '& .text_info': { + color: theme.palette.info.dark, + }, +})) + +export type ButtonTabProps = { + id: string | number; + label: string | ReactNode; + component: ReactNode; +}; +interface ButtonTabsProps { + tabs: ButtonTabProps[]; + tab: string | number; + setTab(tab: string | number): void; + variant?: 'scrollable' | 'standard' | 'fullWidth'; + indicator?: 'primary' | 'secondary' | 'info' | 'error'; + style?: CSSProperties; +} + +const ButtonTabs = ({ + tabs, + variant = 'scrollable', + tab, + setTab, + indicator = 'primary', + style, +}: ButtonTabsProps) => ( + + + {tabs.map((_tab: ButtonTabProps, i: number) => ( + setTab(_tab.id)} + onDragEnter={() => setTab(_tab.id)} + /> + ))} + + {tabs.map( + (t: ButtonTabProps) => + t.component && ( + + {t.component} + + ) + )} + + ) + +export { ButtonTabs } diff --git a/backend/src/components/scheduler/components/common/TodayTypo.tsx b/backend/src/components/scheduler/components/common/TodayTypo.tsx new file mode 100644 index 000000000..24dcb592d --- /dev/null +++ b/backend/src/components/scheduler/components/common/TodayTypo.tsx @@ -0,0 +1,45 @@ +import React from 'react' +import { Typography } from '@mui/material' +import { format, Locale } from 'date-fns' +import { isTimeZonedToday } from '../../helpers/generals' +import useStore from '../../hooks/useStore' + +interface TodayTypoProps { + date: Date; + onClick?(day: Date): void; + locale: Locale; +} + +const TodayTypo = ({ date, onClick, locale }: TodayTypoProps) => { + const { timeZone } = useStore() + const today = isTimeZonedToday({ dateLeft: date, timeZone }) + + return ( +
          + { + e.stopPropagation() + if (onClick) onClick(date) + }} + > + {format(date, 'dd', { locale })} + + + {format(date, 'eee', { locale })} + +
          + ) +} + +export default TodayTypo diff --git a/backend/src/components/scheduler/components/common/WithResources.tsx b/backend/src/components/scheduler/components/common/WithResources.tsx new file mode 100644 index 000000000..5a072751a --- /dev/null +++ b/backend/src/components/scheduler/components/common/WithResources.tsx @@ -0,0 +1,95 @@ +import React, { useMemo } from 'react' +import { Box, useTheme } from '@mui/material' +import { DefaultResource } from '../../types' +import { ResourceHeader } from './ResourceHeader' +import { ButtonTabProps, ButtonTabs } from './Tabs' +import useStore from '../../hooks/useStore' + +interface WithResourcesProps { + renderChildren(resource: DefaultResource): React.ReactNode; +} + +const ResourcesTabTables = ({ renderChildren }: WithResourcesProps) => { + const { resources, resourceFields, selectedResource, handleState, onResourceChange } = useStore() + + const tabs: ButtonTabProps[] = resources.map((res) => ({ + id: res[resourceFields.idField], + label: , + component: <>{renderChildren(res)}, + })) + + const setTab = (tab: DefaultResource['assignee']) => { + handleState(tab, 'selectedResource') + if (typeof onResourceChange === 'function') { + const selected = resources.find((re) => re[resourceFields.idField] === tab) + if (selected) { + onResourceChange(selected) + } + } + } + + const currentTabSafeId = useMemo(() => { + const firstId = resources[0][resourceFields.idField] + if (!selectedResource) { + return firstId + } + // Make sure current selected id is within the resources array + const idx = resources.findIndex((re) => re[resourceFields.idField] === selectedResource) + if (idx < 0) { + return firstId + } + + return selectedResource + }, [resources, selectedResource, resourceFields.idField]) + + return ( + + ) +} + +const WithResources = ({ renderChildren }: WithResourcesProps) => { + const { resources, resourceFields, resourceViewMode } = useStore() + const theme = useTheme() + + if (resourceViewMode === 'tabs') { + return + } if (resourceViewMode === 'vertical') { + return ( + <> + {resources.map((res: DefaultResource) => ( + + + + + + {renderChildren(res)} + + + ))} + + ) + } + return ( + <> + {resources.map((res: DefaultResource) => ( +
          + + {renderChildren(res)} +
          + ))} + + ) +} + +export { WithResources } diff --git a/backend/src/components/scheduler/components/events/Actions.tsx b/backend/src/components/scheduler/components/events/Actions.tsx new file mode 100644 index 000000000..8421b73a0 --- /dev/null +++ b/backend/src/components/scheduler/components/events/Actions.tsx @@ -0,0 +1,66 @@ +import React, { useState } from 'react' +import DeleteRounded from '@mui/icons-material/DeleteRounded' +import EditRounded from '@mui/icons-material/EditRounded' +import { Button, Grow, IconButton, Slide } from '@mui/material' +import { EventActions as Actions } from '../../styles/styles' +import { ProcessedEvent } from '../../types' +import useStore from '../../hooks/useStore' +import useEventPermissions from '../../hooks/useEventPermissions' + +interface Props { + event: ProcessedEvent; + onDelete(): void; + onEdit(): void; +} + +const EventActions = ({ event, onDelete, onEdit }: Props) => { + const { translations, direction } = useStore() + const [deleteConfirm, setDeleteConfirm] = useState(false) + + const handleDelete = () => { + if (!deleteConfirm) { + setDeleteConfirm(true) + return + } + onDelete() + } + + const { canEdit, canDelete } = useEventPermissions(event) + + return ( + + +
          + {canEdit && ( + + + + )} + {canDelete && ( + + + + )} +
          +
          + +
          + + +
          +
          +
          + ) +} + +export default EventActions diff --git a/backend/src/components/scheduler/components/events/AgendaEventsList.tsx b/backend/src/components/scheduler/components/events/AgendaEventsList.tsx new file mode 100644 index 000000000..14802cf2e --- /dev/null +++ b/backend/src/components/scheduler/components/events/AgendaEventsList.tsx @@ -0,0 +1,109 @@ +import React, { Fragment, MouseEvent, useState } from 'react' +import { + useTheme, + List, + ListItemButton, + ListItemAvatar, + Avatar, + ListItemText, +} from '@mui/material' +import { format } from 'date-fns' +import { ProcessedEvent } from '../../types' +import { getHourFormat, isTimeZonedToday } from '../../helpers/generals' +import useStore from '../../hooks/useStore' +import EventItemPopover from './EventItemPopover' + +interface AgendaEventsListProps { + day: Date; + events: ProcessedEvent[]; +} + +const AgendaEventsList = ({ day, events }: AgendaEventsListProps) => { + const [anchorEl, setAnchorEl] = useState(null) + const [selectedEvent, setSelectedEvent] = useState() + const [deleteConfirm, setDeleteConfirm] = useState(false) + const { locale, hourFormat, eventRenderer, onEventClick, timeZone, disableViewer } = useStore() + const theme = useTheme() + const hFormat = getHourFormat(hourFormat) + + const triggerViewer = (el?: MouseEvent) => { + if (!el?.currentTarget && deleteConfirm) { + setDeleteConfirm(false) + } + setAnchorEl(el?.currentTarget || null) + } + + return ( + <> + + {events.map((event) => { + const startIsToday = isTimeZonedToday({ + dateLeft: event.start, + dateRight: day, + timeZone, + }) + const startFormat = startIsToday ? hFormat : `MMM d, ${hFormat}` + const startDate = format(event.start, startFormat, { + locale, + }) + const endIsToday = isTimeZonedToday({ dateLeft: event.end, dateRight: day, timeZone }) + + const endFormat = endIsToday ? hFormat : `MMM d, ${hFormat}` + const endDate = format(event.end, endFormat, { + locale, + }) + + if (typeof eventRenderer === 'function') { + return eventRenderer({ event, onClick: triggerViewer }) + } + + return ( + { + e.preventDefault() + e.stopPropagation() + if (!disableViewer) { + triggerViewer(e) + } + setSelectedEvent(event) + if (typeof onEventClick === 'function') { + onEventClick(event) + } + }} + > + + + {event.agendaAvatar || ' '} + + + + + ) + })} + + + {/* Viewer */} + {selectedEvent && ( + + )} + + ) +} + +export default AgendaEventsList diff --git a/backend/src/components/scheduler/components/events/CurrentTimeBar.tsx b/backend/src/components/scheduler/components/events/CurrentTimeBar.tsx new file mode 100644 index 000000000..6b0556add --- /dev/null +++ b/backend/src/components/scheduler/components/events/CurrentTimeBar.tsx @@ -0,0 +1,49 @@ +import React, { useEffect, useState } from 'react' +import { differenceInMinutes, set } from 'date-fns' +import { BORDER_HEIGHT } from '../../helpers/constants' +import { getTimeZonedDate } from '../../helpers/generals' +import { TimeIndicatorBar } from '../../styles/styles' + +interface CurrentTimeBarProps { + startHour: number; + step: number; + minuteHeight: number; + timeZone?: string; + zIndex?: number; +} + +function calculateTop({ startHour, step, minuteHeight, timeZone }: CurrentTimeBarProps): number { + const now = getTimeZonedDate(new Date(), timeZone) + + const minutesFromTop = differenceInMinutes(now, set(now, { hours: startHour, minutes: 0 })) + const topSpace = minutesFromTop * minuteHeight + const slotsFromTop = minutesFromTop / step + const borderFactor = slotsFromTop + BORDER_HEIGHT + const top = topSpace + borderFactor + + return top +} + +const CurrentTimeBar = (props: CurrentTimeBarProps) => { + const [top, setTop] = useState(calculateTop(props)) + const { startHour, step, minuteHeight, timeZone, zIndex } = props + + useEffect(() => { + const calcProps = { startHour, step, minuteHeight, timeZone } + setTop(calculateTop(calcProps)) + const interval = setInterval(() => setTop(calculateTop(calcProps)), 60 * 1000) + return () => clearInterval(interval) + }, [startHour, step, minuteHeight, timeZone]) + + // Prevent showing bar on top of days/header + if (top < 0) return null + + return ( + +
          +
          + + ) +} + +export default CurrentTimeBar diff --git a/backend/src/components/scheduler/components/events/EmptyAgenda.tsx b/backend/src/components/scheduler/components/events/EmptyAgenda.tsx new file mode 100644 index 000000000..e746230ec --- /dev/null +++ b/backend/src/components/scheduler/components/events/EmptyAgenda.tsx @@ -0,0 +1,25 @@ +import React, { Typography } from '@mui/material' +import { AgendaDiv } from '../../styles/styles' +import useStore from '../../hooks/useStore' + +const EmptyAgenda = () => { + const { height, translations } = useStore() + return ( + +
          + {translations.noDataToDisplay} +
          +
          + ) +} + +export default EmptyAgenda diff --git a/backend/src/components/scheduler/components/events/EventItem.tsx b/backend/src/components/scheduler/components/events/EventItem.tsx new file mode 100644 index 000000000..31f032284 --- /dev/null +++ b/backend/src/components/scheduler/components/events/EventItem.tsx @@ -0,0 +1,151 @@ +import React, { Fragment, MouseEvent, useMemo, useState } from 'react' +import { Typography, ButtonBase, useTheme } from '@mui/material' +import { format } from 'date-fns' +import ArrowRightRoundedIcon from '@mui/icons-material/ArrowRightRounded' +import ArrowLeftRoundedIcon from '@mui/icons-material/ArrowLeftRounded' +import { ProcessedEvent } from '../../types' +import { EventItemPaper } from '../../styles/styles' +import { differenceInDaysOmitTime, getHourFormat } from '../../helpers/generals' +import useStore from '../../hooks/useStore' +import useDragAttributes from '../../hooks/useDragAttributes' +import EventItemPopover from './EventItemPopover' +import useEventPermissions from '../../hooks/useEventPermissions' + +interface EventItemProps { + event: ProcessedEvent; + multiday?: boolean; + hasPrev?: boolean; + hasNext?: boolean; + showdate?: boolean; +} + +const EventItem = ({ event, multiday, hasPrev, hasNext, showdate = true }: EventItemProps) => { + const { direction, locale, hourFormat, eventRenderer, onEventClick, view, disableViewer } = useStore() + const dragProps = useDragAttributes(event) + const [anchorEl, setAnchorEl] = useState(null) + const [deleteConfirm, setDeleteConfirm] = useState(false) + const theme = useTheme() + const hFormat = getHourFormat(hourFormat) + + const NextArrow = direction === 'rtl' ? ArrowLeftRoundedIcon : ArrowRightRoundedIcon + const PrevArrow = direction === 'rtl' ? ArrowRightRoundedIcon : ArrowLeftRoundedIcon + const hideDates = differenceInDaysOmitTime(event.start, event.end) <= 0 && event.allDay + + const { canDrag } = useEventPermissions(event) + + const triggerViewer = (el?: MouseEvent) => { + if (!el?.currentTarget && deleteConfirm) { + setDeleteConfirm(false) + } + setAnchorEl(el?.currentTarget || null) + } + + const renderEvent = useMemo(() => { + // Check if has custom render event method + // only applicable to non-multiday events and not in month-view + if (typeof eventRenderer === 'function' && !multiday && view !== 'month') { + const custom = eventRenderer({ event, onClick: triggerViewer, ...dragProps }) + if (custom) { + return ( + + {custom} + + ) + } + } + + let item = ( +
          + + {event.title} + + {event.subtitle && ( + + {event.subtitle} + + )} + {showdate && ( + + {`${format(event.start, hFormat, { + locale, + })} - ${format(event.end, hFormat, { locale })}`} + + )} +
          + ) + if (multiday) { + item = ( +
          + + {hasPrev ? ( + + ) : ( + showdate && !hideDates && format(event.start, hFormat, { locale }) + )} + + + {event.title} + + + {hasNext ? ( + + ) : ( + showdate && !hideDates && format(event.end, hFormat, { locale }) + )} + +
          + ) + } + return ( + + { + e.preventDefault() + e.stopPropagation() + if (!disableViewer) { + triggerViewer(e) + } + if (typeof onEventClick === 'function') { + onEventClick(event) + } + }} + focusRipple + tabIndex={disableViewer ? -1 : 0} + disableRipple={disableViewer} + disabled={event.disabled} + > +
          + {item} +
          +
          +
          + ) + // eslint-disable-next-line + }, [hasPrev, hasNext, event, canDrag, locale, theme.palette]); + + return ( + <> + {renderEvent} + + {/* Viewer */} + + + ) +} + +export default EventItem diff --git a/backend/src/components/scheduler/components/events/EventItemPopover.tsx b/backend/src/components/scheduler/components/events/EventItemPopover.tsx new file mode 100644 index 000000000..3566446c8 --- /dev/null +++ b/backend/src/components/scheduler/components/events/EventItemPopover.tsx @@ -0,0 +1,175 @@ +import React, { MouseEvent } from 'react' +import { Box, IconButton, Popover, Typography, useTheme } from '@mui/material' +import EventNoteRoundedIcon from '@mui/icons-material/EventNoteRounded' +import ClearRoundedIcon from '@mui/icons-material/ClearRounded' +import SupervisorAccountRoundedIcon from '@mui/icons-material/SupervisorAccountRounded' +import { format } from 'date-fns' +import useStore from '../../hooks/useStore' +import { ProcessedEvent } from '../../types' +import { PopperInner } from '../../styles/styles' +import EventActions from './Actions' +import { differenceInDaysOmitTime, getHourFormat } from '../../helpers/generals' + +type Props = { + event: ProcessedEvent; + anchorEl: Element | null; + onTriggerViewer: (el?: MouseEvent) => void; +}; + +const EventItemPopover = ({ anchorEl, event, onTriggerViewer }: Props) => { + const { + triggerDialog, + onDelete, + events, + handleState, + triggerLoading, + customViewer, + viewerExtraComponent, + fields, + resources, + resourceFields, + locale, + viewerTitleComponent, + viewerSubtitleComponent, + hourFormat, + translations, + onEventEdit, + } = useStore() + const theme = useTheme() + const hideDates = differenceInDaysOmitTime(event.start, event.end) <= 0 && event.allDay + const hFormat = getHourFormat(hourFormat) + const idKey = resourceFields.idField + const hasResource = resources.filter((res) => + (Array.isArray(event[idKey]) ? event[idKey].includes(res[idKey]) : res[idKey] === event[idKey])) + + const handleDelete = async () => { + try { + triggerLoading(true) + let deletedId = event.event_id + // Trigger custom/remote when provided + if (onDelete) { + const remoteId = await onDelete(deletedId) + if (remoteId) { + deletedId = remoteId + } else { + deletedId = '' + } + } + if (deletedId) { + onTriggerViewer() + const updatedEvents = events.filter((e) => e.event_id !== deletedId) + handleState(updatedEvents, 'events') + } + } catch (error) { + console.error(error) + } finally { + triggerLoading(false) + } + } + + return ( + { + onTriggerViewer() + }} + anchorOrigin={{ + vertical: 'center', + horizontal: 'center', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'center', + }} + onClick={(e) => { + e.stopPropagation() + }} + > + {typeof customViewer === 'function' ? ( + customViewer(event, () => onTriggerViewer()) + ) : ( + + +
          +
          + { + onTriggerViewer() + }} + > + + +
          + { + onTriggerViewer() + triggerDialog(true, event) + + if (onEventEdit && typeof onEventEdit === 'function') { + onEventEdit(event) + } + }} + /> +
          + {viewerTitleComponent instanceof Function ? ( + viewerTitleComponent(event) + ) : ( + + {event.title} + + )} +
          +
          + + + {hideDates + ? translations.event.allDay + : `${format(event.start, `dd MMMM yyyy ${hFormat}`, { + locale, + })} - ${format(event.end, `dd MMMM yyyy ${hFormat}`, { + locale, + })}`} + + {viewerSubtitleComponent instanceof Function ? ( + viewerSubtitleComponent(event) + ) : ( + + {event.subtitle} + + )} + {hasResource.length > 0 && ( + + + {hasResource.map((res) => res[resourceFields.textField]).join(', ')} + + )} + {viewerExtraComponent instanceof Function + ? viewerExtraComponent(fields, event) + : viewerExtraComponent} +
          +
          + )} +
          + ) +} + +export default EventItemPopover diff --git a/backend/src/components/scheduler/components/events/MonthEvents.tsx b/backend/src/components/scheduler/components/events/MonthEvents.tsx new file mode 100644 index 000000000..4a659b6be --- /dev/null +++ b/backend/src/components/scheduler/components/events/MonthEvents.tsx @@ -0,0 +1,141 @@ +import React, { Fragment, ReactNode, useMemo } from 'react' +import { + closestTo, + isBefore, + startOfWeek, + differenceInDays, + differenceInCalendarWeeks, + format, +} from 'date-fns' +import { Typography } from '@mui/material' +import { ProcessedEvent } from '../../types' +import EventItem from './EventItem' +import { + MONTH_BAR_HEIGHT, + MONTH_NUMBER_HEIGHT, + MULTI_DAY_EVENT_HEIGHT, +} from '../../helpers/constants' +import { convertEventTimeZone, differenceInDaysOmitTime, sortEventsByTheEarliest } from '../../helpers/generals' +import useStore from '../../hooks/useStore' +import usePosition from '../../positionManger/usePosition' + +interface MonthEventProps { + events: ProcessedEvent[]; + resourceId?: string; + today: Date; + eachWeekStart: Date[]; + eachFirstDayInCalcRow: Date | null; + daysList: Date[]; + onViewMore(day: Date): void; + cellHeight: number; +} + +const MonthEvents = ({ + events: eventsFromProps, + resourceId, + today, + eachWeekStart, + eachFirstDayInCalcRow, + daysList, + onViewMore, + cellHeight, +}: MonthEventProps) => { + const LIMIT = Math.round((cellHeight - MONTH_NUMBER_HEIGHT) / MULTI_DAY_EVENT_HEIGHT - 1) + const { translations, month, locale, timeZone } = useStore() + const { renderedSlots } = usePosition() + + const renderEvents = useMemo(() => { + const elements: ReactNode[] = [] + + const events = sortEventsByTheEarliest(Array.from(eventsFromProps)) + for (let i = 0; i < Math.min(events.length, LIMIT + 1); i += 1) { + const event = convertEventTimeZone(events[i], timeZone) + const fromPrevWeek = !!eachFirstDayInCalcRow && isBefore(event.start, eachFirstDayInCalcRow) + const start = fromPrevWeek && eachFirstDayInCalcRow ? eachFirstDayInCalcRow : event.start + let eventLength = differenceInDaysOmitTime(start, event.end) + 1 + + const toNextWeek = differenceInCalendarWeeks(event.end, start, { + weekStartsOn: month?.weekStartOn, + locale, + }) > 0 + + if (toNextWeek) { + // Rethink it + const NotAccurateWeekStart = startOfWeek(event.start, { + weekStartsOn: month?.weekStartOn, + locale, + }) + const closestStart = closestTo(NotAccurateWeekStart, eachWeekStart) + if (closestStart) { + eventLength = daysList.length + - (!eachFirstDayInCalcRow ? differenceInDays(event.start, closestStart) : 0) + } + } + + const day = format(today, 'yyyy-MM-dd') + const rendered = renderedSlots?.[resourceId || 'all']?.[day] + const position = rendered?.[event.event_id] || 0 + + const topSpace = Math.min(position, LIMIT) * MULTI_DAY_EVENT_HEIGHT + MONTH_NUMBER_HEIGHT + + if (position >= LIMIT) { + elements.push( + { + e.stopPropagation() + // onViewMore(event.start) + onViewMore(today) + }} + > + {`${Math.abs(events.length - i)} ${translations.moreEvents}`} + + ) + break + } + + elements.push( +
          + 0} + hasPrev={fromPrevWeek} + hasNext={toNextWeek} + /> +
          + ) + } + + return elements + }, [ + resourceId, + renderedSlots, + eventsFromProps, + LIMIT, + eachFirstDayInCalcRow, + month?.weekStartOn, + locale, + today, + eachWeekStart, + daysList.length, + translations.moreEvents, + onViewMore, + timeZone, + ]) + + return <>{renderEvents} +} + +export default MonthEvents diff --git a/backend/src/components/scheduler/components/events/TodayEvents.tsx b/backend/src/components/scheduler/components/events/TodayEvents.tsx new file mode 100644 index 000000000..56c1d30f7 --- /dev/null +++ b/backend/src/components/scheduler/components/events/TodayEvents.tsx @@ -0,0 +1,91 @@ +import React, { Fragment } from 'react' +import { differenceInMinutes } from 'date-fns' +import { BORDER_HEIGHT } from '../../helpers/constants' +import { isTimeZonedToday, traversCrossingEvents } from '../../helpers/generals' +import { ProcessedEvent } from '../../types' +import CurrentTimeBar from './CurrentTimeBar' +import EventItem from './EventItem' + +interface TodayEventsProps { + todayEvents: ProcessedEvent[]; + today: Date; + startHour: number; + endHour: number; + step: number; + minuteHeight: number; + direction: 'rtl' | 'ltr'; + timeZone?: string; +} +const TodayEvents = ({ + todayEvents, + today, + startHour, + endHour, + step, + minuteHeight, + direction, + timeZone, +}: TodayEventsProps) => { + const crossingIds: Array = [] + + return ( + <> + {isTimeZonedToday({ dateLeft: today, timeZone }) && ( + + )} + + {todayEvents.map((event, i) => { + const maxHeight = (endHour * 60 - startHour * 60) * minuteHeight + const eventHeight = differenceInMinutes(event.end, event.start) * minuteHeight + const height = Math.min(eventHeight, maxHeight) - BORDER_HEIGHT + + const calendarStartInMins = startHour * 60 + const eventStartInMins = event.start.getHours() * 60 + event.start.getMinutes() + const minituesFromTop = Math.max(eventStartInMins - calendarStartInMins, 0) + + const topSpace = minituesFromTop * minuteHeight + /** Add border factor to height of each slot */ + const slots = height / 60 + const heightBorderFactor = slots * BORDER_HEIGHT + + /** Calculate top space */ + const slotsFromTop = minituesFromTop / step + const top = topSpace + slotsFromTop + + const crossingEvents = traversCrossingEvents(todayEvents, event) + const alreadyRendered = crossingEvents.filter((e) => crossingIds.includes(e.event_id)) + crossingIds.push(event.event_id) + + return ( +
          0 + ? `calc(100% - ${100 - 98 / (alreadyRendered.length + 1)}%)` + : '98%', // Leave some space to click cell + zIndex: todayEvents.length + i, + [direction === 'rtl' ? 'right' : 'left']: + alreadyRendered.length > 0 + ? `${(100 / (crossingEvents.length + 1)) * alreadyRendered.length}%` + : '', + }} + > + +
          + ) + })} + + ) +} + +export default TodayEvents diff --git a/backend/src/components/scheduler/components/hoc/DateProvider.tsx b/backend/src/components/scheduler/components/hoc/DateProvider.tsx new file mode 100644 index 000000000..5988cd2bd --- /dev/null +++ b/backend/src/components/scheduler/components/hoc/DateProvider.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3' +import useStore from '../../hooks/useStore' + +interface AuxProps { + children: React.ReactNode; +} + +const DateProvider = ({ children }: AuxProps) => { + const { locale } = useStore() + + return ( + + {children} + + ) +} + +export default DateProvider diff --git a/backend/src/components/scheduler/components/inputs/DatePicker.tsx b/backend/src/components/scheduler/components/inputs/DatePicker.tsx new file mode 100644 index 000000000..bef693795 --- /dev/null +++ b/backend/src/components/scheduler/components/inputs/DatePicker.tsx @@ -0,0 +1,89 @@ +import React, { useEffect, useState } from 'react' +import { DatePicker } from '@mui/x-date-pickers/DatePicker' +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker' +import DateProvider from '../hoc/DateProvider' +import useStore from '../../hooks/useStore' + +interface EditorDatePickerProps { + type?: 'date' | 'datetime'; + label?: string; + variant?: 'standard' | 'filled' | 'outlined'; + value: Date | string; + name: string; + onChange(name: string, date: Date): void; + error?: boolean; + errMsg?: string; + touched?: boolean; + required?: boolean; +} + +const EditorDatePicker = ({ + type = 'datetime', + value, + label, + name, + onChange, + variant = 'outlined', + error, + errMsg, + touched, + required, +}: EditorDatePickerProps) => { + const { translations } = useStore() + const [state, setState] = useState({ + touched: false, + valid: !!value, + errorMsg: errMsg || (required + ? translations?.validation?.required || 'Required' + : undefined), + }) + + const Picker = type === 'date' ? DatePicker : DateTimePicker + + const hasError = state.touched && (error || !state.valid) + + const handleChange = (_value: string | Date) => { + const isValidDate = !Number.isNaN(Date.parse(_value as string)) + const val = typeof _value === 'string' && isValidDate ? new Date(_value) : _value + let isValid = true + let errorMsg = errMsg + if (required && !val) { + isValid = false + errorMsg = errMsg || translations?.validation?.required || 'Required' + } + + setState((prev) => ({ ...prev, touched: true, valid: isValid, errorMsg })) + + onChange(name, val as Date) + } + + useEffect(() => { + if (touched) { + handleChange(value) + } + // eslint-disable-next-line + }, [touched]) + + return ( + + { + handleChange(e as Date) + }} + minutesStep={5} + slotProps={{ + textField: { + variant, + helperText: hasError && state.errorMsg, + error: hasError, + fullWidth: true, + }, + }} + /> + + ) +} + +export { EditorDatePicker } diff --git a/backend/src/components/scheduler/components/inputs/Input.tsx b/backend/src/components/scheduler/components/inputs/Input.tsx new file mode 100644 index 000000000..9d009623f --- /dev/null +++ b/backend/src/components/scheduler/components/inputs/Input.tsx @@ -0,0 +1,108 @@ +import React, { useState, useEffect } from 'react' +import { TextField, Typography } from '@mui/material' +import useStore from '../../hooks/useStore' + +interface EditorInputProps { + variant?: 'standard' | 'filled' | 'outlined'; + label?: string; + placeholder?: string; + required?: boolean; + min?: number; + max?: number; + email?: boolean; + decimal?: boolean; + disabled?: boolean; + multiline?: boolean; + rows?: number; + value: string; + name: string; + onChange(name: string, value: string, isValid: boolean): void; + touched?: boolean; +} + +const EditorInput = ({ + variant = 'outlined', + label, + placeholder, + value, + name, + required, + min, + max, + email, + decimal, + onChange, + disabled, + multiline, + rows, + touched, +}: EditorInputProps) => { + const [state, setState] = useState({ + touched: false, + valid: false, + errorMsg: '', + }) + const { translations } = useStore() + + const handleChange = (_value: string) => { + const val = _value + let isValid = true + let errorMsg = '' + if (email) { + const reg = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + isValid = reg.test(val) && isValid + errorMsg = translations?.validation?.invalidEmail || 'Invalid Email' + } + if (decimal) { + const reg = /^[0-9]+(\.[0-9]*)?$/ + isValid = reg.test(val) && isValid + errorMsg = translations?.validation?.onlyNumbers || 'Only Numbers Allowed' + } + if (min && `${val}`.trim().length < min) { + isValid = false + errorMsg = typeof translations?.validation?.min === 'function' + ? translations?.validation?.min(min) + : translations?.validation?.min || `Minimum ${min} letters` + } + if (max && `${val}`.trim().length > max) { + isValid = false + errorMsg = typeof translations?.validation?.max === 'function' + ? translations?.validation?.max(max) + : translations?.validation?.max || `Maximum ${max} letters` + } + if (required && `${val}`.trim().length <= 0) { + isValid = false + errorMsg = translations?.validation?.required || 'Required' + } + setState({ touched: true, valid: isValid, errorMsg }) + onChange(name, val, isValid) + } + + useEffect(() => { + if (touched) { + handleChange(value) + } + // eslint-disable-next-line + }, [touched]); + + return ( + {`${label} ${required ? '*' : ''}`}} + value={value} + name={name} + onChange={(e) => handleChange(e.target.value)} + disabled={disabled} + error={state.touched && !state.valid} + helperText={state.touched && !state.valid && state.errorMsg} + multiline={multiline} + rows={rows} + style={{ width: '100%' }} + InputProps={{ + placeholder: placeholder || '', + }} + /> + ) +} + +export { EditorInput } diff --git a/backend/src/components/scheduler/components/inputs/SelectInput.tsx b/backend/src/components/scheduler/components/inputs/SelectInput.tsx new file mode 100644 index 000000000..3196729c7 --- /dev/null +++ b/backend/src/components/scheduler/components/inputs/SelectInput.tsx @@ -0,0 +1,157 @@ +import React, { useState, useEffect } from 'react' +import { + FormControl, + FormHelperText, + MenuItem, + Checkbox, + useTheme, + Chip, + Typography, + CircularProgress, + InputLabel, + Select, +} from '@mui/material' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import useStore from '../../hooks/useStore' + +export type SelectOption = { + id: string | number; + text: string; + value: any; +}; +interface EditorSelectProps { + options: Array; + value: string; + name: string; + onChange(name: string, value: string, isValid: boolean): void; + variant?: 'standard' | 'filled' | 'outlined'; + label?: string; + placeholder?: string; + required?: boolean; + disabled?: boolean; + touched?: boolean; + loading?: boolean; + multiple?: 'default' | 'chips'; + errMsg?: string; +} + +const LoadingIcon = () => + +const EditorSelect = ({ + options, + value, + name, + required, + onChange, + label, + disabled, + touched, + variant = 'outlined', + loading, + multiple, + placeholder, + errMsg, +}: EditorSelectProps) => { + const theme = useTheme() + const { translations } = useStore() + const [state, setState] = useState({ + touched: false, + valid: !!value, + errorMsg: errMsg || (required + ? translations?.validation?.required || 'Required' + : undefined), + }) + + const handleChange = (_value: string | any) => { + const val = _value + let isValid = true + let errorMsg = errMsg + if (required && (multiple ? !val.length : !val)) { + isValid = false + errorMsg = errMsg || translations?.validation?.required || 'Required' + } + setState((prev) => ({ ...prev, touched: true, valid: isValid, errorMsg })) + onChange(name, val, isValid) + } + + useEffect(() => { + if (touched) { + handleChange(value) + } + // eslint-disable-next-line + }, [touched]); + const handleTouched = () => { + if (!state.touched) { + setState((prev) => ({ ...prev, touched: true, errorMsg: errMsg || prev.errorMsg })) + } + } + + return ( + <> + + {label && ( + + {`${label} ${required ? '*' : ''}`} + + )} + + + + {state.touched && !state.valid && state.errorMsg} + + + ) +} + +export { EditorSelect } diff --git a/backend/src/components/scheduler/components/month/MonthTable.tsx b/backend/src/components/scheduler/components/month/MonthTable.tsx new file mode 100644 index 000000000..bf3af94d5 --- /dev/null +++ b/backend/src/components/scheduler/components/month/MonthTable.tsx @@ -0,0 +1,201 @@ +import React, { Avatar, Typography, useTheme } from '@mui/material' +import { + addDays, + endOfDay, + format, + isSameDay, + isSameMonth, + isWithinInterval, + setHours, + startOfDay, + startOfMonth, +} from 'date-fns' +import { Fragment, ReactNode, useCallback } from 'react' +import { + getHourFormat, + getRecurrencesForDate, + getResourcedEvents, + isTimeZonedToday, + sortEventsByTheEarliest, +} from '../../helpers/generals' +import useStore from '../../hooks/useStore' +import useSyncScroll from '../../hooks/useSyncScroll' +import { TableGrid } from '../../styles/styles' +import { DefaultResource } from '../../types' +import Cell from '../common/Cell' +import MonthEvents from '../events/MonthEvents' + +type Props = { + daysList: Date[]; + resource?: DefaultResource; + eachWeekStart: Date[]; +}; + +const MonthTable = ({ daysList, resource, eachWeekStart }: Props) => { + const { + height, + month, + selectedDate, + events, + handleGotoDay, + resourceFields, + fields, + locale, + hourFormat, + stickyNavigation, + timeZone, + onClickMore, + } = useStore() + const { weekDays, startHour, endHour, cellRenderer, headRenderer, disableGoToDay } = month! + const { headersRef, bodyRef } = useSyncScroll() + + const theme = useTheme() + const monthStart = startOfMonth(selectedDate) + const hFormat = getHourFormat(hourFormat) + const CELL_HEIGHT = height / eachWeekStart.length + + const renderCells = useCallback( + (_resource?: DefaultResource) => { + let resourcedEvents = sortEventsByTheEarliest(events) + if (_resource) { + resourcedEvents = getResourcedEvents(events, _resource, resourceFields, fields) + } + const rows: ReactNode[] = [] + + for (const startDay of eachWeekStart) { + const cells = weekDays.map((d) => { + const today = addDays(startDay, d) + const start = new Date(`${format(setHours(today, startHour), `yyyy/MM/dd ${hFormat}`)}`) + const end = new Date(`${format(setHours(today, endHour), `yyyy/MM/dd ${hFormat}`)}`) + const field = resourceFields.idField + const eachFirstDayInCalcRow = isSameDay(startDay, today) ? today : null + const todayEvents = resourcedEvents + .flatMap((e) => getRecurrencesForDate(e, today)) + .filter((e) => { + if (isSameDay(e.start, today)) return true + const dayInterval = { start: startOfDay(e.start), end: endOfDay(e.end) } + if (eachFirstDayInCalcRow && isWithinInterval(eachFirstDayInCalcRow, dayInterval)) return true + return false + }) + const isToday = isTimeZonedToday({ dateLeft: today, timeZone }) + return ( + + + <> + {typeof headRenderer === 'function' ? ( +
          {headRenderer(today)}
          + ) : ( + + { + e.stopPropagation() + if (!disableGoToDay) { + handleGotoDay(today) + } + }} + > + {format(today, 'dd')} + + + )} + + { + if (onClickMore && typeof onClickMore === 'function') { + onClickMore(e, handleGotoDay) + } else { + handleGotoDay(e) + } + }} + cellHeight={CELL_HEIGHT} + /> + +
          + ) + }) + + rows.push({cells}) + } + return rows + }, + [ + CELL_HEIGHT, + cellRenderer, + daysList, + disableGoToDay, + eachWeekStart, + endHour, + events, + fields, + hFormat, + handleGotoDay, + headRenderer, + monthStart, + onClickMore, + resourceFields, + selectedDate, + startHour, + theme.palette.secondary.contrastText, + timeZone, + weekDays, + ] + ) + + return ( + <> + {/* Header Days */} + + {daysList.map((date) => ( + + {format(date, 'EE', { locale })} + + ))} + + {/* Time Cells */} + + {renderCells(resource)} + + + ) +} + +export default MonthTable diff --git a/backend/src/components/scheduler/components/nav/DayDateBtn.tsx b/backend/src/components/scheduler/components/nav/DayDateBtn.tsx new file mode 100644 index 000000000..4e9acc086 --- /dev/null +++ b/backend/src/components/scheduler/components/nav/DayDateBtn.tsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react' +import { DateCalendar } from '@mui/x-date-pickers' +import { Button, Popover } from '@mui/material' +import { format, addDays } from 'date-fns' +import DateProvider from '../hoc/DateProvider' +import { LocaleArrow } from '../common/LocaleArrow' +import useStore from '../../hooks/useStore' + +interface DayDateBtnProps { + selectedDate: Date; + onChange(value: Date): void; +} + +const DayDateBtn = ({ selectedDate, onChange }: DayDateBtnProps) => { + const { locale, navigationPickerProps } = useStore() + const [anchorEl, setAnchorEl] = useState(null) + + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget) + } + const handleClose = () => { + setAnchorEl(null) + } + + const handleChange = (e: Date | null) => { + onChange(e || new Date()) + handleClose() + } + + const handlePrev = () => { + const prevDay = addDays(selectedDate, -1) + onChange(prevDay) + } + const handleNext = () => { + const nexDay = addDays(selectedDate, 1) + onChange(nexDay) + } + return ( + <> + + + + + + + + + + ) +} + +export { DayDateBtn } diff --git a/backend/src/components/scheduler/components/nav/MonthDateBtn.tsx b/backend/src/components/scheduler/components/nav/MonthDateBtn.tsx new file mode 100644 index 000000000..a24375051 --- /dev/null +++ b/backend/src/components/scheduler/components/nav/MonthDateBtn.tsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react' +import { DateCalendar } from '@mui/x-date-pickers' +import { Button, Popover } from '@mui/material' +import { format, getMonth, setMonth } from 'date-fns' +import DateProvider from '../hoc/DateProvider' +import { LocaleArrow } from '../common/LocaleArrow' +import useStore from '../../hooks/useStore' + +interface MonthDateBtnProps { + selectedDate: Date; + onChange(value: Date): void; +} + +const MonthDateBtn = ({ selectedDate, onChange }: MonthDateBtnProps) => { + const { locale, navigationPickerProps } = useStore() + const currentMonth = getMonth(selectedDate) + const [anchorEl, setAnchorEl] = useState(null) + + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget) + } + const handleClose = () => { + setAnchorEl(null) + } + + const handleChange = (e: Date | null) => { + onChange(e || new Date()) + handleClose() + } + const handlePrev = () => { + const prevMonth = currentMonth - 1 + onChange(setMonth(selectedDate, prevMonth)) + } + const handleNext = () => { + const nextMonth = currentMonth + 1 + onChange(setMonth(selectedDate, nextMonth)) + } + return ( + <> + + + + + + + + + + ) +} + +export { MonthDateBtn } diff --git a/backend/src/components/scheduler/components/nav/Navigation.tsx b/backend/src/components/scheduler/components/nav/Navigation.tsx new file mode 100644 index 000000000..6981b23e7 --- /dev/null +++ b/backend/src/components/scheduler/components/nav/Navigation.tsx @@ -0,0 +1,195 @@ +import React, { Fragment, useState } from 'react' +import { + Button, + useTheme, + useMediaQuery, + Popover, + MenuList, + MenuItem, + IconButton, +} from '@mui/material' +import MoreVertIcon from '@mui/icons-material/MoreVert' +import ViewAgendaIcon from '@mui/icons-material/ViewAgenda' +import { WeekDateBtn } from './WeekDateBtn' +import { DayDateBtn } from './DayDateBtn' +import { MonthDateBtn } from './MonthDateBtn' +import useStore from '../../hooks/useStore' +import { NavigationDiv } from '../../styles/styles' +import { getTimeZonedDate } from '../../helpers/generals' + +export type View = 'month' | 'week' | 'day'; + +const Navigation = () => { + const { + selectedDate, + view, + week, + handleState, + getViews, + translations, + navigation, + day, + month, + disableViewNavigator, + onSelectedDateChange, + onViewChange, + stickyNavigation, + timeZone, + agenda, + toggleAgenda, + enableAgenda, + } = useStore() + const [anchorEl, setAnchorEl] = useState(null) + const theme = useTheme() + const isDesktop = useMediaQuery(theme.breakpoints.up('sm')) + const views = getViews() + + const toggleMoreMenu = (el?: Element) => { + setAnchorEl(el || null) + } + + const handleSelectedDateChange = (date: Date) => { + handleState(date, 'selectedDate') + + if (onSelectedDateChange && typeof onSelectedDateChange === 'function') { + onSelectedDateChange(date) + } + } + + const handleChangeView = (_view: View) => { + handleState(_view, 'view') + if (onViewChange && typeof onViewChange === 'function') { + onViewChange(_view, agenda) + } + } + + const renderDateSelector = () => { + switch (view) { + case 'month': + return ( + month?.navigation && ( + + ) + ) + case 'week': + return ( + week?.navigation && ( + + ) + ) + case 'day': + return ( + day?.navigation && ( + + ) + ) + default: + return '' + } + } + + if (!navigation && disableViewNavigator) return null + + return ( + +
          {navigation && renderDateSelector()}
          + +
          + + {enableAgenda + && (isDesktop ? ( + + ) : ( + + + + ))} + + {views.length > 1 + && (isDesktop ? ( + views.map((v) => ( + + )) + ) : ( + <> + { + toggleMoreMenu(e.currentTarget) + }} + > + + + { + toggleMoreMenu() + }} + anchorOrigin={{ + vertical: 'center', + horizontal: 'center', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'center', + }} + > + + {views.map((v) => ( + { + toggleMoreMenu() + handleChangeView(v) + }} + > + {translations.navigation[v]} + + ))} + + + + ))} +
          +
          + ) +} + +export { Navigation } diff --git a/backend/src/components/scheduler/components/nav/WeekDateBtn.tsx b/backend/src/components/scheduler/components/nav/WeekDateBtn.tsx new file mode 100644 index 000000000..1af046dbf --- /dev/null +++ b/backend/src/components/scheduler/components/nav/WeekDateBtn.tsx @@ -0,0 +1,77 @@ +import React, { useState } from 'react' +import { Button, Popover } from '@mui/material' +import { endOfWeek, format, startOfWeek, addDays } from 'date-fns' +import { DateCalendar } from '@mui/x-date-pickers' +import DateProvider from '../hoc/DateProvider' +import { WeekProps } from '../../views/Week' +import { LocaleArrow } from '../common/LocaleArrow' +import useStore from '../../hooks/useStore' + +interface WeekDateBtnProps { + selectedDate: Date; + onChange(value: Date): void; + weekProps: WeekProps; +} + +const WeekDateBtn = ({ selectedDate, onChange, weekProps }: WeekDateBtnProps) => { + const { locale, navigationPickerProps } = useStore() + const [anchorEl, setAnchorEl] = useState(null) + const { weekStartOn } = weekProps + const weekStart = startOfWeek(selectedDate, { weekStartsOn: weekStartOn }) + const weekEnd = endOfWeek(selectedDate, { weekStartsOn: weekStartOn }) + + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget) + } + const handleClose = () => { + setAnchorEl(null) + } + + const handleChange = (e: Date | null) => { + onChange(e || new Date()) + handleClose() + } + + const handlePrev = () => { + const ladtDayPrevWeek = addDays(weekStart, -1) + onChange(ladtDayPrevWeek) + } + + const handleNext = () => { + const firstDayNextWeek = addDays(weekEnd, 1) + onChange(firstDayNextWeek) + } + + return ( + <> + + + + + + + + + + ) +} + +export { WeekDateBtn } diff --git a/backend/src/components/scheduler/components/week/WeekTable.tsx b/backend/src/components/scheduler/components/week/WeekTable.tsx new file mode 100644 index 000000000..5feaf1fd4 --- /dev/null +++ b/backend/src/components/scheduler/components/week/WeekTable.tsx @@ -0,0 +1,204 @@ +import React, { Fragment, useMemo } from 'react' +import { + addMinutes, + endOfDay, + format, + isAfter, + isBefore, + isSameDay, + isToday, + startOfDay, +} from 'date-fns' +import { Typography } from '@mui/material' +import useStore from '../../hooks/useStore' +import { TableGrid } from '../../styles/styles' +import { + differenceInDaysOmitTime, + filterMultiDaySlot, + filterTodayEvents, + getHourFormat, +} from '../../helpers/generals' +import { MULTI_DAY_EVENT_HEIGHT } from '../../helpers/constants' +import { DefaultResource, ProcessedEvent } from '../../types' +import useSyncScroll from '../../hooks/useSyncScroll' +import TodayTypo from '../common/TodayTypo' +import usePosition from '../../positionManger/usePosition' +import EventItem from '../events/EventItem' +import TodayEvents from '../events/TodayEvents' +import Cell from '../common/Cell' + +type Props = { + daysList: Date[]; + hours: Date[]; + cellHeight: number; + minutesHeight: number; + resource?: DefaultResource; + resourcedEvents: ProcessedEvent[]; +}; + +const WeekTable = ({ + daysList, + hours, + cellHeight, + minutesHeight, + resourcedEvents, + resource, +}: Props) => { + const { + week, + events, + handleGotoDay, + resources, + resourceFields, + resourceViewMode, + direction, + locale, + hourFormat, + timeZone, + stickyNavigation, + } = useStore() + const { startHour, endHour, step, cellRenderer, disableGoToDay, headRenderer, hourRenderer } = week! + const { renderedSlots } = usePosition() + const { headersRef, bodyRef } = useSyncScroll() + const MULTI_SPACE = MULTI_DAY_EVENT_HEIGHT + const weekStart = startOfDay(daysList[0]) + const weekEnd = endOfDay(daysList[daysList.length - 1]) + const hFormat = getHourFormat(hourFormat) + + // Equalizing multi-day section height except in resource/tabs mode + const headerHeight = useMemo(() => { + const shouldEqualize = resources.length && resourceViewMode === 'default' + const allWeekMulti = filterMultiDaySlot( + shouldEqualize ? events : resourcedEvents, + daysList, + timeZone, + true + ) + return MULTI_SPACE * allWeekMulti.length + 45 + }, [ + MULTI_SPACE, + daysList, + events, + resourceViewMode, + resourcedEvents, + resources.length, + timeZone, + ]) + + const renderMultiDayEvents = ( + _events: ProcessedEvent[], + today: Date, + _resource?: DefaultResource + ) => { + const isFirstDayInWeek = isSameDay(weekStart, today) + const allWeekMulti = filterMultiDaySlot(_events, daysList, timeZone) + + const multiDays = allWeekMulti + .filter((e) => (isBefore(e.start, weekStart) ? isFirstDayInWeek : isSameDay(e.start, today))) + .sort((a, b) => b.end.getTime() - a.end.getTime()) + return multiDays.map((event) => { + const hasPrev = isBefore(startOfDay(event.start), weekStart) + const hasNext = isAfter(endOfDay(event.end), weekEnd) + const eventLength = differenceInDaysOmitTime(hasPrev ? weekStart : event.start, hasNext ? weekEnd : event.end) + + 1 + + const day = format(today, 'yyyy-MM-dd') + const resourceId = _resource ? _resource[resourceFields.idField] : 'all' + const rendered = renderedSlots?.[resourceId]?.[day] + const position = rendered?.[event.event_id] || 0 + + return ( +
          + +
          + ) + }) + } + + return ( + <> + {/* Header days */} + + + {daysList.map((date) => ( + + {typeof headRenderer === 'function' ? ( +
          {headRenderer(date)}
          + ) : ( + + )} + {renderMultiDayEvents(resourcedEvents, date, resource)} +
          + ))} +
          + {/* Time Cells */} + + {hours.map((h, i) => ( + + + {typeof hourRenderer === 'function' ? ( +
          {hourRenderer(format(h, hFormat, { locale }))}
          + ) : ( + {format(h, hFormat, { locale })} + )} +
          + {daysList.map((date) => { + const start = new Date(`${format(date, 'yyyy/MM/dd')} ${format(h, hFormat)}`) + const end = addMinutes(start, step) + const field = resourceFields.idField + return ( + + {/* Events of each day - run once on the top hour column */} + {i === 0 && ( + + )} + + + ) + })} +
          + ))} +
          + + ) +} + +export default WeekTable diff --git a/backend/src/components/scheduler/helpers/constants.ts b/backend/src/components/scheduler/helpers/constants.ts new file mode 100644 index 000000000..b9da170a3 --- /dev/null +++ b/backend/src/components/scheduler/helpers/constants.ts @@ -0,0 +1,4 @@ +export const BORDER_HEIGHT = 1 +export const MULTI_DAY_EVENT_HEIGHT = 28 +export const MONTH_NUMBER_HEIGHT = 27 +export const MONTH_BAR_HEIGHT = 23 diff --git a/backend/src/components/scheduler/helpers/generals.tsx b/backend/src/components/scheduler/helpers/generals.tsx new file mode 100644 index 000000000..ced5d91b9 --- /dev/null +++ b/backend/src/components/scheduler/helpers/generals.tsx @@ -0,0 +1,304 @@ +import { + addDays, + addMilliseconds, + addMinutes, + addSeconds, + differenceInDays, + differenceInMilliseconds, + endOfDay, + format, + isSameDay, + isWithinInterval, + startOfDay, + subMinutes, +} from 'date-fns' +import { datetime } from 'rrule' +import { View } from '../components/nav/Navigation' +import { + DefaultResource, + FieldProps, + ProcessedEvent, + ResourceFields, + SchedulerProps, +} from '../types' +import { StateEvent } from '../views/Editor' + +export const getOneView = (state: Partial): View => { + if (state.month) { + return 'month' + } if (state.week) { + return 'week' + } if (state.day) { + return 'day' + } + throw new Error('No views were selected') +} + +export const getAvailableViews = (state: SchedulerProps) => { + const views: View[] = [] + if (state.month) { + views.push('month') + } + if (state.week) { + views.push('week') + } + if (state.day) { + views.push('day') + } + return views +} + +export const arraytizeFieldVal = (field: FieldProps, val: any, event?: StateEvent) => { + const arrytize = field.config?.multiple && !Array.isArray(event?.[field.name] || field.default) + const value = arrytize ? (val ? [val] : []) : val + const validity = arrytize ? value.length : value + return { value, validity } +} + +export const getResourcedEvents = ( + events: ProcessedEvent[], + resource: DefaultResource, + resourceFields: ResourceFields, + fields: FieldProps[] +): ProcessedEvent[] => { + const keyName = resourceFields.idField + const resourceField = fields.find((f) => f.name === keyName) + const isMultiple = !!resourceField?.config?.multiple + + const resourcedEvents = [] + + for (const event of events) { + // Handle single select & multiple select accordingly + const arrytize = isMultiple && !Array.isArray(event[keyName]) + const eventVal = arrytize ? [event[keyName]] : event[keyName] + + const isThisResource = isMultiple || Array.isArray(eventVal) + ? eventVal.includes(resource[keyName]) + : eventVal === resource[keyName] + + if (isThisResource) { + resourcedEvents.push({ + ...event, + color: event.color || resource[resourceFields.colorField || ''], + }) + } + } + + return resourcedEvents +} + +export const traversCrossingEvents = ( + todayEvents: ProcessedEvent[], + event: ProcessedEvent +): ProcessedEvent[] => todayEvents.filter( + (e) => + e.event_id !== event.event_id + && (isWithinInterval(addMinutes(event.start, 1), { + start: e.start, + end: e.end, + }) + || isWithinInterval(addMinutes(event.end, -1), { + start: e.start, + end: e.end, + }) + || isWithinInterval(addMinutes(e.start, 1), { + start: event.start, + end: event.end, + }) + || isWithinInterval(addMinutes(e.end, -1), { + start: event.start, + end: event.end, + })) +) + +export const calcMinuteHeight = (cellHeight: number, step: number) => Math.ceil(cellHeight) / step + +export const calcCellHeight = (tableHeight: number, hoursLength: number) => Math.max(tableHeight / hoursLength, 60) + +export const differenceInDaysOmitTime = (start: Date, end: Date) => differenceInDays(endOfDay(addSeconds(end, -1)), startOfDay(start)) + +export const convertDateToRRuleDate = (date: Date) => datetime( + date.getFullYear(), + date.getMonth() + 1, + date.getDate(), + date.getHours(), + date.getMinutes() +) + +export const convertRRuleDateToDate = (rruleDate: Date) => new Date( + rruleDate.getUTCFullYear(), + rruleDate.getUTCMonth(), + rruleDate.getUTCDate(), + rruleDate.getUTCHours(), + rruleDate.getUTCMinutes() +) + +export const getTimeZonedDate = (date: Date, timeZone?: string) => new Date( + new Intl.DateTimeFormat('en-US', { + dateStyle: 'short', + timeStyle: 'medium', + timeZone, + }).format(date) +) + +export const convertEventTimeZone = (event: ProcessedEvent, timeZone?: string) => ({ + ...event, + start: getTimeZonedDate(event.start, timeZone), + end: getTimeZonedDate(event.end, timeZone), + convertedTz: true, +}) + +export const getRecurrencesForDate = (event: ProcessedEvent, today: Date, timeZone?: string) => { + const duration = differenceInMilliseconds(event.end, event.start) + if (event.recurring) { + return event.recurring + ?.between(today, addDays(today, 1), true) + .map((d: Date, index: number) => { + const start = convertRRuleDateToDate(d) + return { + ...event, + recurrenceId: index, + start, + end: addMilliseconds(start, duration), + } + }) + .map((_event) => convertEventTimeZone(_event, timeZone)) + } + return [convertEventTimeZone(event, timeZone)] +} + +export const sortEventsByTheLengthest = (events: ProcessedEvent[]) => events.sort((a, b) => { + const aDiff = a.end.getTime() - a.start.getTime() + const bDiff = b.end.getTime() - b.start.getTime() + return bDiff - aDiff +}) + +export const sortEventsByTheEarliest = (events: ProcessedEvent[]) => events.sort((a, b) => { + const isMulti = a.allDay || differenceInDaysOmitTime(a.start, a.end) > 0 + return isMulti ? -1 : a.start.getTime() - b.start.getTime() +}) + +export const filterTodayEvents = ( + events: ProcessedEvent[], + today: Date, + timeZone?: string +): ProcessedEvent[] => { + const list: ProcessedEvent[] = [] + + for (let i = 0; i < events.length; i += 1) { + for (const rec of getRecurrencesForDate(events[i], today, timeZone)) { + const isToday = !rec.allDay && isSameDay(today, rec.start) && !differenceInDaysOmitTime(rec.start, rec.end) + if (isToday) { + list.push(rec) + } + } + } + + // Sort by the length est event + return sortEventsByTheLengthest(list) +} + +export const filterTodayAgendaEvents = (events: ProcessedEvent[], today: Date) => { + const list: ProcessedEvent[] = events.filter((ev) => + isWithinInterval(today, { + start: startOfDay(ev.start), + end: endOfDay(subMinutes(ev.end, 1)), + })) + + return sortEventsByTheEarliest(list) +} + +export const filterMultiDaySlot = ( + events: ProcessedEvent[], + date: Date | Date[], + timeZone?: string, + lengthOnly?: boolean +) => { + const isMultiDates = Array.isArray(date) + const list: ProcessedEvent[] = [] + const multiPerDay: Record = {} + for (let i = 0; i < events.length; i += 1) { + const event = convertEventTimeZone(events[i], timeZone) + let withinSlot = event.allDay || differenceInDaysOmitTime(event.start, event.end) > 0 + + if (withinSlot) { + if (isMultiDates) { + withinSlot = date.some((weekday) => + isWithinInterval(weekday, { + start: startOfDay(event.start), + end: endOfDay(event.end), + })) + } else { + withinSlot = isWithinInterval(date, { + start: startOfDay(event.start), + end: endOfDay(event.end), + }) + } + + list.push(event) + if (isMultiDates) { + for (const d of date) { + const start = format(d, 'yyyy-MM-dd') + if (isWithinInterval(d, { start: startOfDay(event.start), end: endOfDay(event.end) })) { + multiPerDay[start] = (multiPerDay[start] || []).concat(event) + } + } + } else { + const start = format(event.start, 'yyyy-MM-dd') + multiPerDay[start] = (multiPerDay[start] || []).concat(event) + } + } + } + + if (isMultiDates && lengthOnly) { + return Object.values(multiPerDay).sort((a, b) => b.length - a.length)?.[0] || [] + } + + return list +} + +/** + * Gets the offset in minutes of the provided timeZone. + * @param timeZone The timeZone to get the offset for. + * @returns The offset in minutes of the provided timeZone. + */ +function getTimezoneOffset(timeZone: string) { + const now = new Date() + const localizedTime = new Date(now.toLocaleString('en-US', { timeZone })) + const utcTime = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })) + return Math.round((localizedTime.getTime() - utcTime.getTime()) / (60 * 1000)) +} + +/** + * Performs the reverse of getTimeZonedDate, IE: the given date is assumed + * to already be in the provided timeZone and is reverted to the local + * browser's timeZone. + * @param date The date to convert. + * @param timeZone The timeZone to convert from. + * @returns A new date reverted from the given timeZone to local time. + */ +export const revertTimeZonedDate = (date: Date, timeZone?: string) => { + if (!timeZone) { + return date + } + + // This always gets the offset between the local computer's time + // and UTC. It has nothing to do with the value of the date object, + // despite being an instance method. + const localOffset = -date.getTimezoneOffset() + const desiredOffset = getTimezoneOffset(timeZone) + const diff = localOffset - desiredOffset + return new Date(date.getTime() + diff * 60 * 1000) +} + +export const isTimeZonedToday = ({ + dateLeft, + dateRight, + timeZone, +}: { + dateLeft: Date; + dateRight?: Date; + timeZone?: string; +}) => isSameDay(dateLeft, getTimeZonedDate(dateRight || new Date(), timeZone)) + +export const getHourFormat = (hourFormat: '12' | '24') => (hourFormat === '12' ? 'hh:mm a' : 'HH:mm') diff --git a/backend/src/components/scheduler/hooks/useCellAttributes.ts b/backend/src/components/scheduler/hooks/useCellAttributes.ts new file mode 100644 index 000000000..775b65727 --- /dev/null +++ b/backend/src/components/scheduler/hooks/useCellAttributes.ts @@ -0,0 +1,67 @@ +import { DragEvent } from 'react' +import { alpha, useTheme } from '@mui/material' +import useStore from './useStore' +import { revertTimeZonedDate } from '../helpers/generals' + +interface Props { + start: Date; + end: Date; + resourceKey: string; + resourceVal: string | number; +} +export const useCellAttributes = ({ start, end, resourceKey, resourceVal }: Props) => { + const { + triggerDialog, + onCellClick, + onDrop, + currentDragged, + setCurrentDragged, + editable, + timeZone, + } = useStore() + const theme = useTheme() + + return { + tabIndex: editable ? 0 : -1, + disableRipple: !editable, + onClick: () => { + if (editable) { + triggerDialog(true, { + start, + end, + [resourceKey]: resourceVal, + }) + } + + if (onCellClick && typeof onCellClick === 'function') { + onCellClick(start, end, resourceKey, resourceVal) + } + }, + onDragOver: (e: DragEvent) => { + e.preventDefault() + if (currentDragged) { + e.currentTarget.style.backgroundColor = alpha(theme.palette.secondary.main, 0.3) + } + }, + onDragEnter: (e: DragEvent) => { + if (currentDragged) { + e.currentTarget.style.backgroundColor = alpha(theme.palette.secondary.main, 0.3) + } + }, + onDragLeave: (e: DragEvent) => { + if (currentDragged) { + e.currentTarget.style.backgroundColor = '' + } + }, + onDrop: (e: DragEvent) => { + if (currentDragged && currentDragged.event_id) { + e.preventDefault() + e.currentTarget.style.backgroundColor = '' + const zonedStart = revertTimeZonedDate(start, timeZone) + onDrop(e, currentDragged.event_id.toString(), zonedStart, resourceKey, resourceVal) + setCurrentDragged() + } + }, + [resourceKey]: resourceVal, + } +} diff --git a/backend/src/components/scheduler/hooks/useDragAttributes.ts b/backend/src/components/scheduler/hooks/useDragAttributes.ts new file mode 100644 index 000000000..55d6d51b6 --- /dev/null +++ b/backend/src/components/scheduler/hooks/useDragAttributes.ts @@ -0,0 +1,31 @@ +import { DragEvent } from 'react' +import { useTheme } from '@mui/material' +import { ProcessedEvent } from '../types' +import useStore from './useStore' + +const useDragAttributes = (event: ProcessedEvent) => { + const { setCurrentDragged } = useStore() + const theme = useTheme() + return { + draggable: true, + onDragStart: (e: DragEvent) => { + e.stopPropagation() + setCurrentDragged(event) + e.currentTarget.style.backgroundColor = theme.palette.error.main + }, + onDragEnd: (e: DragEvent) => { + setCurrentDragged() + e.currentTarget.style.backgroundColor = event.color || theme.palette.primary.main + }, + onDragOver: (e: DragEvent) => { + e.stopPropagation() + e.preventDefault() + }, + onDragEnter: (e: DragEvent) => { + e.stopPropagation() + e.preventDefault() + }, + } +} + +export default useDragAttributes diff --git a/backend/src/components/scheduler/hooks/useEventPermissions.ts b/backend/src/components/scheduler/hooks/useEventPermissions.ts new file mode 100644 index 000000000..38495b5ff --- /dev/null +++ b/backend/src/components/scheduler/hooks/useEventPermissions.ts @@ -0,0 +1,42 @@ +import { useMemo } from 'react' +import { ProcessedEvent } from '../types' +import useStore from './useStore' + +const useEventPermissions = (event: ProcessedEvent) => { + const { editable, deletable, draggable } = useStore() + + const canEdit = useMemo(() => { + // Priority control to event specific editable value + if (typeof event.editable !== 'undefined') { + return event.editable + } + return editable + }, [editable, event.editable]) + + const canDelete = useMemo(() => { + // Priority control to event specific deletable value + if (typeof event.deletable !== 'undefined') { + return event.deletable + } + return deletable + }, [deletable, event.deletable]) + + const canDrag = useMemo(() => { + if (!canEdit) { + return false + } + // Priority control to event specific draggable value + if (typeof event.draggable !== 'undefined') { + return event.draggable + } + return draggable + }, [draggable, event.draggable, canEdit]) + + return { + canEdit, + canDelete, + canDrag, + } +} + +export default useEventPermissions diff --git a/backend/src/components/scheduler/hooks/useStore.ts b/backend/src/components/scheduler/hooks/useStore.ts new file mode 100644 index 000000000..52eb712dd --- /dev/null +++ b/backend/src/components/scheduler/hooks/useStore.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react' +import { StoreContext } from '../store/context' + +const useStore = () => useContext(StoreContext) + +export default useStore diff --git a/backend/src/components/scheduler/hooks/useSyncScroll.ts b/backend/src/components/scheduler/hooks/useSyncScroll.ts new file mode 100644 index 000000000..4ad02e9de --- /dev/null +++ b/backend/src/components/scheduler/hooks/useSyncScroll.ts @@ -0,0 +1,31 @@ +import { useEffect, useRef } from 'react' + +/** + * The solution to make headers sticky with overflow + */ +const useSyncScroll = () => { + const headersRef = useRef(null) + const bodyRef = useRef(null) + + useEffect(() => { + const header = headersRef.current + const body = bodyRef.current + const handleScroll = (event: Event) => { + const el = event.currentTarget as HTMLElement + body?.scroll({ left: el.scrollLeft }) + header?.scroll({ left: el.scrollLeft }) + } + + header?.addEventListener('scroll', handleScroll) + body?.addEventListener('scroll', handleScroll) + + return () => { + header?.removeEventListener('scroll', handleScroll) + body?.removeEventListener('scroll', handleScroll) + } + }) + + return { headersRef, bodyRef } +} + +export default useSyncScroll diff --git a/backend/src/components/scheduler/hooks/useWindowResize.ts b/backend/src/components/scheduler/hooks/useWindowResize.ts new file mode 100644 index 000000000..453ffda98 --- /dev/null +++ b/backend/src/components/scheduler/hooks/useWindowResize.ts @@ -0,0 +1,37 @@ +import { useState, useEffect } from 'react' + +export function useWindowResize() { + const [state, setState] = useState({ + width: 0, + height: 0, + }) + + useEffect(() => { + const handler = () => { + setState((_state) => { + const { innerWidth, innerHeight } = window + // Check state for change, return same state if no change happened to prevent rerender + return _state.width !== innerWidth || _state.height !== innerHeight + ? { + width: innerWidth, + height: innerHeight, + } + : _state + }) + } + + if (typeof window !== 'undefined') { + handler() + window.addEventListener('resize', handler, { + capture: false, + passive: true, + }) + } + + return () => { + window.removeEventListener('resize', handler) + } + }, []) + + return state +} diff --git a/backend/src/components/scheduler/index.tsx b/backend/src/components/scheduler/index.tsx new file mode 100644 index 000000000..5730b1ef8 --- /dev/null +++ b/backend/src/components/scheduler/index.tsx @@ -0,0 +1,12 @@ +import React, { forwardRef } from 'react' +import SchedulerComponent from './SchedulerComponent' +import { SchedulerProps, SchedulerRef } from './types' +import { StoreProvider } from './store/provider' + +const Scheduler = forwardRef>((props, ref) => ( + + + + )) + +export { Scheduler } diff --git a/backend/src/components/scheduler/positionManger/context.ts b/backend/src/components/scheduler/positionManger/context.ts new file mode 100644 index 000000000..30e6f5790 --- /dev/null +++ b/backend/src/components/scheduler/positionManger/context.ts @@ -0,0 +1,14 @@ +import { createContext } from 'react' + +export type PositionManagerState = { + renderedSlots: { [day: string]: { [resourceId: string]: { [eventId: string]: number } } }; +}; + +type PositionManagerProps = { + setRenderedSlot(day: string, eventId: string, position: number, resourceId?: string): void; +}; + +export const PositionContext = createContext({ + renderedSlots: {}, + setRenderedSlot: () => {}, +}) diff --git a/backend/src/components/scheduler/positionManger/provider.tsx b/backend/src/components/scheduler/positionManger/provider.tsx new file mode 100644 index 000000000..265bd1969 --- /dev/null +++ b/backend/src/components/scheduler/positionManger/provider.tsx @@ -0,0 +1,101 @@ +import React, { useCallback, useEffect, useState } from 'react' +import { eachDayOfInterval, format } from 'date-fns' +import { PositionManagerState, PositionContext } from './context' +import useStore from '../hooks/useStore' +import { DefaultResource, FieldProps, ProcessedEvent, ResourceFields } from '../types' +import { getResourcedEvents, sortEventsByTheEarliest } from '../helpers/generals' + +type Props = { + children: React.ReactNode; +}; + +const setEventPositions = (events: ProcessedEvent[]) => { + const slots: PositionManagerState['renderedSlots'][string] = {} + let position = 0 + for (let i = 0; i < events.length; i += 1) { + const event = events[i] + const eventLength = eachDayOfInterval({ start: event.start, end: event.end }) + for (let j = 0; j < eventLength.length; j += 1) { + const day = format(eventLength[j], 'yyyy-MM-dd') + if (slots[day]) { + const positions = Object.values(slots[day]) + while (positions.includes(position)) { + position += 1 + } + slots[day][event.event_id] = position + } else { + slots[day] = { [event.event_id]: position } + } + } + + // rest + position = 0 + } + + return slots +} + +const setEventPositionsWithResources = ( + events: ProcessedEvent[], + resources: DefaultResource[], + rFields: ResourceFields, + fields: FieldProps[] +) => { + // const sorted = sortEventsByTheEarliest(events) + const sorted = sortEventsByTheEarliest(Array.from(events)) + const slots: PositionManagerState['renderedSlots'] = {} + if (resources.length) { + for (const resource of resources) { + const resourcedEvents = getResourcedEvents(sorted, resource, rFields, fields) + const positions = setEventPositions(resourcedEvents) + slots[resource[rFields.idField]] = positions + } + } else { + slots.all = setEventPositions(sorted) + } + + return slots +} + +export const PositionProvider = ({ children }: Props) => { + const { events, resources, resourceFields, fields } = useStore() + const [state, set] = useState({ + renderedSlots: setEventPositionsWithResources(events, resources, resourceFields, fields), + }) + + useEffect(() => { + set((prev) => ({ + ...prev, + renderedSlots: setEventPositionsWithResources(events, resources, resourceFields, fields), + })) + }, [events, fields, resourceFields, resources]) + + const setRenderedSlot = useCallback((day: string, eventId: string, position: number, resourceId?: string) => { + set((prev) => ({ + ...prev, + renderedSlots: { + ...prev.renderedSlots, + [resourceId || 'all']: { + ...prev.renderedSlots?.[resourceId || 'all'], + [day]: prev.renderedSlots?.[resourceId || 'all']?.[day] + ? { + ...prev.renderedSlots?.[resourceId || 'all']?.[day], + [eventId]: position, + } + : { [eventId]: position }, + }, + }, + })) + }, []) + + const contextValue = React.useMemo(() => ({ + ...state, + setRenderedSlot, + }), [state, setRenderedSlot]) + + return ( + + {children} + + ) +} diff --git a/backend/src/components/scheduler/positionManger/usePosition.ts b/backend/src/components/scheduler/positionManger/usePosition.ts new file mode 100644 index 000000000..f8dbf677e --- /dev/null +++ b/backend/src/components/scheduler/positionManger/usePosition.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react' +import { PositionContext } from './context' + +const usePosition = () => useContext(PositionContext) + +export default usePosition diff --git a/backend/src/components/scheduler/store/context.ts b/backend/src/components/scheduler/store/context.ts new file mode 100644 index 000000000..318f66932 --- /dev/null +++ b/backend/src/components/scheduler/store/context.ts @@ -0,0 +1,5 @@ +import { createContext } from 'react' +import { initialStore } from './default' +import { Store } from './types' + +export const StoreContext = createContext(initialStore) diff --git a/backend/src/components/scheduler/store/default.ts b/backend/src/components/scheduler/store/default.ts new file mode 100644 index 000000000..02979b357 --- /dev/null +++ b/backend/src/components/scheduler/store/default.ts @@ -0,0 +1,150 @@ +import { Breakpoint } from '@mui/material' +import { enUS } from 'date-fns/locale' +import { HourFormat, ResourceViewMode, SchedulerDirection, SchedulerProps } from '../types' +import { getOneView, getTimeZonedDate } from '../helpers/generals' + +const defaultMonth = { + weekDays: [0, 1, 2, 3, 4, 5, 6], + weekStartOn: 6, + startHour: 9, + endHour: 17, + navigation: true, + disableGoToDay: false, +} + +const defaultWeek = { + weekDays: [0, 1, 2, 3, 4, 5, 6], + weekStartOn: 6, + startHour: 9, + endHour: 17, + step: 60, + navigation: true, + disableGoToDay: false, +} + +const defaultDay = { + startHour: 9, + endHour: 17, + step: 60, + navigation: true, +} + +const defaultResourceFields = { + idField: 'assignee', + textField: 'text', + subTextField: 'subtext', + avatarField: 'avatar', + colorField: 'color', +} + +const defaultTranslations = (trans: Partial = {}) => { + const { navigation, form, event, ...other } = trans + + return { + navigation: { + month: 'Month', + week: 'Week', + day: 'Day', + agenda: 'Agenda', + today: 'Today', + ...navigation + }, + form: { + addTitle: 'Add Event', + editTitle: 'Edit Event', + confirm: 'Confirm', + delete: 'Delete', + cancel: 'Cancel', + ...form + }, + event: { + title: 'Title', + start: 'Start', + end: 'End', + allDay: 'All Day', + ...event + }, + ...({ + moreEvents: 'More...', + loading: 'Loading...', + noDataToDisplay: 'No data to display', + ...other + }), + } +} + +const defaultViews = (props: Partial) => { + const { month, week, day } = props + return { + month: month !== null ? Object.assign(defaultMonth, month) : null, + week: week !== null ? Object.assign(defaultWeek, week) : null, + day: day !== null ? Object.assign(defaultDay, day) : null, + } +} + +export const defaultProps = (props: Partial) => { + const { + month, + week, + day, + translations, + resourceFields, + view, + agenda, + selectedDate, + ...otherProps + } = props + const views = defaultViews(props) + const defaultView = view || 'week' + const initialView = views[defaultView] ? defaultView : getOneView(views) + return { + ...views, + translations: defaultTranslations(translations), + resourceFields: Object.assign(defaultResourceFields, resourceFields), + view: initialView, + selectedDate: getTimeZonedDate(selectedDate || new Date(), props.timeZone), + ...({ + height: 600, + navigation: true, + disableViewNavigator: false, + events: [], + fields: [], + loading: undefined, + customEditor: undefined, + onConfirm: undefined, + onDelete: undefined, + viewerExtraComponent: undefined, + resources: [], + resourceHeaderComponent: undefined, + resourceViewMode: 'default' as ResourceViewMode, + direction: 'ltr' as SchedulerDirection, + dialogMaxWidth: 'md' as (false | Breakpoint | undefined), + locale: enUS, + deletable: true, + editable: true, + hourFormat: '12' as HourFormat, + draggable: true, + agenda, + enableAgenda: typeof agenda === 'undefined' || agenda, + ...otherProps + }), + } +} + +export const initialStore = { + ...defaultProps({}), + setProps: () => { }, + dialog: false, + selectedRange: undefined, + selectedEvent: undefined, + selectedResource: undefined, + handleState: () => { }, + getViews: () => [], + toggleAgenda: () => { }, + triggerDialog: () => { }, + triggerLoading: () => { }, + handleGotoDay: () => { }, + confirmEvent: () => { }, + setCurrentDragged: () => { }, + onDrop: () => { }, +} diff --git a/backend/src/components/scheduler/store/provider.tsx b/backend/src/components/scheduler/store/provider.tsx new file mode 100644 index 000000000..d0359b693 --- /dev/null +++ b/backend/src/components/scheduler/store/provider.tsx @@ -0,0 +1,203 @@ +import React, { DragEvent, useEffect, useState } from 'react' +import { addMinutes, differenceInMinutes, isEqual } from 'date-fns' +import { EventActions, ProcessedEvent, SchedulerProps } from '../types' +import { defaultProps, initialStore } from './default' +import { StoreContext } from './context' +import { SchedulerState, SelectedRange, Store } from './types' +import { arraytizeFieldVal, getAvailableViews } from '../helpers/generals' +import { View } from '../components/nav/Navigation' + +type Props = { + children: React.ReactNode; + initial: Partial; +}; + +export const StoreProvider = ({ children, initial }: Props) => { + const [state, set] = useState({ ...initialStore, ...defaultProps(initial) }) + + useEffect(() => { + set((prev) => ({ + ...prev, + onEventDrop: initial.onEventDrop, + customEditor: initial.customEditor, + events: initial.events || [], + })) + // Rerender if changed on some props + }, [initial.onEventDrop, initial.customEditor, initial.events]) + + const handleState = (value: SchedulerState[keyof SchedulerState], name: keyof SchedulerState) => { + set((prev) => ({ ...prev, [name]: value })) + } + + const getViews = () => getAvailableViews(state) + + const toggleAgenda = () => { + set((prev) => { + const newStatus = !prev.agenda + + if (state.onViewChange && typeof state.onViewChange === 'function') { + state.onViewChange(state.view, newStatus) + } + + return { ...prev, agenda: newStatus } + }) + } + + const triggerDialog = (status: boolean, selected?: SelectedRange | ProcessedEvent) => { + const isEvent = selected as ProcessedEvent + + set((prev) => ({ + ...prev, + dialog: status, + selectedRange: isEvent?.event_id + ? undefined + : isEvent || { + start: new Date(), + end: new Date(Date.now() + 60 * 60 * 1000), + }, + selectedEvent: isEvent?.event_id ? isEvent : undefined, + selectedResource: prev.selectedResource || isEvent?.[state.resourceFields?.idField], + })) + } + + const triggerLoading = (status: boolean) => { + // Trigger if not out-sourced by props + if (typeof initial.loading === 'undefined') { + set((prev) => ({ ...prev, loading: status })) + } + } + + const handleGotoDay = (day: Date) => { + const currentViews = getViews() + let view: View | undefined + if (currentViews.includes('day')) { + view = 'day' + set((prev) => ({ ...prev, view: 'day', selectedDate: day })) + } else if (currentViews.includes('week')) { + view = 'week' + set((prev) => ({ ...prev, view: 'week', selectedDate: day })) + } else { + console.warn('No Day/Week views available') + } + + if (!!view && state.onViewChange && typeof state.onViewChange === 'function') { + state.onViewChange(view, state.agenda) + } + + if (!!view && state.onSelectedDateChange && typeof state.onSelectedDateChange === 'function') { + state.onSelectedDateChange(day) + } + } + + const confirmEvent = (event: ProcessedEvent | ProcessedEvent[], action: EventActions) => { + let updatedEvents: ProcessedEvent[] + if (action === 'edit') { + if (Array.isArray(event)) { + updatedEvents = state.events.map((e) => { + const exist = event.find((ex) => ex.event_id === e.event_id) + return exist ? { ...e, ...exist } : e + }) + } else { + updatedEvents = state.events.map((e) => + (e.event_id === event.event_id ? { ...e, ...event } : e)) + } + } else { + updatedEvents = state.events.concat(event) + } + set((prev) => ({ ...prev, events: updatedEvents })) + } + + const setCurrentDragged = (event?: ProcessedEvent) => { + set((prev) => ({ ...prev, currentDragged: event })) + } + + const onDrop = async ( + event: DragEvent, + eventId: string, + startTime: Date, + resKey?: string, + resVal?: string | number + ) => { + // Get dropped event + const droppedEvent = state.events.find((e) => { + if (typeof e.event_id === 'number') { + return e.event_id === +eventId + } + return e.event_id === eventId + }) as ProcessedEvent + + // Check if has resource and if is multiple + const resField = state.fields.find((f) => f.name === resKey) + const isMultiple = !!resField?.config?.multiple + let newResource = resVal as string | number | string[] | number[] + if (resField) { + const eResource = droppedEvent[resKey as string] + const currentRes = arraytizeFieldVal(resField, eResource, droppedEvent).value + if (isMultiple) { + // if dropped on already owned resource + if (currentRes.includes(resVal)) { + // Omit if dropped on same time slot for multiple event + if (isEqual(droppedEvent.start, startTime)) { + return + } + newResource = currentRes + } else { + // if have multiple resource ? add other : move to other + newResource = currentRes.length > 1 ? [...currentRes, resVal] : [resVal] + } + } + } + + // Omit if dropped on same time slot for non multiple events + if (isEqual(droppedEvent.start, startTime)) { + if (!newResource || (!isMultiple && newResource === droppedEvent[resKey as string])) { + return + } + } + + // Update event time according to original duration & update resources/owners + const diff = differenceInMinutes(droppedEvent.end, droppedEvent.start) + const updatedEvent: ProcessedEvent = { + ...droppedEvent, + start: startTime, + end: addMinutes(startTime, diff), + recurring: undefined, + [resKey as string]: newResource || '', + } + + // Local + if (!state.onEventDrop || typeof state.onEventDrop !== 'function') { + confirmEvent(updatedEvent, 'edit') + return + } + // Remote + try { + triggerLoading(true) + const _event = await state.onEventDrop(event, startTime, updatedEvent, droppedEvent) + if (_event) { + confirmEvent(_event, 'edit') + } + } finally { + triggerLoading(false) + } + } + + const contextValue = React.useMemo(() => ({ + ...state, + handleState, + getViews, + toggleAgenda, + triggerDialog, + triggerLoading, + handleGotoDay, + confirmEvent, + setCurrentDragged, + onDrop, + }), [state]) // eslint-disable-line react-hooks/exhaustive-deps + + return ( + + {children} + + ) +} diff --git a/backend/src/components/scheduler/store/types.ts b/backend/src/components/scheduler/store/types.ts new file mode 100644 index 000000000..3f518e16d --- /dev/null +++ b/backend/src/components/scheduler/store/types.ts @@ -0,0 +1,32 @@ +import { DragEvent } from 'react' +import { View } from '../components/nav/Navigation' +import { DefaultResource, EventActions, ProcessedEvent, SchedulerProps } from '../types' + +export type SelectedRange = { start: Date; end: Date }; + +export interface SchedulerState extends SchedulerProps { + dialog: boolean; + selectedRange?: SelectedRange; + selectedEvent?: ProcessedEvent; + selectedResource?: DefaultResource['assignee']; + currentDragged?: ProcessedEvent; + enableAgenda?: boolean; +} + +export interface Store extends SchedulerState { + handleState(value: SchedulerState[keyof SchedulerState], name: keyof SchedulerState): void; + getViews(): View[]; + toggleAgenda: () => void; + triggerDialog(status: boolean, event?: SelectedRange | ProcessedEvent): void; + triggerLoading(status: boolean): void; + handleGotoDay(day: Date): void; + confirmEvent(event: ProcessedEvent | ProcessedEvent[], action: EventActions): void; + setCurrentDragged(event?: ProcessedEvent): void; + onDrop( + event: DragEvent, + eventId: string, + droppedStartTime: Date, + resourceKey?: string, + resourceVal?: string | number + ): void; +} diff --git a/backend/src/components/scheduler/styles/styles.ts b/backend/src/components/scheduler/styles/styles.ts new file mode 100644 index 000000000..91e9f1f5d --- /dev/null +++ b/backend/src/components/scheduler/styles/styles.ts @@ -0,0 +1,240 @@ +import { Paper, alpha, styled } from '@mui/material' + +export const Wrapper = styled('div')<{ dialog: number }>(({ theme, dialog }) => ({ + position: 'relative', + '& .rs__table_loading': { + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + zIndex: 999999, + '& .rs__table_loading_internal': { + background: dialog ? '' : alpha(theme.palette.background.paper, 0.4), + height: '100%', + '& > span': { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + flexDirection: 'column', + '& >span': { + marginBottom: 15, + }, + }, + }, + }, +})) + +export const Table = styled('div')<{ resource_count: number }>(({ resource_count: resourceCount }) => ({ + position: 'relative', + display: 'flex', + flexDirection: resourceCount > 1 ? 'row' : 'column', + width: '100%', + boxSizing: 'content-box', + '& > div': { + flexShrink: 0, + flexGrow: 1, + }, +})) + +export const NavigationDiv = styled(Paper)<{ sticky?: string }>(({ sticky = '0' }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + position: sticky === '1' ? 'sticky' : 'relative', + top: sticky === '1' ? 0 : undefined, + zIndex: sticky === '1' ? 999 : undefined, + boxShadow: 'none', + padding: '2px 0', + '& > .rs__view_navigator': { + display: 'flex', + alignItems: 'center', + }, +})) + +export const AgendaDiv = styled('div')(({ theme }) => ({ + borderStyle: 'solid', + borderColor: theme.palette.grey[300], + borderWidth: '1px 1px 0 0', + '& > .rs__agenda_row': { + display: 'flex', + '& >.rs__agenda__cell': { + padding: 4, + width: '100%', + maxWidth: 60, + '& > .MuiTypography-root': { + position: 'sticky', + top: 0, + '&.rs__hover__op': { + cursor: 'pointer', + '&:hover': { + opacity: 0.7, + textDecoration: 'underline', + }, + }, + }, + }, + '& .rs__cell': { + borderStyle: 'solid', + borderColor: theme.palette.grey[300], + borderWidth: '0 0 1px 1px', + }, + '& > .rs__agenda_items': { + flexGrow: 1, + }, + }, +})) + +export const TableGrid = styled('div')<{ + days: number; + sticky?: string; + stickyNavigation?: boolean; + indent?: string; +}>(({ days, sticky = '0', stickyNavigation, indent = '1', theme }) => ({ + display: 'grid', + gridTemplateColumns: +indent > 0 ? `10% repeat(${days}, 1fr)` : `repeat(${days}, 1fr)`, + overflowX: 'auto', + overflowY: 'hidden', + position: sticky === '1' ? 'sticky' : 'relative', + top: sticky === '1' ? (stickyNavigation ? 36 : 0) : undefined, + zIndex: sticky === '1' ? 99 : undefined, + [theme.breakpoints.down('sm')]: { + gridTemplateColumns: +indent > 0 ? `30px repeat(${days}, 1fr)` : '', + }, + borderStyle: 'solid', + borderColor: theme.palette.grey[300], + borderWidth: '0 0 0 1px', + '&:first-of-type': { + borderWidth: '1px 0 0 1px', + }, + '&:last-of-type': { + borderWidth: '0 0 1px 1px', + }, + '& .rs__cell': { + background: theme.palette.background.paper, + position: 'relative', + borderStyle: 'solid', + borderColor: theme.palette.grey[300], + borderWidth: '0 1px 1px 0', + '&.rs__header': { + '& > :first-of-type': { + padding: '2px 5px', + }, + }, + '&.rs__header__center': { + padding: '6px 0px', + }, + '&.rs__time': { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + position: 'sticky', + left: 0, + zIndex: 99, + [theme.breakpoints.down('sm')]: { + writingMode: 'vertical-rl', + }, + }, + '& > button': { + width: '100%', + height: '100%', + borderRadius: 0, + cursor: 'pointer', + '&:hover': { + background: alpha(theme.palette.primary.main, 0.1), + }, + }, + '& .rs__event__item': { + position: 'absolute', + zIndex: 1, + }, + '& .rs__multi_day': { + position: 'absolute', + zIndex: 1, + textOverflow: 'ellipsis', + }, + '& .rs__block_col': { + display: 'block', + position: 'relative', + }, + '& .rs__hover__op': { + cursor: 'pointer', + '&:hover': { + opacity: 0.7, + textDecoration: 'underline', + }, + }, + '&:not(.rs__time)': { + minWidth: 65, + }, + }, +})) + +export const EventItemPaper = styled(Paper)<{ disabled?: boolean }>(({ disabled }) => ({ + width: '99.5%', + height: '100%', + display: 'block', + cursor: disabled ? 'not-allowed' : 'pointer', + overflow: 'hidden', + '& .MuiButtonBase-root': { + width: '100%', + height: '100%', + display: 'block', + textAlign: 'left', + '& > div': { + height: '100%', + // padding: "2px 4px", + }, + }, +})) + +export const PopperInner = styled('div')(({ theme }) => ({ + maxWidth: '100%', + width: 400, + '& > div': { + padding: '5px 10px', + '& .rs__popper_actions': { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + '& .MuiIconButton-root': { + color: theme.palette.primary.contrastText, + }, + }, + }, +})) + +export const EventActions = styled('div')(({ theme }) => ({ + display: 'inherit', + '& .MuiIconButton-root': { + color: theme.palette.primary.contrastText, + }, + '& .MuiButton-root': { + '&.delete': { + color: theme.palette.error.main, + }, + '&.cancel': { + color: theme.palette.action.disabled, + }, + }, +})) + +export const TimeIndicatorBar = styled('div')(({ theme }) => ({ + position: 'absolute', + zIndex: 9, + width: '100%', + display: 'flex', + '& > div:first-of-type': { + height: 12, + width: 12, + borderRadius: '50%', + background: theme.palette.error.light, + marginLeft: -6, + marginTop: -5, + }, + '& > div:last-of-type': { + borderTop: `solid 2px ${theme.palette.error.light}`, + width: '100%', + }, +})) diff --git a/backend/src/components/scheduler/types.ts b/backend/src/components/scheduler/types.ts new file mode 100644 index 000000000..2b7d9070e --- /dev/null +++ b/backend/src/components/scheduler/types.ts @@ -0,0 +1,356 @@ +import { DialogProps, GridSize } from '@mui/material' +import { DateCalendarProps } from '@mui/x-date-pickers' +import { Locale } from 'date-fns' +import { DragEvent, ReactNode } from 'react' +import type { RRule } from 'rrule' +import { SelectOption } from './components/inputs/SelectInput' +import { View } from './components/nav/Navigation' +import { Store } from './store/types' +import { DayProps } from './views/Day' +import { StateItem } from './views/Editor' +import { MonthProps } from './views/Month' +import { WeekProps } from './views/Week' + +export type DayHours = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22 + | 23 + | 24; + +export interface CellRenderedProps { + day: Date; + start: Date; + end: Date; + height: number; + onClick(): void; + onDragOver(e: DragEvent): void; + onDragEnter(e: DragEvent): void; + onDragLeave(e: DragEvent): void; + onDrop(e: DragEvent): void; +} +interface CalendarEvent { + event_id: number | string; + title: React.ReactNode; + subtitle?: React.ReactNode; + start: Date; + end: Date; + recurring?: RRule; + disabled?: boolean; + color?: string; + textColor?: string; + editable?: boolean; + deletable?: boolean; + draggable?: boolean; + allDay?: boolean; + /** + * @default " " + * passed as a children to mui component + */ + agendaAvatar?: React.ReactElement | string; +} +export interface Translations { + navigation: Record & { today: string; agenda: string }; + form: { + addTitle: string; + editTitle: string; + confirm: string; + delete: string; + cancel: string; + }; + event: Record & { + title: string; + subtitle?: string; + start: string; + end: string; + allDay: string; + }; + validation?: { + required?: string; + invalidEmail?: string; + onlyNumbers?: string; + min?: string | ((min: number) => string); + max?: string | ((max: number) => string); + }; + moreEvents: string; + noDataToDisplay: string; + loading: string; +} + +export type InputTypes = 'input' | 'date' | 'select' | 'hidden'; + +export type ProcessedEvent = CalendarEvent & Record; + +export interface EventRendererProps + extends Pick< + React.HTMLAttributes, + 'draggable' | 'onDragStart' | 'onDragEnd' | 'onDragOver' | 'onDragEnter' | 'onClick' + > { + event: ProcessedEvent; +} +export interface FieldInputProps { + /** Available to all InputTypes */ + label?: string; + /** Available to all InputTypes */ + placeholder?: string; + /** Available to all InputTypes + * @default false + */ + required?: boolean; + /** Available to all InputTypes + * @default "outline" + */ + variant?: 'standard' | 'filled' | 'outlined'; + /** Available to all InputTypes */ + disabled?: boolean; + /** Available when @input="text" ONLY - Minimum length */ + min?: number; + /** Available when @input="text" ONLY - Maximum length */ + max?: number; + /** Available when @input="text" ONLY - Apply email Regex */ + email?: boolean; + /** Available when @input="text" ONLY - Only numbers(int/float) allowed */ + decimal?: boolean; + /** Available when @input="text" ONLY - Allow Multiline input. Use @rows property to set initial rows height */ + multiline?: boolean; + /** Available when @input="text" ONLY - initial rows height */ + rows?: number; + /** Available when @input="date" ONLY + * @default "datetime" + */ + type?: 'date' | 'datetime'; + /** Available when @input="select" ONLY - Multi-Select input style. + * if you use "default" property with this, make sure your "default" property is an instance of Array + */ + multiple?: 'chips' | 'default'; + /** Available when @input="select" ONLY - display loading spinner instead of expand arrow */ + loading?: boolean; + /** Available when @input="select" ONLY - Custom error message */ + errMsg?: string; + + /* Used for Grid alignment in a single row md | sm | xs */ + md?: GridSize; + /* Used for Grid alignment in a single row md | sm | xs */ + sm?: GridSize; + /* Used for Grid alignment in a single row md | sm | xs */ + xs?: GridSize; +} +export interface FieldProps { + name: string; + type: InputTypes; + /** Required for type="select" */ + options?: Array; + default?: string | number | Date | any; + config?: FieldInputProps; +} + +export type EventActions = 'create' | 'edit'; +export type RemoteQuery = { + start: Date; + end: Date; + view: 'day' | 'week' | 'month'; +}; +export type DefaultResource = { + assignee?: string | number; + text?: string; + subtext?: string; + avatar?: string; + color?: string; +} & Record; +export type ResourceFields = { + idField: string; + textField: string; + subTextField?: string; + avatarField?: string; + colorField?: string; +} & Record; + +export interface SchedulerHelpers { + state: Record; + close(): void; + loading(status: boolean): void; + edited?: ProcessedEvent; + onConfirm(event: ProcessedEvent | ProcessedEvent[], action: EventActions): void; + [resourceKey: string]: unknown; +} + +export type ResourceViewMode = 'default' | 'vertical' | 'tabs' + +export type SchedulerDirection = 'rtl' | 'ltr' + +export type HourFormat = '12' | '24' + +export interface SchedulerProps { + /** Min height of table + * @default 600 + */ + height: number; + /** Initial view to load */ + view: View; + /** Activate Agenda view */ + agenda?: boolean; + /** if true, day rows without event will be shown */ + alwaysShowAgendaDays?: boolean; + /** Month view settings */ + month: MonthProps | null; + /** Week view settings */ + week: WeekProps | null; + /** Day view settings */ + day: DayProps | null; + /** Initial date selected */ + selectedDate: Date; + /** Show/Hide date navigation */ + navigation?: boolean; + /** Show/Hide view navigator */ + disableViewNavigator?: boolean; + /** */ + navigationPickerProps?: Partial< + Omit< + DateCalendarProps, + 'open' | 'onClose' | 'openTo' | 'views' | 'value' | 'readOnly' | 'onChange' + > + >; + /** Events to display */ + events: ProcessedEvent[]; + /** Custom event render method */ + eventRenderer?: (props: EventRendererProps) => ReactNode | null; + /** Async function to load remote data with current view data. */ + getRemoteEvents?(params: RemoteQuery): Promise; + /** Custom additional fields with it's settings */ + fields: FieldProps[]; + /** Table loading state */ + loading?: boolean; + /** Custom loading component */ + loadingComponent?: ReactNode; + /** Async function triggered when add/edit event */ + onConfirm?(event: ProcessedEvent, action: EventActions): Promise; + /** Async function triggered when delete event */ + onDelete?(deletedId: string | number): Promise; + /** Override editor modal */ + customEditor?(scheduler: SchedulerHelpers): ReactNode; + /** Custom viewer/popper component. If used, `viewerExtraComponent` & `viewerTitleComponent` will be ignored */ + customViewer?(event: ProcessedEvent, close: () => void): ReactNode; + /** Additional component in event viewer popper */ + viewerExtraComponent?: + | ReactNode + | ((fields: FieldProps[], event: ProcessedEvent) => ReactNode); + /** Override viewer title component */ + viewerTitleComponent?(event: ProcessedEvent): ReactNode; + /** Override viewer subtitle component */ + viewerSubtitleComponent?(event: ProcessedEvent): ReactNode; + /** if true, the viewer popover will be disabled globally */ + disableViewer?: boolean; + /** Resources array to split event views with resources */ + resources: DefaultResource[]; + /** Map resources fields */ + resourceFields: ResourceFields; + /** Override header component of resource */ + resourceHeaderComponent?(resource: DefaultResource): ReactNode; + /** Triggered when resource tabs changes */ + onResourceChange?(resource: DefaultResource): void; + /** Resource header view mode + * @default "default" + */ + resourceViewMode: ResourceViewMode; + /** Direction of table */ + direction: SchedulerDirection; + /** Editor dialog maxWith + * @default "md" + */ + dialogMaxWidth: DialogProps['maxWidth']; + /** + * date-fns Locale object + */ + locale: Locale; + /** + * Localization + */ + translations: Translations; + /** + * Hour Format + */ + hourFormat: HourFormat; + /** + * Time zone IANA ID: https://data.iana.org/time-zones/releases + */ + timeZone?: string; + /** + * Triggered when event is dropped on time slot. + */ + onEventDrop?( + event: DragEvent, + droppedOn: Date, + updatedEvent: ProcessedEvent, + originalEvent: ProcessedEvent + ): Promise; + /** + * + */ + onEventClick?(event: ProcessedEvent): void; + /** + * Triggered when an event item is being edited from the popover + */ + onEventEdit?(event: ProcessedEvent): void; + /** + * If event is deletable, applied to all events globally, overridden by event specific deletable prop + * @default true + */ + deletable?: boolean; + /** + * If calendar is editable, applied to all events/cells globally, overridden by event specific editable prop + * @default true + */ + editable?: boolean; + /** + * If event is draggable, applied to all events globally, overridden by event specific draggable prop + * @default true + */ + draggable?: boolean; + /** + * Triggered when the `selectedDate` prop changes by navigation date picker or `today` button. + */ + onSelectedDateChange?(date: Date): void; + /** + * Triggered when navigation view changes. + */ + onViewChange?(view: View, agenda?: boolean): void; + /** + * If true, the navigation controller bar will be sticky + */ + stickyNavigation?: boolean; + /** + * Overrides the default behavior of more events button + */ + onClickMore?(date: Date, gotToDay: (day: Date) => void): void; + /** + * + */ + onCellClick?(start: Date, end: Date, resourceKey?: string, resourceVal?: string | number): void; +} + +export interface SchedulerRef { + el: HTMLDivElement; + scheduler: Store; +} + +// export interface Scheduler extends Partial {} diff --git a/backend/src/components/scheduler/views/Day.tsx b/backend/src/components/scheduler/views/Day.tsx new file mode 100644 index 000000000..74fb79263 --- /dev/null +++ b/backend/src/components/scheduler/views/Day.tsx @@ -0,0 +1,216 @@ +import React, { useEffect, useCallback, Fragment, ReactNode } from 'react' +import { Typography } from '@mui/material' +import { + format, + eachMinuteOfInterval, + isToday, + isBefore, + isAfter, + startOfDay, + endOfDay, + addDays, + addMinutes, + set, +} from 'date-fns' +import TodayTypo from '../components/common/TodayTypo' +import EventItem from '../components/events/EventItem' +import { CellRenderedProps, DayHours, DefaultResource, ProcessedEvent } from '../types' +import { + calcCellHeight, + calcMinuteHeight, + filterMultiDaySlot, + filterTodayEvents, + getHourFormat, + getResourcedEvents, +} from '../helpers/generals' +import { WithResources } from '../components/common/WithResources' +import Cell from '../components/common/Cell' +import TodayEvents from '../components/events/TodayEvents' +import { TableGrid } from '../styles/styles' +import { MULTI_DAY_EVENT_HEIGHT } from '../helpers/constants' +import useStore from '../hooks/useStore' +import { DayAgenda } from './DayAgenda' + +export interface DayProps { + startHour: DayHours; + endHour: DayHours; + step: number; + cellRenderer?(props: CellRenderedProps): ReactNode; + headRenderer?(day: Date): ReactNode; + hourRenderer?(hour: string): ReactNode; + navigation?: boolean; +} + +const Day = () => { + const { + day, + selectedDate, + events, + height, + getRemoteEvents, + triggerLoading, + handleState, + resources, + resourceFields, + resourceViewMode, + fields, + direction, + locale, + hourFormat, + timeZone, + stickyNavigation, + agenda, + } = useStore() + + const { startHour, endHour, step, cellRenderer, headRenderer, hourRenderer } = day! + const START_TIME = set(selectedDate, { hours: startHour, minutes: 0, seconds: 0 }) + const END_TIME = set(selectedDate, { hours: endHour, minutes: -step, seconds: 0 }) + const hours = eachMinuteOfInterval( + { + start: START_TIME, + end: END_TIME, + }, + { step } + ) + const CELL_HEIGHT = calcCellHeight(height, hours.length) + const MINUTE_HEIGHT = calcMinuteHeight(CELL_HEIGHT, step) + const hFormat = getHourFormat(hourFormat) + + const fetchEvents = useCallback(async () => { + try { + triggerLoading(true) + const start = addDays(START_TIME, -1) + const end = addDays(END_TIME, 1) + const _events = await getRemoteEvents!({ + start, + end, + view: 'day', + }) + if (_events && _events?.length) { + handleState(_events, 'events') + } + } finally { + triggerLoading(false) + } + // eslint-disable-next-line + }, [selectedDate, getRemoteEvents]); + + useEffect(() => { + if (getRemoteEvents instanceof Function) { + fetchEvents() + } + }, [fetchEvents, getRemoteEvents]) + + const renderMultiDayEvents = (_events: ProcessedEvent[]) => { + const todayMulti = filterMultiDaySlot(_events, selectedDate, timeZone) + return ( +
          + {todayMulti.map((event, i) => { + const hasPrev = isBefore(event.start, startOfDay(selectedDate)) + const hasNext = isAfter(event.end, endOfDay(selectedDate)) + return ( +
          + +
          + ) + })} +
          + ) + } + + const renderTable = (resource?: DefaultResource) => { + let resourcedEvents = events + if (resource) { + resourcedEvents = getResourcedEvents(events, resource, resourceFields, fields) + } + + if (agenda) { + return + } + + // Equalizing multi-day section height + const shouldEqualize = resources.length && resourceViewMode === 'default' + const allWeekMulti = filterMultiDaySlot( + shouldEqualize ? events : resourcedEvents, + selectedDate, + timeZone + ) + const headerHeight = MULTI_DAY_EVENT_HEIGHT * allWeekMulti.length + 45 + return ( + <> + {/* Header */} + + + + {typeof headRenderer === 'function' ? ( +
          {headRenderer(selectedDate)}
          + ) : ( + + )} + {renderMultiDayEvents(resourcedEvents)} +
          +
          + + {/* Body */} + {hours.map((h, i) => { + const start = new Date(`${format(selectedDate, 'yyyy/MM/dd')} ${format(h, hFormat)}`) + const end = addMinutes(start, step) + const field = resourceFields.idField + return ( + + {/* Time Cells */} + + {typeof hourRenderer === 'function' ? ( +
          {hourRenderer(format(h, hFormat, { locale }))}
          + ) : ( + {format(h, hFormat, { locale })} + )} +
          + + {/* Events of this day - run once on the top hour column */} + {i === 0 && ( + + )} + {/* Cell */} + + +
          + ) + })} +
          + + ) + } + + return resources.length ? : renderTable() +} + +export { Day } diff --git a/backend/src/components/scheduler/views/DayAgenda.tsx b/backend/src/components/scheduler/views/DayAgenda.tsx new file mode 100644 index 000000000..f1a231dff --- /dev/null +++ b/backend/src/components/scheduler/views/DayAgenda.tsx @@ -0,0 +1,46 @@ +import React, { useMemo } from 'react' +import { format } from 'date-fns' +import { Typography } from '@mui/material' +import { AgendaDiv } from '../styles/styles' +import { ProcessedEvent } from '../types' +import useStore from '../hooks/useStore' +import { filterTodayAgendaEvents } from '../helpers/generals' +import AgendaEventsList from '../components/events/AgendaEventsList' +import EmptyAgenda from '../components/events/EmptyAgenda' + +type Props = { + events: ProcessedEvent[]; +}; +const DayAgenda = ({ events }: Props) => { + const { day, locale, selectedDate, translations, alwaysShowAgendaDays } = useStore() + const { headRenderer } = day! + + const dayEvents = useMemo(() => filterTodayAgendaEvents(events, selectedDate), [events, selectedDate]) + + if (!alwaysShowAgendaDays && !dayEvents.length) { + return + } + + return ( + +
          +
          + {typeof headRenderer === 'function' ? ( +
          {headRenderer(selectedDate)}
          + ) : ( + {format(selectedDate, 'dd E', { locale })} + )} +
          +
          + {dayEvents.length > 0 ? ( + + ) : ( + {translations.noDataToDisplay} + )} +
          +
          +
          + ) +} + +export { DayAgenda } diff --git a/backend/src/components/scheduler/views/Editor.tsx b/backend/src/components/scheduler/views/Editor.tsx new file mode 100644 index 000000000..e6ab2f004 --- /dev/null +++ b/backend/src/components/scheduler/views/Editor.tsx @@ -0,0 +1,258 @@ +import React, { Fragment, useState } from 'react' +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Grid, + useMediaQuery, + useTheme, +} from '@mui/material' +import { addMinutes, differenceInMinutes } from 'date-fns' +import { EditorDatePicker } from '../components/inputs/DatePicker' +import { EditorInput } from '../components/inputs/Input' +import { EditorSelect } from '../components/inputs/SelectInput' +import { arraytizeFieldVal, revertTimeZonedDate } from '../helpers/generals' +import useStore from '../hooks/useStore' +import { SelectedRange } from '../store/types' +import { + EventActions, + FieldInputProps, + FieldProps, + InputTypes, + ProcessedEvent, + SchedulerHelpers, +} from '../types' + +export type StateItem = { + value: any; + validity: boolean; + type: InputTypes; + config?: FieldInputProps; +}; + +export type StateEvent = (ProcessedEvent & SelectedRange) | Record; + +const initialState = (fields: FieldProps[], event?: StateEvent): Record => { + const customFields = {} as Record + for (const field of fields) { + const defVal = arraytizeFieldVal(field, field.default, event) + const eveVal = arraytizeFieldVal(field, event?.[field.name], event) + + customFields[field.name] = { + value: eveVal.value || defVal.value || '', + validity: field.config?.required ? !!eveVal.validity || !!defVal.validity : true, + type: field.type, + config: field.config, + } + } + + return { + event_id: { + value: event?.event_id || null, + validity: true, + type: 'hidden', + }, + title: { + value: event?.title || '', + validity: !!event?.title, + type: 'input', + config: { label: 'Title', required: true, min: 3 }, + }, + subtitle: { + value: event?.subtitle || '', + validity: true, + type: 'input', + config: { label: 'Subtitle', required: false }, + }, + start: { + value: event?.start || new Date(), + validity: true, + type: 'date', + config: { label: 'Start', sm: 6 }, + }, + end: { + value: event?.end || new Date(), + validity: true, + type: 'date', + config: { label: 'End', sm: 6 }, + }, + ...customFields, + } +} + +const Editor = () => { + const { + fields, + dialog, + triggerDialog, + selectedRange, + selectedEvent, + resourceFields, + selectedResource, + triggerLoading, + onConfirm, + customEditor, + confirmEvent, + dialogMaxWidth, + translations, + timeZone, + } = useStore() + const [state, setState] = useState(initialState(fields, selectedEvent || selectedRange)) + const [touched, setTouched] = useState(false) + const theme = useTheme() + const isMobile = useMediaQuery(theme.breakpoints.down('sm')) + + const handleEditorState = (name: string, value: any, validity: boolean) => { + setState((prev) => ({ + ...prev, + [name]: { ...prev[name], value, validity }, + })) + } + + const handleClose = (clearState?: boolean) => { + if (clearState) { + setState(initialState(fields)) + } + triggerDialog(false) + } + + const handleConfirm = async () => { + let body = {} as ProcessedEvent + for (const key in state) { + if (Object.prototype.hasOwnProperty.call(state, key)) { + body[key] = state[key].value + if (!customEditor && !state[key].validity) { + setTouched(true) + return + } + } + } + try { + triggerLoading(true) + // Auto fix date + body.end = body.start >= body.end + ? addMinutes(body.start, differenceInMinutes(selectedRange?.end ?? new Date(), selectedRange?.start ?? new Date())) + : body.end + // Specify action + const action: EventActions = selectedEvent?.event_id ? 'edit' : 'create' + // Trigger custom/remote when provided + if (onConfirm) { + body = await onConfirm(body, action) + } else { + // Create/Edit local data + body.event_id = selectedEvent?.event_id || Date.now().toString(36) + Math.random().toString(36).slice(2) + } + + body.start = revertTimeZonedDate(body.start, timeZone) + body.end = revertTimeZonedDate(body.end, timeZone) + + confirmEvent(body, action) + handleClose(true) + } catch (error) { + console.error(error) + } finally { + triggerLoading(false) + } + } + const renderInputs = (key: string) => { + const stateItem = state[key] + switch (stateItem.type) { + case 'input': + return ( + + ) + case 'date': + return ( + handleEditorState(...args, true)} + touched={touched} + {...stateItem.config} + label={translations.event[key] || stateItem.config?.label} + /> + ) + case 'select': { + const field = fields.find((f) => f.name === key) + return ( + + ) + } + default: + return '' + } + } + + const renderEditor = () => { + if (customEditor) { + const schedulerHelpers: SchedulerHelpers = { + state, + close: () => triggerDialog(false), + loading: (load) => triggerLoading(load), + edited: selectedEvent, + onConfirm: confirmEvent, + [resourceFields.idField]: selectedResource, + } + return customEditor(schedulerHelpers) + } + return ( + <> + + {selectedEvent ? translations.form.editTitle : translations.form.addTitle} + + + + {Object.keys(state).map((key) => { + const item = state[key] + return ( + + {renderInputs(key)} + + ) + })} + + + + + + + + ) + } + + return ( + { + triggerDialog(false) + }} + > + {renderEditor()} + + ) +} + +export default Editor diff --git a/backend/src/components/scheduler/views/Month.tsx b/backend/src/components/scheduler/views/Month.tsx new file mode 100644 index 000000000..35c2f4fd8 --- /dev/null +++ b/backend/src/components/scheduler/views/Month.tsx @@ -0,0 +1,92 @@ +import React, { useEffect, useCallback, ReactNode } from 'react' +import { addDays, eachWeekOfInterval, endOfMonth, startOfMonth } from 'date-fns' +import { CellRenderedProps, DayHours, DefaultResource } from '../types' +import { getResourcedEvents, sortEventsByTheEarliest } from '../helpers/generals' +import { WithResources } from '../components/common/WithResources' +import useStore from '../hooks/useStore' +import { MonthAgenda } from './MonthAgenda' +import MonthTable from '../components/month/MonthTable' + +export type WeekDays = 0 | 1 | 2 | 3 | 4 | 5 | 6; +export interface MonthProps { + weekDays: WeekDays[]; + weekStartOn: WeekDays; + startHour: DayHours; + endHour: DayHours; + cellRenderer?(props: CellRenderedProps): ReactNode; + headRenderer?(day: Date): ReactNode; + navigation?: boolean; + disableGoToDay?: boolean; +} + +const Month = () => { + const { + month, + selectedDate, + events, + getRemoteEvents, + triggerLoading, + handleState, + resources, + resourceFields, + fields, + agenda, + } = useStore() + + const { weekStartOn, weekDays } = month! + const monthStart = startOfMonth(selectedDate) + const monthEnd = endOfMonth(selectedDate) + const eachWeekStart = eachWeekOfInterval( + { + start: monthStart, + end: monthEnd, + }, + { weekStartsOn: weekStartOn } + ) + const daysList = weekDays.map((d) => addDays(eachWeekStart[0], d)) + + const fetchEvents = useCallback(async () => { + try { + triggerLoading(true) + const start = eachWeekStart[0] + const end = addDays(eachWeekStart[eachWeekStart.length - 1], daysList.length) + const _events = await getRemoteEvents!({ + start, + end, + view: 'month', + }) + if (_events && _events?.length) { + handleState(_events, 'events') + } + } finally { + triggerLoading(false) + } + // eslint-disable-next-line + }, [selectedDate, getRemoteEvents]); + + useEffect(() => { + if (getRemoteEvents instanceof Function) { + fetchEvents() + } + }, [fetchEvents, getRemoteEvents]) + + const renderTable = useCallback( + (resource?: DefaultResource) => { + if (agenda) { + let resourcedEvents = sortEventsByTheEarliest(events) + if (resource) { + resourcedEvents = getResourcedEvents(events, resource, resourceFields, fields) + } + + return + } + + return + }, + [agenda, daysList, eachWeekStart, events, fields, resourceFields] + ) + + return resources.length ? : renderTable() +} + +export { Month } diff --git a/backend/src/components/scheduler/views/MonthAgenda.tsx b/backend/src/components/scheduler/views/MonthAgenda.tsx new file mode 100644 index 000000000..c99972762 --- /dev/null +++ b/backend/src/components/scheduler/views/MonthAgenda.tsx @@ -0,0 +1,79 @@ +import React, { useMemo } from 'react' +import { format, isSameMonth, getDaysInMonth, isToday } from 'date-fns' +import { Typography } from '@mui/material' +import { AgendaDiv } from '../styles/styles' +import { ProcessedEvent } from '../types' +import useStore from '../hooks/useStore' +import { filterTodayAgendaEvents, isTimeZonedToday } from '../helpers/generals' +import AgendaEventsList from '../components/events/AgendaEventsList' +import EmptyAgenda from '../components/events/EmptyAgenda' + +type Props = { + events: ProcessedEvent[]; +}; +const MonthAgenda = ({ events }: Props) => { + const { + month, + handleGotoDay, + locale, + timeZone, + selectedDate, + translations, + alwaysShowAgendaDays, + } = useStore() + const { disableGoToDay, headRenderer } = month! + const daysOfMonth = getDaysInMonth(selectedDate) + const daysList = Array.from({ length: daysOfMonth }, (_, i) => i + 1) + + const monthEvents = useMemo(() => events.filter((event) => isSameMonth(event.start, selectedDate)), [events, selectedDate]) + + if (!alwaysShowAgendaDays && !monthEvents.length) { + return + } + + return ( + + {daysList.map((i) => { + const day = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), i) + const today = isTimeZonedToday({ dateLeft: day, timeZone }) + const dayEvents = filterTodayAgendaEvents(events, day) + + if (!alwaysShowAgendaDays && !dayEvents.length) return null + + return ( +
          +
          + {typeof headRenderer === 'function' ? ( +
          {headRenderer(day)}
          + ) : ( + { + e.stopPropagation() + if (!disableGoToDay) { + handleGotoDay(day) + } + }} + > + {format(day, 'dd E', { locale })} + + )} +
          +
          + {dayEvents.length > 0 ? ( + + ) : ( + {translations.noDataToDisplay} + )} +
          +
          + ) + })} +
          + ) +} + +export { MonthAgenda } diff --git a/backend/src/components/scheduler/views/Week.tsx b/backend/src/components/scheduler/views/Week.tsx new file mode 100644 index 000000000..1c9e8f56a --- /dev/null +++ b/backend/src/components/scheduler/views/Week.tsx @@ -0,0 +1,104 @@ +import React, { useEffect, useCallback, ReactNode } from 'react' +import { startOfWeek, addDays, eachMinuteOfInterval, endOfDay, startOfDay, set } from 'date-fns' +import { CellRenderedProps, DayHours, DefaultResource } from '../types' +import { WeekDays } from './Month' +import { calcCellHeight, calcMinuteHeight, getResourcedEvents } from '../helpers/generals' +import { WithResources } from '../components/common/WithResources' +import useStore from '../hooks/useStore' +import { WeekAgenda } from './WeekAgenda' +import WeekTable from '../components/week/WeekTable' + +export interface WeekProps { + weekDays: WeekDays[]; + weekStartOn: WeekDays; + startHour: DayHours; + endHour: DayHours; + step: number; + cellRenderer?(props: CellRenderedProps): ReactNode; + headRenderer?(day: Date): ReactNode; + hourRenderer?(hour: string): ReactNode; + navigation?: boolean; + disableGoToDay?: boolean; +} + +const Week = () => { + const { + week, + selectedDate, + height, + events, + getRemoteEvents, + triggerLoading, + handleState, + resources, + resourceFields, + fields, + agenda, + } = useStore() + const { weekStartOn, weekDays, startHour, endHour, step } = week! + const _weekStart = startOfWeek(selectedDate, { weekStartsOn: weekStartOn }) + const daysList = weekDays.map((d) => addDays(_weekStart, d)) + const weekStart = startOfDay(daysList[0]) + const weekEnd = endOfDay(daysList[daysList.length - 1]) + const START_TIME = set(selectedDate, { hours: startHour, minutes: 0, seconds: 0 }) + const END_TIME = set(selectedDate, { hours: endHour, minutes: -step, seconds: 0 }) + const hours = eachMinuteOfInterval( + { + start: START_TIME, + end: END_TIME, + }, + { step } + ) + const CELL_HEIGHT = calcCellHeight(height, hours.length) + const MINUTE_HEIGHT = calcMinuteHeight(CELL_HEIGHT, step) + + const fetchEvents = useCallback(async () => { + try { + triggerLoading(true) + + const _events = await getRemoteEvents!({ + start: weekStart, + end: weekEnd, + view: 'week', + }) + if (Array.isArray(_events)) { + handleState(_events, 'events') + } + } finally { + triggerLoading(false) + } + // eslint-disable-next-line + }, [selectedDate, getRemoteEvents]); + + useEffect(() => { + if (getRemoteEvents instanceof Function) { + fetchEvents() + } + }, [fetchEvents, getRemoteEvents]) + + const renderTable = (resource?: DefaultResource) => { + let resourcedEvents = events + if (resource) { + resourcedEvents = getResourcedEvents(events, resource, resourceFields, fields) + } + + if (agenda) { + return + } + + return ( + + ) + } + + return resources.length ? : renderTable() +} + +export { Week } diff --git a/backend/src/components/scheduler/views/WeekAgenda.tsx b/backend/src/components/scheduler/views/WeekAgenda.tsx new file mode 100644 index 000000000..2079334f7 --- /dev/null +++ b/backend/src/components/scheduler/views/WeekAgenda.tsx @@ -0,0 +1,69 @@ +import React, { useMemo } from 'react' +import { format, isToday } from 'date-fns' +import { Typography } from '@mui/material' +import { AgendaDiv } from '../styles/styles' +import { ProcessedEvent } from '../types' +import useStore from '../hooks/useStore' +import { filterTodayAgendaEvents, isTimeZonedToday } from '../helpers/generals' +import AgendaEventsList from '../components/events/AgendaEventsList' +import EmptyAgenda from '../components/events/EmptyAgenda' + +type Props = { + daysList: Date[]; + events: ProcessedEvent[]; +}; +const WeekAgenda = ({ daysList, events }: Props) => { + const { week, handleGotoDay, locale, timeZone, translations, alwaysShowAgendaDays } = useStore() + const { disableGoToDay, headRenderer } = week! + + const hasEvents = useMemo(() => daysList.some((day) => filterTodayAgendaEvents(events, day).length > 0), [daysList, events]) + + if (!alwaysShowAgendaDays && !hasEvents) { + return + } + + return ( + + {daysList.map((day) => { + const today = isTimeZonedToday({ dateLeft: day, timeZone }) + const dayEvents = filterTodayAgendaEvents(events, day) + + if (!alwaysShowAgendaDays && !dayEvents.length) return null + + return ( +
          +
          + {typeof headRenderer === 'function' ? ( +
          {headRenderer(day)}
          + ) : ( + { + e.stopPropagation() + if (!disableGoToDay) { + handleGotoDay(day) + } + }} + > + {format(day, 'dd E', { locale })} + + )} +
          +
          + {dayEvents.length > 0 ? ( + + ) : ( + {translations.noDataToDisplay} + )} +
          +
          + ) + })} +
          + ) +} + +export { WeekAgenda } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 07c6a3955..03a2a362a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,23 +10,23 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.4.0", - "@mui/material": "^6.4.0", - "@mui/x-data-grid": "^7.24.0", - "@mui/x-date-pickers": "^7.24.0", + "@mui/icons-material": "^6.4.1", + "@mui/material": "^6.4.1", + "@mui/x-data-grid": "^7.24.1", + "@mui/x-date-pickers": "^7.24.1", "@stripe/react-stripe-js": "^3.1.1", "@stripe/stripe-js": "^5.5.0", "@types/leaflet": "^1.9.16", "@types/leaflet-boundary-canvas": "^1.0.3", - "@types/node": "^22.10.7", + "@types/node": "^22.10.10", "@types/nprogress": "^0.2.3", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -40,18 +40,18 @@ "leaflet-boundary-canvas": "^1.0.0", "localized-strings": "^2.0.3", "nprogress": "^0.2.0", - "react": "^18.3.1", + "react": "^19.0.0", "react-circle-flags": "^0.0.23", - "react-dom": "^18.3.1", + "react-dom": "^19.0.0", "react-ga4": "^2.1.0", - "react-leaflet": "^4.2.1", + "react-leaflet": "^5.0.0", "react-router-dom": "^7.1.3", "react-slick": "^0.30.3", "react-toastify": "^11.0.3", "slick-carousel": "^1.8.1", "typescript": "^5.2.2", "validator": "^13.12.0", - "vite": "^6.0.7" + "vite": "^6.0.11" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.25.9", @@ -59,7 +59,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.13.2", + "stylelint": "^16.14.0", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" @@ -1408,9 +1408,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.0.tgz", - "integrity": "sha512-6u74wi+9zeNlukrCtYYET8Ed/n9AS27DiaXCZKAD3TRGFaqiyYSsQgN2disW83pI/cM1Q2lJY1JX4YfwvNtlNw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.1.tgz", + "integrity": "sha512-SfDLWMV5b5oXgDf3NTa2hCTPC1d2defhDH2WgFKmAiejC4mSfXYbyi+AFCLzpizauXhgBm8OaZy9BHKnrSpahQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -1418,9 +1418,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.0.tgz", - "integrity": "sha512-zF0Vqt8a+Zp2Oz8P+WvJflba6lLe3PhxIz1NNqn+n4A+wKLPbkeqY8ShmKjPyiCTg0RMbPrp993oUDl9xGsDlQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.1.tgz", + "integrity": "sha512-wsxFcUTQxt4s+7Bg4GgobqRjyaHLmZGNOs+HJpbwrwmLbT6mhIJxhpqsKzzWq9aDY8xIe7HCjhpH7XI5UD6teA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -1433,7 +1433,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.4.0", + "@mui/material": "^6.4.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1444,16 +1444,16 @@ } }, "node_modules/@mui/material": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.0.tgz", - "integrity": "sha512-hNIgwdM9U3DNmowZ8mU59oFmWoDKjc92FqQnQva3Pxh6xRKWtD2Ej7POUHMX8Dwr1OpcSUlT2+tEMeLb7WYsIg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.1.tgz", + "integrity": "sha512-MFBfia6UiKxyoLeGkAh8M15bkeDmfnsUTMRJd/vTQue6YQ8AQ6lw9HqDthyYghzDEWIvZO/lQQzLrZE8XwNJLA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.0", - "@mui/system": "^6.4.0", + "@mui/core-downloads-tracker": "^6.4.1", + "@mui/system": "^6.4.1", "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.1", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", @@ -1472,7 +1472,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.0", + "@mui/material-pigment-css": "^6.4.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1493,13 +1493,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.0.tgz", - "integrity": "sha512-rNHci8MP6NOdEWAfZ/RBMO5Rhtp1T6fUDMSmingg9F1T6wiUeodIQ+NuTHh2/pMoUSeP9GdHdgMhMmfsXxOMuw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.1.tgz", + "integrity": "sha512-DcT7mwK89owwgcEuiE7w458te4CIjHbYWW6Kn6PiR6eLtxBsoBYphA968uqsQAOBQDpbYxvkuFLwhgk4bxoN/Q==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.1", "prop-types": "^15.8.1" }, "engines": { @@ -1554,16 +1554,16 @@ } }, "node_modules/@mui/system": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.0.tgz", - "integrity": "sha512-wTDyfRlaZCo2sW2IuOsrjeE5dl0Usrs6J7DxE3GwNCVFqS5wMplM2YeNiV3DO7s53RfCqbho+gJY6xaB9KThUA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.1.tgz", + "integrity": "sha512-rgQzgcsHCTtzF9MZ+sL0tOhf2ZBLazpjrujClcb4Siju5lTrK0xX4PsiropActzCemNfM+mOu+0jezAVnfRK8g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.4.0", + "@mui/private-theming": "^6.4.1", "@mui/styled-engine": "^6.4.0", "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.1", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1608,9 +1608,9 @@ } }, "node_modules/@mui/utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", - "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.1.tgz", + "integrity": "sha512-iQUDUeYh87SvR4lVojaRaYnQix8BbRV51MxaV6MBmqthecQoxwSbS5e2wnbDJUeFxY2ppV505CiqPLtd0OWkqw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1638,14 +1638,14 @@ } }, "node_modules/@mui/x-data-grid": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.0.tgz", - "integrity": "sha512-goYTKDp+e+dXw7E+WndWUhWXTjX3aTqN8W2dCKhXnmE9Gu8dFwG6Azl7GK9l2m5YHGuqYmpWqcSG9etLdwYaVg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.24.1.tgz", + "integrity": "sha512-4sYTbMwsDotuTd2Cwa2JGTPXPWQs8RGJvocAKnIsNOzNdZNMrikE//HO35snriK8s4dauAApY7RVbeisjpVT+A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.24.0", + "@mui/x-internals": "7.24.1", "clsx": "^2.1.1", "prop-types": "^15.8.1", "reselect": "^5.1.1" @@ -1675,14 +1675,14 @@ } }, "node_modules/@mui/x-date-pickers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.0.tgz", - "integrity": "sha512-oBM9Yp2H3tJ7qoHB4APQJYxZG4rz6JD4CwLzbzD9o3r+E1HGpGSLhwK3rDEz9VEjbOq8893Z2TGYLLWoyjeFXQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.24.1.tgz", + "integrity": "sha512-ykQugMQHuQKBk3kViW/r0PpubtHQOlrd54bgbdafgTMCeM2VpXvv4zimzOu5IGnM6wEN8hupC7EXZbkrT6x46w==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.24.0", + "@mui/x-internals": "7.24.1", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -1741,9 +1741,9 @@ } }, "node_modules/@mui/x-internals": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.0.tgz", - "integrity": "sha512-lYa/XLltxNMY8YAFDopIHrXda2EAoqMCilyGMuPMz+WTG+b+StlUKqtj8cgFPQ/sa5dQ2fR7R3KJdjLREKUrlQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.1.tgz", + "integrity": "sha512-9BvJzpLJnS9BDphvkiv6v0QOLxbnu8jhwcexFjtCQ2ZyxtVuVsWzGZ2npT9sGOil7+eaFDmWnJtea/tgrPvSwQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", @@ -1806,14 +1806,14 @@ } }, "node_modules/@react-leaflet/core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", - "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", "license": "Hippocratic-2.1", "peerDependencies": { "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" } }, "node_modules/@rollup/pluginutils": { @@ -2195,9 +2195,9 @@ } }, "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2222,22 +2222,21 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "version": "19.0.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", + "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.3.tgz", + "integrity": "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==", "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" + "@types/react": "^19.0.0" } }, "node_modules/@types/react-recaptcha-v3": { @@ -2274,16 +2273,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", - "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", + "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/type-utils": "8.20.0", - "@typescript-eslint/utils": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/type-utils": "8.21.0", + "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2303,15 +2302,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", - "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", + "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4" }, "engines": { @@ -2327,13 +2326,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", - "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", + "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0" + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2344,13 +2343,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", - "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", + "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/utils": "8.21.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -2367,9 +2366,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", - "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", + "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2380,13 +2379,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", - "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", + "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2406,15 +2405,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", - "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", + "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0" + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2429,12 +2428,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", - "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", + "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/types": "8.21.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -6641,9 +6640,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -6660,7 +6659,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6785,13 +6784,10 @@ "license": "MIT" }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } @@ -6809,16 +6805,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" } }, "node_modules/react-ga4": { @@ -6834,17 +6829,17 @@ "license": "MIT" }, "node_modules/react-leaflet": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", - "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", "license": "Hippocratic-2.1", "dependencies": { - "@react-leaflet/core": "^2.1.0" + "@react-leaflet/core": "^3.0.0" }, "peerDependencies": { "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" } }, "node_modules/react-refresh": { @@ -7176,13 +7171,10 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" }, "node_modules/semver": { "version": "7.6.3", @@ -7542,9 +7534,9 @@ } }, "node_modules/stylelint": { - "version": "16.13.2", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.13.2.tgz", - "integrity": "sha512-wDlgh0mRO9RtSa3TdidqHd0nOG8MmUyVKl+dxA6C1j8aZRzpNeEgdhFmU5y4sZx4Fc6r46p0fI7p1vR5O2DZqA==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.0.tgz", + "integrity": "sha512-orePw2dKxzXC0hd1VmxrDBqgf1KUV9DYsZY4guKLE9XcQD7m0BxVnWMaoQqMNsQIG14MyyTHf6zoajvOnDra8g==", "dev": true, "funding": [ { @@ -7576,7 +7568,7 @@ "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^7.0.1", + "ignore": "^7.0.3", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", @@ -7585,7 +7577,7 @@ "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.4.49", + "postcss": "^8.5.1", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.0.0", @@ -8121,9 +8113,9 @@ } }, "node_modules/vite": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", - "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz", + "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==", "license": "MIT", "dependencies": { "esbuild": "^0.24.2", diff --git a/frontend/package.json b/frontend/package.json index 1b0c4f064..3200877f2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,30 +13,30 @@ "preview": "vite preview", "fix": "eslint --fix .", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh,react,react-dom,@types/react,@types/react-dom,react-leaflet", + "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", "stylelint": "stylelint \"src/**/*.css\"", "stylelint:fix": "stylelint \"src/**/*.css\" --fix" }, "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.4.0", - "@mui/material": "^6.4.0", - "@mui/x-data-grid": "^7.24.0", - "@mui/x-date-pickers": "^7.24.0", + "@mui/icons-material": "^6.4.1", + "@mui/material": "^6.4.1", + "@mui/x-data-grid": "^7.24.1", + "@mui/x-date-pickers": "^7.24.1", "@stripe/react-stripe-js": "^3.1.1", "@stripe/stripe-js": "^5.5.0", "@types/leaflet": "^1.9.16", "@types/leaflet-boundary-canvas": "^1.0.3", - "@types/node": "^22.10.7", + "@types/node": "^22.10.10", "@types/nprogress": "^0.2.3", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", + "@typescript-eslint/eslint-plugin": "^8.21.0", + "@typescript-eslint/parser": "^8.21.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", @@ -50,11 +50,11 @@ "leaflet-boundary-canvas": "^1.0.0", "localized-strings": "^2.0.3", "nprogress": "^0.2.0", - "react": "^18.3.1", + "react": "^19.0.0", "react-circle-flags": "^0.0.23", - "react-dom": "^18.3.1", + "react-dom": "^19.0.0", "react-ga4": "^2.1.0", - "react-leaflet": "^4.2.1", + "react-leaflet": "^5.0.0", "react-router-dom": "^7.1.3", "react-slick": "^0.30.3", "react-toastify": "^11.0.3", @@ -69,7 +69,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.13.2", + "stylelint": "^16.14.0", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" diff --git a/packages/reactjs-social-login/package-lock.json b/packages/reactjs-social-login/package-lock.json index be4daf315..e914cec07 100644 --- a/packages/reactjs-social-login/package-lock.json +++ b/packages/reactjs-social-login/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "devDependencies": { "@types/node": "^22.10.3", - "@types/react": "^18.3.12", + "@types/react": "^19.0.8", "microbundle-crl": "^0.13.11", - "react": "^18.3.1", + "react": "^19.0.0", "rimraf": "^6.0.1", "typescript": "^5.7.2" } @@ -2317,13 +2317,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/q": { "version": "1.5.8", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", @@ -2332,13 +2325,12 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "version": "19.0.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", + "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", "dev": true, "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, @@ -5243,19 +5235,6 @@ "dev": true, "license": "MIT" }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7127,14 +7106,11 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "dev": true, "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } diff --git a/packages/reactjs-social-login/package.json b/packages/reactjs-social-login/package.json index cd7608b72..58b7ac990 100644 --- a/packages/reactjs-social-login/package.json +++ b/packages/reactjs-social-login/package.json @@ -10,9 +10,9 @@ }, "devDependencies": { "@types/node": "^22.10.3", - "@types/react": "^18.3.12", + "@types/react": "^19.0.8", "microbundle-crl": "^0.13.11", - "react": "^18.3.1", + "react": "^19.0.0", "rimraf": "^6.0.1", "typescript": "^5.7.2" }, From f10b0c602414001c387f629c3bc35b7b88d05f06 Mon Sep 17 00:00:00 2001 From: aelassas Date: Sun, 26 Jan 2025 21:46:27 +0100 Subject: [PATCH 36/42] Remove unused code and comments from SelectInput and default props --- .../scheduler/components/inputs/SelectInput.tsx | 11 ++++++----- backend/src/components/scheduler/store/default.ts | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/src/components/scheduler/components/inputs/SelectInput.tsx b/backend/src/components/scheduler/components/inputs/SelectInput.tsx index 3196729c7..0644f1658 100644 --- a/backend/src/components/scheduler/components/inputs/SelectInput.tsx +++ b/backend/src/components/scheduler/components/inputs/SelectInput.tsx @@ -80,11 +80,12 @@ const EditorSelect = ({ } // eslint-disable-next-line }, [touched]); - const handleTouched = () => { - if (!state.touched) { - setState((prev) => ({ ...prev, touched: true, errorMsg: errMsg || prev.errorMsg })) - } - } + + // const handleTouched = () => { + // if (!state.touched) { + // setState((prev) => ({ ...prev, touched: true, errorMsg: errMsg || prev.errorMsg })) + // } + // } return ( <> diff --git a/backend/src/components/scheduler/store/default.ts b/backend/src/components/scheduler/store/default.ts index 02979b357..dcfc86079 100644 --- a/backend/src/components/scheduler/store/default.ts +++ b/backend/src/components/scheduler/store/default.ts @@ -84,9 +84,9 @@ const defaultViews = (props: Partial) => { export const defaultProps = (props: Partial) => { const { - month, - week, - day, + // month, + // week, + // day, translations, resourceFields, view, From b71072ffd16d62630a455e94b0a7f61f64ff2cc1 Mon Sep 17 00:00:00 2001 From: aelassas Date: Mon, 27 Jan 2025 02:09:48 +0100 Subject: [PATCH 37/42] Initialize event position to zero for new days in scheduler --- backend/src/components/scheduler/positionManger/provider.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/components/scheduler/positionManger/provider.tsx b/backend/src/components/scheduler/positionManger/provider.tsx index 265bd1969..13a48f8b0 100644 --- a/backend/src/components/scheduler/positionManger/provider.tsx +++ b/backend/src/components/scheduler/positionManger/provider.tsx @@ -24,7 +24,8 @@ const setEventPositions = (events: ProcessedEvent[]) => { } slots[day][event.event_id] = position } else { - slots[day] = { [event.event_id]: position } + // slots[day] = { [event.event_id]: position } + slots[day] = { [event.event_id]: 0 } } } From 84cc09fe69262074b58a37693e8f70eaa9703dc4 Mon Sep 17 00:00:00 2001 From: aelassas Date: Mon, 27 Jan 2025 13:40:20 +0100 Subject: [PATCH 38/42] Fix: sorting logic in car retrieval functions --- api/src/controllers/carController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/controllers/carController.ts b/api/src/controllers/carController.ts index 1a6196f8a..a58758653 100644 --- a/api/src/controllers/carController.ts +++ b/api/src/controllers/carController.ts @@ -555,7 +555,7 @@ export const getCars = async (req: Request, res: Response) => { { $facet: { resultData: [{ $sort: { updatedAt: -1, _id: 1 } }, { $skip: (page - 1) * size }, { $limit: size }], - // resultData: [{ $sort: { price: 1, _id: 1 } }, { $skip: (page - 1) * size }, { $limit: size }], + // resultData: [{ $sort: { dailyPrice: 1, _id: 1 } }, { $skip: (page - 1) * size }, { $limit: size }], pageInfo: [ { $count: 'totalRecords', @@ -771,7 +771,7 @@ export const getFrontendCars = async (req: Request, res: Response) => { $facet: { resultData: [ { - $sort: { price: 1, fullyBooked: 1, comingSoon: 1, _id: 1 }, + $sort: { fullyBooked: 1, comingSoon: 1, dailyPrice: 1, _id: 1 }, }, { $skip: (page - 1) * size }, { $limit: size }, From e5fb57659c2503d3ed7b888e1d4884744d3296de Mon Sep 17 00:00:00 2001 From: aelassas Date: Mon, 27 Jan 2025 20:01:48 +0100 Subject: [PATCH 39/42] Replace empty string with null in SchedulerComponent and adjust cell height calculation in MonthTable --- .../src/components/scheduler/SchedulerComponent.tsx | 2 +- .../scheduler/components/month/MonthTable.tsx | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/src/components/scheduler/SchedulerComponent.tsx b/backend/src/components/scheduler/SchedulerComponent.tsx index 23aeccebc..2c58df59c 100644 --- a/backend/src/components/scheduler/SchedulerComponent.tsx +++ b/backend/src/components/scheduler/SchedulerComponent.tsx @@ -23,7 +23,7 @@ const SchedulerComponent = forwardRef((_, ref) => { case 'day': return default: - return '' + return null } }, [view]) diff --git a/backend/src/components/scheduler/components/month/MonthTable.tsx b/backend/src/components/scheduler/components/month/MonthTable.tsx index bf3af94d5..bedd00f9c 100644 --- a/backend/src/components/scheduler/components/month/MonthTable.tsx +++ b/backend/src/components/scheduler/components/month/MonthTable.tsx @@ -61,6 +61,7 @@ const MonthTable = ({ daysList, resource, eachWeekStart }: Props) => { resourcedEvents = getResourcedEvents(events, _resource, resourceFields, fields) } const rows: ReactNode[] = [] + const cellHeights: number[] = [] for (const startDay of eachWeekStart) { const cells = weekDays.map((d) => { @@ -78,13 +79,17 @@ const MonthTable = ({ daysList, resource, eachWeekStart }: Props) => { return false }) const isToday = isTimeZonedToday({ dateLeft: today, timeZone }) + const _cellHeight = 27 + 26 * todayEvents.length + 17 + 12 + 10 + cellHeights.push(_cellHeight) + // const cellHeight = Math.max(CELL_HEIGHT, ...cellHeights) + const cellHeight = CELL_HEIGHT return ( - + { handleGotoDay(e) } }} - cellHeight={CELL_HEIGHT} + cellHeight={cellHeight} /> @@ -147,7 +152,7 @@ const MonthTable = ({ daysList, resource, eachWeekStart }: Props) => { return rows }, [ - CELL_HEIGHT, + // CELL_HEIGHT, cellRenderer, daysList, disableGoToDay, From ebc46d2bcb1268712aba5f956fd87e24a5fa54cb Mon Sep 17 00:00:00 2001 From: aelassas Date: Mon, 27 Jan 2025 20:03:00 +0100 Subject: [PATCH 40/42] Restore CELL_HEIGHT constant in MonthTable component --- .../src/components/scheduler/components/month/MonthTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/components/scheduler/components/month/MonthTable.tsx b/backend/src/components/scheduler/components/month/MonthTable.tsx index bedd00f9c..50bca0f18 100644 --- a/backend/src/components/scheduler/components/month/MonthTable.tsx +++ b/backend/src/components/scheduler/components/month/MonthTable.tsx @@ -152,7 +152,7 @@ const MonthTable = ({ daysList, resource, eachWeekStart }: Props) => { return rows }, [ - // CELL_HEIGHT, + CELL_HEIGHT, cellRenderer, daysList, disableGoToDay, From 13cbfcf6b24803f79ee5ce0dccbd2a6889400613 Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 28 Jan 2025 15:22:45 +0100 Subject: [PATCH 41/42] Bump date-fns to 4.1.0 and update dependencies --- api/package-lock.json | 276 ++++++++++++++++++------------------- api/package.json | 18 +-- backend/package-lock.json | 120 ++++++++-------- backend/package.json | 14 +- frontend/package-lock.json | 120 ++++++++-------- frontend/package.json | 14 +- 6 files changed, 274 insertions(+), 288 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index 880607753..8e56cc615 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -10,9 +10,9 @@ "license": "ISC", "dependencies": { "@babel/cli": "^7.26.4", - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.7", "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/preset-env": "^7.26.0", + "@babel/preset-env": "^7.26.7", "@babel/preset-typescript": "^7.26.0", "@types/bcrypt": "^5.0.2", "@types/compression": "^1.7.5", @@ -21,7 +21,7 @@ "@types/express": "^4.17.21", "@types/jest": "^29.5.14", "@types/multer": "^1.4.12", - "@types/node": "^22.10.7", + "@types/node": "^22.12.0", "@types/nodemailer": "^6.4.17", "@types/supertest": "^6.0.2", "@types/validator": "^13.12.2", @@ -46,17 +46,17 @@ "multer": "^1.4.5-lts.1", "nanoid": "^5.0.9", "nocache": "^4.0.0", - "nodemailer": "^6.9.16", + "nodemailer": "^6.10.0", "rimraf": "^6.0.1", - "stripe": "^17.5.0", + "stripe": "^17.6.0", "supertest": "^7.0.0", - "typescript": "^5.4.5", + "typescript": "^5.7.3", "validator": "^13.12.0", "winston": "^3.17.0" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.31.0", @@ -195,30 +195,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -242,13 +242,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -269,26 +269,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -446,9 +433,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -543,25 +530,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -920,12 +907,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1095,12 +1082,11 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -1316,12 +1302,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1581,12 +1567,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1678,14 +1664,14 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", + "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1699,7 +1685,7 @@ "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.25.9", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1710,7 +1696,7 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.25.9", "@babel/plugin-transform-function-name": "^7.25.9", @@ -1719,12 +1705,12 @@ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -1741,7 +1727,7 @@ "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", @@ -1827,16 +1813,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1845,9 +1831,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -3296,9 +3282,9 @@ } }, "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -3407,17 +3393,17 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", - "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/type-utils": "8.20.0", - "@typescript-eslint/utils": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3437,16 +3423,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", - "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4" }, "engines": { @@ -3462,14 +3448,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", - "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0" + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3480,14 +3466,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", - "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -3504,9 +3490,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", - "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", "dev": true, "license": "MIT", "engines": { @@ -3518,14 +3504,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", - "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3571,16 +3557,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", - "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0" + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3595,13 +3581,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", - "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/types": "8.22.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -8342,9 +8328,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", - "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -9883,9 +9869,9 @@ } }, "node_modules/stripe": { - "version": "17.5.0", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-17.5.0.tgz", - "integrity": "sha512-kcyeAkDFjGsVl17FqnG7q/+xIjt0ZjOo9Dm+q8deAvs2Xe4iAHrhxyoP4etUVFc+/LZJANjIPVR+ZOnt9hr/Ug==", + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-17.6.0.tgz", + "integrity": "sha512-+HB6+SManp0gSRB0dlPmXO+io18krlAe0uimXhhIkL/RG/VIRigkfoM3QDJPkqbuSW0XsA6uzsivNCJU1ELEDA==", "license": "MIT", "dependencies": { "@types/node": ">=8.1.0", @@ -10340,9 +10326,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/api/package.json b/api/package.json index d464ea3f6..086eb7db9 100644 --- a/api/package.json +++ b/api/package.json @@ -10,7 +10,7 @@ "start": "npm run build && node dist/src", "test": "rimraf coverage && npm run build && cross-env NODE_OPTIONS=\"--experimental-vm-modules\" jest --coverage --no-cache", "lint": "eslint --ext .ts .", - "ncu": "ncu -u -x typescript,eslint,@types/express", + "ncu": "ncu -u -x eslint,@types/express", "script:db": "npm run build && node dist/scripts/db.js" }, "keywords": [], @@ -18,9 +18,9 @@ "license": "ISC", "dependencies": { "@babel/cli": "^7.26.4", - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.7", "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/preset-env": "^7.26.0", + "@babel/preset-env": "^7.26.7", "@babel/preset-typescript": "^7.26.0", "@types/bcrypt": "^5.0.2", "@types/compression": "^1.7.5", @@ -29,7 +29,7 @@ "@types/express": "^4.17.21", "@types/jest": "^29.5.14", "@types/multer": "^1.4.12", - "@types/node": "^22.10.7", + "@types/node": "^22.12.0", "@types/nodemailer": "^6.4.17", "@types/supertest": "^6.0.2", "@types/validator": "^13.12.2", @@ -54,17 +54,17 @@ "multer": "^1.4.5-lts.1", "nanoid": "^5.0.9", "nocache": "^4.0.0", - "nodemailer": "^6.9.16", + "nodemailer": "^6.10.0", "rimraf": "^6.0.1", - "stripe": "^17.5.0", + "stripe": "^17.6.0", "supertest": "^7.0.0", - "typescript": "^5.4.5", + "typescript": "^5.7.3", "validator": "^13.12.0", "winston": "^3.17.0" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.31.0", diff --git a/backend/package-lock.json b/backend/package-lock.json index 16b625322..fc2848d56 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -14,16 +14,16 @@ "@mui/material": "^6.4.1", "@mui/x-data-grid": "^7.24.1", "@mui/x-date-pickers": "^7.24.1", - "@types/node": "^22.10.10", + "@types/node": "^22.12.0", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.21.0", - "@typescript-eslint/parser": "^8.21.0", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", - "date-fns": "^3.2.0", + "date-fns": "^4.1.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", @@ -34,7 +34,7 @@ "react-router-dom": "^7.1.3", "react-toastify": "^11.0.3", "rrule": "^2.8.1", - "typescript": "^5.2.2", + "typescript": "^5.7.3", "validator": "^13.12.0", "vite": "^6.0.11" }, @@ -44,7 +44,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.14.0", + "stylelint": "^16.14.1", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" @@ -2121,9 +2121,9 @@ "peer": true }, "node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2175,16 +2175,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/type-utils": "8.21.0", - "@typescript-eslint/utils": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2204,15 +2204,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4" }, "engines": { @@ -2228,13 +2228,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0" + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2245,13 +2245,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -2268,9 +2268,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2281,13 +2281,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2307,15 +2307,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0" + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2330,12 +2330,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/types": "8.22.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3362,9 +3362,9 @@ } }, "node_modules/date-fns": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.2.0.tgz", - "integrity": "sha512-E4KWKavANzeuusPi0jUjpuI22SURAznGkx7eZV+4i6x2A+IZxAMcajgkvuDAU1bg40+xuhW1zRdVIIM/4khuIg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "license": "MIT", "funding": { "type": "github", @@ -7324,9 +7324,9 @@ } }, "node_modules/stylelint": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.0.tgz", - "integrity": "sha512-orePw2dKxzXC0hd1VmxrDBqgf1KUV9DYsZY4guKLE9XcQD7m0BxVnWMaoQqMNsQIG14MyyTHf6zoajvOnDra8g==", + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.1.tgz", + "integrity": "sha512-oqCL7AC3786oTax35T/nuLL8p2C3k/8rHKAooezrPGRvUX0wX+qqs5kMWh5YYT4PHQgVDobHT4tw55WgpYG6Sw==", "dev": true, "funding": [ { @@ -7801,9 +7801,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/backend/package.json b/backend/package.json index d67d19559..487fac9ff 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,7 +11,7 @@ "preview": "vite preview", "fix": "eslint --fix .", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", + "ncu": "ncu -u -x eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", "stylelint": "stylelint \"src/**/*.css\"", "stylelint:fix": "stylelint \"src/**/*.css\" --fix" }, @@ -22,16 +22,16 @@ "@mui/material": "^6.4.1", "@mui/x-data-grid": "^7.24.1", "@mui/x-date-pickers": "^7.24.1", - "@types/node": "^22.10.10", + "@types/node": "^22.12.0", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.21.0", - "@typescript-eslint/parser": "^8.21.0", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", - "date-fns": "^3.2.0", + "date-fns": "^4.1.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", @@ -42,7 +42,7 @@ "react-router-dom": "^7.1.3", "react-toastify": "^11.0.3", "rrule": "^2.8.1", - "typescript": "^5.2.2", + "typescript": "^5.7.3", "validator": "^13.12.0", "vite": "^6.0.11" }, @@ -52,7 +52,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.14.0", + "stylelint": "^16.14.1", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 03a2a362a..a3ee44bd7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,19 +18,19 @@ "@stripe/stripe-js": "^5.5.0", "@types/leaflet": "^1.9.16", "@types/leaflet-boundary-canvas": "^1.0.3", - "@types/node": "^22.10.10", + "@types/node": "^22.12.0", "@types/nprogress": "^0.2.3", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.21.0", - "@typescript-eslint/parser": "^8.21.0", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", - "date-fns": "^3.2.0", + "date-fns": "^4.1.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", @@ -49,7 +49,7 @@ "react-slick": "^0.30.3", "react-toastify": "^11.0.3", "slick-carousel": "^1.8.1", - "typescript": "^5.2.2", + "typescript": "^5.7.3", "validator": "^13.12.0", "vite": "^6.0.11" }, @@ -59,7 +59,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.14.0", + "stylelint": "^16.14.1", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" @@ -2195,9 +2195,9 @@ } }, "node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2273,16 +2273,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/type-utils": "8.21.0", - "@typescript-eslint/utils": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2302,15 +2302,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4" }, "engines": { @@ -2326,13 +2326,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0" + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2343,13 +2343,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -2366,9 +2366,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2379,13 +2379,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2405,15 +2405,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0" + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2428,12 +2428,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/types": "8.22.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3466,9 +3466,9 @@ } }, "node_modules/date-fns": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.2.0.tgz", - "integrity": "sha512-E4KWKavANzeuusPi0jUjpuI22SURAznGkx7eZV+4i6x2A+IZxAMcajgkvuDAU1bg40+xuhW1zRdVIIM/4khuIg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "license": "MIT", "funding": { "type": "github", @@ -7534,9 +7534,9 @@ } }, "node_modules/stylelint": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.0.tgz", - "integrity": "sha512-orePw2dKxzXC0hd1VmxrDBqgf1KUV9DYsZY4guKLE9XcQD7m0BxVnWMaoQqMNsQIG14MyyTHf6zoajvOnDra8g==", + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.1.tgz", + "integrity": "sha512-oqCL7AC3786oTax35T/nuLL8p2C3k/8rHKAooezrPGRvUX0wX+qqs5kMWh5YYT4PHQgVDobHT4tw55WgpYG6Sw==", "dev": true, "funding": [ { @@ -8012,9 +8012,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/frontend/package.json b/frontend/package.json index 3200877f2..aa780b63a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,7 @@ "preview": "vite preview", "fix": "eslint --fix .", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "ncu": "ncu -u -x typescript,date-fns,eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", + "ncu": "ncu -u -x eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh", "stylelint": "stylelint \"src/**/*.css\"", "stylelint:fix": "stylelint \"src/**/*.css\" --fix" }, @@ -28,19 +28,19 @@ "@stripe/stripe-js": "^5.5.0", "@types/leaflet": "^1.9.16", "@types/leaflet-boundary-canvas": "^1.0.3", - "@types/node": "^22.10.10", + "@types/node": "^22.12.0", "@types/nprogress": "^0.2.3", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@types/react-recaptcha-v3": "^1.1.5", "@types/react-slick": "^0.23.13", "@types/validator": "^13.12.2", - "@typescript-eslint/eslint-plugin": "^8.21.0", - "@typescript-eslint/parser": "^8.21.0", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", "@vitejs/plugin-react": "^4.3.4", "axios": "^1.7.9", "cross-env": "^7.0.3", - "date-fns": "^3.2.0", + "date-fns": "^4.1.0", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.11", @@ -59,7 +59,7 @@ "react-slick": "^0.30.3", "react-toastify": "^11.0.3", "slick-carousel": "^1.8.1", - "typescript": "^5.2.2", + "typescript": "^5.7.3", "validator": "^13.12.0", "vite": "^6.0.11" }, @@ -69,7 +69,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "npm-check-updates": "^17.1.14", - "stylelint": "^16.14.0", + "stylelint": "^16.14.1", "stylelint-config-standard": "^37.0.0", "terser": "^5.37.0", "vite-plugin-html": "^3.2.2" From 4b87821dbd768aa56a28886284861e5e402a6c5e Mon Sep 17 00:00:00 2001 From: aelassas Date: Tue, 28 Jan 2025 15:56:26 +0100 Subject: [PATCH 42/42] Update backend fontFamily --- backend/src/main.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/main.tsx b/backend/src/main.tsx index 640d18d04..38915e2b9 100644 --- a/backend/src/main.tsx +++ b/backend/src/main.tsx @@ -72,16 +72,16 @@ const theme = createTheme( { typography: { fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', 'Roboto', - "'Helvetica Neue'", + '"Helvetica Neue"', 'Arial', 'sans-serif', - "'Apple Color Emoji'", - "'Segoe UI Emoji'", - "'Segoe UI Symbol'", - '-apple-system', - 'BlinkMacSystemFont', - "'Segoe UI'", + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', ].join(','), }, components: {