diff --git a/.gitignore b/.gitignore index 44300dc1f..77ec94cd7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ dist build # environment variables -.env +.env.* .env.local .env.development.local .env.test.local diff --git a/apphosting.yaml b/apphosting.yaml new file mode 100644 index 000000000..2325bef5d --- /dev/null +++ b/apphosting.yaml @@ -0,0 +1,23 @@ +# Settings for Backend (on Cloud Run). +# See https://firebase.google.com/docs/app-hosting/configure#cloud-run +runConfig: + minInstances: 0 + # maxInstances: 100 + # concurrency: 80 + # cpu: 1 + # memoryMiB: 512 + +# Environment variables and secrets. +# env: + # Configure environment variables. + # See https://firebase.google.com/docs/app-hosting/configure#user-defined-environment + # - variable: MESSAGE + # value: Hello world! + # availability: + # - BUILD + # - RUNTIME + + # Grant access to secrets in Cloud Secret Manager. + # See https://firebase.google.com/docs/app-hosting/configure#secret-parameters + # - variable: MY_SECRET + # secret: mySecretRef diff --git a/index.html b/index.html index efb687cbc..0870fcfa9 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - Minimal UI Kit + PABGM Portal diff --git a/package-lock.json b/package-lock.json index bcd1a82ae..0066b5add 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@minimal/material-kit-react", - "version": "2.0.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@minimal/material-kit-react", - "version": "2.0.0", + "version": "1.0.0", "dependencies": { "@emotion/cache": "^11.13.1", "@emotion/react": "^11.13.3", @@ -16,9 +16,14 @@ "@iconify/react": "^5.0.2", "@mui/lab": "^5.0.0-alpha.173", "@mui/material": "^5.16.7", + "@paystack/inline-js": "^2.22.1", "apexcharts": "^3.52.0", + "bcryptjs": "^2.4.3", + "crypto-js": "^4.2.0", "dayjs": "^1.11.13", + "firebase": "^11.0.1", "history": "^5.3.0", + "jwt-decode": "^4.0.0", "react": "^18.3.1", "react-apexcharts": "^1.4.1", "react-dom": "^18.3.1", @@ -27,7 +32,10 @@ "simplebar-react": "^3.2.6" }, "devDependencies": { + "@types/bcryptjs": "^2.4.6", + "@types/crypto-js": "^4.2.2", "@types/node": "^22.5.0", + "@types/paystack__inline-js": "^1.0.0", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.18.0", @@ -65,12 +73,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -78,73 +87,59 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.4.tgz", - "integrity": "sha512-NFtZmZsyzDPJnk9Zg3BbTfKKc9UlHYzD0E//p2Z3B9nCwwtJW9T0gVbCz8+fBngnn4zf1Dr3IK8PHQQHq0lDQw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -154,9 +149,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", - "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -166,30 +161,30 @@ } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -198,14 +193,13 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -289,15 +283,15 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.1.tgz", - "integrity": "sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", + "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", "license": "MIT", "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.0", + "@emotion/utils": "^1.4.1", "csstype": "^3.0.2" } }, @@ -346,9 +340,9 @@ } }, "node_modules/@emotion/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", + "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==", "license": "MIT" }, "node_modules/@emotion/weak-memoize": { @@ -861,6 +855,612 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@firebase/analytics": { + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.9.tgz", + "integrity": "sha512-FrvW6u6xDBKXUGYUy1WIUh0J9tvbppMsk90mig0JhHST8iLveKu/dIBVeVE/ZYZhmXy4fkI7SPSWvD1V0O4tXw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.15.tgz", + "integrity": "sha512-C5to422Sr8FkL0MPwXcIecbMnF4o2Ll7MtoWvIm4Q/LPJvvM+tWa1DiU+LzsCdsd1/CYE9EIW9Ma3ko9XnAAYw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/analytics": "0.10.9", + "@firebase/analytics-types": "0.8.2", + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app": { + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.15.tgz", + "integrity": "sha512-he6qlG3pmwL+LHdG/BrSMBQeJzzutciq4fpXN3lGa1uSwYSijJ24VtakS/bP2X9SiDf8jGywJ4u+OgXAenJsNg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.9.tgz", + "integrity": "sha512-YzVn1mMLzD2JboMPVVO0Pe20YOgWzrF+aXoAmmd0v3xec051n83YpxSUZbacL69uYvk0dHrEsbea44QtQ5WPDA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.16", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.16.tgz", + "integrity": "sha512-AxIGzLRXrTFNL+H6V+4BO0w/gERloROfRbWI/FoJUnQd0qPZIzyfdHZBbThFzFGLfDt/mVs2kdjYFx/l9I8NhQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check": "0.8.9", + "@firebase/app-check-types": "0.5.2", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.45", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.45.tgz", + "integrity": "sha512-5rYbXq1ndtMTg+07oH4WrkYuP+NZq61uzVwW1hlmybp/gr4cXq2SfaP9fc6/9IzTKmu3dh3H0fjj++HG7Z7o/w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app": "0.10.15", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.8.0.tgz", + "integrity": "sha512-/O7UDWE5S5ux456fzNHSLx/0YN/Kykw/WyAzgDQ6wvkddZhSEmPX19EzxgsFldzhuFjsl5uOZTz8kzlosCiJjg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.15.tgz", + "integrity": "sha512-jz6k1ridPiecKI8CBRiqCM6IMOhwYp2MD+YvoxnMiK8nQLSTm57GvHETlPNX3WlbyQnCjMCOvrAhe27whyxAEg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth": "1.8.0", + "@firebase/auth-types": "0.12.2", + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", + "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.10.tgz", + "integrity": "sha512-OsNbEKyz9iLZSmMUhsl6+kCADzte00iisJIRUspnUqvDCX+RSGZOBIqekukv/jN177ovjApBQNFaxSYIDc/SyQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.1.1.tgz", + "integrity": "sha512-RBJ7XE/a3oXFv31Jlw8cbMRdsxQoI8F3L7xm4n93ab+bIr1NQUiYGgW9L7TTw7obdNev91ZnW0xfqJtXcPA5yA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.9.tgz", + "integrity": "sha512-EkiPSKSu2TJJGtOjyISASf3UFpFJDil1lMbfqnxilfbmIsilvC8DzgjuLoYD+eOitcug4wtU9Fh1tt2vgBhskA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.0.tgz", + "integrity": "sha512-2xlODKWwf/vNAxCmou0GFhymx2pqZKkhXMN9B5aiTjZ6+81sOxGim53ELY2lj+qKG2IvgiCYFc4X+ZJA2Ad5vg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/database": "1.0.9", + "@firebase/database-types": "1.0.6", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.6.tgz", + "integrity": "sha512-sMI7IynSZBsyGbUugc8PKE1jwKbnvaieAz/RxuM57PZQNCi6Rteiviwcw/jqZOX6igqYJwXWZ3UzKOZo2nUDRA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.2", + "@firebase/util": "1.10.1" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.4.tgz", + "integrity": "sha512-K2nq4w+NF8J1waGawY5OHLawP/Aw5CYxyDstVv1NZemGPcM3U+LZ9EPaXr1PatYIrPA7fS4DxZoWcbB0aGJ8Zg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "@firebase/webchannel-wrapper": "1.0.2", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.39", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.39.tgz", + "integrity": "sha512-CsK8g34jNeHx95LISDRTcArJLonW+zJCqHI1Ez9WNiLAK2X8FeQ4UiD+RwOwxAIR+t2a6xED/5Fe6ZIqx7MuoQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/firestore": "4.7.4", + "@firebase/firestore-types": "3.0.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", + "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.9.tgz", + "integrity": "sha512-dhO5IUfQRCsrc20YD20nSOX+QCT+cH6N86HlZOLz2XgyEFgzOdBQnUot4EabBJQRkMBI7fZWUrbYfRcnov53ug==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.10", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.15.tgz", + "integrity": "sha512-eiHpc6Sd9Y/SNhBsGi944SapiFbfTPKsiSUQ74QxNSs0yoxvABeIRolVMFk4TokP57NGmstGYpYte02XGNPcYw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/functions": "0.11.9", + "@firebase/functions-types": "0.6.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/installations": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.10.tgz", + "integrity": "sha512-TuGSOMqkFrllxa0X/8VZIqBCRH4POndU/iWKWkRmkh12+/xKSpdp+y/kWaVbsySrelltan6LeYlcYPmLibWbwg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.10.tgz", + "integrity": "sha512-YTonkcVz3AK7RF8xFhvs5CwDuJ0xbzzCJIwXoV14gnzdYbMgy6vWlUUbzkvbtEDXzPRHB0n7aGZl56oy9dLOFw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/installations-types": "0.5.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", + "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.3.tgz", + "integrity": "sha512-Th42bWJg18EF5bJwhRosn2M/eYxmbWCwXZr4hHX7ltO0SE3QLrpgiMKeRBR/NW7vJke7i0n3i8esbCW2s93qBw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.13", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.13.tgz", + "integrity": "sha512-YLa8PWl+BgiOVR5WOyzl21fVJFJeBRfniNuN25d9DBrQzppSAahuN6yS+vt1OIjvZNPN4pZ/lcRLYupbGu4W0w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.10.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.13.tgz", + "integrity": "sha512-9ootPClS6m2c2KIzo7AqSHaWzAw28zWcjQPjVv7WeQDu6wjufpbOg+7tuVzb+gqpF9Issa3lDoYOwlO0ZudO3g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/messaging": "0.12.13", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/performance": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.10.tgz", + "integrity": "sha512-x/mNYKGxq7A+QV0EiEZeD2S+E+kw+UcZ8FXuE7qDJyGGt/0Wd+bIIL7RakG/VrFt7/UYc//nKygDc7/Ig7sOmQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.10.tgz", + "integrity": "sha512-0h1qYkF6I79DSSpHfTQFvb91fo8shmmwiPzWFYAPdPK02bSWpKwVssNYlZX2iUnumxerDMbl7dWN+Im/W3bnXA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/performance": "0.6.10", + "@firebase/performance-types": "0.2.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.10.tgz", + "integrity": "sha512-jTRjy3TdqzVna19m5a1HEHE5BG4Z3BQTxBgvQRTmMKlHacx4QS0CToAas7R9M9UkxpgFcVuAE7FpWIOWQGCEWw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.10.tgz", + "integrity": "sha512-fIi5OB2zk0zpChMV/tTd0oEZcZI8TlwQDlLlcrDpMOV5l5dqd0JNlWKh6Fwmh4izmytk+rZIAIpnak/NjGVesQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/remote-config": "0.4.10", + "@firebase/remote-config-types": "0.3.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/storage": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.3.tgz", + "integrity": "sha512-B5HiJ7isYKaT4dOEV43f2ySdhQxzq+SQEm7lqXebJ8AYCsebdHrgGzrPR0LR962xGjPzJHFKx63gA8Be/P2MCw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.13.tgz", + "integrity": "sha512-15kje7JALswRCBKsCSvKg5FbqUYykaIMqMbZRD7I6uVRWwdyTvez5MBQfMhBia2JcEmPiDpXhJTXH4PAWFiA8g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/storage": "0.13.3", + "@firebase/storage-types": "0.8.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", + "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.1.tgz", + "integrity": "sha512-AIhFnCCjM8FmCqSNlNPTuOk3+gpHC1RkeNUBLtPbcqGYpN5MxI5q7Yby+rxycweOZOCboDzfIj8WyaY4tpQG/g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/vertexai": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/vertexai/-/vertexai-1.0.0.tgz", + "integrity": "sha512-48N3Lp/9GgiCCRfrSdHS+Y1IiMdYXvnHFO/f+HL1PgUtBq7WQ/fWmYOX3mzAN36zvytq13nb68ImF+GALopp+Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.2.tgz", + "integrity": "sha512-3F4iA2E+NtdMbOU0XC1cHE8q6MqpGIKRj62oGOF38S6AAx5VHR9cXmoDUSj7ejvTAT7m6jxuEeQkHeq0F+mU2w==", + "license": "Apache-2.0" + }, "node_modules/@floating-ui/core": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", @@ -911,6 +1511,37 @@ "integrity": "sha512-4F+rbfklgWHatFheB3ZQgTFjkqzMiWfHomy69TWSGc0qU+w+QhX9dGz7IVQRksvKciJoXAhxijCxwAJw418g4Q==", "license": "OFL-1.1" }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1270,12 +1901,12 @@ } }, "node_modules/@mui/types": { - "version": "7.2.15", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", - "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", + "version": "7.2.19", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", + "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==", "license": "MIT", "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1351,6 +1982,16 @@ "node": ">= 8" } }, + "node_modules/@paystack/inline-js": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/@paystack/inline-js/-/inline-js-2.22.1.tgz", + "integrity": "sha512-h9cf+3UbFY/+/GRA5XeWj769KaSHuedqIcuYDdV6voKGnwF9qcmJ3BorpIST45Y3qVXljOXsexL2tS6GZXJbmg==", + "license": "ISC", + "engines": { + "node": ">= 18.0.0", + "npm": ">= 10.0.0" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -1364,15 +2005,79 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" }, "node_modules/@remix-run/router": { "version": "1.19.1", @@ -1833,6 +2538,20 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -1866,7 +2585,6 @@ "version": "22.5.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -1878,10 +2596,17 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "license": "MIT" }, + "node_modules/@types/paystack__inline-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/paystack__inline-js/-/paystack__inline-js-1.0.0.tgz", + "integrity": "sha512-LCU5rSBs3FAG8tkn1hgV9FHTTRk+Kj2s/DupNlJRem5NaVabj6AASjWJ9tBtJX6dt7bcKhkf8c5MzgJUkpOG2g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", "license": "MIT" }, "node_modules/@types/react": { @@ -2192,24 +2917,11 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "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/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -2488,6 +3200,12 @@ "dev": true, "license": "MIT" }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2550,29 +3268,6 @@ "node": ">=6" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2614,6 +3309,20 @@ "node": ">= 6" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2623,21 +3332,6 @@ "node": ">=6" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -2699,6 +3393,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3153,6 +3853,15 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4019,6 +4728,18 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4068,6 +4789,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.0.1.tgz", + "integrity": "sha512-qsFb8dMcQINEDhJteG7RP+GqwgSRvfyiexQqHd5JToDdm87i9I2rGC4XQsGawKGxzKwZ/ISdgwNWxXAFYdCC6A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/analytics": "0.10.9", + "@firebase/analytics-compat": "0.2.15", + "@firebase/app": "0.10.15", + "@firebase/app-check": "0.8.9", + "@firebase/app-check-compat": "0.3.16", + "@firebase/app-compat": "0.2.45", + "@firebase/app-types": "0.9.2", + "@firebase/auth": "1.8.0", + "@firebase/auth-compat": "0.5.15", + "@firebase/data-connect": "0.1.1", + "@firebase/database": "1.0.9", + "@firebase/database-compat": "2.0.0", + "@firebase/firestore": "4.7.4", + "@firebase/firestore-compat": "0.3.39", + "@firebase/functions": "0.11.9", + "@firebase/functions-compat": "0.3.15", + "@firebase/installations": "0.6.10", + "@firebase/installations-compat": "0.2.10", + "@firebase/messaging": "0.12.13", + "@firebase/messaging-compat": "0.2.13", + "@firebase/performance": "0.6.10", + "@firebase/performance-compat": "0.2.10", + "@firebase/remote-config": "0.4.10", + "@firebase/remote-config-compat": "0.2.10", + "@firebase/storage": "0.13.3", + "@firebase/storage-compat": "0.3.13", + "@firebase/util": "1.10.1", + "@firebase/vertexai": "1.0.0" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -4174,6 +4931,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -4367,15 +5133,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -4467,6 +5224,18 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "license": "MIT" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4728,6 +5497,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", @@ -5004,15 +5782,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-parse-even-better-errors": { @@ -5077,6 +5855,15 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -5145,6 +5932,12 @@ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "license": "MIT" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5152,6 +5945,12 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5545,9 +6344,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { @@ -5658,6 +6457,30 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5861,6 +6684,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -6004,6 +6836,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -6191,6 +7043,26 @@ "node": ">= 0.4" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/string.prototype.includes": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", @@ -6296,7 +7168,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6334,18 +7205,6 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "license": "MIT" }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -6490,15 +7349,6 @@ "dev": true, "license": "MIT" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6542,7 +7392,6 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -6682,7 +7531,6 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { @@ -7002,6 +7850,29 @@ "dev": true, "license": "MIT" }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7101,6 +7972,56 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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==", + "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/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/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/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7108,6 +8029,15 @@ "dev": true, "license": "ISC" }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -7117,6 +8047,33 @@ "node": ">= 6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 8842c7bea..969ea8861 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,14 @@ "name": "@minimal/material-kit-react", "author": "minimals.cc", "licence": "MIT", - "version": "2.0.0", + "version": "1.0.0", "private": false, "type": "module", "scripts": { "dev": "vite", "start": "vite preview", "build": "tsc && vite build", + "host": "firebase deploy --only hosting", "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", "lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx}\"", "fm:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"", @@ -32,9 +33,14 @@ "@iconify/react": "^5.0.2", "@mui/lab": "^5.0.0-alpha.173", "@mui/material": "^5.16.7", + "@paystack/inline-js": "^2.22.1", "apexcharts": "^3.52.0", + "bcryptjs": "^2.4.3", + "crypto-js": "^4.2.0", "dayjs": "^1.11.13", + "firebase": "^11.0.1", "history": "^5.3.0", + "jwt-decode": "^4.0.0", "react": "^18.3.1", "react-apexcharts": "^1.4.1", "react-dom": "^18.3.1", @@ -43,7 +49,10 @@ "simplebar-react": "^3.2.6" }, "devDependencies": { + "@types/bcryptjs": "^2.4.6", + "@types/crypto-js": "^4.2.2", "@types/node": "^22.5.0", + "@types/paystack__inline-js": "^1.0.0", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.18.0", diff --git a/public/404.html b/public/404.html new file mode 100644 index 000000000..829eda8fd --- /dev/null +++ b/public/404.html @@ -0,0 +1,33 @@ + + + + + + Page Not Found + + + + +
+

404

+

Page Not Found

+

The specified file was not found on this website. Please check the URL for mistakes and try again.

+

Why am I seeing this?

+

This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html file in your project's configured public directory.

+
+ + diff --git a/public/assets/icons/glass/ic-donate.svg b/public/assets/icons/glass/ic-donate.svg new file mode 100644 index 000000000..b4d273e5c --- /dev/null +++ b/public/assets/icons/glass/ic-donate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icons/glass/ic-glass-bag.svg b/public/assets/icons/glass/ic-glass-bag.svg deleted file mode 100644 index 5875b6f39..000000000 --- a/public/assets/icons/glass/ic-glass-bag.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/assets/icons/glass/ic-wallet.svg b/public/assets/icons/glass/ic-wallet.svg new file mode 100644 index 000000000..3b30bbf91 --- /dev/null +++ b/public/assets/icons/glass/ic-wallet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icons/navbar/ic-donate.svg b/public/assets/icons/navbar/ic-donate.svg new file mode 100644 index 000000000..80a633dc6 --- /dev/null +++ b/public/assets/icons/navbar/ic-donate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icons/navbar/ic-exit.svg b/public/assets/icons/navbar/ic-exit.svg new file mode 100644 index 000000000..4e637896c --- /dev/null +++ b/public/assets/icons/navbar/ic-exit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icons/status/ic-failed.svg b/public/assets/icons/status/ic-failed.svg new file mode 100644 index 000000000..0bda5ac56 --- /dev/null +++ b/public/assets/icons/status/ic-failed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icons/status/ic-pending.svg b/public/assets/icons/status/ic-pending.svg new file mode 100644 index 000000000..b3df1c3da --- /dev/null +++ b/public/assets/icons/status/ic-pending.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icons/status/ic-success.svg b/public/assets/icons/status/ic-success.svg new file mode 100644 index 000000000..cdfff398c --- /dev/null +++ b/public/assets/icons/status/ic-success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/images/logo.png b/public/assets/images/logo.png new file mode 100644 index 000000000..8a5addbd3 Binary files /dev/null and b/public/assets/images/logo.png differ diff --git a/public/favicon.ico b/public/favicon.ico index 5c435e6bb..26fe61f5b 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 000000000..0ebc2eb15 --- /dev/null +++ b/public/index.html @@ -0,0 +1,89 @@ + + + + + + Welcome to Firebase Hosting + + + + + + + + + + + + + + + + + + + +
+

