diff --git a/package-lock.json b/package-lock.json index cce8a8e..36e1853 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,9 @@ "@storybook/nextjs": "^8.1.3", "@storybook/react": "^8.1.3", "@storybook/test": "^8.1.3", + "@tanstack/react-query": "^5.40.1", "@tanstack/react-query-devtools": "^5.44.0", + "@tanstack/react-table": "^8.17.3", "@testing-library/dom": "^10.1.0", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", @@ -6855,6 +6857,7 @@ "version": "5.50.1", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.50.1.tgz", "integrity": "sha512-lpfhKPrJlyV2DSVcQb/HuozH3Av3kws4ge22agx+lNGpFkS4vLZ7St0l3GLwlAD+bqB+qXGex3JdRKUNtMviEQ==", + "dev": true, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -6874,6 +6877,7 @@ "version": "5.50.1", "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.50.1.tgz", "integrity": "sha512-s0DW3rVBDPReDDovUjVqItVa3R2nPfUANK9nqGvarO2DwTiY9U4EBTsqizMxItRCoGgK5apeM7D3mxlHrSKpdQ==", + "dev": true, "dependencies": { "@tanstack/query-core": "5.50.1" }, @@ -6906,6 +6910,7 @@ "version": "8.19.2", "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.19.2.tgz", "integrity": "sha512-itoSIAkA/Vsg+bjY23FSemcTyPhc5/1YjYyaMsr9QSH/cdbZnQxHVWrpWn0Sp2BWN71qkzR7e5ye8WuMmwyOjg==", + "dev": true, "dependencies": { "@tanstack/table-core": "8.19.2" }, @@ -6925,6 +6930,7 @@ "version": "8.19.2", "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.19.2.tgz", "integrity": "sha512-KpRjhgehIhbfH78ARm/GJDXGnpdw4bCg3qas6yjWSi7czJhI/J6pWln7NHtmBkGE9ZbohiiNtLqwGzKmBfixig==", + "dev": true, "engines": { "node": ">=12" }, @@ -7747,16 +7753,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz", - "integrity": "sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", + "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/type-utils": "7.15.0", - "@typescript-eslint/utils": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/type-utils": "7.16.0", + "@typescript-eslint/utils": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -7780,15 +7786,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.15.0.tgz", - "integrity": "sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", + "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/typescript-estree": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "debug": "^4.3.4" }, "engines": { @@ -7808,13 +7814,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", - "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", + "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0" + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7825,13 +7831,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz", - "integrity": "sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", + "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.15.0", - "@typescript-eslint/utils": "7.15.0", + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/utils": "7.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -7852,9 +7858,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", - "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", + "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7865,13 +7871,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", - "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", + "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -7905,15 +7911,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", - "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", + "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/typescript-estree": "7.15.0" + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7927,12 +7933,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", - "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", + "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/types": "7.16.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -9866,9 +9872,9 @@ } }, "node_modules/chromatic": { - "version": "11.5.4", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.5.4.tgz", - "integrity": "sha512-+J+CopeUSyGUIQJsU6X7CfvSmeVBs0j6LZ9AgF4+XTjI4pFmUiUXsTc00rH9x9W1jCppOaqDXv2kqJJXGDK3mA==", + "version": "11.5.5", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.5.5.tgz", + "integrity": "sha512-YS0GJwegF0vpMbwZE68/xJlI4SlUGMqI78V2ATAF19YwTHaq8jGP1CPQGKUSlgWUhzPtyu3ELy6Dvv/owYljAg==", "dev": true, "bin": { "chroma": "dist/bin.js", @@ -11455,9 +11461,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.818", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.818.tgz", - "integrity": "sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==", + "version": "1.4.820", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.820.tgz", + "integrity": "sha512-kK/4O/YunacfboFEk/BDf7VO1HoPmDudLTJAU9NmXIOSjsV7qVIX3OrI4REZo0VmdqhcpUcncQc6N8Q3aEXlHg==", "dev": true }, "node_modules/elliptic": { @@ -13098,9 +13104,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -13454,9 +13460,9 @@ } }, "node_modules/filesize": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.2.tgz", - "integrity": "sha512-Dx770ai81ohflojxhU+oG+Z2QGvKdYxgEr9OSA8UVrqhwNHjfH9A8f5NKfg83fEH8ZFA5N5llJo5T3PIoZ4CRA==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.4.tgz", + "integrity": "sha512-ryBwPIIeErmxgPnm6cbESAzXjuEFubs+yKYLBZvg3CaiNcmkJChoOGcBSrZ6IwkMwPABwPpVXE6IlNdGJJrvEg==", "dev": true, "engines": { "node": ">= 10.4.0" @@ -13634,9 +13640,9 @@ "dev": true }, "node_modules/flow-parser": { - "version": "0.239.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.239.0.tgz", - "integrity": "sha512-U5dgOsS6cg4FGNzzTD/zHRDM4bliL6laUgD0LUCSMzI2zEfKMnRV2/wgDv8nKmO2Z1R8ri5pE1YoldmrSV7FOw==", + "version": "0.239.1", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.239.1.tgz", + "integrity": "sha512-topOrETNxJ6T2gAnQiWqAlzGPj8uI2wtmNOlDIMNB+qyvGJZ6R++STbUOTAYmvPhOMz2gXnXPH0hOvURYmrBow==", "dev": true, "engines": { "node": ">=0.4.0" @@ -14126,9 +14132,9 @@ "dev": true }, "node_modules/glob": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.3.tgz", - "integrity": "sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -14140,9 +14146,6 @@ "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -14478,11 +14481,6 @@ "react-is": "^16.7.0" } }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -15646,14 +15644,14 @@ } }, "node_modules/jackspeak": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.1.tgz", - "integrity": "sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.2.tgz", + "integrity": "sha512-qH3nOSj8q/8+Eg8LUPOq3C+6HWkpUioIjDsq1+D4zY91oZvpPttw8GwtF1nReRYKXl+1AORyFqtm2f5Q1SB6/Q==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=18" + "node": "14 >=14.21 || 16 >=16.20 || >=18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -21690,8 +21688,6 @@ }, "node_modules/npx/node_modules/ansi-align": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", "inBundle": true, "license": "ISC", "dependencies": { @@ -21700,8 +21696,6 @@ }, "node_modules/npx/node_modules/ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "inBundle": true, "license": "MIT", "engines": { @@ -21757,15 +21751,11 @@ }, "node_modules/npx/node_modules/builtins": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", "inBundle": true, "license": "MIT" }, "node_modules/npx/node_modules/camelcase": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", "inBundle": true, "license": "MIT", "engines": { @@ -21797,8 +21787,6 @@ }, "node_modules/npx/node_modules/cli-boxes": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", "inBundle": true, "license": "MIT", "engines": { @@ -21807,8 +21795,6 @@ }, "node_modules/npx/node_modules/cliui": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", "inBundle": true, "license": "ISC", "dependencies": { @@ -21832,8 +21818,6 @@ }, "node_modules/npx/node_modules/code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", "inBundle": true, "license": "MIT", "engines": { @@ -21869,8 +21853,6 @@ }, "node_modules/npx/node_modules/create-error-class": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", "inBundle": true, "license": "MIT", "dependencies": { @@ -21900,8 +21882,6 @@ }, "node_modules/npx/node_modules/crypto-random-string": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", "inBundle": true, "license": "MIT", "engines": { @@ -21910,8 +21890,6 @@ }, "node_modules/npx/node_modules/decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "inBundle": true, "license": "MIT", "engines": { @@ -21967,8 +21945,6 @@ }, "node_modules/npx/node_modules/find-up": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22017,8 +21993,6 @@ }, "node_modules/npx/node_modules/got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22072,8 +22046,6 @@ }, "node_modules/npx/node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "inBundle": true, "license": "MIT", "engines": { @@ -22090,8 +22062,6 @@ }, "node_modules/npx/node_modules/invert-kv": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", "inBundle": true, "license": "MIT", "engines": { @@ -22100,8 +22070,6 @@ }, "node_modules/npx/node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "inBundle": true, "license": "MIT" }, @@ -22118,8 +22086,6 @@ }, "node_modules/npx/node_modules/is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22131,8 +22097,6 @@ }, "node_modules/npx/node_modules/is-npm": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", "inBundle": true, "license": "MIT", "engines": { @@ -22141,8 +22105,6 @@ }, "node_modules/npx/node_modules/is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", "inBundle": true, "license": "MIT", "engines": { @@ -22151,8 +22113,6 @@ }, "node_modules/npx/node_modules/is-redirect": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", "inBundle": true, "license": "MIT", "engines": { @@ -22169,8 +22129,6 @@ }, "node_modules/npx/node_modules/is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "inBundle": true, "license": "MIT", "engines": { @@ -22179,15 +22137,11 @@ }, "node_modules/npx/node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "inBundle": true, "license": "ISC" }, "node_modules/npx/node_modules/latest-version": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22207,8 +22161,6 @@ }, "node_modules/npx/node_modules/lcid": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22220,8 +22172,6 @@ }, "node_modules/npx/node_modules/load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22236,8 +22186,6 @@ }, "node_modules/npx/node_modules/locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22278,8 +22226,6 @@ }, "node_modules/npx/node_modules/mem": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha512-nOBDrc/wgpkd3X/JOhMqYR+/eLqlfLP4oQfoBA6QExIxEl+GU01oyEkwWyueyO8110pUKijtiHGhEmYoOn88oQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22332,8 +22278,6 @@ }, "node_modules/npx/node_modules/npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22345,8 +22289,6 @@ }, "node_modules/npx/node_modules/number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", "inBundle": true, "license": "MIT", "engines": { @@ -22355,8 +22297,6 @@ }, "node_modules/npx/node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "inBundle": true, "license": "MIT", "engines": { @@ -22365,8 +22305,6 @@ }, "node_modules/npx/node_modules/os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "inBundle": true, "license": "MIT", "engines": { @@ -22388,8 +22326,6 @@ }, "node_modules/npx/node_modules/os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "inBundle": true, "license": "MIT", "engines": { @@ -22407,8 +22343,6 @@ }, "node_modules/npx/node_modules/p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "inBundle": true, "license": "MIT", "engines": { @@ -22425,8 +22359,6 @@ }, "node_modules/npx/node_modules/p-locate": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22438,8 +22370,6 @@ }, "node_modules/npx/node_modules/package-json": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22454,8 +22384,6 @@ }, "node_modules/npx/node_modules/parse-json": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22467,8 +22395,6 @@ }, "node_modules/npx/node_modules/path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "inBundle": true, "license": "MIT", "engines": { @@ -22477,8 +22403,6 @@ }, "node_modules/npx/node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "inBundle": true, "license": "MIT", "engines": { @@ -22487,8 +22411,6 @@ }, "node_modules/npx/node_modules/path-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22500,8 +22422,6 @@ }, "node_modules/npx/node_modules/pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "inBundle": true, "license": "MIT", "engines": { @@ -22529,8 +22449,6 @@ }, "node_modules/npx/node_modules/prepend-http": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", "inBundle": true, "license": "MIT", "engines": { @@ -22563,8 +22481,6 @@ }, "node_modules/npx/node_modules/read-pkg": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22578,8 +22494,6 @@ }, "node_modules/npx/node_modules/read-pkg-up": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22601,8 +22515,6 @@ }, "node_modules/npx/node_modules/registry-url": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22614,8 +22526,6 @@ }, "node_modules/npx/node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "inBundle": true, "license": "MIT", "engines": { @@ -22624,8 +22534,6 @@ }, "node_modules/npx/node_modules/require-main-filename": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", "inBundle": true, "license": "ISC" }, @@ -22656,8 +22564,6 @@ }, "node_modules/npx/node_modules/semver-diff": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22669,8 +22575,6 @@ }, "node_modules/npx/node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "inBundle": true, "license": "ISC" }, @@ -22727,8 +22631,6 @@ }, "node_modules/npx/node_modules/strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22740,8 +22642,6 @@ }, "node_modules/npx/node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "inBundle": true, "license": "MIT", "engines": { @@ -22750,8 +22650,6 @@ }, "node_modules/npx/node_modules/strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "inBundle": true, "license": "MIT", "engines": { @@ -22760,8 +22658,6 @@ }, "node_modules/npx/node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "inBundle": true, "license": "MIT", "engines": { @@ -22824,8 +22720,6 @@ }, "node_modules/npx/node_modules/timed-out": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", "inBundle": true, "license": "MIT", "engines": { @@ -22834,8 +22728,6 @@ }, "node_modules/npx/node_modules/unique-string": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22847,8 +22739,6 @@ }, "node_modules/npx/node_modules/unzip-response": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha512-N0XH6lqDtFH84JxptQoZYmloF4nzrQqqrAymNj+/gW60AO2AZgOcf4O/nUXJcYfyQkqvMo9lSupBZmmgvuVXlw==", "inBundle": true, "license": "MIT", "engines": { @@ -22875,8 +22765,6 @@ }, "node_modules/npx/node_modules/url-parse-lax": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22897,8 +22785,6 @@ }, "node_modules/npx/node_modules/validate-npm-package-name": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", "inBundle": true, "license": "ISC", "dependencies": { @@ -22947,8 +22833,6 @@ }, "node_modules/npx/node_modules/wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", "inBundle": true, "license": "MIT", "dependencies": { @@ -22984,8 +22868,6 @@ }, "node_modules/npx/node_modules/xdg-basedir": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==", "inBundle": true, "license": "MIT", "engines": { @@ -23024,8 +22906,6 @@ }, "node_modules/npx/node_modules/yargs-parser": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha512-WhzC+xgstid9MbVUktco/bf+KJG+Uu6vMX0LN1sLJvwmbCQVxb4D8LzogobonKycNasCZLdOzTAk1SK7+K7swg==", "inBundle": true, "license": "ISC", "dependencies": { @@ -23775,11 +23655,11 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.1.tgz", - "integrity": "sha512-9/8QXrtbGeMB6LxwQd4x1tIMnsmUxMvIH/qWGsccz6bt9Uln3S+sgAaqfQNhbGA8ufzs2fHuP/yqapGgP9Hh2g==", + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.2.tgz", + "integrity": "sha512-voV4dDrdVZVNz84n39LFKDaRzfwhdzJ7akpyXfTMxCgRUp07U3lcJUXRlhTKP17rgt09sUzLi5iCitpEAr+6ug==", "engines": { - "node": ">=18" + "node": "14 || 16 || 18 || 20 || >=22" } }, "node_modules/path-to-regexp": { @@ -24312,6 +24192,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -24359,12 +24245,6 @@ "react-is": "^16.13.1" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -24466,9 +24346,9 @@ ] }, "node_modules/qs": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.2.tgz", - "integrity": "sha512-x+NLUpx9SYrcwXtX7ob1gnkSems4i/mGZX5SlYxwIau6RrUSODO89TR/XDGGpn5RPWSYIB+aSfuSlV5+CmbTBg==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.3.tgz", + "integrity": "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==", "dev": true, "dependencies": { "side-channel": "^1.0.6" @@ -24706,10 +24586,9 @@ } }, "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-query": { "version": "3.39.3", @@ -27474,9 +27353,9 @@ "dev": true }, "node_modules/ts-jest": { - "version": "29.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", - "integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==", + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.0.tgz", + "integrity": "sha512-eFmkE9MG0+oT6nqSOcUwL+2UUmK2IvhhUV8hFDsCHnc++v2WCCbQQZh5vvjsa8sgOY/g9T0325hmkEmi6rninA==", "dev": true, "dependencies": { "bs-logger": "0.x", diff --git a/package.json b/package.json index 9bcbfca..0da81b9 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,8 @@ "@storybook/nextjs": "^8.1.3", "@storybook/react": "^8.1.3", "@storybook/test": "^8.1.3", + "@tanstack/react-query": "^5.40.1", + "@tanstack/react-table": "^8.17.3", "@tanstack/react-query-devtools": "^5.44.0", "@testing-library/dom": "^10.1.0", "@testing-library/jest-dom": "^6.4.6", diff --git a/src/__tests__/ZcartManagement.test.tsx b/src/__tests__/ZcartManagement.test.tsx index 77cb048..cedc001 100644 --- a/src/__tests__/ZcartManagement.test.tsx +++ b/src/__tests__/ZcartManagement.test.tsx @@ -14,16 +14,16 @@ const mockedRequest = request as jest.Mocked; const localStorageMock = (function () { let store:any = {}; return { - getItem(key) { + getItem(key: string | number) { return store[key] || null; }, - setItem(key, value) { + setItem(key: string | number, value: { toString: () => any; }) { store[key] = value.toString(); }, clear() { store = {}; }, - removeItem(key) { + removeItem(key: string | number) { delete store[key]; }, }; diff --git a/src/__tests__/productList.test.tsx b/src/__tests__/productList.test.tsx index b710431..532a3a0 100644 --- a/src/__tests__/productList.test.tsx +++ b/src/__tests__/productList.test.tsx @@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-quer import SideBar from "@/components/Side" import Provider from '@/app/providers'; import Page from '@/app/products/page'; - +jest.setTimeout(15000) const queryClient = new QueryClient(); const ProductListTest = () => { const { data } = useQuery({ diff --git a/src/__tests__/reviewPopupHook.test.tsx b/src/__tests__/reviewPopupHook.test.tsx new file mode 100644 index 0000000..58edf23 --- /dev/null +++ b/src/__tests__/reviewPopupHook.test.tsx @@ -0,0 +1,30 @@ +// WishlistOverlay.test.js + +import { renderHook, act } from '@testing-library/react'; +import ReviewPopup from '@/hooks/reviewPopup'; + +describe('ReviewPopup', () => { + it('should initialize with isOpen as false', () => { + const { result } = renderHook(() => ReviewPopup()); + + expect(result.current.isReviewPopupOpen).toBe(false); + }); + + it('should toggle isOpen when toggleReviewPopup is called', () => { + const { result } = renderHook(() => ReviewPopup()); + + expect(result.current.isReviewPopupOpen).toBe(false); + + act(() => { + result.current.toggleReviewPopup(); + }); + + expect(result.current.isReviewPopupOpen).toBe(true); + + act(() => { + result.current.toggleReviewPopup(); + }); + + expect(result.current.isReviewPopupOpen).toBe(false); + }); +}); diff --git a/src/__tests__/wiishlistOverlayHook.test.tsx b/src/__tests__/wiishlistOverlayHook.test.tsx new file mode 100644 index 0000000..1096866 --- /dev/null +++ b/src/__tests__/wiishlistOverlayHook.test.tsx @@ -0,0 +1,30 @@ +// WishlistOverlay.test.js + +import { renderHook, act } from '@testing-library/react'; +import useWishlistOverlay from '@/hooks/wishlistOverlay'; + +describe('useWishlistOverlay', () => { + it('should initialize with isOpen as false', () => { + const { result } = renderHook(() => useWishlistOverlay()); + + expect(result.current.isWishlistOverlayOpen).toBe(false); + }); + + it('should toggle isOpen when toggleWishlistSlider is called', () => { + const { result } = renderHook(() => useWishlistOverlay()); + + expect(result.current.isWishlistOverlayOpen).toBe(false); + + act(() => { + result.current.toggleWishlistSlider(); + }); + + expect(result.current.isWishlistOverlayOpen).toBe(true); + + act(() => { + result.current.toggleWishlistSlider(); + }); + + expect(result.current.isWishlistOverlayOpen).toBe(false); + }); +}); diff --git a/src/app/products/[id]/page.tsx b/src/app/products/[id]/page.tsx index 97cfa6a..0a77bab 100644 --- a/src/app/products/[id]/page.tsx +++ b/src/app/products/[id]/page.tsx @@ -1,216 +1,19 @@ // BuyerProductView 'use client'; -import React, { useState } from 'react'; -import { Swiper, SwiperSlide } from 'swiper/react'; -import 'swiper/css'; -import 'swiper/css/free-mode'; -import 'swiper/css/navigation'; -import 'swiper/css/thumbs'; -import { FreeMode, Navigation, Thumbs } from 'swiper/modules'; -import Image from 'next/image'; //@ts-ignore -import ReactStars from 'react-rating-stars-component'; -import { MdOutlineShoppingCart } from 'react-icons/md'; -import { FaRegHeart } from 'react-icons/fa6'; -import { useParams } from 'next/navigation'; -import { Product } from '@/utils/requests'; -import { - ProductObj, - ProductType, - ReviewType, - imageType, -} from '@/types/Product'; -import Card from '@/components/Card'; + import Header from '@/components/Header'; import Footer from '@/components/Footer'; -import image from '../../../../public/product.png'; -import { useQuery } from '@tanstack/react-query'; -import ReviewCard from '@/components/ReviewCard'; -import Button from '@/components/Button'; -import { averageReviews } from '@/utils/averageReviews'; -import { useAppDispatch } from '@/redux/store'; -import { handleUserAddCart } from '@/redux/slices/userCartSlice'; -//import StripeProvider from '@/components/StripeProvider'; - -function Page() { - const [thumbsSwiper, setThumbsSwiper] = useState(null); - const { id } :any= useParams(); - const handleSwiper = (swiper: any) => { - setThumbsSwiper(swiper); - }; - const _id: string = id.toLocaleString(); - const { data, isLoading, error } = useQuery({ - queryKey: ['product', id], - queryFn: async () => { - try { - const response: ProductType = (await Product.single( - _id, - )) as ProductType; +import ProductWrapper from '@/components/productWrapper'; - return response; - } catch (error) { - throw new Error('Error fetching product data'); - } - }, - }); - if (isLoading) return Loading...; - if (error) return Error: {error.message}; - - const { - productPictures, - productName, - productPrice, - productDescription, - reviews, - } = data.product; - console.log('this is reviews >>>>>>>>>', reviews); - console.log(' this is average reviews', productPictures); - const { relatedProducts } = data; - const dispatch = useAppDispatch(); - const handleNewItem = () => { - const productId = data.product.id; - dispatch(handleUserAddCart({ productPrice, productId })); - }; +function Page() { + return (
{/* // */}
-
-
-
-
-
- {productPictures && productPictures.length > 0 ? ( - - {productPictures.map((image: imageType) => { - return ( - - image - - ); - })} - - ) : ( - {'no - )} -
-
- {productPictures && productPictures.length > 0 ? ( -
- - {productPictures.map((image: imageType) => { - return ( - - image - - ); - })} - -
- ) : ( -

no image found!

- )} -
-
-
-
-

{productName}

-
-
-
-
- -
-
- { - handleNewItem(); - }} - /> -
-
- - RWF {productPrice} - -
-
- -
-
-

Description:

-

{productDescription}

-
-
-
-
-

Related products:

-
-
- {relatedProducts && relatedProducts.length > 0 ? ( - relatedProducts.map((product: ProductType) => ( - - )) - ) : ( -

- No related products available. -

- )} -
-
-
-
-
-

Reviews:

- -
-
- {reviews && reviews.length > 0 ? ( - reviews.map((review: ReviewType) => ( - - )) - ) : ( -

No ratings yet.

- )} -
-
-
-
+
{/* */}
diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 899b49b..feef769 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,7 +1,8 @@ 'use client'; import React from 'react'; import { IoChevronBack } from 'react-icons/io5'; -import { IoMdClose } from "react-icons/io"; +import { MdClose } from "react-icons/md"; + interface Properties { name?: string; handle?: () => void; @@ -44,8 +45,8 @@ export const BackButton: React.FC = ({ handle, isDisabled, rotate }) export const CloseButton: React.FC = ({ handle, isDisabled, rotate }) => { return ( <> - ) diff --git a/src/components/BuyerOrdersList.tsx b/src/components/BuyerOrdersList.tsx index 72f2368..ae98bac 100644 --- a/src/components/BuyerOrdersList.tsx +++ b/src/components/BuyerOrdersList.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import ReviewProduct from './ReviewProductPopup'; import { useQuery } from "@tanstack/react-query"; -import requestAxios from '@/utils/axios'; +import request from '@/utils/axios'; import { ColumnDef, flexRender, @@ -37,15 +37,13 @@ const BuyerOrdersList = () => { const [orderId, setOrderId] = useState(null); const { isLoading, refetch, error, data } = useQuery({ queryKey: ['BuyerOrdersList'], - queryFn: () => requestAxios.get('/orders'), + queryFn: () => request.get('/orders'), }); - console.log("----------------------------------------------------------------------",data) const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 7 }) var ordersData = data?.orders ?? []; - console.log("----------------------------------------------------------------------",ordersData) const columns: ColumnDef[] = [ { header: 'No', @@ -67,7 +65,7 @@ const BuyerOrdersList = () => { header: 'Price', accessorFn: row => row.Product.productPrice, cell: (row: any) => <> - {row.row.original.Product.productPrice} { row.row.original.Product.productCurrency} + {row.row.original.Product.productPrice.toLocaleString()} { row.row.original.Product.productCurrency} }, { @@ -164,6 +162,7 @@ const BuyerOrdersList = () => { ))} + {ordersData.length > 6 ?
< div className='flex gap-3' > table.previousPage()} isDisabled={!table.getCanPreviousPage()} /> @@ -171,6 +170,7 @@ const BuyerOrdersList = () => { < BackButton handle={() => table.nextPage()} isDisabled={!table.getCanNextPage()} rotate />
+ :""} diff --git a/src/components/BuyerWishlist.tsx b/src/components/BuyerWishlist.tsx new file mode 100644 index 0000000..e7fde9b --- /dev/null +++ b/src/components/BuyerWishlist.tsx @@ -0,0 +1,177 @@ +"use client" +import React, { useState } from 'react'; +import ReviewProduct from './ReviewProductPopup'; +import { useMutation, useQuery } from "@tanstack/react-query"; +import request from '@/utils/axios'; +import { + ColumnDef, + flexRender, + getCoreRowModel, + getPaginationRowModel, + useReactTable, +} from '@tanstack/react-table' +import { BackButton, DeleteButton, ReviewButton } from './Button'; +import { BsTrash } from "react-icons/bs"; +import DeleteWishlistPopup from './DeleteWishlistPopup'; +import { showToast } from '@/helpers/toast'; +import { handleWishlistCount } from '@/redux/slices/wishlistSlice'; +import { RootState, useAppDispatch, useAppSelector } from '@/redux/store'; + +type ordersTable = { + No: number; + id: string; + product:{ + productName: string; + productThumbnail: string; + stockLevel: string; + productPrice: number; + productCurrency:string; + } +} +type PaginationState = { + pageIndex: number + pageSize: number +} +const BuyerWishList = () => { + const [isDeleteWishlistModalOpen, setIsDeleteWishlistModalOpen] = useState(false); + const [productId, setProductId] = useState(''); + const [deleting, setDeleting] = useState(''); + const { wishNumber } = useAppSelector( + (state: RootState) => state.wishlist + ) + const dispatch = useAppDispatch(); + const { isLoading, refetch, error, data } = useQuery({ + queryKey: ['BuyerWishList'], + queryFn: () => request.get('/wishes') + }); + + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 7 + }) + var wishesData = data?.wishes ?? []; + console.log('this is wishes from the database', wishesData) + const columns: ColumnDef[] = [ + + { + header: 'Image', + accessorFn: row => row.product.productThumbnail, + cell: (val: any) => + <> + + + }, + { + header: 'Product Name', + accessorFn: row => row.product.productName, + cell: (val: any) => {val.getValue()} + }, + { + header: 'Price', + accessorFn: row => row.product.productPrice, + cell: (row: any) => <> + {row.row.original.product.productPrice.toLocaleString()} {row.row.original.product.productCurrency} + + }, + { + id: 'Action', + header: () =>
Action
, + accessorFn: row => row.id, + cell: (row: any) => ( +
+ handleOpenWishlistPopup(row.row.original.product.id, 'product')}/> +
+ ) + }, + ] + const table = useReactTable({ + data: wishesData, + columns, + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + getCoreRowModel: getCoreRowModel(), + state: { + pagination + } + }) + const handleOpenWishlistPopup = (productId: string, deleting:string) => { + setProductId(productId); + setIsDeleteWishlistModalOpen(true); + setDeleting(deleting); + }; + + const handleClosePopup = () => { + setIsDeleteWishlistModalOpen(false); + setProductId(''); + }; + + const mutation = useMutation({ + mutationFn: () => { + return request.delete('/wishes') + }, + onError: (error) => console.log(error), + onSuccess: async () => { + dispatch(handleWishlistCount(await wishNumber - await wishNumber)); + showToast('successfully cleared your wish list','success') + await refetch(); + handleClosePopup(); + + }, + }); + + const handleClearAll =() => { + mutation.mutate(); + }; + return ( + <> +
+ Wishlist +
+ <> +
+ + + {table.getHeaderGroups().map(headerGroup => + ( + { + headerGroup.headers.map(header => + + ) + } + + ))} + + + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + ))} + + ))} + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+
+ handleOpenWishlistPopup(productId, 'wishlist')}/> +
+ +
+
+ {isDeleteWishlistModalOpen && + + } + + ); +}; + +export default BuyerWishList; diff --git a/src/components/Card.tsx b/src/components/Card.tsx index b5b1972..fe3ad73 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -9,7 +9,9 @@ import Link from 'next/link'; import { averageReviews } from '@/utils/averageReviews'; import { RootState, useAppDispatch, useAppSelector } from '@/redux/store'; import { handleUserAddCart } from '@/redux/slices/userCartSlice'; - +import request from '@/utils/axios'; +import { showToast } from '@/helpers/toast'; +import { handleWishlistCount } from '@/redux/slices/wishlistSlice'; // import { useRouter } from 'next/router'; function Card({ @@ -20,13 +22,27 @@ function Card({ id, reviews, }: Cards) { - +const { wishNumber } = useAppSelector( + (state: RootState) => state.wishlist +) + const productId=id const dispatch = useAppDispatch(); const handleNewItem=()=>{ - -dispatch(handleUserAddCart({productPrice,productId})); + dispatch(handleUserAddCart({productPrice,productId})); +} +const handleAddRemoveWish = async(event: { preventDefault: () => void; })=>{ + event.preventDefault(); + const response:any = await request.post('/wishes', { productId:id }); + + if(response.status == 200 || response.status == 203){ + const { status } = response; + dispatch(handleWishlistCount(status == 200 ? await wishNumber + 1 : await wishNumber - 1)); + showToast(response.message, 'success') + } + console.log('this is response', response) + } return (
@@ -54,7 +70,7 @@ dispatch(handleUserAddCart({productPrice,productId}));
- {productPrice} RWF + {productPrice.toLocaleString()} RWF - + {handleNewItem()}} />
diff --git a/src/components/DeleteWishlistPopup.tsx b/src/components/DeleteWishlistPopup.tsx new file mode 100644 index 0000000..cfada58 --- /dev/null +++ b/src/components/DeleteWishlistPopup.tsx @@ -0,0 +1,66 @@ +import React, { useEffect, useRef } from 'react'; +import { Button, CloseButton } from './Button'; +import request from '@/utils/axios'; +import { useMutation, RefetchOptions, QueryObserverResult } from "@tanstack/react-query"; +import { showToast } from '@/helpers/toast'; +import { handleWishlistCount } from '@/redux/slices/wishlistSlice'; +import { RootState, useAppDispatch, useAppSelector } from '@/redux/store'; + +interface DeleteWishlistInterface { + isOpen: boolean, + id: string | null; + handleClose: () => void + refetch: (options?: RefetchOptions | undefined) => Promise>; + deleting:string; + handleClearWishlist:()=>void; +} + +const DeleteWishlistPopup: React.FC = ({ id, isOpen, handleClose, refetch, deleting, handleClearWishlist}) => { + const { wishNumber } = useAppSelector( + (state: RootState) => state.wishlist + ) + const dispatch = useAppDispatch(); + + const dialogRef = useRef(null); + useEffect(() => { + if (isOpen && dialogRef.current) { + dialogRef.current.showModal(); + } else if (dialogRef.current) { + dialogRef.current.close(); + } + }, [isOpen]); + + const mutation = useMutation({ + mutationFn: () => { + return request.post('/wishes', {productId:id} ) + }, + onError: (error) => console.log(error), + onSuccess: async (data:any) => { + const { status } = data; + dispatch(handleWishlistCount(status == 200 ? await wishNumber + 1 : await wishNumber - 1)); + showToast(data.message ,'success') + await refetch(); + handleClose(); + + }, + }) + + const handleDeletion = async () => { + mutation.mutate(); + }; + const action = deleting == 'product'? handleDeletion : handleClearWishlist; + return ( + e.stopPropagation()} className='z-50 bg-white backdrop-filter backdrop-brightness-75 rounded shadow-lg w-[80%] py-6 px-4 sm:w-[30%] mx-auto'> + +
+

Delete Wished Prroduct

+
+ Are you sure you want to {deleting == 'product' ? 'remove this product from your wish list?' : 'clear your wish list?!'} +
+
+
+ ); +}; + +export default DeleteWishlistPopup; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 9738fe5..5ddd9c5 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -17,11 +17,18 @@ import { handleFetchUserCart } from '@/hooks/userCart'; import Logout from '@/hooks/logout'; import NotificationIcon from './ui-components/NotificationIcon'; import Notification from './ui-components/Notification'; +import WishlistOverlay from '@/hooks/wishlistOverlay'; +import WishlistContainer from './wishlistContainer'; const Header = () => { + const { wishNumber } = useAppSelector( + (state: RootState) => state.wishlist + ) + const { isOrdersOverlayOpen, toggleOrdersSlider } = OrdersOverlay(); - + const { isWishlistOverlayOpen, toggleWishlistSlider } = WishlistOverlay(); const [showlModal, setShowmodal] = useState(false); + const [showCart, setShowCart] = useState(false); const [showNotification, setShowNotification] = useState(false); @@ -52,16 +59,7 @@ const Header = () => { const handleshow = () => { setShowmodal(!showlModal); }; - // //const [cartItems, setCartItems]=useState(0); - - // const handleshow = () => { - // setShowmodal(!showlModal); - // }; - // const handleshowCart = () => { - // setShowmodal(!showlModal); - // setShowCart(!showCart); - // }; const [viewMenu, setViewmenu] = useState(false); const [userdata, setUserdata] = useState(null); @@ -90,17 +88,24 @@ const Header = () => {
{userdata && userdata.User.Role.name === 'buyer' && ( + <> {cart?.product.length} + + + {wishNumber} + + + + )} {userdata ? ( <> - { '' )}
+ {isWishlistOverlayOpen ? ( + + ) : ( + '' + )} {overlayComponent && ( {overlayComponent === 'cart' && ( @@ -248,4 +261,4 @@ const Header = () => { ); }; -export default Header; +export default Header; \ No newline at end of file diff --git a/src/components/OrdersContainer.tsx b/src/components/OrdersContainer.tsx index d420ee5..dec9cf2 100644 --- a/src/components/OrdersContainer.tsx +++ b/src/components/OrdersContainer.tsx @@ -1,6 +1,6 @@ import React from 'react'; import BuyerOrdersList from './BuyerOrdersList'; -import OrdersOverlay from '@/hooks/ordersOverlay'; + interface OrdersContainerInterface{ toggleOrdersSlider?:()=>void; isOrdersOverlayOpen?:boolean; diff --git a/src/components/ReviewProductPopup.tsx b/src/components/ReviewProductPopup.tsx index ed6a1a1..23727f4 100644 --- a/src/components/ReviewProductPopup.tsx +++ b/src/components/ReviewProductPopup.tsx @@ -28,7 +28,7 @@ export const ReviewProduct: React.FC = ({ id, isOpen, h dialogRef.current.close(); } }, [isOpen]); - const ratingChanged = (newRating:any) => { + const ratingChanged = async (newRating:any) => { setRating(newRating); }; const mutation = useMutation({ diff --git a/src/components/ReviewsWrapper.tsx b/src/components/ReviewsWrapper.tsx new file mode 100644 index 0000000..22306ef --- /dev/null +++ b/src/components/ReviewsWrapper.tsx @@ -0,0 +1,70 @@ +import { ReviewType } from '@/types/Product'; +import { QueryObserverResult, RefetchOptions, useQuery } from '@tanstack/react-query'; +import React, { useEffect, useState } from 'react'; +import ReviewCard from './ReviewCard'; +import Button from './Button'; +import ReviewPopup from '@/hooks/reviewPopup'; +import ReviewProduct from './ReviewProductPopup'; +import request from '@/utils/axios'; +import { current } from '@reduxjs/toolkit'; + +interface reviewWrapper{ + refetch: (options?: RefetchOptions | undefined) => Promise>; + reviews:ReviewType[]; + productId:string; +} + +const ReviewWrapper:React.FC=({ refetch, reviews, productId })=>{ + const { isReviewPopupOpen, setIsReviewPopupOpen, toggleReviewPopup} = ReviewPopup(); + const [currentUser, setCurrentUser] = useState([]); + + useEffect(()=>{ + const { User } = JSON.parse(localStorage.getItem('profile') as string); + const { id } = User + if(User){ + setCurrentUser(id) + } + }) + + const { isLoading, error, data } = useQuery({ + queryKey: ['CheckBuyerOrders'], + queryFn: () => request.get('/orders'), + }); + console.log('this is data', data); + //setOrders(data) + var ordersData = data?.orders ?? []; + console.log("----------------------------------------------------------------------",ordersData) + const canUserAddReview = ordersData.filter((item: { buyerId: any; productId:string; isPaid: any; }) => item.buyerId == currentUser && item.productId == productId && item.isPaid) + return( + <> +
+
+

Reviews:

+ {canUserAddReview.length ? + + :"" } +
+
+ {reviews && reviews.length > 0 ? ( + reviews.map((review: ReviewType) => ( + + )) + ) : ( +

No ratings yet.

+ )} +
+
+ {isReviewPopupOpen && + + } + + ) +}; + +export default ReviewWrapper; \ No newline at end of file diff --git a/src/components/productWrapper.tsx b/src/components/productWrapper.tsx new file mode 100644 index 0000000..db14f6f --- /dev/null +++ b/src/components/productWrapper.tsx @@ -0,0 +1,207 @@ +import React, { useState } from 'react'; +import image from '../../public/product.png'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import ReviewCard from '@/components/ReviewCard'; +import Button from '@/components/Button'; +import { averageReviews } from '@/utils/averageReviews'; +import { RootState, useAppDispatch, useAppSelector } from '@/redux/store'; +import { handleUserAddCart } from '@/redux/slices/userCartSlice'; +import { Swiper, SwiperSlide } from 'swiper/react'; +import 'swiper/css'; +import 'swiper/css/free-mode'; +import 'swiper/css/navigation'; +import 'swiper/css/thumbs'; +import { FreeMode, Navigation, Thumbs } from 'swiper/modules'; +import Image from 'next/image'; //@ts-ignore +import ReactStars from 'react-rating-stars-component'; +import { MdOutlineShoppingCart } from 'react-icons/md'; +import { FaRegHeart } from 'react-icons/fa6'; +import { useParams } from 'next/navigation'; +import { Product } from '@/utils/requests'; +import { + ProductObj, + ProductType, + ReviewType, + imageType, +} from '@/types/Product'; +import Card from '@/components/Card'; +import ReviewWrapper from './ReviewsWrapper'; +import request from '@/utils/axios'; +import { showToast } from '@/helpers/toast'; +import { handleWishlistCount } from '@/redux/slices/wishlistSlice'; +const ProductWrapper = ()=>{ + const { wishNumber } = useAppSelector( + (state: RootState) => state.wishlist + ) + const [thumbsSwiper, setThumbsSwiper] = useState(null); + + const { id } :any= useParams(); + const handleSwiper = (swiper: any) => { + setThumbsSwiper(swiper); + }; + const _id: string = id.toLocaleString(); + const { data, isLoading, error , refetch} = useQuery({ + queryKey: ['product', id], + queryFn: async () => { + try { + const response: ProductType = (await Product.single( + _id, + )) as ProductType; + + return response; + } catch (error) { + throw new Error('Error fetching product data'); + } + }, + }); + if (isLoading) return Loading...; + + if (error) return Error: {error.message}; + + const { + productPictures, + productName, + productPrice, + productDescription, + reviews + } = data.product; + console.log('this is data.product >>>>>>>>', data.product); + const { relatedProducts } = data; + const dispatch = useAppDispatch(); + const handleNewItem = () => { + const productId = data.product.id; + dispatch(handleUserAddCart({ productPrice, productId })); + }; + + const handleAddRemoveWish = async(event: { preventDefault: () => void; })=>{ + event.preventDefault(); + const response:any = await request.post('/wishes', { productId:id }); + + if(response.status == 200 || response.status == 203){ + const { status } = response; + dispatch(handleWishlistCount(status == 200 ? await wishNumber + 1 : await wishNumber - 1)); + showToast(response.message, 'success') + } + console.log('this is response', response) + + } + return( +
+
+
+
+
+ {productPictures && productPictures.length > 0 ? ( + + {productPictures.map((image: imageType) => { + return ( + + image + + ); + })} + + ) : ( + {'no + )} +
+
+ {productPictures && productPictures.length > 0 ? ( +
+ + {productPictures.map((image: imageType) => { + return ( + + image + + ); + })} + +
+ ) : ( +

no image found!

+ )} +
+
+
+
+

{productName}

+
+
+
+
+ +
+
+ { + handleNewItem(); + }} + /> +
+
+ + RWF {productPrice.toLocaleString()} + +
+
+ +
+
+

Description:

+

{productDescription}

+
+
+
+
+

Related products:

+
+
+ {relatedProducts && relatedProducts.length > 0 ? ( + relatedProducts.map((product: ProductType) => ( + + )) + ) : ( +

+ No related products available. +

+ )} +
+
+
+ +
+
+ ) +}; + +export default ProductWrapper \ No newline at end of file diff --git a/src/components/wishlistContainer.tsx b/src/components/wishlistContainer.tsx new file mode 100644 index 0000000..7c77edb --- /dev/null +++ b/src/components/wishlistContainer.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import BuyerWishList from './BuyerWishlist'; + +interface WishlistContainerInterface{ + toggleWishlistSlider?:()=>void; + isWishlistOverlayOpen?:boolean; +} +const WishlistContainer:React.FC = ({ isWishlistOverlayOpen, toggleWishlistSlider }) => { + return ( + <> +
+
+
+
+ + + +
+
+ +
+
+
+ + ); +}; +export default WishlistContainer; \ No newline at end of file diff --git a/src/hooks/reviewPopup.tsx b/src/hooks/reviewPopup.tsx new file mode 100644 index 0000000..b0d2659 --- /dev/null +++ b/src/hooks/reviewPopup.tsx @@ -0,0 +1,18 @@ + +import React, { useState } from 'react'; + +function ReviewPopup() { + const [isReviewPopupOpen, setIsReviewPopupOpen] = useState(false) + const toggleReviewPopup = () => { + setIsReviewPopupOpen(!isReviewPopupOpen); + }; + + return{ + isReviewPopupOpen, + setIsReviewPopupOpen, + toggleReviewPopup + } +}; + +export default ReviewPopup; + \ No newline at end of file diff --git a/src/hooks/wishlistOverlay.tsx b/src/hooks/wishlistOverlay.tsx new file mode 100644 index 0000000..a38e9af --- /dev/null +++ b/src/hooks/wishlistOverlay.tsx @@ -0,0 +1,18 @@ + +import React, { useState } from 'react'; + +function WishlistOverlay() { + const [isWishlistOverlayOpen, setIsWishlistOverlayOpen] = useState(false) + const toggleWishlistSlider = () => { + setIsWishlistOverlayOpen(!isWishlistOverlayOpen); + }; + + return{ + isWishlistOverlayOpen, + setIsWishlistOverlayOpen, + toggleWishlistSlider + } +}; + +export default WishlistOverlay; + \ No newline at end of file diff --git a/src/redux/slices/wishlistSlice.ts b/src/redux/slices/wishlistSlice.ts new file mode 100644 index 0000000..5c37055 --- /dev/null +++ b/src/redux/slices/wishlistSlice.ts @@ -0,0 +1,31 @@ +import request from '@/utils/axios'; +import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; + +const handleFetchUserWishes = async ():Promise=>{ + const token = localStorage.getItem('token'); + if(token){ + const response:any = await request.get('/wishes'); + console.log('this is data from wishlist', response); + if(response.status == 200){ + return response.wishes.length; + } + } + return 0; +}; + +const initialState = { + wishNumber: handleFetchUserWishes() +}; +export const userWishlistSlice = createSlice({ + name: 'wishlistCounter', + initialState, + reducers: { + handleWishlistCount: (state, action: PayloadAction) => { + const result = action.payload; + state.wishNumber = result; + } + } +}); + +export const { handleWishlistCount } = userWishlistSlice.actions; +export default userWishlistSlice.reducer; \ No newline at end of file diff --git a/src/redux/store.ts b/src/redux/store.ts index 0a92616..4372474 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -14,6 +14,9 @@ import userProfileSlice from '@/redux/slices/profileSlice'; import notificationSlice from './slices/notificationSlice'; +import userWishlistSlice from './slices/wishlistSlice'; + + const rootReducer = combineReducers({ auth: auth, productsAddReducers, @@ -24,6 +27,7 @@ const rootReducer = combineReducers({ profileReducer, userProfile: userProfileSlice, notification: notificationSlice, + wishlist: userWishlistSlice }); export const store = configureStore({