Welcome

+

Firebase Hosting Setup Complete

+

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

+ Open Hosting Documentation +
+

Firebase SDK Loading…

+ + + + diff --git a/src/_mock/_countries.ts b/src/_mock/_countries.ts new file mode 100644 index 000000000..1875f2c86 --- /dev/null +++ b/src/_mock/_countries.ts @@ -0,0 +1,1247 @@ +export default [ + { + code: 'af', + label: 'Afghanistan', + icon: 'twemoji:flag-afghanistan', + }, + { + code: 'ax', + label: 'Aland Islands', + icon: 'twemoji:flag-aland-islands', + }, + { + code: 'al', + label: 'Albania', + icon: 'twemoji:flag-albania', + }, + { + code: 'dz', + label: 'Algeria', + icon: 'twemoji:flag-algeria', + }, + { + code: 'as', + label: 'American Samoa', + icon: 'twemoji:flag-american-samoa', + }, + { + code: 'ad', + label: 'Andorra', + icon: 'twemoji:flag-andorra', + }, + { + code: 'ao', + label: 'Angola', + icon: 'twemoji:flag-angola', + }, + { + code: 'ai', + label: 'Anguilla', + icon: 'twemoji:flag-anguilla', + }, + { + code: 'aq', + label: 'Antarctica', + icon: 'twemoji:flag-antarctica', + }, + { + code: 'ag', + label: 'Antigua and Barbuda', + icon: 'twemoji:flag-antigua-and-barbuda', + }, + { + code: 'ar', + label: 'Argentina', + icon: 'twemoji:flag-argentina', + }, + { + code: 'am', + label: 'Armenia', + icon: 'twemoji:flag-armenia', + }, + { + code: 'aw', + label: 'Aruba', + icon: 'twemoji:flag-aruba', + }, + { + code: 'au', + label: 'Australia', + icon: 'twemoji:flag-australia', + }, + { + code: 'at', + label: 'Austria', + icon: 'twemoji:flag-austria', + }, + { + code: 'az', + label: 'Azerbaijan', + icon: 'twemoji:flag-azerbaijan', + }, + { + code: 'bs', + label: 'Bahamas', + icon: 'twemoji:flag-bahamas', + }, + { + code: 'bh', + label: 'Bahrain', + icon: 'twemoji:flag-bahrain', + }, + { + code: 'bd', + label: 'Bangladesh', + icon: 'twemoji:flag-bangladesh', + }, + { + code: 'bb', + label: 'Barbados', + icon: 'twemoji:flag-barbados', + }, + { + code: 'by', + label: 'Belarus', + icon: 'twemoji:flag-belarus', + }, + { + code: 'be', + label: 'Belgium', + icon: 'twemoji:flag-belgium', + }, + { + code: 'bz', + label: 'Belize', + icon: 'twemoji:flag-belize', + }, + { + code: 'bj', + label: 'Benin', + icon: 'twemoji:flag-benin', + }, + { + code: 'bm', + label: 'Bermuda', + icon: 'twemoji:flag-bermuda', + }, + { + code: 'bt', + label: 'Bhutan', + icon: 'twemoji:flag-bhutan', + }, + { + code: 'bo', + label: 'Bolivia', + icon: 'twemoji:flag-bolivia', + }, + { + code: 'ba', + label: 'Bosnia and Herzegovina', + icon: 'twemoji:flag-bosnia-and-herzegovina', + }, + { + code: 'bw', + label: 'Botswana', + icon: 'twemoji:flag-botswana', + }, + { + code: 'bv', + label: 'Bouvet Island', + icon: 'twemoji:flag-bouvet-island', + }, + { + code: 'br', + label: 'Brazil', + icon: 'twemoji:flag-brazil', + }, + { + code: 'io', + label: 'British Indian Ocean Territory', + icon: 'twemoji:flag-british-indian-ocean-territory', + }, + { + code: 'vg', + label: 'British Virgin Islands', + icon: 'twemoji:flag-british-virgin-islands', + }, + { + code: 'bn', + label: 'Brunei', + icon: 'twemoji:flag-brunei', + }, + { + code: 'bg', + label: 'Bulgaria', + icon: 'twemoji:flag-bulgaria', + }, + { + code: 'bf', + label: 'Burkina Faso', + icon: 'twemoji:flag-burkina-faso', + }, + { + code: 'bi', + label: 'Burundi', + icon: 'twemoji:flag-burundi', + }, + { + code: 'kh', + label: 'Cambodia', + icon: 'twemoji:flag-cambodia', + }, + { + code: 'cm', + label: 'Cameroon', + icon: 'twemoji:flag-cameroon', + }, + { + code: 'ca', + label: 'Canada', + icon: 'twemoji:flag-canada', + }, + { + code: 'cv', + label: 'Cape Verde', + icon: 'twemoji:flag-cape-verde', + }, + { + code: 'ky', + label: 'Cayman Islands', + icon: 'twemoji:flag-cayman-islands', + }, + { + code: 'cf', + label: 'Central African Republic', + icon: 'twemoji:flag-central-african-republic', + }, + { + code: 'td', + label: 'Chad', + icon: 'twemoji:flag-chad', + }, + { + code: 'cl', + label: 'Chile', + icon: 'twemoji:flag-chile', + }, + { + code: 'cn', + label: 'China', + icon: 'twemoji:flag-china', + }, + { + code: 'cx', + label: 'Christmas Island', + icon: 'twemoji:flag-christmas-island', + }, + { + code: 'cc', + label: 'Cocos Islands', + icon: 'twemoji:flag-cocos-keeling-islands', + }, + { + code: 'co', + label: 'Colombia', + icon: 'twemoji:flag-colombia', + }, + { + code: 'km', + label: 'Comoros', + icon: 'twemoji:flag-comoros', + }, + { + code: 'ck', + label: 'Cook Islands', + icon: 'twemoji:flag-cook-islands', + }, + { + code: 'cr', + label: 'Costa Rica', + icon: 'twemoji:flag-costa-rica', + }, + { + code: 'hr', + label: 'Croatia', + icon: 'twemoji:flag-croatia', + }, + { + code: 'cu', + label: 'Cuba', + icon: 'twemoji:flag-cuba', + }, + { + code: 'cw', + label: 'Curacao', + icon: 'twemoji:flag-curacao', + }, + { + code: 'cy', + label: 'Cyprus', + icon: 'twemoji:flag-cyprus', + }, + { + code: 'cz', + label: 'Czech Republic', + icon: 'twemoji:flag-czechia', + }, + { + code: 'cd', + label: 'Democratic Republic of the Congo', + icon: 'twemoji:flag-congo-kinshasa', + }, + { + code: 'dk', + label: 'Denmark', + icon: 'twemoji:flag-denmark', + }, + { + code: 'dj', + label: 'Djibouti', + icon: 'twemoji:flag-djibouti', + }, + { + code: 'dm', + label: 'Dominica', + icon: 'twemoji:flag-dominica', + }, + { + code: 'do', + label: 'Dominican Republic', + icon: 'twemoji:flag-dominican-republic', + }, + { + code: 'tl', + label: 'East Timor', + icon: 'twemoji:flag-timor-leste', + }, + { + code: 'ec', + label: 'Ecuador', + icon: 'twemoji:flag-ecuador', + }, + { + code: 'eg', + label: 'Egypt', + icon: 'twemoji:flag-egypt', + }, + { + code: 'sv', + label: 'El Salvador', + icon: 'twemoji:flag-el-salvador', + }, + { + code: 'gq', + label: 'Equatorial Guinea', + icon: 'twemoji:flag-equatorial-guinea', + }, + { + code: 'er', + label: 'Eritrea', + icon: 'twemoji:flag-eritrea', + }, + { + code: 'ee', + label: 'Estonia', + icon: 'twemoji:flag-estonia', + }, + { + code: 'et', + label: 'Ethiopia', + icon: 'twemoji:flag-ethiopia', + }, + { + code: 'fk', + label: 'Falkland Islands', + icon: 'twemoji:flag-falkland-islands', + }, + { + code: 'fo', + label: 'Faroe Islands', + icon: 'twemoji:flag-faroe-islands', + }, + { + code: 'fj', + label: 'Fiji', + icon: 'twemoji:flag-fiji', + }, + { + code: 'fi', + label: 'Finland', + icon: 'twemoji:flag-finland', + }, + { + code: 'fr', + label: 'France', + icon: 'twemoji:flag-france', + }, + { + code: 'gf', + label: 'French Guiana', + icon: 'twemoji:flag-french-guiana', + }, + { + code: 'pf', + label: 'French Polynesia', + icon: 'twemoji:flag-french-polynesia', + }, + { + code: 'tf', + label: 'French Southern Territories', + icon: 'twemoji:flag-french-southern-territories', + }, + { + code: 'ga', + label: 'Gabon', + icon: 'twemoji:flag-gabon', + }, + { + code: 'gm', + label: 'Gambia', + icon: 'twemoji:flag-gambia', + }, + { + code: 'ge', + label: 'Georgia', + icon: 'twemoji:flag-georgia', + }, + { + code: 'de', + label: 'Germany', + icon: 'twemoji:flag-germany', + }, + { + code: 'gh', + label: 'Ghana', + icon: 'twemoji:flag-ghana', + }, + { + code: 'gi', + label: 'Gibraltar', + icon: 'twemoji:flag-gibraltar', + }, + { + code: 'gr', + label: 'Greece', + icon: 'twemoji:flag-greece', + }, + { + code: 'gl', + label: 'Greenland', + icon: 'twemoji:flag-greenland', + }, + { + code: 'gd', + label: 'Grenada', + icon: 'twemoji:flag-grenada', + }, + { + code: 'gp', + label: 'Guadeloupe', + icon: 'twemoji:flag-guadeloupe', + }, + { + code: 'gu', + label: 'Guam', + icon: 'twemoji:flag-guam', + }, + { + code: 'gt', + label: 'Guatemala', + icon: 'twemoji:flag-guatemala', + }, + { + code: 'gg', + label: 'Guernsey', + icon: 'twemoji:flag-guernsey', + }, + { + code: 'gn', + label: 'Guinea', + icon: 'twemoji:flag-guinea', + }, + { + code: 'gw', + label: 'Guinea-Bissau', + icon: 'twemoji:flag-guinea-bissau', + }, + { + code: 'gy', + label: 'Guyana', + icon: 'twemoji:flag-guyana', + }, + { + code: 'ht', + label: 'Haiti', + icon: 'twemoji:flag-haiti', + }, + { + code: 'hm', + label: 'Heard Island and McDonald Islands', + icon: 'twemoji:flag-heard-and-mcdonald-islands', + }, + { + code: 'hn', + label: 'Honduras', + icon: 'twemoji:flag-honduras', + }, + { + code: 'hk', + label: 'Hong Kong', + icon: 'twemoji:flag-hong-kong-sar-china', + }, + { + code: 'hu', + label: 'Hungary', + icon: 'twemoji:flag-hungary', + }, + { + code: 'is', + label: 'Iceland', + icon: 'twemoji:flag-iceland', + }, + { + code: 'in', + label: 'India', + icon: 'twemoji:flag-india', + }, + { + code: 'id', + label: 'Indonesia', + icon: 'twemoji:flag-indonesia', + }, + { + code: 'ir', + label: 'Iran', + icon: 'twemoji:flag-iran', + }, + { + code: 'iq', + label: 'Iraq', + icon: 'twemoji:flag-iraq', + }, + { + code: 'ie', + label: 'Ireland', + icon: 'twemoji:flag-ireland', + }, + { + code: 'im', + label: 'Isle of Man', + icon: 'twemoji:flag-isle-of-man', + }, + { + code: 'il', + label: 'Israel', + icon: 'twemoji:flag-israel', + }, + { + code: 'it', + label: 'Italy', + icon: 'twemoji:flag-italy', + }, + { + code: 'ci', + label: 'Ivory Coast', + icon: 'twemoji:flag-cote-divoire', + }, + { + code: 'jm', + label: 'Jamaica', + icon: 'twemoji:flag-jamaica', + }, + { + code: 'jp', + label: 'Japan', + icon: 'twemoji:flag-japan', + }, + { + code: 'je', + label: 'Jersey', + icon: 'twemoji:flag-jersey', + }, + { + code: 'jo', + label: 'Jordan', + icon: 'twemoji:flag-jordan', + }, + { + code: 'kz', + label: 'Kazakhstan', + icon: 'twemoji:flag-kazakhstan', + }, + { + code: 'ke', + label: 'Kenya', + icon: 'twemoji:flag-kenya', + }, + { + code: 'ki', + label: 'Kiribati', + icon: 'twemoji:flag-kiribati', + }, + { + code: 'xk', + label: 'Kosovo', + icon: 'twemoji:flag-kosovo', + }, + { + code: 'kw', + label: 'Kuwait', + icon: 'twemoji:flag-kuwait', + }, + { + code: 'kg', + label: 'Kyrgyzstan', + icon: 'twemoji:flag-kyrgyzstan', + }, + { + code: 'la', + label: 'Laos', + icon: 'twemoji:flag-laos', + }, + { + code: 'lv', + label: 'Latvia', + icon: 'twemoji:flag-latvia', + }, + { + code: 'lb', + label: 'Lebanon', + icon: 'twemoji:flag-lebanon', + }, + { + code: 'ls', + label: 'Lesotho', + icon: 'twemoji:flag-lesotho', + }, + { + code: 'lr', + label: 'Liberia', + icon: 'twemoji:flag-liberia', + }, + { + code: 'ly', + label: 'Libya', + icon: 'twemoji:flag-libya', + }, + { + code: 'li', + label: 'Liechtenstein', + icon: 'twemoji:flag-liechtenstein', + }, + { + code: 'lt', + label: 'Lithuania', + icon: 'twemoji:flag-lithuania', + }, + { + code: 'lu', + label: 'Luxembourg', + icon: 'twemoji:flag-luxembourg', + }, + { + code: 'mo', + label: 'Macao', + icon: 'twemoji:flag-macao-sar-china', + }, + { + code: 'mk', + label: 'Macedonia', + icon: 'twemoji:flag-north-macedonia', + }, + { + code: 'mg', + label: 'Madagascar', + icon: 'twemoji:flag-madagascar', + }, + { + code: 'mw', + label: 'Malawi', + icon: 'twemoji:flag-malawi', + }, + { + code: 'my', + label: 'Malaysia', + icon: 'twemoji:flag-malaysia', + }, + { + code: 'mv', + label: 'Maldives', + icon: 'twemoji:flag-maldives', + }, + { + code: 'ml', + label: 'Mali', + icon: 'twemoji:flag-mali', + }, + { + code: 'mt', + label: 'Malta', + icon: 'twemoji:flag-malta', + }, + { + code: 'mh', + label: 'Marshall Islands', + icon: 'twemoji:flag-marshall-islands', + }, + { + code: 'mq', + label: 'Martinique', + icon: 'twemoji:flag-martinique', + }, + { + code: 'mr', + label: 'Mauritania', + icon: 'twemoji:flag-mauritania', + }, + { + code: 'mu', + label: 'Mauritius', + icon: 'twemoji:flag-mauritius', + }, + { + code: 'yt', + label: 'Mayotte', + icon: 'twemoji:flag-mayotte', + }, + { + code: 'mx', + label: 'Mexico', + icon: 'twemoji:flag-mexico', + }, + { + code: 'fm', + label: 'Micronesia', + icon: 'twemoji:flag-micronesia', + }, + { + code: 'md', + label: 'Moldova', + icon: 'twemoji:flag-moldova', + }, + { + code: 'mc', + label: 'Monaco', + icon: 'twemoji:flag-monaco', + }, + { + code: 'mn', + label: 'Mongolia', + icon: 'twemoji:flag-mongolia', + }, + { + code: 'me', + label: 'Montenegro', + icon: 'twemoji:flag-montenegro', + }, + { + code: 'ms', + label: 'Montserrat', + icon: 'twemoji:flag-montserrat', + }, + { + code: 'ma', + label: 'Morocco', + icon: 'twemoji:flag-morocco', + }, + { + code: 'mz', + label: 'Mozambique', + icon: 'twemoji:flag-mozambique', + }, + { + code: 'mm', + label: 'Myanmar', + icon: 'twemoji:flag-myanmar-burma', + }, + { + code: 'na', + label: 'Namibia', + icon: 'twemoji:flag-namibia', + }, + { + code: 'nr', + label: 'Nauru', + icon: 'twemoji:flag-nauru', + }, + { + code: 'np', + label: 'Nepal', + icon: 'twemoji:flag-nepal', + }, + { + code: 'nl', + label: 'Netherlands', + icon: 'twemoji:flag-netherlands', + }, + { + code: 'nc', + label: 'New Caledonia', + icon: 'twemoji:flag-new-caledonia', + }, + { + code: 'nz', + label: 'New Zealand', + icon: 'twemoji:flag-new-zealand', + }, + { + code: 'ni', + label: 'Nicaragua', + icon: 'twemoji:flag-nicaragua', + }, + { + code: 'ne', + label: 'Niger', + icon: 'twemoji:flag-niger', + }, + { + code: 'ng', + label: 'Nigeria', + icon: 'twemoji:flag-nigeria', + }, + { + code: 'nu', + label: 'Niue', + icon: 'twemoji:flag-niue', + }, + { + code: 'nf', + label: 'Norfolk Island', + icon: 'twemoji:flag-norfolk-island', + }, + { + code: 'kp', + label: 'North Korea', + icon: 'twemoji:flag-north-korea', + }, + { + code: 'mp', + label: 'Northern Mariana Islands', + icon: 'twemoji:flag-northern-mariana-islands', + }, + { + code: 'no', + label: 'Norway', + icon: 'twemoji:flag-norway', + }, + { + code: 'om', + label: 'Oman', + icon: 'twemoji:flag-oman', + }, + { + code: 'pk', + label: 'Pakistan', + icon: 'twemoji:flag-pakistan', + }, + { + code: 'pw', + label: 'Palau', + icon: 'twemoji:flag-palau', + }, + { + code: 'ps', + label: 'Palestinian Territory', + icon: 'twemoji:flag-palestinian-territories', + }, + { + code: 'pa', + label: 'Panama', + icon: 'twemoji:flag-panama', + }, + { + code: 'pg', + label: 'Papua New Guinea', + icon: 'twemoji:flag-papua-new-guinea', + }, + { + code: 'py', + label: 'Paraguay', + icon: 'twemoji:flag-paraguay', + }, + { + code: 'pe', + label: 'Peru', + icon: 'twemoji:flag-peru', + }, + { + code: 'ph', + label: 'Philippines', + icon: 'twemoji:flag-philippines', + }, + { + code: 'pn', + label: 'Pitcairn', + icon: 'twemoji:flag-pitcairn-islands', + }, + { + code: 'pl', + label: 'Poland', + icon: 'twemoji:flag-poland', + }, + { + code: 'pt', + label: 'Portugal', + icon: 'twemoji:flag-portugal', + }, + { + code: 'pr', + label: 'Puerto Rico', + icon: 'twemoji:flag-puerto-rico', + }, + { + code: 'qa', + label: 'Qatar', + icon: 'twemoji:flag-qatar', + }, + { + code: 'cg', + label: 'Republic of the Congo', + icon: 'twemoji:flag-congo-brazzaville', + }, + { + code: 're', + label: 'Reunion', + icon: 'twemoji:flag-reunion', + }, + { + code: 'ro', + label: 'Romania', + icon: 'twemoji:flag-romania', + }, + { + code: 'ru', + label: 'Russia', + icon: 'twemoji:flag-russia', + }, + { + code: 'rw', + label: 'Rwanda', + icon: 'twemoji:flag-rwanda', + }, + { + code: 'bl', + label: 'Saint Barthelemy', + icon: 'twemoji:flag-st-barthelemy', + }, + { + code: 'sh', + label: 'Saint Helena', + icon: 'twemoji:flag-st-helena', + }, + { + code: 'kn', + label: 'Saint Kitts and Nevis', + icon: 'twemoji:flag-st-kitts-and-nevis', + }, + { + code: 'lc', + label: 'Saint Lucia', + icon: 'twemoji:flag-st-lucia', + }, + { + code: 'mf', + label: 'Saint Martin', + icon: 'twemoji:flag-st-martin', + }, + { + code: 'pm', + label: 'Saint Pierre and Miquelon', + icon: 'twemoji:flag-st-pierre-and-miquelon', + }, + { + code: 'vc', + label: 'Saint Vincent and the Grenadines', + icon: 'twemoji:flag-st-vincent-and-grenadines', + }, + { + code: 'ws', + label: 'Samoa', + icon: 'twemoji:flag-samoa', + }, + { + code: 'sm', + label: 'San Marino', + icon: 'twemoji:flag-san-marino', + }, + { + code: 'st', + label: 'Sao Tome and Principe', + icon: 'twemoji:flag-sao-tome-and-principe', + }, + { + code: 'sa', + label: 'Saudi Arabia', + icon: 'twemoji:flag-saudi-arabia', + }, + { + code: 'sn', + label: 'Senegal', + icon: 'twemoji:flag-senegal', + }, + { + code: 'rs', + label: 'Serbia', + icon: 'twemoji:flag-serbia', + }, + { + code: 'sc', + label: 'Seychelles', + icon: 'twemoji:flag-seychelles', + }, + { + code: 'sl', + label: 'Sierra Leone', + icon: 'twemoji:flag-sierra-leone', + }, + { + code: 'sg', + label: 'Singapore', + icon: 'twemoji:flag-singapore', + }, + { + code: 'sx', + label: 'Sint Maarten', + icon: 'twemoji:flag-sint-maarten', + }, + { + code: 'sk', + label: 'Slovakia', + icon: 'twemoji:flag-slovakia', + }, + { + code: 'si', + label: 'Slovenia', + icon: 'twemoji:flag-slovenia', + }, + { + code: 'sb', + label: 'Solomon Islands', + icon: 'twemoji:flag-solomon-islands', + }, + { + code: 'so', + label: 'Somalia', + icon: 'twemoji:flag-somalia', + }, + { + code: 'za', + label: 'South Africa', + icon: 'twemoji:flag-south-africa', + }, + { + code: 'gs', + label: 'South Georgia and the South Sandwich Islands', + icon: 'twemoji:flag-south-georgia-and-south-sandwich-islands', + }, + { + code: 'kr', + label: 'South Korea', + icon: 'twemoji:flag-south-korea', + }, + { + code: 'ss', + label: 'South Sudan', + icon: 'twemoji:flag-south-sudan', + }, + { + code: 'es', + label: 'Spain', + icon: 'twemoji:flag-spain', + }, + { + code: 'lk', + label: 'Sri Lanka', + icon: 'twemoji:flag-sri-lanka', + }, + { + code: 'sd', + label: 'Sudan', + icon: 'twemoji:flag-sudan', + }, + { + code: 'sr', + label: 'Suriname', + icon: 'twemoji:flag-suriname', + }, + { + code: 'sj', + label: 'Svalbard and Jan Mayen', + icon: 'twemoji:flag-svalbard-and-jan-mayen', + }, + { + code: 'sz', + label: 'Swaziland', + icon: 'emojione-v1:flag-for-swaziland', + }, + { + code: 'se', + label: 'Sweden', + icon: 'twemoji:flag-sweden', + }, + { + code: 'ch', + label: 'Switzerland', + icon: 'twemoji:flag-switzerland', + }, + { + code: 'sy', + label: 'Syria', + icon: 'twemoji:flag-syria', + }, + { + code: 'tw', + label: 'Taiwan', + icon: 'twemoji:flag-taiwan', + }, + { + code: 'tj', + label: 'Tajikistan', + icon: 'twemoji:flag-tajikistan', + }, + { + code: 'tz', + label: 'Tanzania', + icon: 'twemoji:flag-tanzania', + }, + { + code: 'th', + label: 'Thailand', + icon: 'twemoji:flag-thailand', + }, + { + code: 'tg', + label: 'Togo', + icon: 'twemoji:flag-togo', + }, + { + code: 'tk', + label: 'Tokelau', + icon: 'twemoji:flag-tokelau', + }, + { + code: 'to', + label: 'Tonga', + icon: 'twemoji:flag-tonga', + }, + { + code: 'tt', + label: 'Trinidad and Tobago', + icon: 'twemoji:flag-trinidad-and-tobago', + }, + { + code: 'tn', + label: 'Tunisia', + icon: 'twemoji:flag-tunisia', + }, + { + code: 'tr', + label: 'Turkey', + icon: 'twemoji:flag-for-flag-turkey', + }, + { + code: 'tm', + label: 'Turkmenistan', + icon: 'twemoji:flag-turkmenistan', + }, + { + code: 'tc', + label: 'Turks and Caicos Islands', + icon: 'twemoji:flag-turks-and-caicos-islands', + }, + { + code: 'tv', + label: 'Tuvalu', + icon: 'twemoji:flag-tuvalu', + }, + { + code: 'vi', + label: 'U.S. Virgin Islands', + icon: 'twemoji:flag-us-virgin-islands', + }, + { + code: 'ug', + label: 'Uganda', + icon: 'twemoji:flag-uganda', + }, + { + code: 'ua', + label: 'Ukraine', + icon: 'twemoji:flag-ukraine', + }, + { + code: 'ae', + label: 'United Arab Emirates', + icon: 'twemoji:flag-united-arab-emirates', + }, + { + code: 'gb', + label: 'United Kingdom', + icon: 'twemoji:flag-united-kingdom', + }, + { + code: 'us', + label: 'United States', + icon: 'twemoji:flag-united-states', + }, + { + code: 'um', + label: 'United States Minor Outlying Islands', + icon: 'twemoji:flag-us-outlying-islands', + }, + { + code: 'uy', + label: 'Uruguay', + icon: 'twemoji:flag-uruguay', + }, + { + code: 'uz', + label: 'Uzbekistan', + icon: 'twemoji:flag-uzbekistan', + }, + { + code: 'vu', + label: 'Vanuatu', + icon: 'twemoji:flag-vanuatu', + }, + { + code: 'va', + label: 'Vatican', + icon: 'twemoji:flag-vatican-city', + }, + { + code: 've', + label: 'Venezuela', + icon: 'twemoji:flag-venezuela', + }, + { + code: 'vn', + label: 'Vietnam', + icon: 'twemoji:flag-vietnam', + }, + { + code: 'wf', + label: 'Wallis and Futuna', + icon: 'twemoji:flag-wallis-and-futuna', + }, + { + code: 'eh', + label: 'Western Sahara', + icon: 'twemoji:flag-western-sahara', + }, + { + code: 'ye', + label: 'Yemen', + icon: 'twemoji:flag-yemen', + }, + { + code: 'zm', + label: 'Zambia', + icon: 'twemoji:flag-zambia', + }, + { + code: 'zw', + label: 'Zimbabwe', + icon: 'twemoji:flag-zimbabwe', + }, +]; diff --git a/src/_mock/_data.ts b/src/_mock/_data.ts index d6dadc0eb..4455c52c2 100644 --- a/src/_mock/_data.ts +++ b/src/_mock/_data.ts @@ -1,7 +1,12 @@ + +import type { ContributionProps } from 'src/sections/contributions/contributions-table-row'; + import { _id, _price, _times, + _months, + _amount, _company, _boolean, _fullName, @@ -63,6 +68,18 @@ export const _posts = [...Array(23)].map((_, index) => ({ // ---------------------------------------------------------------------- +export const _contributions: ContributionProps[] = [...Array(24)].map((_, index) => ({ + id: _id(index), + sender: { id: `${_id(index)}'2'`, name: _fullName(index) }, + months: _months(Math.ceil(Math.random() * 3)), + status: index % 4 ? 'success' : 'failed', + timestamp: new Date(), + amount: _amount(), + code: 's', +})); + +// ---------------------------------------------------------------------- + const COLORS = [ '#00AB55', '#000000', @@ -116,6 +133,272 @@ export const _langs = [ }, ]; +export const _countries = [ + { + value: 'GH', + icon: 'twemoji:flag-ghana', + label: 'Ghana', + }, + { + value: 'ng', + icon: 'twemoji:flag-nigeria', + label: 'Nigeria', + }, +]; + +export const COUNTRIES = { + BD: 'Bangladesh', + BE: 'Belgium', + BF: 'Burkina Faso', + BG: 'Bulgaria', + BA: 'Bosnia and Herzegovina', + BB: 'Barbados', + WF: 'Wallis and Futuna', + BL: 'Saint Barthelemy', + BM: 'Bermuda', + BN: 'Brunei', + BO: 'Bolivia', + BH: 'Bahrain', + BI: 'Burundi', + BJ: 'Benin', + BT: 'Bhutan', + JM: 'Jamaica', + BV: 'Bouvet Island', + BW: 'Botswana', + WS: 'Samoa', + BQ: 'Bonaire, Saint Eustatius and Saba ', + BR: 'Brazil', + BS: 'Bahamas', + JE: 'Jersey', + BY: 'Belarus', + BZ: 'Belize', + RU: 'Russia', + RW: 'Rwanda', + RS: 'Serbia', + TL: 'East Timor', + RE: 'Reunion', + TM: 'Turkmenistan', + TJ: 'Tajikistan', + RO: 'Romania', + TK: 'Tokelau', + GW: 'Guinea-Bissau', + GU: 'Guam', + GT: 'Guatemala', + GS: 'South Georgia and the South Sandwich Islands', + GR: 'Greece', + GQ: 'Equatorial Guinea', + GP: 'Guadeloupe', + JP: 'Japan', + GY: 'Guyana', + GG: 'Guernsey', + GF: 'French Guiana', + GE: 'Georgia', + GD: 'Grenada', + GB: 'United Kingdom', + GA: 'Gabon', + SV: 'El Salvador', + GN: 'Guinea', + GM: 'Gambia', + GL: 'Greenland', + GI: 'Gibraltar', + GH: 'Ghana', + OM: 'Oman', + TN: 'Tunisia', + JO: 'Jordan', + HR: 'Croatia', + HT: 'Haiti', + HU: 'Hungary', + HK: 'Hong Kong', + HN: 'Honduras', + HM: 'Heard Island and McDonald Islands', + VE: 'Venezuela', + PR: 'Puerto Rico', + PS: 'Palestinian Territory', + PW: 'Palau', + PT: 'Portugal', + SJ: 'Svalbard and Jan Mayen', + PY: 'Paraguay', + IQ: 'Iraq', + PA: 'Panama', + PF: 'French Polynesia', + PG: 'Papua New Guinea', + PE: 'Peru', + PK: 'Pakistan', + PH: 'Philippines', + PN: 'Pitcairn', + PL: 'Poland', + PM: 'Saint Pierre and Miquelon', + ZM: 'Zambia', + EH: 'Western Sahara', + EE: 'Estonia', + EG: 'Egypt', + ZA: 'South Africa', + EC: 'Ecuador', + IT: 'Italy', + VN: 'Vietnam', + SB: 'Solomon Islands', + ET: 'Ethiopia', + SO: 'Somalia', + ZW: 'Zimbabwe', + SA: 'Saudi Arabia', + ES: 'Spain', + ER: 'Eritrea', + ME: 'Montenegro', + MD: 'Moldova', + MG: 'Madagascar', + MF: 'Saint Martin', + MA: 'Morocco', + MC: 'Monaco', + UZ: 'Uzbekistan', + MM: 'Myanmar', + ML: 'Mali', + MO: 'Macao', + MN: 'Mongolia', + MH: 'Marshall Islands', + MK: 'Macedonia', + MU: 'Mauritius', + MT: 'Malta', + MW: 'Malawi', + MV: 'Maldives', + MQ: 'Martinique', + MP: 'Northern Mariana Islands', + MS: 'Montserrat', + MR: 'Mauritania', + IM: 'Isle of Man', + UG: 'Uganda', + TZ: 'Tanzania', + MY: 'Malaysia', + MX: 'Mexico', + IL: 'Israel', + FR: 'France', + IO: 'British Indian Ocean Territory', + SH: 'Saint Helena', + FI: 'Finland', + FJ: 'Fiji', + FK: 'Falkland Islands', + FM: 'Micronesia', + FO: 'Faroe Islands', + NI: 'Nicaragua', + NL: 'Netherlands', + NO: 'Norway', + NA: 'Namibia', + VU: 'Vanuatu', + NC: 'New Caledonia', + NE: 'Niger', + NF: 'Norfolk Island', + NG: 'Nigeria', + NZ: 'New Zealand', + NP: 'Nepal', + NR: 'Nauru', + NU: 'Niue', + CK: 'Cook Islands', + XK: 'Kosovo', + CI: 'Ivory Coast', + CH: 'Switzerland', + CO: 'Colombia', + CN: 'China', + CM: 'Cameroon', + CL: 'Chile', + CC: 'Cocos Islands', + CA: 'Canada', + CG: 'Republic of the Congo', + CF: 'Central African Republic', + CD: 'Democratic Republic of the Congo', + CZ: 'Czech Republic', + CY: 'Cyprus', + CX: 'Christmas Island', + CR: 'Costa Rica', + CW: 'Curacao', + CV: 'Cape Verde', + CU: 'Cuba', + SZ: 'Swaziland', + SY: 'Syria', + SX: 'Sint Maarten', + KG: 'Kyrgyzstan', + KE: 'Kenya', + SS: 'South Sudan', + SR: 'Suriname', + KI: 'Kiribati', + KH: 'Cambodia', + KN: 'Saint Kitts and Nevis', + KM: 'Comoros', + ST: 'Sao Tome and Principe', + SK: 'Slovakia', + KR: 'South Korea', + SI: 'Slovenia', + KP: 'North Korea', + KW: 'Kuwait', + SN: 'Senegal', + SM: 'San Marino', + SL: 'Sierra Leone', + SC: 'Seychelles', + KZ: 'Kazakhstan', + KY: 'Cayman Islands', + SG: 'Singapore', + SE: 'Sweden', + SD: 'Sudan', + DO: 'Dominican Republic', + DM: 'Dominica', + DJ: 'Djibouti', + DK: 'Denmark', + VG: 'British Virgin Islands', + DE: 'Germany', + YE: 'Yemen', + DZ: 'Algeria', + US: 'United States', + UY: 'Uruguay', + YT: 'Mayotte', + UM: 'United States Minor Outlying Islands', + LB: 'Lebanon', + LC: 'Saint Lucia', + LA: 'Laos', + TV: 'Tuvalu', + TW: 'Taiwan', + TT: 'Trinidad and Tobago', + TR: 'Turkey', + LK: 'Sri Lanka', + LI: 'Liechtenstein', + LV: 'Latvia', + TO: 'Tonga', + LT: 'Lithuania', + LU: 'Luxembourg', + LR: 'Liberia', + LS: 'Lesotho', + TH: 'Thailand', + TF: 'French Southern Territories', + TG: 'Togo', + TD: 'Chad', + TC: 'Turks and Caicos Islands', + LY: 'Libya', + VA: 'Vatican', + VC: 'Saint Vincent and the Grenadines', + AE: 'United Arab Emirates', + AD: 'Andorra', + AG: 'Antigua and Barbuda', + AF: 'Afghanistan', + AI: 'Anguilla', + VI: 'U.S. Virgin Islands', + IS: 'Iceland', + IR: 'Iran', + AM: 'Armenia', + AL: 'Albania', + AO: 'Angola', + AQ: 'Antarctica', + AS: 'American Samoa', + AR: 'Argentina', + AU: 'Australia', + AT: 'Austria', + AW: 'Aruba', + IN: 'India', + AX: 'Aland Islands', + AZ: 'Azerbaijan', + IE: 'Ireland', + ID: 'Indonesia', + UA: 'Ukraine', + QA: 'Qatar', + MZ: 'Mozambique', +}; + // ---------------------------------------------------------------------- export const _timeline = [...Array(5)].map((_, index) => ({ diff --git a/src/_mock/_mock.ts b/src/_mock/_mock.ts index 46f120bb1..70bce3624 100644 --- a/src/_mock/_mock.ts +++ b/src/_mock/_mock.ts @@ -1,5 +1,12 @@ +import { fShortenNumber } from 'src/utils/format-number'; + +const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + export const _id = (index: number) => `e99f09a7-dd88-49d5-b1c8-1daf80c2d7b${index}`; +export const _amount = () => + fShortenNumber(Math.ceil(Math.max(Math.random() * 500, Math.random() * 5000))).toString(); + export const _times = (index: number) => // 'MM/DD/YYYY' [ @@ -91,6 +98,9 @@ export const _company = (index: number) => 'Streich Group', ][index]; +export const _months = (length: number) => + new Array(length).fill(0).map(() => MONTHS[Math.ceil(Math.random() * 11)]); + export const _boolean = (index: number) => [ true, diff --git a/src/app.tsx b/src/app.tsx index 68bbf1784..97ac7f115 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,44 +1,27 @@ import 'src/global.css'; -import Fab from '@mui/material/Fab'; - import { Router } from 'src/routes/sections'; import { useScrollToTop } from 'src/hooks/use-scroll-to-top'; import { ThemeProvider } from 'src/theme/theme-provider'; -import { Iconify } from 'src/components/iconify'; +import { AlertUtil } from './utils'; +import AppAlert from './components/shared/alert'; // ---------------------------------------------------------------------- export default function App() { useScrollToTop(); - const githubButton = ( - - - - ); + const setAlert = (ref: any) => { + AlertUtil.setRef(ref); + }; return ( - {githubButton} + setAlert(ref)} /> ); } diff --git a/src/components/country-select/index.tsx b/src/components/country-select/index.tsx new file mode 100644 index 000000000..5cf8d7148 --- /dev/null +++ b/src/components/country-select/index.tsx @@ -0,0 +1,38 @@ +import type { SelectProps } from '@mui/material'; + +import { Select, MenuItem, InputLabel, FormControl } from '@mui/material'; + +import _countries from 'src/_mock/_countries'; + +import { Iconify } from '../iconify'; + +function CountrySelect({ label = 'Country', ...props }: SelectProps) { + return ( + + {label} + + + ); +} + +export default CountrySelect; diff --git a/src/components/loader/index.tsx b/src/components/loader/index.tsx new file mode 100644 index 000000000..6582b4be8 --- /dev/null +++ b/src/components/loader/index.tsx @@ -0,0 +1,23 @@ +import type { FC } from 'react'; +import type { BoxProps } from '@mui/material/Box'; + +import { Box, LinearProgress, linearProgressClasses } from '@mui/material'; + +import { varAlpha } from 'src/theme/styles'; + +interface LoaderProps extends BoxProps {} + +const Loader: FC = (props) => ( + + varAlpha(theme.vars.palette.text.primaryChannel, 0.16), + [`& .${linearProgressClasses.bar}`]: { bgcolor: 'text.primary' }, + }} + /> + +); + +export default Loader; diff --git a/src/components/logo/logo.tsx b/src/components/logo/logo.tsx index fae19d7ce..405e21ee4 100644 --- a/src/components/logo/logo.tsx +++ b/src/components/logo/logo.tsx @@ -212,7 +212,8 @@ export const Logo = forwardRef( }} {...other} > - {isSingle ? singleLogo : fullLogo} + {/* {isSingle ? singleLogo : fullLogo} */} + logo ); } diff --git a/src/components/provider/index.tsx b/src/components/provider/index.tsx new file mode 100644 index 000000000..7cdeaf3cd --- /dev/null +++ b/src/components/provider/index.tsx @@ -0,0 +1,72 @@ +import type { ReactNode} from 'react'; +import type { User } from 'src/services/user/user.dto'; + +import React, { useMemo, useState, useEffect, useCallback } from 'react'; + +import { useRouter } from 'src/routes/hooks'; + +import UserService from 'src/services/user'; +import { Cache, CacheKeys } from 'src/utils'; + +const IS_ADMIN_MODE = !!Cache.get(CacheKeys.AdminMode); + +interface IUserContext { + user?: User; + isAdminMode: boolean; + pay: () => void; + reFetchUser: () => void; + setIsAdmin: (val: boolean) => void; +} + +export const UserContext = React.createContext({ + isAdminMode: IS_ADMIN_MODE, + user: undefined, + pay: () => {}, + reFetchUser: () => {}, + setIsAdmin: () => {}, +}); + +const AppProvider = ({ children, pay }: { children: ReactNode; pay: () => void }) => { + const [user, setUser] = useState(); + const { replace } = useRouter(); + + const fetchUser = useCallback(async () => { + try { + const res = await UserService.me(); + if (res) { + setUser(res); + } + } catch (error) { + console.error(error); + } + }, []); + + const updateAdminMode = useCallback( + async (val: boolean) => { + replace('/'); + Cache.set(CacheKeys.AdminMode, val); + }, + [replace] + ); + + useEffect(() => { + fetchUser(); + }, [fetchUser]); + + const contextValues: IUserContext = useMemo( + () => ({ + user, + isAdminMode: IS_ADMIN_MODE, + pay, + setIsAdmin: updateAdminMode, + reFetchUser: fetchUser, + }), + [user, updateAdminMode, fetchUser, pay] + ); + + return {children}; +}; + +export default AppProvider; + +// { user: User | undefined; isAdminMode: boolean; setAdminMode: (val: boolean) => void; reFetchUser: () => Promise; } diff --git a/src/components/shared/alert/index.tsx b/src/components/shared/alert/index.tsx new file mode 100644 index 000000000..26f2aa353 --- /dev/null +++ b/src/components/shared/alert/index.tsx @@ -0,0 +1,100 @@ +import type { AlertProps } from '@mui/material'; +import type { IconifyProps } from 'src/components/iconify'; + +import { useRef, useState, forwardRef, useCallback, useImperativeHandle } from 'react'; + +import { Box, Alert, Slide, Portal, IconButton } from '@mui/material'; + +import { Iconify } from 'src/components/iconify'; + +export interface AppAlertProps { + id?: string; + label: string; + icon?: IconifyProps['icon']; + type: AlertProps['severity']; + duration?: number; +} + +export interface AppAlertMethods { + show: (data: AppAlertProps, cb?: () => void) => void; + close: (data: AppAlertProps, cb?: () => void) => void; +} + +const AppAlert = forwardRef((_, ref) => { + const [visible, setVisible] = useState(false); + const [show, setShow] = useState(visible); + const dataRef = useRef(null); + const [value, setValue] = useState(null); + + const container = useRef(); + + const clearData = () => { + setValue(null); + }; + + const handleOpenClose = useCallback((open: boolean, data?: AppAlertProps | null) => { + if (open) { + setValue(data ?? null); + setVisible(open); + setShow(open); + setTimeout( + () => { + setVisible(false); + }, + (data?.duration ?? 5) * 1000 + ); + } else { + setVisible(open); + clearData(); + } + }, []); + + const onClose = () => { + setVisible(false); + }; + + const onExited = () => { + setShow(false); + }; + + useImperativeHandle( + ref, + () => ({ + show(data) { + handleOpenClose(true, data); + }, + close() { + handleOpenClose(false); + }, + }), + [handleOpenClose] + ); + + return show ? ( + + + + } + action={ + + + + } + > + {value?.label} + + + + + ) : null; +}); + +export default AppAlert; diff --git a/src/components/shared/modals/contributeForm.tsx b/src/components/shared/modals/contributeForm.tsx new file mode 100644 index 000000000..562bfa4c3 --- /dev/null +++ b/src/components/shared/modals/contributeForm.tsx @@ -0,0 +1,171 @@ +import type { FormEvent, ChangeEvent } from 'react'; + +import React, { useState, useEffect } from 'react'; + +import { LoadingButton } from '@mui/lab'; +import { + Box, + Chip, + Link, + Modal, + TextField, + IconButton, + Typography, + Autocomplete, +} from '@mui/material'; + +import { useRouter } from 'src/routes/hooks'; + +import useUser from 'src/hooks/useUser'; + +import { errCb } from 'src/utils'; +import PayService from 'src/services/pay'; + +import { Iconify } from 'src/components/iconify'; + +const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + +interface PaymentFormModalProps { + open: boolean; + amount?: string | number; + handleClose: () => void; +} + +const PaymentFormModal: React.FC = ({ + open, + amount: pledge, + handleClose, +}) => { + const { user } = useUser(); + const { refresh } = useRouter(); + + const [amount, setAmount] = useState(user?.pledgeAmount ?? pledge ?? ''); + const [selectedMonths, setSelectedMonths] = useState([]); + const [loading, setLoading] = useState(false); + + const handleAmountChange = (event: ChangeEvent) => { + setAmount(event.target.value); + }; + + const handleSubmit = async (event: FormEvent) => { + try { + setLoading(true); + event.preventDefault(); + await PayService.init(Number(amount), selectedMonths); + closeModal(); + setLoading(false); + } catch (error) { + errCb(error.message); + } + }; + + const closeModal = () => { + setAmount(''); + setSelectedMonths([]); + handleClose(); + }; + + useEffect(() => { + setAmount(user?.pledgeAmount ?? ''); + }, [user?.pledgeAmount]); + + useEffect(() => { + if (open) { + setAmount(user?.pledgeAmount ?? ''); + } + }, [open, user?.pledgeAmount]); + + const hasValues = !!amount && !!selectedMonths.length; + + return ( + + + + + + + Contribute + + {!!selectedMonths.length && amount && ( + + You are making a contribution of{' '} + + GHS{amount} + {' '} + for{' '} + + {selectedMonths.join(', ')} + + + )} +
+ Change pledge amount?} + /> + setSelectedMonths(newValue)} + renderTags={(value, getTagProps) => + value.map((option, index) => ( + + )) + } + renderInput={(params) => ( + + )} + sx={{ mb: 3 }} + /> + + + Submit + + + +
+
+ ); +}; + +export default PaymentFormModal; diff --git a/src/components/shared/switch/toggle/index.tsx b/src/components/shared/switch/toggle/index.tsx new file mode 100644 index 000000000..57221a0ee --- /dev/null +++ b/src/components/shared/switch/toggle/index.tsx @@ -0,0 +1,19 @@ +import type { FC } from 'react' +import type { SwitchProps} from '@mui/material'; + +import { Box, Switch, Typography } from '@mui/material' + +export interface ToggleSwitchProps extends SwitchProps { + label:string +} + +const ToggleSwitch:FC = ({label, checked, ...props}) => ( + + + {label} + + + +) + +export default ToggleSwitch \ No newline at end of file diff --git a/src/config-global.ts b/src/config-global.ts index 49eefa7f0..1ef641acb 100644 --- a/src/config-global.ts +++ b/src/config-global.ts @@ -10,6 +10,6 @@ export type ConfigValue = { // ---------------------------------------------------------------------- export const CONFIG: ConfigValue = { - appName: 'Minimal UI', + appName: 'PABGM Partnership', appVersion: packageJson.version, }; diff --git a/src/configs/firebase.ts b/src/configs/firebase.ts new file mode 100644 index 000000000..c2c1f5b49 --- /dev/null +++ b/src/configs/firebase.ts @@ -0,0 +1,80 @@ +// Import the functions you need from the SDKs you need +import type { User } from 'firebase/auth'; + +import { initializeApp } from 'firebase/app'; +import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'; +import { getFunctions, httpsCallable, connectFunctionsEmulator } from 'firebase/functions'; +import { + getAuth, + signOut, + onAuthStateChanged, + connectAuthEmulator, + confirmPasswordReset, + sendPasswordResetEmail, + signInWithEmailAndPassword, + createUserWithEmailAndPassword, +} from 'firebase/auth'; + +import type { ApiRoute } from '../constants/fxns'; + +const USE_EMULATORS = false; + +const firebaseConfig = { + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGE_SENDER_ID, + appId: import.meta.env.VITE_FIREBASE_APP_ID, + measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID, +}; + +// Initialize Firebase +const app = initializeApp(firebaseConfig); + +const appAuth = getAuth(); + +const auth = { + createUser(email: string, password: string) { + return createUserWithEmailAndPassword(appAuth, email, password); + }, + signIn(email: string, password: string) { + return signInWithEmailAndPassword(appAuth, email, password); + }, + resetPassword(email: string) { + return sendPasswordResetEmail(appAuth, email); + }, + confirmPassReset(code: string, newPass: string) { + return confirmPasswordReset(appAuth, code, newPass); + }, + logout() { + return signOut(appAuth); + }, + listen(cb: (val: User | null) => void) { + onAuthStateChanged(appAuth, cb); + }, +}; + +const db = getFirestore(app); +const fxns = getFunctions(app); + +const fx = { + async call(path: ApiRoute, data?: T) { + const callable = httpsCallable(fxns, path); + const result = await callable(data ?? null); + return result.data; + }, +}; + +const runEmulators = (val: boolean = USE_EMULATORS) => { + const local = '127.0.0.1'; + if (val) { + connectFirestoreEmulator(db, local, 8080); + connectAuthEmulator(appAuth, `http://${local}:9099`); + connectFunctionsEmulator(fxns, local, 5001); + } +}; + +runEmulators(); + +export { db, fx, app, auth }; diff --git a/src/configs/index.tsx b/src/configs/index.tsx new file mode 100644 index 000000000..5588511bf --- /dev/null +++ b/src/configs/index.tsx @@ -0,0 +1 @@ +export * from './firebase'; diff --git a/src/constants/factory.ts b/src/constants/factory.ts new file mode 100644 index 000000000..5fd2ddae9 --- /dev/null +++ b/src/constants/factory.ts @@ -0,0 +1,8 @@ +export enum Collection { + Users = 'USERS', + Contributions = 'CONTRIBUTIONS', + Transactions = 'TRXS', + AdminStats = 'ADMIN_STATS', + PartnerStats = 'PARTNER_STATS', + Secret = 'SECRET', +} diff --git a/src/constants/fxns.ts b/src/constants/fxns.ts new file mode 100644 index 000000000..41c7335e9 --- /dev/null +++ b/src/constants/fxns.ts @@ -0,0 +1,9 @@ +export enum ApiRoute { + Donate = 'contribute', + InitPayment = 'initPay', + PaymentHook = 'payHook', + GetUser = 'getUser', + GetStats = 'getStats', + GetUserStats = 'getUserStats', + GetContributions = 'getContributions', +} diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 000000000..cc0202961 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,2 @@ +export * from './urls'; +export * from './factory'; diff --git a/src/constants/urls.ts b/src/constants/urls.ts new file mode 100644 index 000000000..7995183aa --- /dev/null +++ b/src/constants/urls.ts @@ -0,0 +1 @@ +export const API_URL = ''; diff --git a/src/hooks/useAdmin.ts b/src/hooks/useAdmin.ts new file mode 100644 index 000000000..989e73e48 --- /dev/null +++ b/src/hooks/useAdmin.ts @@ -0,0 +1,16 @@ +import { useContext } from 'react'; + +import { Cache, CacheKeys } from 'src/utils'; + +import { UserContext } from 'src/components/provider'; + +const useAdmin = () => { + const { setIsAdmin } = useContext(UserContext); + const isAdminMode = Cache.get(CacheKeys.AdminMode) ?? false; + return { + isAdminMode, + setIsAdmin, + }; +}; + +export default useAdmin; diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts new file mode 100644 index 000000000..601730401 --- /dev/null +++ b/src/hooks/useAuth.ts @@ -0,0 +1,32 @@ +import type { User } from 'firebase/auth'; + +import { useState, useEffect } from 'react'; + +import AuthService from 'src/services/auth'; + +const useAuth = () => { + const [data, setData] = useState<{ userExists: boolean; user: User | null }>({ + userExists: false, + user: null, + }); + + const checkCurrentUser = (user: User | null) => { + if (user) { + setData({ userExists: true, user }); + } else { + AuthService.setToken(null); + setData({ + userExists: false, + user: null, + }); + } + }; + + useEffect(() => { + AuthService.listen(checkCurrentUser); + }, []); + + return data; +}; + +export default useAuth; diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts new file mode 100644 index 000000000..0e65640ba --- /dev/null +++ b/src/hooks/useUser.ts @@ -0,0 +1,16 @@ +import { useState, useEffect, useContext } from 'react'; + +import { UserContext } from 'src/components/provider'; + +const useUser = () => { + const data = useContext(UserContext); + const [userContext, setUserContext] = useState(data); + + useEffect(() => { + setUserContext(data); + }, [data]); + + return userContext; +}; + +export default useUser; diff --git a/src/layouts/components/account-popover.tsx b/src/layouts/components/account-popover.tsx index 995efce0a..559b13816 100644 --- a/src/layouts/components/account-popover.tsx +++ b/src/layouts/components/account-popover.tsx @@ -3,7 +3,6 @@ import type { IconButtonProps } from '@mui/material/IconButton'; import { useState, useCallback } from 'react'; import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; import Avatar from '@mui/material/Avatar'; import Popover from '@mui/material/Popover'; import Divider from '@mui/material/Divider'; @@ -14,7 +13,8 @@ import MenuItem, { menuItemClasses } from '@mui/material/MenuItem'; import { useRouter, usePathname } from 'src/routes/hooks'; -import { _myAccount } from 'src/_mock'; +import useUser from 'src/hooks/useUser'; + // ---------------------------------------------------------------------- @@ -31,6 +31,7 @@ export function AccountPopover({ data = [], sx, ...other }: AccountPopoverProps) const router = useRouter(); const pathname = usePathname(); + const { user } = useUser(); const [openPopover, setOpenPopover] = useState(null); @@ -64,8 +65,8 @@ export function AccountPopover({ data = [], sx, ...other }: AccountPopoverProps) }} {...other} > - - {_myAccount.displayName.charAt(0).toUpperCase()} + + {user?.fname.charAt(0).toUpperCase()} @@ -83,11 +84,11 @@ export function AccountPopover({ data = [], sx, ...other }: AccountPopoverProps) > - {_myAccount?.displayName} + {`${user?.fname} ${user?.lname}`} - {_myAccount?.email} + {user?.email} @@ -125,14 +126,6 @@ export function AccountPopover({ data = [], sx, ...other }: AccountPopoverProps) ))} - - - - - - ); diff --git a/src/layouts/config-nav-dashboard.tsx b/src/layouts/config-nav-dashboard.tsx index 2acb80796..c4b3bf2db 100644 --- a/src/layouts/config-nav-dashboard.tsx +++ b/src/layouts/config-nav-dashboard.tsx @@ -1,4 +1,3 @@ -import { Label } from 'src/components/label'; import { SvgColor } from 'src/components/svg-color'; // ---------------------------------------------------------------------- @@ -14,33 +13,43 @@ export const navData = [ icon: icon('ic-analytics'), }, { - title: 'User', - path: '/user', - icon: icon('ic-user'), - }, - { - title: 'Product', - path: '/products', - icon: icon('ic-cart'), - info: ( - - ), + title: 'Contributions', + path: '/contributions', + icon: icon('ic-donate'), }, { - title: 'Blog', - path: '/blog', - icon: icon('ic-blog'), - }, - { - title: 'Sign in', - path: '/sign-in', - icon: icon('ic-lock'), + title: 'Partners', + path: '/user', + icon: icon('ic-user'), }, { - title: 'Not found', - path: '/404', - icon: icon('ic-disabled'), + title: 'Logout', + path: '/logout', + icon: icon('ic-exit'), }, + // { + // title: 'Product', + // path: '/products', + // icon: icon('ic-cart'), + // info: ( + // + // ), + // }, + // { + // title: 'Blog', + // path: '/blog', + // icon: icon('ic-blog'), + // }, + // { + // title: 'Sign in', + // path: '/sign-in', + // icon: icon('ic-lock'), + // }, + // { + // title: 'Not found', + // path: '/404', + // icon: icon('ic-disabled'), + // }, ]; diff --git a/src/layouts/dashboard/layout.tsx b/src/layouts/dashboard/layout.tsx index 281305821..dce538642 100644 --- a/src/layouts/dashboard/layout.tsx +++ b/src/layouts/dashboard/layout.tsx @@ -1,27 +1,31 @@ import type { Theme, SxProps, Breakpoint } from '@mui/material/styles'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import Box from '@mui/material/Box'; import Alert from '@mui/material/Alert'; import { useTheme } from '@mui/material/styles'; -import { _langs, _notifications } from 'src/_mock'; +import { useRouter } from 'src/routes/hooks'; -import { Iconify } from 'src/components/iconify'; +import useAuth from 'src/hooks/useAuth'; +import useUser from 'src/hooks/useUser'; +import useAdmin from 'src/hooks/useAdmin'; + +import AuthService from 'src/services/auth'; +import { UserRole } from 'src/services/user/user.dto'; + +import ToggleSwitch from 'src/components/shared/switch/toggle'; import { Main } from './main'; import { layoutClasses } from '../classes'; import { NavMobile, NavDesktop } from './nav'; import { navData } from '../config-nav-dashboard'; -import { Searchbar } from '../components/searchbar'; import { _workspaces } from '../config-nav-workspace'; import { MenuButton } from '../components/menu-button'; -import { LayoutSection } from '../core/layout-section'; import { HeaderSection } from '../core/header-section'; +import { LayoutSection } from '../core/layout-section'; import { AccountPopover } from '../components/account-popover'; -import { LanguagePopover } from '../components/language-popover'; -import { NotificationsPopover } from '../components/notifications-popover'; // ---------------------------------------------------------------------- @@ -35,11 +39,28 @@ export type DashboardLayoutProps = { export function DashboardLayout({ sx, children, header }: DashboardLayoutProps) { const theme = useTheme(); + const { userExists } = useAuth(); + const router = useRouter(); + const { user } = useUser(); + const { isAdminMode, setIsAdmin } = useAdmin(); + + const isAdmin = !!user?.role.includes(UserRole.Admin); const [navOpen, setNavOpen] = useState(false); const layoutQuery: Breakpoint = 'lg'; + const onToggleAdmin = (event: React.ChangeEvent) => { + setIsAdmin(event.target.checked); + }; + + useEffect(() => { + if (!AuthService.hasToken()) { + router.replace('/signin'); + } + }, [userExists, router]); + + const navOptions = isAdminMode ? navData : navData.filter((nav) => nav.title !== 'Partners'); return ( - - - + {isAdmin && ( + + )} + {/* */} , - }, - { - label: 'Profile', - href: '#', - icon: , - }, - { - label: 'Settings', - href: '#', - icon: , - }, - ]} + data={ + [ + // { + // label: 'Profile', + // href: '#', + // icon: , + // }, + // { + // label: 'Settings', + // href: '#', + // icon: , + // }, + ] + } /> ), @@ -111,7 +130,7 @@ export function DashboardLayout({ sx, children, header }: DashboardLayoutProps) * Sidebar *************************************** */ sidebarSection={ - + } /** ************************************** * Footer diff --git a/src/layouts/dashboard/nav.tsx b/src/layouts/dashboard/nav.tsx index 3b19cb940..cc905ed15 100644 --- a/src/layouts/dashboard/nav.tsx +++ b/src/layouts/dashboard/nav.tsx @@ -16,9 +16,6 @@ import { varAlpha } from 'src/theme/styles'; import { Logo } from 'src/components/logo'; import { Scrollbar } from 'src/components/scrollbar'; -import { NavUpgrade } from '../components/nav-upgrade'; -import { WorkspacesPopover } from '../components/workspaces-popover'; - import type { WorkspacesPopoverProps } from '../components/workspaces-popover'; // ---------------------------------------------------------------------- @@ -120,16 +117,20 @@ export function NavContent({ data, slots, workspaces, sx }: NavContentProps) { return ( <> - {slots?.topArea} - - - - + {data.map((item) => { const isActived = item.path === pathname; + if (item.path === '/logout') return null; return ( @@ -173,10 +174,32 @@ export function NavContent({ data, slots, workspaces, sx }: NavContentProps) { - {slots?.bottomArea} + + + {data[data.length - 1].icon} + - + + {data[data.length - 1].title} + + ); } diff --git a/src/pages/contributions.tsx b/src/pages/contributions.tsx new file mode 100644 index 000000000..98bb80ec0 --- /dev/null +++ b/src/pages/contributions.tsx @@ -0,0 +1,18 @@ +import { Helmet } from 'react-helmet-async'; + +import { CONFIG } from 'src/config-global'; + +import ContributionsList from 'src/sections/contributions/view/contributions-list'; + +function Contributions() { + return ( + <> + + {`Contributions - ${CONFIG.appName}`} + + + + ); +} + +export default Contributions; diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 53838c922..ff5a3ebfd 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -11,11 +11,8 @@ export default function Page() { <> {`Dashboard - ${CONFIG.appName}`} - - + + diff --git a/src/pages/logout.tsx b/src/pages/logout.tsx new file mode 100644 index 000000000..0cbb0e428 --- /dev/null +++ b/src/pages/logout.tsx @@ -0,0 +1,22 @@ +import { useEffect, useCallback } from 'react'; + +import { useRouter } from 'src/routes/hooks'; + +import AuthService from 'src/services/auth'; + +function Logout() { + const { replace } = useRouter(); + + const logout = useCallback(async () => { + await AuthService.logout(); + replace('/signin'); + }, [replace]); + + useEffect(() => { + logout(); + }, [logout]); + + return null; +} + +export default Logout; diff --git a/src/pages/user.tsx b/src/pages/user.tsx index f9b41608a..76d587a9d 100644 --- a/src/pages/user.tsx +++ b/src/pages/user.tsx @@ -2,7 +2,7 @@ import { Helmet } from 'react-helmet-async'; import { CONFIG } from 'src/config-global'; -import { UserView } from 'src/sections/user/view'; +import PartnersView from 'src/sections/user/view/partners-view'; // ---------------------------------------------------------------------- @@ -10,10 +10,10 @@ export default function Page() { return ( <> - {`Users - ${CONFIG.appName}`} + {`Partners - ${CONFIG.appName}`} - + ); } diff --git a/src/routes/sections.tsx b/src/routes/sections.tsx index 34a673b10..70429663b 100644 --- a/src/routes/sections.tsx +++ b/src/routes/sections.tsx @@ -1,13 +1,22 @@ -import { lazy, Suspense } from 'react'; +import { lazy, Suspense, useState } from 'react'; import { Outlet, Navigate, useRoutes } from 'react-router-dom'; import Box from '@mui/material/Box'; +import { Fab } from '@mui/material'; import LinearProgress, { linearProgressClasses } from '@mui/material/LinearProgress'; +import useUser from 'src/hooks/useUser'; +import useAdmin from 'src/hooks/useAdmin'; + import { varAlpha } from 'src/theme/styles'; import { AuthLayout } from 'src/layouts/auth'; import { DashboardLayout } from 'src/layouts/dashboard'; +import { Iconify } from 'src/components/iconify'; +import PaymentFormModal from 'src/components/shared/modals/contributeForm'; + +const AppProvider = lazy(() => import('src/components/provider')); + // ---------------------------------------------------------------------- export const HomePage = lazy(() => import('src/pages/home')); @@ -15,6 +24,8 @@ export const BlogPage = lazy(() => import('src/pages/blog')); export const UserPage = lazy(() => import('src/pages/user')); export const SignInPage = lazy(() => import('src/pages/sign-in')); export const ProductsPage = lazy(() => import('src/pages/products')); +export const ContributionsPage = lazy(() => import('src/pages/contributions')); +export const LogoutPage = lazy(() => import('src/pages/logout')); export const Page404 = lazy(() => import('src/pages/page-not-found')); // ---------------------------------------------------------------------- @@ -33,30 +44,79 @@ const renderFallback = ( ); export function Router() { + const { user } = useUser(); + const [open, setOpen] = useState(false); + const { isAdminMode } = useAdmin(); + + const onOpen = () => { + setOpen(true); + }; + + const onClose = () => { + setOpen(false); + }; + + const contributeBtn = ( + + + + ); + + const dashRoutes = [ + { element: , index: true }, + { path: 'user', element: }, + { path: 'products', element: }, + { path: 'blog', element: }, + { path: 'contributions', element: }, + ]; + + const routes = !isAdminMode ? dashRoutes.filter((route) => route.path !== 'user') : dashRoutes; + return useRoutes([ { element: ( - - - - - + + + + + + + {contributeBtn} + + + + ), - children: [ - { element: , index: true }, - { path: 'user', element: }, - { path: 'products', element: }, - { path: 'blog', element: }, - ], + children: routes, }, { - path: 'sign-in', + path: 'signin', element: ( ), }, + { + path: 'logout', + element: , + }, { path: '404', element: , diff --git a/src/sections/auth/sign-in-view.tsx b/src/sections/auth/sign-in-view.tsx index 52f619215..6682ee0b5 100644 --- a/src/sections/auth/sign-in-view.tsx +++ b/src/sections/auth/sign-in-view.tsx @@ -1,7 +1,10 @@ -import { useState, useCallback } from 'react'; +import type { CreateUserBody } from 'src/services/auth/auth.dto'; + +import { useState, useEffect, useCallback } from 'react'; import Box from '@mui/material/Box'; import Link from '@mui/material/Link'; +import { Button } from '@mui/material'; import Divider from '@mui/material/Divider'; import TextField from '@mui/material/TextField'; import IconButton from '@mui/material/IconButton'; @@ -11,45 +14,194 @@ import InputAdornment from '@mui/material/InputAdornment'; import { useRouter } from 'src/routes/hooks'; +import { errCb } from 'src/utils'; +import AuthService from 'src/services/auth'; + import { Iconify } from 'src/components/iconify'; +import CountrySelect from 'src/components/country-select'; // ---------------------------------------------------------------------- +const SIGN_IN = { + email: '', + password: '', +}; + +const SIGN_UP = { + fname: '', + lname: '', + country: '', + email: '', + password: '', + pledgeAmount: 0, + secret: '', +}; + export function SignInView() { const router = useRouter(); const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [isSignin, setIsSignin] = useState(true); + const [authenticating, setAuthenticating] = useState(false); + const [confirmPass, setConfirmPass] = useState(''); + const [data, setData] = useState>(SIGN_IN); + + const onGetStarted = () => { + setIsSignin((val) => !val); + }; + + const onAuthenticate = async () => { + try { + setAuthenticating(true); + if (isSignin) { + if (!data.email || !data.password) { + throw new Error('Email and password is required'); + } else { + await AuthService.login({ + email: data.email!, + password: data.password!, + }); + } + } else { + await AuthService.register(data as CreateUserBody); + } + router.replace('/'); + } catch (error) { + errCb(error.message); + } finally { + setAuthenticating(false); + } + }; + + const updateField = (field: keyof CreateUserBody, val: string) => { + setData((value) => ({ ...value, [field]: val })); + }; - const handleSignIn = useCallback(() => { - router.push('/'); - }, [router]); + const checkPass = useCallback(() => { + if (data.password !== confirmPass && !isSignin) { + return 'Passwords do not match'; + } + return null; + }, [data.password, confirmPass, isSignin]); + + const checkEmptyForm = useCallback( + () => [...Object.values(data), !isSignin ? confirmPass : 'true'].some((val) => val === ''), + [data, confirmPass, isSignin] + ); + + useEffect(() => { + if (isSignin) { + setData(SIGN_IN); + } else { + setData(SIGN_UP); + } + }, [isSignin]); + + const passErr = checkPass(); + const emptyForm = checkEmptyForm(); const renderForm = ( + {!isSignin && ( + updateField('secret', e.target.value)} + InputLabelProps={{ shrink: true }} + sx={{ mb: 3 }} + helperText={ + + Enter the secret code provided in your invite message. + + } + /> + )} + {!isSignin && ( + + updateField('fname', e.target.value)} + value={data.fname} + InputLabelProps={{ shrink: true }} + sx={{ mb: 3 }} + /> + updateField('lname', e.target.value)} + InputLabelProps={{ shrink: true }} + value={data.lname} + sx={{ mb: 3 }} + /> + + )} updateField('email', e.target.value)} InputLabelProps={{ shrink: true }} sx={{ mb: 3 }} /> + {!isSignin && ( + <> + updateField('pledgeAmount', e.target.value)} + InputLabelProps={{ shrink: true }} + sx={{ mb: 3 }} + /> + + updateField('country', e.target.value as string)} + required + /> + + + )} - - Forgot password? - + {isSignin && ( + + Forgot password? + + )} updateField('password', e.target.value)} InputProps={{ endAdornment: ( - setShowPassword(!showPassword)} edge="end"> + setShowPassword((val) => !val)} edge="end"> @@ -57,16 +209,45 @@ export function SignInView() { }} sx={{ mb: 3 }} /> + {!isSignin && ( + setConfirmPass(e.target.value)} + InputProps={{ + endAdornment: ( + + setShowConfirmPassword((val) => !val)} edge="end"> + + + + ), + }} + sx={{ mb: 3 }} + /> + )} - Sign in + {isSignin ? 'Sign in' : 'Sign up'} ); @@ -74,37 +255,34 @@ export function SignInView() { return ( <> - Sign in + {!isSignin ? 'Sign up' : 'Sign in'} Don’t have an account? - - Get started - + +
{renderForm}
- {renderForm} + {/* {isSignin && ( + <> + + + OR + + - - - OR - - - - - - - - - - - - - - + + + + + + + )} */} ); } diff --git a/src/sections/contributions/contributions-table-head.tsx b/src/sections/contributions/contributions-table-head.tsx new file mode 100644 index 000000000..23c2c05fb --- /dev/null +++ b/src/sections/contributions/contributions-table-head.tsx @@ -0,0 +1,73 @@ +import Box from '@mui/material/Box'; +import TableRow from '@mui/material/TableRow'; +import Checkbox from '@mui/material/Checkbox'; +import TableHead from '@mui/material/TableHead'; +import TableCell from '@mui/material/TableCell'; +import TableSortLabel from '@mui/material/TableSortLabel'; + +import { visuallyHidden } from './utils'; + +// ---------------------------------------------------------------------- + +type ContributionTableHeadProps = { + orderBy: string; + rowCount: number; + numSelected: number; + noMultiSelect?: boolean; + order: 'asc' | 'desc'; + onSort: (id: string) => void; + headLabel: Record[]; + onSelectAllRows: (checked: boolean) => void; +}; + +export function ContributionTableHead({ + order, + onSort, + orderBy, + rowCount, + headLabel, + numSelected, + noMultiSelect = false, + onSelectAllRows, +}: ContributionTableHeadProps) { + return ( + + + {!noMultiSelect && ( + + 0 && numSelected < rowCount} + checked={rowCount > 0 && numSelected === rowCount} + onChange={(event: React.ChangeEvent) => + onSelectAllRows(event.target.checked) + } + /> + + )} + + {headLabel.map((headCell) => ( + + onSort(headCell.id)} + > + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + + ))} + + + ); +} diff --git a/src/sections/contributions/contributions-table-row.tsx b/src/sections/contributions/contributions-table-row.tsx new file mode 100644 index 000000000..a4154153d --- /dev/null +++ b/src/sections/contributions/contributions-table-row.tsx @@ -0,0 +1,165 @@ +import type { LabelColor } from 'src/components/label'; + +import { useState, useCallback } from 'react'; + +import Box from '@mui/material/Box'; +import { Button } from '@mui/material'; +import Popover from '@mui/material/Popover'; +import Checkbox from '@mui/material/Checkbox'; +import MenuList from '@mui/material/MenuList'; +import TableRow from '@mui/material/TableRow'; +import TableCell from '@mui/material/TableCell'; +import MenuItem, { menuItemClasses } from '@mui/material/MenuItem'; + +import useUser from 'src/hooks/useUser'; + +import { fDateTime } from 'src/utils/format-time'; + +import PayService from 'src/services/pay'; + +import { Label } from 'src/components/label'; +import { Iconify } from 'src/components/iconify'; +import ContributionService from 'src/services/cont'; +import { LoadingButton } from '@mui/lab'; + +// ---------------------------------------------------------------------- + +export type ContributionProps = { + id: string; + sender: { + id: string; + name: string; + }; + amount: string; + timestamp: Date; + status: 'pending' | 'success' | 'failed'; + months: string[]; + code: string; +}; + +type ContributionsTableRowProps = { + row: ContributionProps; + selected: boolean; + noSelection?: boolean; + onSelectRow: () => void; +}; + +export function ContributionsTableRow({ + row, + selected, + noSelection, + onSelectRow, +}: ContributionsTableRowProps) { + const [openPopover, setOpenPopover] = useState(null); + const [cancelling, setCancelling] = useState(false); + const { user } = useUser(); + + const handleOpenPopover = useCallback((event: React.MouseEvent) => { + setOpenPopover(event.currentTarget); + }, []); + + const handleClosePopover = useCallback(() => { + setOpenPopover(null); + }, []); + + const getStatus = (val: ContributionProps['status']): LabelColor => { + if (val === 'failed') return 'error'; + if (val === 'pending') return 'info'; + return 'success'; + }; + + const onResume = (code: string) => { + PayService.resume(code); + }; + + const onCancel = async (id: string) => { + setCancelling(true); + await ContributionService.cancel(id); + setCancelling(false); + }; + + return ( + <> + + {!noSelection && ( + + + + )} + + + + {row.sender.name} + + + + {row.months.join(', ')} + + {`GHS ${row.amount}`} + + {fDateTime(row.timestamp)} + + + + + + + + {row.status === 'pending' && row.sender.id === user?.id ? ( + + + onCancel(row.id)} + > + Cancel + + + ) : ( + '' + )} + + + + + + + + + Edit + + + + + Delete + + + + + ); +} diff --git a/src/sections/contributions/contributions-table-toolbar.tsx b/src/sections/contributions/contributions-table-toolbar.tsx new file mode 100644 index 000000000..34766ae5e --- /dev/null +++ b/src/sections/contributions/contributions-table-toolbar.tsx @@ -0,0 +1,67 @@ +import Toolbar from '@mui/material/Toolbar'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import InputAdornment from '@mui/material/InputAdornment'; + +import { Iconify } from 'src/components/iconify'; + +// ---------------------------------------------------------------------- + +type ContributionsTableToolbarProps = { + numSelected: number; + filterName: string; + onFilterName: (event: React.ChangeEvent) => void; +}; + +export function ContributionsTableToolbar({ + numSelected, + filterName, + onFilterName, +}: ContributionsTableToolbarProps) { + return ( + theme.spacing(0, 1, 0, 3), + ...(numSelected > 0 && { + color: 'primary.main', + bgcolor: 'primary.lighter', + }), + }} + > + {/* {numSelected > 0 ? ( + + {numSelected} selected + + ) : ( */} + + + + } + sx={{ maxWidth: 320 }} + /> + {/* )} */} + + {/* {numSelected > 0 ? ( + + + + + + ) : ( + + + + + + )} */} + + ); +} diff --git a/src/sections/contributions/table-empty-rows.tsx b/src/sections/contributions/table-empty-rows.tsx new file mode 100644 index 000000000..4127993fb --- /dev/null +++ b/src/sections/contributions/table-empty-rows.tsx @@ -0,0 +1,31 @@ +import type { TableRowProps } from '@mui/material/TableRow'; + +import TableRow from '@mui/material/TableRow'; +import TableCell from '@mui/material/TableCell'; + +// ---------------------------------------------------------------------- + +type TableEmptyRowsProps = TableRowProps & { + emptyRows: number; + height?: number; +}; + +export function TableEmptyRows({ emptyRows, height, sx, ...other }: TableEmptyRowsProps) { + if (!emptyRows) { + return null; + } + + return ( + + + + ); +} diff --git a/src/sections/contributions/table-no-data.tsx b/src/sections/contributions/table-no-data.tsx new file mode 100644 index 000000000..2d9051369 --- /dev/null +++ b/src/sections/contributions/table-no-data.tsx @@ -0,0 +1,35 @@ +import type { TableRowProps } from '@mui/material/TableRow'; + +import Box from '@mui/material/Box'; +import TableRow from '@mui/material/TableRow'; +import TableCell from '@mui/material/TableCell'; +import Typography from '@mui/material/Typography'; + +// ---------------------------------------------------------------------- + +type TableNoDataProps = TableRowProps & { + searchQuery?: string; + label?: string; +}; + +export function TableNoData({ searchQuery, label, ...other }: TableNoDataProps) { + return ( + + + + + {label} + + + {false && ( + + No results found for   + "{searchQuery}". +
Try checking for typos or using complete words. +
+ )} +
+
+
+ ); +} diff --git a/src/sections/contributions/utils.ts b/src/sections/contributions/utils.ts new file mode 100644 index 000000000..0a275e8eb --- /dev/null +++ b/src/sections/contributions/utils.ts @@ -0,0 +1,80 @@ +import type { ContributionProps } from './contributions-table-row'; + +// ---------------------------------------------------------------------- + +export const visuallyHidden = { + border: 0, + margin: -1, + padding: 0, + width: '1px', + height: '1px', + overflow: 'hidden', + position: 'absolute', + whiteSpace: 'nowrap', + clip: 'rect(0 0 0 0)', +} as const; + +// ---------------------------------------------------------------------- + +export function emptyRows(page: number, rowsPerPage: number, arrayLength: number) { + return page ? Math.max(0, (1 + page) * rowsPerPage - arrayLength) : 0; +} + +// ---------------------------------------------------------------------- + +function descendingComparator(a: T, b: T, orderBy: keyof T) { + if (b[orderBy] < a[orderBy]) { + return -1; + } + if (b[orderBy] > a[orderBy]) { + return 1; + } + return 0; +} + +// ---------------------------------------------------------------------- + +export function getComparator( + order: 'asc' | 'desc', + orderBy: Key +): ( + a: { + [key in Key]: number | string; + }, + b: { + [key in Key]: number | string; + } +) => number { + return order === 'desc' + ? (a, b) => descendingComparator(a, b, orderBy) + : (a, b) => -descendingComparator(a, b, orderBy); +} + +// ---------------------------------------------------------------------- + +type ApplyFilterProps = { + inputData: ContributionProps[]; + filterName: string; + comparator: (a: any, b: any) => number; +}; + +export function applyFilter({ inputData, comparator, filterName }: ApplyFilterProps) { + const stabilizedThis = inputData.map((el, index) => [el, index] as const); + + stabilizedThis.sort((a, b) => { + const order = comparator(a[0], b[0]); + if (order !== 0) return order; + return a[1] - b[1]; + }); + + inputData = stabilizedThis.map((el) => el[0]); + + if (filterName) { + inputData = inputData.filter( + (contribution) => + contribution.sender.name.toLowerCase().indexOf(filterName.toLowerCase()) !== -1 + ); + } + + return inputData; +} diff --git a/src/sections/contributions/view/contributions-list.tsx b/src/sections/contributions/view/contributions-list.tsx new file mode 100644 index 000000000..edeef025b --- /dev/null +++ b/src/sections/contributions/view/contributions-list.tsx @@ -0,0 +1,63 @@ +import type { Contribution} from 'src/services/cont/contribute.dto'; + +import { useState, useEffect, useCallback } from 'react'; + +import useUser from 'src/hooks/useUser'; +import useAdmin from 'src/hooks/useAdmin'; + +import ContributionService from 'src/services/cont'; +import { ContributionStatus } from 'src/services/cont/contribute.dto'; + +import { ContributionsView } from './contributions-view'; + +import type { ContributionProps } from '../contributions-table-row'; + +const ContributionsList = () => { + const [data, setData] = useState(); + const [loading, setLoading] = useState(true); + const { isAdminMode } = useAdmin(); + const { user } = useUser(); + + const getStatus = (status: ContributionStatus): ContributionProps['status'] => { + if (status === ContributionStatus.Failed) return 'failed'; + if (status === ContributionStatus.Pending) return 'pending'; + return 'success'; + }; + + const getContributions = useCallback( + (result: Contribution[]): ContributionProps[] => + result.map((item) => ({ + amount: item.amount, + id: item.id, + months: item.months, + sender: item.donor, + status: getStatus(item.status), + timestamp: (item.completedAt || item.createdAt).toDate(), + code: item?.trxCode, + })), + [] + ); + + const init = useCallback(async () => { + const promise = isAdminMode + ? ContributionService.getList({ count: 20, page: 1 }) + : ContributionService.getByUserId(user?.id!, 20); + const res = await promise; + + // TODO: Implement pagination + // pageRef.current.set(res.metadata.page, res.data[res.data.length - 1].id); + + const result = 'metadata' in res ? res.data : res; + const contributions = getContributions(result); + setData(contributions); + setLoading(false); + }, [getContributions, isAdminMode, user?.id]); + + useEffect(() => { + init(); + }, [init]); + + return ; +}; + +export default ContributionsList; diff --git a/src/sections/contributions/view/contributions-view.tsx b/src/sections/contributions/view/contributions-view.tsx new file mode 100644 index 000000000..ae4755178 --- /dev/null +++ b/src/sections/contributions/view/contributions-view.tsx @@ -0,0 +1,255 @@ +import { useMemo, useState, useContext, useCallback } from 'react'; + +import Box from '@mui/material/Box'; +import Card from '@mui/material/Card'; +import Table from '@mui/material/Table'; +import Button from '@mui/material/Button'; +import TableBody from '@mui/material/TableBody'; +import Typography from '@mui/material/Typography'; +import TableContainer from '@mui/material/TableContainer'; +import TablePagination from '@mui/material/TablePagination'; + +import { useRouter } from 'src/routes/hooks'; + +import { _users } from 'src/_mock'; +import { DashboardContent } from 'src/layouts/dashboard'; + +import Loader from 'src/components/loader'; +import { Iconify } from 'src/components/iconify'; +import { Scrollbar } from 'src/components/scrollbar'; +import { UserContext } from 'src/components/provider'; + +import { TableNoData } from '../table-no-data'; +import { TableEmptyRows } from '../table-empty-rows'; +import { emptyRows, applyFilter, getComparator } from '../utils'; +import { ContributionTableHead } from '../contributions-table-head'; +import { ContributionsTableToolbar } from '../contributions-table-toolbar'; +import { ContributionsTableRow, type ContributionProps } from '../contributions-table-row'; + +// ---------------------------------------------------------------------- + +interface ContributionsViewProps { + ignoreDashContent?: boolean; + noMultiSelect?: boolean; + noToolbar?: boolean; + hideBtn?: boolean; + title?: string; + noPagination?: boolean; + data?: ContributionProps[]; + loading?: boolean; + viewMore?: boolean; +} + +export function ContributionsView({ + ignoreDashContent, + noMultiSelect, + noToolbar, + hideBtn, + noPagination, + title = 'Contributions', + loading, + viewMore, + data = [], +}: ContributionsViewProps) { + const table = useTable(); + + const [filterName, setFilterName] = useState(''); + const { pay } = useContext(UserContext); + const { push } = useRouter(); + + const onViewMore = () => { + push('/contributions'); + }; + + const dataFiltered: ContributionProps[] = useMemo( + () => + applyFilter({ + inputData: data, + comparator: getComparator(table.order, table.orderBy), + filterName, + }), + [filterName, data, table.order, table.orderBy] + ); + + const notFound = (!dataFiltered.length && !!filterName) || (!dataFiltered.length && !filterName); + + const content = ( + <> + + + {title} + + {!hideBtn && ( + + )} + {viewMore && ( + + )} + + + {loading ? ( + + ) : ( + <> + {!noToolbar && ( + ) => { + setFilterName(event.target.value); + table.onResetPage(); + }} + /> + )} + + + + + + table.onSelectAllRows( + checked, + _users.map((user) => user.id) + ) + } + headLabel={[ + { id: 'name', label: 'Name' }, + { id: 'months', label: 'Month(s)' }, + { id: 'amount', label: 'Amount' }, + { id: 'date', label: 'Date' }, + { id: 'status', label: 'Status' }, + { id: 'resume', label: '' }, + ]} + /> + + <> + {dataFiltered + .slice( + table.page * table.rowsPerPage, + table.page * table.rowsPerPage + table.rowsPerPage + ) + .map((row) => ( + table.onSelectRow(row.id)} + /> + ))} + + + {notFound && ( + + )} + + +
+
+
+ + {!noPagination && ( + + )} + + )} +
+ + ); + + return ignoreDashContent ? content : {content}; +} + +// ---------------------------------------------------------------------- + +export function useTable() { + const [page, setPage] = useState(0); + const [orderBy, setOrderBy] = useState('name'); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [selected, setSelected] = useState([]); + const [order, setOrder] = useState<'asc' | 'desc'>('asc'); + + const onSort = useCallback( + (id: string) => { + const isAsc = orderBy === id && order === 'asc'; + setOrder(isAsc ? 'desc' : 'asc'); + setOrderBy(id); + }, + [order, orderBy] + ); + + const onSelectAllRows = useCallback((checked: boolean, newSelecteds: string[]) => { + if (checked) { + setSelected(newSelecteds); + return; + } + setSelected([]); + }, []); + + const onSelectRow = useCallback( + (inputValue: string) => { + const newSelected = selected.includes(inputValue) + ? selected.filter((value) => value !== inputValue) + : [...selected, inputValue]; + + setSelected(newSelected); + }, + [selected] + ); + + const onResetPage = useCallback(() => { + setPage(0); + }, []); + + const onChangePage = useCallback((event: unknown, newPage: number) => { + setPage(newPage); + }, []); + + const onChangeRowsPerPage = useCallback( + (event: React.ChangeEvent) => { + setRowsPerPage(parseInt(event.target.value, 10)); + onResetPage(); + }, + [onResetPage] + ); + + return { + page, + order, + onSort, + orderBy, + selected, + rowsPerPage, + onSelectRow, + onResetPage, + onChangePage, + onSelectAllRows, + onChangeRowsPerPage, + }; +} diff --git a/src/sections/contributions/view/index.ts b/src/sections/contributions/view/index.ts new file mode 100644 index 000000000..0902724a1 --- /dev/null +++ b/src/sections/contributions/view/index.ts @@ -0,0 +1 @@ +export * from './contributions-view'; diff --git a/src/sections/overview/analytics-widget-summary.tsx b/src/sections/overview/analytics-widget-summary.tsx index 79694c0aa..6f271f27c 100644 --- a/src/sections/overview/analytics-widget-summary.tsx +++ b/src/sections/overview/analytics-widget-summary.tsx @@ -6,20 +6,18 @@ import Box from '@mui/material/Box'; import Card from '@mui/material/Card'; import { useTheme } from '@mui/material/styles'; -import { fNumber, fPercent, fShortenNumber } from 'src/utils/format-number'; +import { fShortenNumber } from 'src/utils/format-number'; import { varAlpha, bgGradient } from 'src/theme/styles'; -import { Iconify } from 'src/components/iconify'; import { SvgColor } from 'src/components/svg-color'; -import { Chart, useChart } from 'src/components/chart'; // ---------------------------------------------------------------------- type Props = CardProps & { title: string; - total: number; - percent: number; + total: number|string; + percent?: number; color?: ColorType; icon: React.ReactNode; chart: { @@ -43,42 +41,23 @@ export function AnalyticsWidgetSummary({ const chartColors = [theme.palette[color].dark]; - const chartOptions = useChart({ - chart: { sparkline: { enabled: true } }, - colors: chartColors, - xaxis: { categories: chart.categories }, - grid: { - padding: { - top: 6, - left: 6, - right: 6, - bottom: 6, - }, - }, - tooltip: { - y: { formatter: (value: number) => fNumber(value), title: { formatter: () => '' } }, - }, - ...chart.options, - }); - - const renderTrending = ( - - - - {percent > 0 && '+'} - {fPercent(percent)} - - - ); + // const chartOptions = useChart({ + // chart: { sparkline: { enabled: true } }, + // colors: chartColors, + // xaxis: { categories: chart.categories }, + // grid: { + // padding: { + // top: 6, + // left: 6, + // right: 6, + // bottom: 6, + // }, + // }, + // tooltip: { + // y: { formatter: (value: number) => fNumber(value), title: { formatter: () => '' } }, + // }, + // ...chart.options, + // }); return ( {icon} - {renderTrending} - {fShortenNumber(total)} - + /> */} (); + + const getStatus = (status: ContributionStatus): ContributionProps['status'] => { + if (status === ContributionStatus.Failed) return 'failed'; + if (status === ContributionStatus.Pending) return 'pending'; + return 'success'; + }; + + const getContributions = useCallback( + (result: Contribution[]): ContributionProps[] => + result.map((item) => ({ + amount: item.amount, + id: item.id, + months: item.months, + sender: item.donor, + status: getStatus(item.status), + timestamp: (item.completedAt || item.createdAt).toDate(), + code: item?.trxCode, + })), + [] + ); + + const getStats = useCallback(async () => { + const stats = isAdminMode ? await StatsService.get() : await StatsService.getByUser(); + setData((val) => (val ? { ...val, stats } : { stats })); + }, [isAdminMode]); + + const onLatestContribution = useCallback( + (value: Contribution[]) => { + const cons = getContributions(value); + setData((val) => (val ? { ...val, cons } : { cons })); + getStats(); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [getContributions, getStats] + ); + + const init = useCallback(async () => { + if (!user?.id) return; + try { + ContributionService.listenLatest(onLatestContribution, isAdminMode ? undefined : user.id); + await getStats(); + } catch (error) { + errCb(error.message); + } finally { + setLoading(false); + } + }, [user?.id, onLatestContribution, isAdminMode, getStats]); + + useEffect(() => { + init(); + }, [init]); + + if (loading) { + return ( + + varAlpha(theme.vars.palette.text.primaryChannel, 0.16), + [`& .${linearProgressClasses.bar}`]: { bgcolor: 'text.primary' }, + }} + /> + + ); + } + + const mdQuery = isAdminMode ? 3 : 4; + return ( - Hi, Welcome back 👋 + Hi, {user?.fname} 👋 - + } + title="No. of contributions" + total={data?.stats?.contributionCount || 0} + icon={icon} chart={{ categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], - series: [22, 8, 35, 50, 82, 84, 77, 12], + series: [], }} /> - + } chart={{ categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], - series: [56, 47, 40, 62, 73, 30, 23, 54], + series: [], }} /> - + {isAdminMode ? ( + + } + chart={{ + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], + series: [10, 6, 9], + }} + /> + + ) : null} + + } + icon={icon} chart={{ categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], - series: [40, 70, 50, 28, 70, 75, 7, 64], + series: [10, 6, 9], }} /> - + {/* } chart={{ categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], - series: [56, 30, 23, 54, 47, 40, 62, 73], + series: [], }} /> - - - + */} + {/* - - - + */} + {/* - - - + */} + {/* + */} + + - - - - - - + {/* - - - + */} + {/* - + */} ); diff --git a/src/sections/user/table-no-data.tsx b/src/sections/user/table-no-data.tsx index 914e23f38..689d3062b 100644 --- a/src/sections/user/table-no-data.tsx +++ b/src/sections/user/table-no-data.tsx @@ -17,14 +17,16 @@ export function TableNoData({ searchQuery, ...other }: TableNoDataProps) { - Not found + No users available - - No results found for   - "{searchQuery}". -
Try checking for typos or using complete words. -
+ {false && ( + + No results found for   + "{searchQuery}". +
Try checking for typos or using complete words. +
+ )}
diff --git a/src/sections/user/user-table-head.tsx b/src/sections/user/user-table-head.tsx index 3888ee712..204efeedb 100644 --- a/src/sections/user/user-table-head.tsx +++ b/src/sections/user/user-table-head.tsx @@ -1,6 +1,5 @@ import Box from '@mui/material/Box'; import TableRow from '@mui/material/TableRow'; -import Checkbox from '@mui/material/Checkbox'; import TableHead from '@mui/material/TableHead'; import TableCell from '@mui/material/TableCell'; import TableSortLabel from '@mui/material/TableSortLabel'; @@ -31,7 +30,7 @@ export function UserTableHead({ return ( - + {/* 0 && numSelected < rowCount} checked={rowCount > 0 && numSelected === rowCount} @@ -39,7 +38,7 @@ export function UserTableHead({ onSelectAllRows(event.target.checked) } /> - + */} {headLabel.map((headCell) => ( (null); + const [updating, setUpdating] = useState(false); + const { refresh } = useRouter(); + + const user = useUser(); const handleOpenPopover = useCallback((event: React.MouseEvent) => { setOpenPopover(event.currentTarget); @@ -42,25 +52,44 @@ export function UserTableRow({ row, selected, onSelectRow }: UserTableRowProps) setOpenPopover(null); }, []); + const onClick = useCallback( + async (val: 'makeAdmin' | 'delete') => { + setOpenPopover(null); + if (val === 'makeAdmin') { + setUpdating(true); + const userData = await UserService.get(row.id); + if (userData) { + const { role } = userData; + if (!role.includes(UserRole.Admin)) { + await UserService.update(row.id, { role: [...role, UserRole.Admin] }); + } + } + } + refresh(); + setUpdating(false); + }, + [row.id, refresh] + ); + return ( <> - - + + {/* - + */} - + - - {row.name} + + {row.name} {row.email === user.user?.email ? '(You)' : ''} - {row.company} - {row.role} + {row.email} + {`${fCurrency(row.pledge)}`} - + {/* {row.isVerified ? ( ) : ( @@ -70,12 +99,18 @@ export function UserTableRow({ row, selected, onSelectRow }: UserTableRowProps) - + */} - - - + {updating ? ( + + ) : ( + !row.role.includes(UserRole.Admin) && ( + + + + ) + )} @@ -102,15 +137,18 @@ export function UserTableRow({ row, selected, onSelectRow }: UserTableRowProps) }, }} > - - - Edit - + {!row.role.includes(UserRole.Admin) && ( + onClick('makeAdmin')}> + + Make Admin + + )} - + {/* TODO: Implement delete for collection and auth */} + {/* onClick('delete')} sx={{ color: 'error.main' }}> Delete - + */} diff --git a/src/sections/user/view/partners-view.tsx b/src/sections/user/view/partners-view.tsx new file mode 100644 index 000000000..574564a5c --- /dev/null +++ b/src/sections/user/view/partners-view.tsx @@ -0,0 +1,26 @@ +import type { User } from 'src/services/user/user.dto'; + +import { useState, useEffect } from 'react'; + +import UserService from 'src/services/user'; + +import { UserView } from './user-view'; + +const PartnersView = () => { + const [data, setData] = useState(); + const [loading, setLoading] = useState(true); + + const init = async () => { + const list = await UserService.list(); + setData(list); + setLoading(false); + }; + + useEffect(() => { + init(); + }, []); + + return ; +}; + +export default PartnersView; diff --git a/src/sections/user/view/user-view.tsx b/src/sections/user/view/user-view.tsx index 5c5e96a2f..eb0b62e6d 100644 --- a/src/sections/user/view/user-view.tsx +++ b/src/sections/user/view/user-view.tsx @@ -1,120 +1,130 @@ +import type { User } from 'src/services/user/user.dto'; + import { useState, useCallback } from 'react'; import Box from '@mui/material/Box'; import Card from '@mui/material/Card'; import Table from '@mui/material/Table'; -import Button from '@mui/material/Button'; import TableBody from '@mui/material/TableBody'; import Typography from '@mui/material/Typography'; import TableContainer from '@mui/material/TableContainer'; -import TablePagination from '@mui/material/TablePagination'; import { _users } from 'src/_mock'; import { DashboardContent } from 'src/layouts/dashboard'; -import { Iconify } from 'src/components/iconify'; +import Loader from 'src/components/loader'; import { Scrollbar } from 'src/components/scrollbar'; import { TableNoData } from '../table-no-data'; import { UserTableRow } from '../user-table-row'; import { UserTableHead } from '../user-table-head'; import { TableEmptyRows } from '../table-empty-rows'; -import { UserTableToolbar } from '../user-table-toolbar'; import { emptyRows, applyFilter, getComparator } from '../utils'; -import type { UserProps } from '../user-table-row'; - // ---------------------------------------------------------------------- -export function UserView() { +interface PartnersViewProps { + data?: User[]; + loading?: boolean; +} + +export function UserView({ data = [], loading }: PartnersViewProps) { const table = useTable(); const [filterName, setFilterName] = useState(''); - const dataFiltered: UserProps[] = applyFilter({ - inputData: _users, + const dataFiltered = applyFilter({ + inputData: data.map((item) => ({ + id: item.id, + name: `${item.fname} ${item.lname}`, + role: item.role.join(', '), + email: item.email, + pledge: item.pledgeAmount, + })), comparator: getComparator(table.order, table.orderBy), filterName, }); - const notFound = !dataFiltered.length && !!filterName; return ( - Users + Partners - + Invite Partner + */} - ) => { setFilterName(event.target.value); table.onResetPage(); }} - /> - - - - - - table.onSelectAllRows( - checked, - _users.map((user) => user.id) - ) - } - headLabel={[ - { id: 'name', label: 'Name' }, - { id: 'company', label: 'Company' }, - { id: 'role', label: 'Role' }, - { id: 'isVerified', label: 'Verified', align: 'center' }, - { id: 'status', label: 'Status' }, - { id: '' }, - ]} - /> - - {dataFiltered - .slice( - table.page * table.rowsPerPage, - table.page * table.rowsPerPage + table.rowsPerPage - ) - .map((row) => ( - table.onSelectRow(row.id)} - /> - ))} - - */} + + {loading ? ( + + ) : ( + + +
+ + table.onSelectAllRows( + checked, + data.map((user) => user.id) + ) + } + headLabel={[ + { id: 'name', label: 'Name' }, + { id: 'role', label: 'Role' }, + { id: 'email', label: 'Email' }, + { id: 'pledge', label: 'Pledge' }, + { id: 'action', label: '' }, + ]} /> - - {notFound && } - -
-
-
- - + {dataFiltered + .slice( + table.page * table.rowsPerPage, + table.page * table.rowsPerPage + table.rowsPerPage + ) + .map((row) => ( + table.onSelectRow(row.id)} + /> + ))} + + + + {notFound && } + + + + + )} + + {/* + /> */}
); diff --git a/src/services/auth/auth.dto.ts b/src/services/auth/auth.dto.ts new file mode 100644 index 000000000..f181f2e46 --- /dev/null +++ b/src/services/auth/auth.dto.ts @@ -0,0 +1,14 @@ +export interface CreateUserBody { + fname: string; + lname: string; + email: string; + password?: string; + country: string; + pledgeAmount: number; + secret?: string; +} + +export interface UserLoginBody { + email: string; + password: string; +} diff --git a/src/services/auth/index.ts b/src/services/auth/index.ts new file mode 100644 index 000000000..78078867d --- /dev/null +++ b/src/services/auth/index.ts @@ -0,0 +1,71 @@ +import type { User } from 'firebase/auth'; + +import { jwtDecode } from 'jwt-decode'; + +import { hash } from 'src/utils/encrypt'; + +import { auth } from 'src/configs/firebase'; +import { Cache, CacheKeys } from 'src/utils'; + +import UserService from '../user'; +import { UserRole } from '../user/user.dto'; + +import type { UserLoginBody, CreateUserBody } from './auth.dto'; + +export default class AuthService { + private static token: string | null = Cache.get(CacheKeys.Token); + + static async register(data: CreateUserBody) { + const valid = await UserService.validateSecret(data.secret!); + delete data?.secret; + if (!valid) throw new Error('INVALID_SECRET_KEY'); + const pass = hash(data.password!); + const user = await auth.createUser(data.email, pass); + const token = await user.user.getIdToken(); + this.setToken(token); + delete data.password; + return UserService.create({ + ...data, + pledgeAmount: Number(data.pledgeAmount), + id: user.user.uid, + role: [UserRole.Partner], + }); + } + + static async login(data: UserLoginBody) { + const pass = hash(data.password); + const user = await auth.signIn(data.email, pass); + const token = await user.user.getIdToken(); + this.setToken(token); + return UserService.get(user.user.uid); + } + + static async logout() { + await auth.logout(); + Cache.clear(); + } + + static setToken(token: string | null) { + this.token = token; + Cache.set(CacheKeys.Token, token); + } + + static getToken(): string | null { + return this.token || Cache.get(CacheKeys.Token); + } + + static hasToken() { + return !!this.getToken(); + } + + static decodeToken(token = this.getToken()) { + if (!token) return null; + const decoded = jwtDecode(token); + console.log(decoded); + return decoded; + } + + static listen(cb: (val: User | null) => void) { + auth.listen(cb); + } +} diff --git a/src/services/cont/contribute.dto.ts b/src/services/cont/contribute.dto.ts new file mode 100644 index 000000000..b78a40cdb --- /dev/null +++ b/src/services/cont/contribute.dto.ts @@ -0,0 +1,36 @@ +import type { Timestamp } from 'firebase/firestore'; + +export enum ContributionStatus { + Pending = 'PENDING', + Failed = 'FAILED', + Success = 'COMPLETED', +} + +export interface Contribution { + id: string; + months: string[]; + amount: string; + status: ContributionStatus; + trxRef: string; + trxCode: string; + createdAt: Timestamp; + modifiedAt: Timestamp | null; + completedAt: Timestamp | null; + donor: { + name: string; + id: string; + }; +} + +export interface ContributionResponse + extends Omit { + createdAt: { _seconds: number; _nanoseconds: number }; + modifiedAt: { _seconds: number; _nanoseconds: number } | null; + completedAt: { _seconds: number; _nanoseconds: number } | null; +} + +export interface CreateContributionParams { + amount: string; + months: string[]; + donor: Contribution['donor']; +} diff --git a/src/services/cont/index.ts b/src/services/cont/index.ts new file mode 100644 index 000000000..e04b073a3 --- /dev/null +++ b/src/services/cont/index.ts @@ -0,0 +1,97 @@ +import { + limit, + query, + where, + getDoc, + getDocs, + orderBy, + Timestamp, + onSnapshot, + deleteDoc, +} from 'firebase/firestore'; + +import { db, fx } from 'src/configs'; +import { Collection } from 'src/constants'; +import { colRef, docRef } from 'src/utils'; +import { ApiRoute } from 'src/constants/fxns'; + +import { ContributionStatus } from './contribute.dto'; + +import type { Contribution, ContributionResponse } from './contribute.dto'; + +export default class ContributionService { + private static ref = colRef(Collection.Contributions); + + static async getByUserId(id: string, count = 15) { + const q = query( + this.ref, + where('donor.id', '==', id), + limit(count), + orderBy('createdAt', 'desc') + ); + const docs = await getDocs(q); + const data: Contribution[] = []; + docs.forEach((doc) => { + data.push(doc.data() as Contribution); + }); + return data; + } + + static async listenLatest(cb: (val: Contribution[]) => void, id?: string, count = 15) { + const q = query( + this.ref, + id ? where('donor.id', '==', id) : where('status', '==', ContributionStatus.Success), + orderBy('createdAt', 'desc'), + limit(count) + ); + + return onSnapshot(q, ({ docs }) => { + const data: Contribution[] = docs.map((doc) => doc.data() as Contribution); + cb(data); + }); + } + + static async get(id: string): Promise { + const ref = docRef(id, Collection.Contributions); + const docRes = await getDoc(ref); + return docRes.data() as Contribution; + } + + static async getAllLatest() { + const q = query(this.ref, limit(15)); + const docs = await getDocs(q); + const data: Contribution[] = []; + docs.forEach((doc) => { + data.push(doc.data() as Contribution); + }); + return data; + } + + static async getList(data?: { page: number; count: number }) { + const res = await fx.call< + typeof data, + { + data: ContributionResponse[]; + metadata: { page: number; next: boolean; prev: boolean; pages: number; rows: number }; + } + >(ApiRoute.GetContributions, data); + return { + metadata: res.metadata, + data: res.data.map(({ createdAt, completedAt, ...item }) => ({ + ...item, + createdAt: new Timestamp(createdAt._seconds, createdAt._nanoseconds), + completedAt: completedAt + ? new Timestamp(completedAt._seconds, completedAt._nanoseconds) + : completedAt, + })) as Contribution[], + }; + } + + static async cancel(id: string) { + const data = await this.get(id); + if (data && data.status === ContributionStatus.Pending) { + const doc = docRef(id, Collection.Contributions); + await deleteDoc(doc); + } + } +} diff --git a/src/services/pay/index.ts b/src/services/pay/index.ts new file mode 100644 index 000000000..f6098d654 --- /dev/null +++ b/src/services/pay/index.ts @@ -0,0 +1,38 @@ +import PaystackPop from '@paystack/inline-js'; + +import { fx } from 'src/configs'; +import { errCb } from 'src/utils'; +import { ApiRoute } from 'src/constants/fxns'; + +import type { ContributeInit } from './pay.dto'; + +export default class PayService { + static async init(amount: number, months: string[]) { + try { + const res = await fx.call(ApiRoute.InitPayment, { + amount, + months, + callbackUrl: window.location.href, + }); + const popup = new PaystackPop(); + popup.resumeTransaction(res.code as any); + return popup; + } catch (error) { + const err = error as Error; + errCb(err.message); + return null; + } + } + + static async resume(code: string) { + try { + const popup = new PaystackPop(); + popup.resumeTransaction(code as any); + return popup; + } catch (error) { + const err = error as Error; + errCb(err.message); + return null; + } + } +} diff --git a/src/services/pay/pay.dto.ts b/src/services/pay/pay.dto.ts new file mode 100644 index 000000000..b0d1087c1 --- /dev/null +++ b/src/services/pay/pay.dto.ts @@ -0,0 +1,5 @@ +export interface ContributeInit { + amount: number | null; + months: string[]; + callbackUrl?: string; +} diff --git a/src/services/stats/index.ts b/src/services/stats/index.ts new file mode 100644 index 000000000..aac377837 --- /dev/null +++ b/src/services/stats/index.ts @@ -0,0 +1,14 @@ +import { fx } from 'src/configs'; +import { ApiRoute } from 'src/constants/fxns'; + +import type { Stats, UserStats } from './stats.dto'; + +export default class StatsService { + static async get() { + return fx.call<{}, Stats>(ApiRoute.GetStats); + } + + static async getByUser() { + return fx.call<{}, UserStats>(ApiRoute.GetUserStats); + } +} diff --git a/src/services/stats/stats.dto.ts b/src/services/stats/stats.dto.ts new file mode 100644 index 000000000..c37799cc9 --- /dev/null +++ b/src/services/stats/stats.dto.ts @@ -0,0 +1,12 @@ +export interface Stats { + partnersCount: number; + contributionCount: number; + totalAmount: number; + expectedMonthly: number; +} + +export interface UserStats { + contributionCount: number; + totalContribution: number; + pledge: number; +} diff --git a/src/services/trxn/trx.dto.ts b/src/services/trxn/trx.dto.ts new file mode 100644 index 000000000..c7f12165b --- /dev/null +++ b/src/services/trxn/trx.dto.ts @@ -0,0 +1,30 @@ +import type { Timestamp } from 'firebase/firestore'; + +export enum TrxStatus { + Initiated = 'INITIATED', + Pending = 'PENDING', + Completed = 'COMPLETED', + Cancelled = 'CANCELLED', +} + +export interface InitTransactionPayload { + email: string; + amount: string; +} + +export interface Trxn { + id: string; + status: TrxStatus; + initiateAt: Timestamp; + completeAt: Timestamp | null; + modifiedAt: Timestamp | null; + trxRef: string; + trxCode: string; + trxUrl: string; + amount: string; + initiator: { + name: string; + id: string; + email: string; + }; +} diff --git a/src/services/user/index.ts b/src/services/user/index.ts new file mode 100644 index 000000000..eb61f860b --- /dev/null +++ b/src/services/user/index.ts @@ -0,0 +1,46 @@ +import { getDoc, setDoc, getDocs, updateDoc } from 'firebase/firestore'; + +import { fx } from 'src/configs'; +import { colRef, docRef } from 'src/utils'; +import { ApiRoute } from 'src/constants/fxns'; +import { Collection } from 'src/constants/factory'; + +import type { User} from './user.dto'; + +export default class UserService { + static async get(id: string): Promise { + const ref = docRef(id, Collection.Users); + const docRes = await getDoc(ref); + return docRes.data() as User; + } + + static async me() { + return fx.call<{}, User>(ApiRoute.GetUser, {}); + } + + static async create(data: User) { + const ref = docRef(data.id, Collection.Users); + await setDoc(ref, data); + return data; + } + + static async validateSecret(val: string) { + const ref = docRef(val, Collection.Secret); + const secretDoc = await getDoc(ref); + return secretDoc.exists(); + } + + static async update(id: string, data: Partial) { + const ref = docRef(id, Collection.Users); + await updateDoc(ref, data); + } + + static async list() { + const ref = colRef(Collection.Users); + const { docs, empty } = await getDocs(ref); + if (!empty) { + return docs.map((document) => document.data() as User); + } + return []; + } +} diff --git a/src/services/user/user.dto.ts b/src/services/user/user.dto.ts new file mode 100644 index 000000000..d14f7fcbd --- /dev/null +++ b/src/services/user/user.dto.ts @@ -0,0 +1,16 @@ +export enum UserRole { + Admin = 'ADMIN', + Partner = 'PARTNER', +} + +export interface User { + id: string; + fname: string; + lname: string; + email: string; + country: string; + pledgeAmount: number; + role: UserRole[]; +} + +export interface UserUpdateBody extends Omit {} diff --git a/src/utils/alert.ts b/src/utils/alert.ts new file mode 100644 index 000000000..29c4dbf7b --- /dev/null +++ b/src/utils/alert.ts @@ -0,0 +1,13 @@ +import type { AppAlertMethods } from 'src/components/shared/alert'; + +export class AlertUtil { + private static ref: AppAlertMethods | null; + + public static setRef(_ref: AppAlertMethods | null) { + this.ref = _ref; + } + + public static getRef(): AppAlertMethods | null { + return this.ref; + } +} diff --git a/src/utils/cache.ts b/src/utils/cache.ts new file mode 100644 index 000000000..96f9b9c57 --- /dev/null +++ b/src/utils/cache.ts @@ -0,0 +1,39 @@ +import { payloadHash, payloadDeHash } from './encrypt'; + +export enum CacheKeys { + Token = 'my.auth.id.t', + Admin = 'dash.admin.mode', + AdminMode = 'dash.is.admin.mode', +} + +export class Cache { + static set(id: CacheKeys, data: T) { + return localStorage.setItem(id as any, this.parse(data)); + } + + static get(id: CacheKeys) { + const hash = localStorage.getItem(id); + if (!hash) return null; + return this.unparse(hash) as T; + } + + private static parse(data: T) { + if (!data) return ''; + const payloadString = JSON.stringify(data); + return payloadHash(payloadString); + } + + private static unparse(hash: string) { + if (!hash) return null; + const payloadString = payloadDeHash(hash); + return JSON.parse(payloadString); + } + + static delete(id: CacheKeys) { + localStorage.removeItem(id); + } + + static clear() { + localStorage.clear(); + } +} diff --git a/src/utils/encrypt.ts b/src/utils/encrypt.ts new file mode 100644 index 000000000..149694c1a --- /dev/null +++ b/src/utils/encrypt.ts @@ -0,0 +1,15 @@ +import crypto from 'crypto-js'; + +const KEY = '1NF1U3N51NG11V35'; + +export function hash(plainText: string) { + return crypto.SHA256(KEY + plainText).toString(crypto.enc.Hex); +} + +export function payloadHash(plainText: string) { + return crypto.AES.encrypt(plainText, KEY).toString(); +} + +export function payloadDeHash(hashVal: string) { + return crypto.AES.decrypt(hashVal, KEY).toString(crypto.enc.Utf8); +} diff --git a/src/utils/errors.ts b/src/utils/errors.ts new file mode 100644 index 000000000..b5574ef09 --- /dev/null +++ b/src/utils/errors.ts @@ -0,0 +1,5 @@ +import { AlertUtil } from './alert'; + +export const errCb = (val: string) => { + AlertUtil.getRef()?.show({ label: val, type: 'error' }); +}; diff --git a/src/utils/format-number.ts b/src/utils/format-number.ts index e0c5198b9..ecc799396 100644 --- a/src/utils/format-number.ts +++ b/src/utils/format-number.ts @@ -7,7 +7,7 @@ export type InputNumberValue = string | number | null | undefined; type Options = Intl.NumberFormatOptions | undefined; -const DEFAULT_LOCALE = { code: 'en-US', currency: 'USD' }; +const DEFAULT_LOCALE = { code: 'en-US', currency: 'GHS' }; function processInput(inputValue: InputNumberValue): number | null { if (inputValue == null || Number.isNaN(inputValue)) return null; diff --git a/src/utils/fstore.ts b/src/utils/fstore.ts new file mode 100644 index 000000000..2da02bc03 --- /dev/null +++ b/src/utils/fstore.ts @@ -0,0 +1,13 @@ +import type { Collection } from 'src/constants/factory'; + +import { doc, collection } from 'firebase/firestore'; + +import { db } from 'src/configs'; + +export function docRef(id: string, col: Collection) { + return doc(db, col, id); +} + +export function colRef(col:Collection){ + return collection(db,col) +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 000000000..5d9b03c6b --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,5 @@ +export * from './alert'; +export * from './cache'; +export * from './fstore'; +export * from './errors'; +export * from './encrypt'; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe2a..d5066fece 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,16 @@ /// +/// + +interface ImportMetaEnv { + readonly VITE_FIREBASE_API_KEY: string; + readonly VITE_FIREBASE_AUTH_DOMAIN: string; + readonly VITE_FIREBASE_PROJECT_ID: string; + readonly VITE_FIREBASE_STORAGE_BUCKET: string; + readonly VITE_FIREBASE_MESSAGE_SENDER_ID: string; + readonly VITE_FIREBASE_APP_ID: string; + readonly VITE_FIREBASE_MEASUREMENT_ID: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/tsconfig.json b/tsconfig.json index 9bce082b7..a8aa76b30 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,7 @@ "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true }, - "include": ["src"], + "include": ["src", "env.d.ts"], "exclude": ["node_modules"], "references": [ { diff --git a/yarn.lock b/yarn.lock index eb21c020b..b9e7ce203 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,96 +7,87 @@ resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz" - integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.25.9": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: - "@babel/highlight" "^7.24.7" + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/generator@^7.25.4": - version "7.25.4" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.25.4.tgz" - integrity sha512-NFtZmZsyzDPJnk9Zg3BbTfKKc9UlHYzD0E//p2Z3B9nCwwtJW9T0gVbCz8+fBngnn4zf1Dr3IK8PHQQHq0lDQw== +"@babel/generator@^7.25.9": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz" + integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw== dependencies: - "@babel/types" "^7.25.4" + "@babel/parser" "^7.26.2" + "@babel/types" "^7.26.0" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" + jsesc "^3.0.2" "@babel/helper-module-imports@^7.16.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz" - integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== - dependencies: - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" - -"@babel/helper-string-parser@^7.24.8": - version "7.24.8" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz" - integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== - -"@babel/helper-validator-identifier@^7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz" - integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== - -"@babel/highlight@^7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz" - integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== - dependencies: - "@babel/helper-validator-identifier" "^7.24.7" - chalk "^2.4.2" - js-tokens "^4.0.0" - picocolors "^1.0.0" + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/parser@^7.25.0", "@babel/parser@^7.25.4": - version "7.25.4" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz" - integrity sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA== +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/parser@^7.25.9", "@babel/parser@^7.26.2": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz" + integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ== dependencies: - "@babel/types" "^7.25.4" + "@babel/types" "^7.26.0" "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7": - version "7.25.4" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz" - integrity sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w== + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.25.0": - version "7.25.0" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz" - integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== - dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/parser" "^7.25.0" - "@babel/types" "^7.25.0" - -"@babel/traverse@^7.24.7": - version "7.25.4" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz" - integrity sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg== - dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/generator" "^7.25.4" - "@babel/parser" "^7.25.4" - "@babel/template" "^7.25.0" - "@babel/types" "^7.25.4" +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/traverse@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz" + integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/generator" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/template" "^7.25.9" + "@babel/types" "^7.25.9" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.4": - version "7.25.4" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz" - integrity sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ== +"@babel/types@^7.25.9", "@babel/types@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz" + integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== dependencies: - "@babel/helper-string-parser" "^7.24.8" - "@babel/helper-validator-identifier" "^7.24.7" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" "@emotion/babel-plugin@^11.12.0": version "11.12.0" @@ -158,14 +149,14 @@ hoist-non-react-statics "^3.3.1" "@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.0", "@emotion/serialize@^1.3.1": - version "1.3.1" - resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.1.tgz" - integrity sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA== + version "1.3.2" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz" + integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA== dependencies: "@emotion/hash" "^0.9.2" "@emotion/memoize" "^0.9.0" "@emotion/unitless" "^0.10.0" - "@emotion/utils" "^1.4.0" + "@emotion/utils" "^1.4.1" csstype "^3.0.2" "@emotion/sheet@^1.4.0": @@ -195,20 +186,20 @@ resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz" integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== -"@emotion/utils@^1.4.0": - version "1.4.0" - resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz" - integrity sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ== +"@emotion/utils@^1.4.0", "@emotion/utils@^1.4.1": + version "1.4.1" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz" + integrity sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA== "@emotion/weak-memoize@^0.4.0": version "0.4.0" resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@esbuild/darwin-arm64@0.21.5": +"@esbuild/win32-x64@0.21.5": version "0.21.5" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" @@ -242,6 +233,396 @@ resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz" integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@firebase/analytics-compat@0.2.15": + version "0.2.15" + resolved "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.15.tgz" + integrity sha512-C5to422Sr8FkL0MPwXcIecbMnF4o2Ll7MtoWvIm4Q/LPJvvM+tWa1DiU+LzsCdsd1/CYE9EIW9Ma3ko9XnAAYw== + dependencies: + "@firebase/analytics" "0.10.9" + "@firebase/analytics-types" "0.8.2" + "@firebase/component" "0.6.10" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/analytics-types@0.8.2": + version "0.8.2" + resolved "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz" + integrity sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw== + +"@firebase/analytics@0.10.9": + version "0.10.9" + resolved "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.9.tgz" + integrity sha512-FrvW6u6xDBKXUGYUy1WIUh0J9tvbppMsk90mig0JhHST8iLveKu/dIBVeVE/ZYZhmXy4fkI7SPSWvD1V0O4tXw== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/installations" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/app-check-compat@0.3.16": + version "0.3.16" + resolved "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.16.tgz" + integrity sha512-AxIGzLRXrTFNL+H6V+4BO0w/gERloROfRbWI/FoJUnQd0qPZIzyfdHZBbThFzFGLfDt/mVs2kdjYFx/l9I8NhQ== + dependencies: + "@firebase/app-check" "0.8.9" + "@firebase/app-check-types" "0.5.2" + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/app-check-interop-types@0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz" + integrity sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ== + +"@firebase/app-check-types@0.5.2": + version "0.5.2" + resolved "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz" + integrity sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA== + +"@firebase/app-check@0.8.9": + version "0.8.9" + resolved "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.9.tgz" + integrity sha512-YzVn1mMLzD2JboMPVVO0Pe20YOgWzrF+aXoAmmd0v3xec051n83YpxSUZbacL69uYvk0dHrEsbea44QtQ5WPDA== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/app-compat@0.2.45", "@firebase/app-compat@0.x": + version "0.2.45" + resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.45.tgz" + integrity sha512-5rYbXq1ndtMTg+07oH4WrkYuP+NZq61uzVwW1hlmybp/gr4cXq2SfaP9fc6/9IzTKmu3dh3H0fjj++HG7Z7o/w== + dependencies: + "@firebase/app" "0.10.15" + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/app-types@0.9.2", "@firebase/app-types@0.x": + version "0.9.2" + resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz" + integrity sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ== + +"@firebase/app@0.10.15", "@firebase/app@0.x": + version "0.10.15" + resolved "https://registry.npmjs.org/@firebase/app/-/app-0.10.15.tgz" + integrity sha512-he6qlG3pmwL+LHdG/BrSMBQeJzzutciq4fpXN3lGa1uSwYSijJ24VtakS/bP2X9SiDf8jGywJ4u+OgXAenJsNg== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/auth-compat@0.5.15": + version "0.5.15" + resolved "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.15.tgz" + integrity sha512-jz6k1ridPiecKI8CBRiqCM6IMOhwYp2MD+YvoxnMiK8nQLSTm57GvHETlPNX3WlbyQnCjMCOvrAhe27whyxAEg== + dependencies: + "@firebase/auth" "1.8.0" + "@firebase/auth-types" "0.12.2" + "@firebase/component" "0.6.10" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/auth-interop-types@0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz" + integrity sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ== + +"@firebase/auth-types@0.12.2": + version "0.12.2" + resolved "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz" + integrity sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w== + +"@firebase/auth@1.8.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@firebase/auth/-/auth-1.8.0.tgz" + integrity sha512-/O7UDWE5S5ux456fzNHSLx/0YN/Kykw/WyAzgDQ6wvkddZhSEmPX19EzxgsFldzhuFjsl5uOZTz8kzlosCiJjg== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/component@0.6.10": + version "0.6.10" + resolved "https://registry.npmjs.org/@firebase/component/-/component-0.6.10.tgz" + integrity sha512-OsNbEKyz9iLZSmMUhsl6+kCADzte00iisJIRUspnUqvDCX+RSGZOBIqekukv/jN177ovjApBQNFaxSYIDc/SyQ== + dependencies: + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/data-connect@0.1.1": + version "0.1.1" + resolved "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.1.1.tgz" + integrity sha512-RBJ7XE/a3oXFv31Jlw8cbMRdsxQoI8F3L7xm4n93ab+bIr1NQUiYGgW9L7TTw7obdNev91ZnW0xfqJtXcPA5yA== + dependencies: + "@firebase/auth-interop-types" "0.2.3" + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/database-compat@2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.0.tgz" + integrity sha512-2xlODKWwf/vNAxCmou0GFhymx2pqZKkhXMN9B5aiTjZ6+81sOxGim53ELY2lj+qKG2IvgiCYFc4X+ZJA2Ad5vg== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/database" "1.0.9" + "@firebase/database-types" "1.0.6" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/database-types@1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.6.tgz" + integrity sha512-sMI7IynSZBsyGbUugc8PKE1jwKbnvaieAz/RxuM57PZQNCi6Rteiviwcw/jqZOX6igqYJwXWZ3UzKOZo2nUDRA== + dependencies: + "@firebase/app-types" "0.9.2" + "@firebase/util" "1.10.1" + +"@firebase/database@1.0.9": + version "1.0.9" + resolved "https://registry.npmjs.org/@firebase/database/-/database-1.0.9.tgz" + integrity sha512-EkiPSKSu2TJJGtOjyISASf3UFpFJDil1lMbfqnxilfbmIsilvC8DzgjuLoYD+eOitcug4wtU9Fh1tt2vgBhskA== + dependencies: + "@firebase/app-check-interop-types" "0.3.2" + "@firebase/auth-interop-types" "0.2.3" + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + faye-websocket "0.11.4" + tslib "^2.1.0" + +"@firebase/firestore-compat@0.3.39": + version "0.3.39" + resolved "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.39.tgz" + integrity sha512-CsK8g34jNeHx95LISDRTcArJLonW+zJCqHI1Ez9WNiLAK2X8FeQ4UiD+RwOwxAIR+t2a6xED/5Fe6ZIqx7MuoQ== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/firestore" "4.7.4" + "@firebase/firestore-types" "3.0.2" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/firestore-types@3.0.2": + version "3.0.2" + resolved "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz" + integrity sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg== + +"@firebase/firestore@4.7.4": + version "4.7.4" + resolved "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.4.tgz" + integrity sha512-K2nq4w+NF8J1waGawY5OHLawP/Aw5CYxyDstVv1NZemGPcM3U+LZ9EPaXr1PatYIrPA7fS4DxZoWcbB0aGJ8Zg== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + "@firebase/webchannel-wrapper" "1.0.2" + "@grpc/grpc-js" "~1.9.0" + "@grpc/proto-loader" "^0.7.8" + tslib "^2.1.0" + +"@firebase/functions-compat@0.3.15": + version "0.3.15" + resolved "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.15.tgz" + integrity sha512-eiHpc6Sd9Y/SNhBsGi944SapiFbfTPKsiSUQ74QxNSs0yoxvABeIRolVMFk4TokP57NGmstGYpYte02XGNPcYw== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/functions" "0.11.9" + "@firebase/functions-types" "0.6.2" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/functions-types@0.6.2": + version "0.6.2" + resolved "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz" + integrity sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w== + +"@firebase/functions@0.11.9": + version "0.11.9" + resolved "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.9.tgz" + integrity sha512-dhO5IUfQRCsrc20YD20nSOX+QCT+cH6N86HlZOLz2XgyEFgzOdBQnUot4EabBJQRkMBI7fZWUrbYfRcnov53ug== + dependencies: + "@firebase/app-check-interop-types" "0.3.2" + "@firebase/auth-interop-types" "0.2.3" + "@firebase/component" "0.6.10" + "@firebase/messaging-interop-types" "0.2.2" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/installations-compat@0.2.10": + version "0.2.10" + resolved "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.10.tgz" + integrity sha512-YTonkcVz3AK7RF8xFhvs5CwDuJ0xbzzCJIwXoV14gnzdYbMgy6vWlUUbzkvbtEDXzPRHB0n7aGZl56oy9dLOFw== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/installations" "0.6.10" + "@firebase/installations-types" "0.5.2" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/installations-types@0.5.2": + version "0.5.2" + resolved "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz" + integrity sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA== + +"@firebase/installations@0.6.10": + version "0.6.10" + resolved "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.10.tgz" + integrity sha512-TuGSOMqkFrllxa0X/8VZIqBCRH4POndU/iWKWkRmkh12+/xKSpdp+y/kWaVbsySrelltan6LeYlcYPmLibWbwg== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/util" "1.10.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/logger@0.4.3": + version "0.4.3" + resolved "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.3.tgz" + integrity sha512-Th42bWJg18EF5bJwhRosn2M/eYxmbWCwXZr4hHX7ltO0SE3QLrpgiMKeRBR/NW7vJke7i0n3i8esbCW2s93qBw== + dependencies: + tslib "^2.1.0" + +"@firebase/messaging-compat@0.2.13": + version "0.2.13" + resolved "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.13.tgz" + integrity sha512-9ootPClS6m2c2KIzo7AqSHaWzAw28zWcjQPjVv7WeQDu6wjufpbOg+7tuVzb+gqpF9Issa3lDoYOwlO0ZudO3g== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/messaging" "0.12.13" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/messaging-interop-types@0.2.2": + version "0.2.2" + resolved "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz" + integrity sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA== + +"@firebase/messaging@0.12.13": + version "0.12.13" + resolved "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.13.tgz" + integrity sha512-YLa8PWl+BgiOVR5WOyzl21fVJFJeBRfniNuN25d9DBrQzppSAahuN6yS+vt1OIjvZNPN4pZ/lcRLYupbGu4W0w== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/installations" "0.6.10" + "@firebase/messaging-interop-types" "0.2.2" + "@firebase/util" "1.10.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/performance-compat@0.2.10": + version "0.2.10" + resolved "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.10.tgz" + integrity sha512-0h1qYkF6I79DSSpHfTQFvb91fo8shmmwiPzWFYAPdPK02bSWpKwVssNYlZX2iUnumxerDMbl7dWN+Im/W3bnXA== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/performance" "0.6.10" + "@firebase/performance-types" "0.2.2" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/performance-types@0.2.2": + version "0.2.2" + resolved "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz" + integrity sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA== + +"@firebase/performance@0.6.10": + version "0.6.10" + resolved "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.10.tgz" + integrity sha512-x/mNYKGxq7A+QV0EiEZeD2S+E+kw+UcZ8FXuE7qDJyGGt/0Wd+bIIL7RakG/VrFt7/UYc//nKygDc7/Ig7sOmQ== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/installations" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/remote-config-compat@0.2.10": + version "0.2.10" + resolved "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.10.tgz" + integrity sha512-fIi5OB2zk0zpChMV/tTd0oEZcZI8TlwQDlLlcrDpMOV5l5dqd0JNlWKh6Fwmh4izmytk+rZIAIpnak/NjGVesQ== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/remote-config" "0.4.10" + "@firebase/remote-config-types" "0.3.2" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/remote-config-types@0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz" + integrity sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA== + +"@firebase/remote-config@0.4.10": + version "0.4.10" + resolved "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.10.tgz" + integrity sha512-jTRjy3TdqzVna19m5a1HEHE5BG4Z3BQTxBgvQRTmMKlHacx4QS0CToAas7R9M9UkxpgFcVuAE7FpWIOWQGCEWw== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/installations" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/storage-compat@0.3.13": + version "0.3.13" + resolved "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.13.tgz" + integrity sha512-15kje7JALswRCBKsCSvKg5FbqUYykaIMqMbZRD7I6uVRWwdyTvez5MBQfMhBia2JcEmPiDpXhJTXH4PAWFiA8g== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/storage" "0.13.3" + "@firebase/storage-types" "0.8.2" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/storage-types@0.8.2": + version "0.8.2" + resolved "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz" + integrity sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g== + +"@firebase/storage@0.13.3": + version "0.13.3" + resolved "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.3.tgz" + integrity sha512-B5HiJ7isYKaT4dOEV43f2ySdhQxzq+SQEm7lqXebJ8AYCsebdHrgGzrPR0LR962xGjPzJHFKx63gA8Be/P2MCw== + dependencies: + "@firebase/component" "0.6.10" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/util@1.10.1", "@firebase/util@1.x": + version "1.10.1" + resolved "https://registry.npmjs.org/@firebase/util/-/util-1.10.1.tgz" + integrity sha512-AIhFnCCjM8FmCqSNlNPTuOk3+gpHC1RkeNUBLtPbcqGYpN5MxI5q7Yby+rxycweOZOCboDzfIj8WyaY4tpQG/g== + dependencies: + tslib "^2.1.0" + +"@firebase/vertexai@1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@firebase/vertexai/-/vertexai-1.0.0.tgz" + integrity sha512-48N3Lp/9GgiCCRfrSdHS+Y1IiMdYXvnHFO/f+HL1PgUtBq7WQ/fWmYOX3mzAN36zvytq13nb68ImF+GALopp+Q== + dependencies: + "@firebase/app-check-interop-types" "0.3.2" + "@firebase/component" "0.6.10" + "@firebase/logger" "0.4.3" + "@firebase/util" "1.10.1" + tslib "^2.1.0" + +"@firebase/webchannel-wrapper@1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.2.tgz" + integrity sha512-3F4iA2E+NtdMbOU0XC1cHE8q6MqpGIKRj62oGOF38S6AAx5VHR9cXmoDUSj7ejvTAT7m6jxuEeQkHeq0F+mU2w== + "@floating-ui/core@^1.6.0": version "1.6.7" resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz" @@ -279,6 +660,24 @@ resolved "https://registry.npmjs.org/@fontsource/barlow/-/barlow-5.0.14.tgz" integrity sha512-4F+rbfklgWHatFheB3ZQgTFjkqzMiWfHomy69TWSGc0qU+w+QhX9dGz7IVQRksvKciJoXAhxijCxwAJw418g4Q== +"@grpc/grpc-js@~1.9.0": + version "1.9.15" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz" + integrity sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.8": + version "0.7.13" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz" + integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.5" + yargs "^17.7.2" + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" @@ -425,9 +824,9 @@ prop-types "^15.8.1" "@mui/types@^7.2.14", "@mui/types@^7.2.15": - version "7.2.15" - resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz" - integrity sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q== + version "7.2.19" + resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz" + integrity sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA== "@mui/utils@^5.15.14", "@mui/utils@^5.16.5", "@mui/utils@^5.16.6": version "5.16.6" @@ -462,6 +861,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@paystack/inline-js@^2.22.1": + version "2.22.1" + resolved "https://registry.npmjs.org/@paystack/inline-js/-/inline-js-2.22.1.tgz" + integrity sha512-h9cf+3UbFY/+/GRA5XeWj769KaSHuedqIcuYDdV6voKGnwF9qcmJ3BorpIST45Y3qVXljOXsexL2tS6GZXJbmg== + "@pkgr/core@^0.1.0": version "0.1.1" resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz" @@ -472,20 +876,73 @@ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + "@remix-run/router@1.19.1": version "1.19.1" resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz" integrity sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg== -"@rollup/rollup-darwin-arm64@4.21.0": +"@rollup/rollup-win32-x64-msvc@4.21.0": version "4.21.0" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz" - integrity sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA== + resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz" + integrity sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ== -"@swc/core-darwin-arm64@1.7.14": +"@swc/core-win32-x64-msvc@1.7.14": version "1.7.14" - resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.14.tgz" - integrity sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ== + resolved "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.14.tgz" + integrity sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q== "@swc/core@^1.5.7": version "1.7.14" @@ -518,6 +975,16 @@ dependencies: "@swc/counter" "^0.1.3" +"@types/bcryptjs@^2.4.6": + version "2.4.6" + resolved "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz" + integrity sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ== + +"@types/crypto-js@^4.2.2": + version "4.2.2" + resolved "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz" + integrity sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ== + "@types/estree@1.0.5": version "1.0.5" resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" @@ -540,7 +1007,7 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz" integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== -"@types/node@^18.0.0 || >=20.0.0", "@types/node@^22.5.0": +"@types/node@^18.0.0 || >=20.0.0", "@types/node@^22.5.0", "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "22.5.0" resolved "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz" integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg== @@ -552,10 +1019,15 @@ resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== +"@types/paystack__inline-js@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/paystack__inline-js/-/paystack__inline-js-1.0.0.tgz" + integrity sha512-LCU5rSBs3FAG8tkn1hgV9FHTTRk+Kj2s/DupNlJRem5NaVabj6AASjWJ9tBtJX6dt7bcKhkf8c5MzgJUkpOG2g== + "@types/prop-types@*", "@types/prop-types@^15.7.12": - version "15.7.12" - resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz" - integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + version "15.7.13" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz" + integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== "@types/react-dom@^18.3.0": version "18.3.0" @@ -571,7 +1043,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17.0.0 || ^18.0.0", "@types/react@^18.3.4": +"@types/react@*", "@types/react@^17.0.0 || ^18.0.0", "@types/react@^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react@^18.3.4": version "18.3.4" resolved "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz" integrity sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw== @@ -709,12 +1181,12 @@ ansi-regex@^5.0.1: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.1: - 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== +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: - color-convert "^1.9.0" + color-convert "^2.0.1" ansi-styles@^4.1.0: version "4.3.0" @@ -887,6 +1359,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bcryptjs@^2.4.3: + version "2.4.3" + resolved "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz" + integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" @@ -930,15 +1407,6 @@ callsites@^3.0.0: resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^4.0.0: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" @@ -970,18 +1438,20 @@ chokidar@^3.5.1: optionalDependencies: fsevents "~2.3.2" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clsx@^2.1.0, clsx@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" @@ -994,11 +1464,6 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - commander@^8.0.0: version "8.3.0" resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" @@ -1039,6 +1504,11 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" @@ -1170,6 +1640,11 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + emoji-regex@^9.2.2: version "9.2.2" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" @@ -1350,10 +1825,10 @@ esbuild@^0.21.3: "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^4.0.0: version "4.0.0" @@ -1651,6 +2126,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +faye-websocket@0.11.4: + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -1678,6 +2160,40 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +firebase@^11.0.1: + version "11.0.1" + resolved "https://registry.npmjs.org/firebase/-/firebase-11.0.1.tgz" + integrity sha512-qsFb8dMcQINEDhJteG7RP+GqwgSRvfyiexQqHd5JToDdm87i9I2rGC4XQsGawKGxzKwZ/ISdgwNWxXAFYdCC6A== + dependencies: + "@firebase/analytics" "0.10.9" + "@firebase/analytics-compat" "0.2.15" + "@firebase/app" "0.10.15" + "@firebase/app-check" "0.8.9" + "@firebase/app-check-compat" "0.3.16" + "@firebase/app-compat" "0.2.45" + "@firebase/app-types" "0.9.2" + "@firebase/auth" "1.8.0" + "@firebase/auth-compat" "0.5.15" + "@firebase/data-connect" "0.1.1" + "@firebase/database" "1.0.9" + "@firebase/database-compat" "2.0.0" + "@firebase/firestore" "4.7.4" + "@firebase/firestore-compat" "0.3.39" + "@firebase/functions" "0.11.9" + "@firebase/functions-compat" "0.3.15" + "@firebase/installations" "0.6.10" + "@firebase/installations-compat" "0.2.10" + "@firebase/messaging" "0.12.13" + "@firebase/messaging-compat" "0.2.13" + "@firebase/performance" "0.6.10" + "@firebase/performance-compat" "0.2.10" + "@firebase/remote-config" "0.4.10" + "@firebase/remote-config-compat" "0.2.10" + "@firebase/storage" "0.13.3" + "@firebase/storage-compat" "0.3.13" + "@firebase/util" "1.10.1" + "@firebase/vertexai" "1.0.0" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" @@ -1712,11 +2228,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -1737,6 +2248,11 @@ functions-have-names@^1.2.3: resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz" @@ -1850,11 +2366,6 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - has-flag@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" @@ -1905,6 +2416,16 @@ hoist-non-react-statics@^3.3.1: dependencies: react-is "^16.7.0" +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +idb@7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" @@ -2040,6 +2561,11 @@ is-finalizationregistry@^1.0.2: dependencies: call-bind "^1.0.2" +is-fullwidth-code-point@^3.0.0: + 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== + is-generator-function@^1.0.10: version "1.0.10" resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz" @@ -2175,10 +2701,10 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-parse-even-better-errors@^2.3.0: version "2.3.1" @@ -2221,6 +2747,11 @@ jsonfile@^6.0.1: object.assign "^4.1.4" object.values "^1.1.6" +jwt-decode@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz" + integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA== + language-subtag-registry@^0.3.20: version "0.3.23" resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz" @@ -2258,6 +2789,11 @@ lodash-es@^4.17.21: resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" @@ -2268,6 +2804,11 @@ lodash@^4.17.21: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +long@^5.0.0: + version "5.2.3" + resolved "https://registry.npmjs.org/long/-/long-5.2.3.tgz" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" @@ -2507,9 +3048,9 @@ path-type@^4.0.0: integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== picocolors@^1.0.0, picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" @@ -2556,6 +3097,24 @@ prop-types@^15.6.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +protobufjs@^7.2.5: + version "7.4.0" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz" + integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + punycode@^2.1.0: version "2.3.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" @@ -2677,6 +3236,11 @@ regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.2: es-errors "^1.3.0" set-function-name "^2.0.1" +require-directory@^2.1.1: + 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== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -2759,6 +3323,11 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" +safe-buffer@>=5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" @@ -2877,6 +3446,15 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.includes@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz" @@ -2961,13 +3539,6 @@ stylis@4.2.0: resolved "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" @@ -3058,11 +3629,6 @@ tiny-invariant@^1.1.0: resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -3085,7 +3651,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.6.2: +tslib@^2.1.0, tslib@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== @@ -3258,6 +3824,20 @@ vscode-uri@^3.0.2: resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz" integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" @@ -3315,16 +3895,48 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"