diff --git a/package-lock.json b/package-lock.json index f1a6e01..3e23cbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,15 @@ { - "name": "html", + "name": "pandora", "lockfileVersion": 3, "requires": true, "packages": { "": { "devDependencies": { + "@ant-design/pro-layout": "^7.8.3", + "@loadable/component": "^5.15.3", "@reduxjs/toolkit": "^1.9.3", + "@types/loadable__component": "^5.13.4", + "@types/nprogress": "^0.2.0", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@typescript-eslint/eslint-plugin": "^5.54.0", @@ -21,6 +25,7 @@ "eslint-plugin-react": "^7.32.1", "laravel-vite-plugin": "^0.7.2", "lodash": "^4.17.19", + "nprogress": "^0.2.0", "postcss": "^8.4.21", "prettier": "^2.8.3", "react": "^18.2.0", @@ -28,6 +33,7 @@ "react-icons": "^4.8.0", "react-redux": "^8.0.5", "react-router-dom": "^6.8.0", + "redux-persist": "^6.0.0", "sonner": "^0.1.6", "tailwindcss": "^3.2.4", "typescript": "^4.9.5", @@ -101,6 +107,119 @@ "integrity": "sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==", "dev": true }, + "node_modules/@ant-design/pro-layout": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@ant-design/pro-layout/-/pro-layout-7.8.3.tgz", + "integrity": "sha512-ZW4FyQpp0rAyQBq5LuzPhMfBR5pcY5Whswp79JeRQCVO0G7/UNw+B6PcUPXf6fR3lEA/y4jjukOK6puxn86ndg==", + "dev": true, + "dependencies": { + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.4.3", + "@ant-design/pro-utils": "2.5.9", + "@babel/runtime": "^7.18.0", + "@umijs/route-utils": "^4.0.0", + "@umijs/use-params": "^1.0.9", + "classnames": "^2.2.6", + "lodash.merge": "^4.6.2", + "omit.js": "^2.0.2", + "path-to-regexp": "2.4.0", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.0.6", + "swr": "^2.0.0", + "use-json-comparison": "^1.0.3", + "use-media-antd-query": "^1.1.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "antd": ">=4.23.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/pro-provider": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@ant-design/pro-provider/-/pro-provider-2.4.3.tgz", + "integrity": "sha512-BefEAY9XUE1U5SxLAzA9se4e2VOykMRDyev+cGzz4bewdon/sbX82dcw300rl1n5IB9r+K3wYCzFXX+KaneFSA==", + "dev": true, + "dependencies": { + "@ant-design/cssinjs": "^1.5.6", + "@babel/runtime": "^7.18.0", + "@ctrl/tinycolor": "^3.4.0", + "rc-util": "^5.0.1", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": ">=4.23.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/pro-utils": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/@ant-design/pro-utils/-/pro-utils-2.5.9.tgz", + "integrity": "sha512-hRADZ66benHc+kFyKQ04+/X6hiDyri2UgVDyqVfchAKHoUIusMF1e3sbuP/tzClaWndieUSWt8gJz7067eVHvg==", + "dev": true, + "dependencies": { + "@ant-design/icons": "^4.3.0", + "@ant-design/pro-provider": "2.4.3", + "@babel/runtime": "^7.18.0", + "classnames": "^2.2.6", + "dayjs": "^1.11.4", + "rc-util": "^5.0.6", + "react-sortable-hoc": "^2.0.0", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": ">=4.23.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/pro-utils/node_modules/@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "dev": true, + "dependencies": { + "@ctrl/tinycolor": "^3.4.0" + } + }, + "node_modules/@ant-design/pro-utils/node_modules/@ant-design/icons": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.8.0.tgz", + "integrity": "sha512-T89P2jG2vM7OJ0IfGx2+9FC5sQjtTzRSz+mCHTXkFn/ELZc2YpfStmYHmqzq2Jx55J0F7+O6i5/ZKFSVNWCKNg==", + "dev": true, + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1", + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "rc-util": "^5.9.4" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/pro-utils/node_modules/react-sortable-hoc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz", + "integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.2.0", + "invariant": "^2.2.4", + "prop-types": "^15.5.7" + }, + "peerDependencies": { + "prop-types": "^15.5.7", + "react": "^16.3.0 || ^17.0.0", + "react-dom": "^16.3.0 || ^17.0.0" + } + }, "node_modules/@ant-design/react-slick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.0.0.tgz", @@ -168,6 +287,15 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { "version": "7.21.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", @@ -216,6 +344,15 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", @@ -972,6 +1109,27 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@loadable/component": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.3.tgz", + "integrity": "sha512-VOgYgCABn6+/7aGIpg7m0Ruj34tGetaJzt4bQ345FwEovDQZ+dua+NWLmuJKv8rWZyxOUSfoJkmGnzyDXH2BAQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.7", + "hoist-non-react-statics": "^3.3.1", + "react-is": "^16.12.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1070,15 +1228,15 @@ } }, "node_modules/@rc-component/tour": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.6.0.tgz", - "integrity": "sha512-b/s7LCb7bW4wxpWfZyNpl7khHUzSyObSlsLaIScRGd+W/v1wFVk8F7gRytl/z8ik9ZSXbLWx9EvexIuHoO/RcQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.8.0.tgz", + "integrity": "sha512-rrRGioHTLQlGca27G2+lw7QpRb3uuMYCUIJjj31/B44VCJS0P2tqYhOgtzvWQmaLMlWH3ZlpzotkKX13NT4XEA==", "dev": true, "dependencies": { "@babel/runtime": "^7.18.0", "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^1.3.6", "classnames": "^2.3.2", - "rc-trigger": "^5.3.4", "rc-util": "^5.24.4" }, "engines": { @@ -1089,6 +1247,28 @@ "react-dom": ">=16.9.0" } }, + "node_modules/@rc-component/trigger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-1.5.0.tgz", + "integrity": "sha512-uF5H7FjRbSDaCIWe+aASk/vdTJtmhZvAIcH5IbO/jBXdEbLz9OYoY7QfHWfmUwNGaEX640+z8mAawiVBQGOwFw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-align": "^4.0.0", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.27.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz", @@ -1138,6 +1318,21 @@ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "node_modules/@types/loadable__component": { + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@types/loadable__component/-/loadable__component-5.13.4.tgz", + "integrity": "sha512-YhoCCxyuvP2XeZNbHbi8Wb9EMaUJuA2VGHxJffcQYrJKIKSkymJrhbzsf9y4zpTmr5pExAAEh5hbF628PAZ8Dg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==", + "dev": true + }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", @@ -1216,39 +1411,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { "version": "5.54.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.0.tgz", @@ -1360,39 +1522,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { "version": "5.54.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.0.tgz", @@ -1419,61 +1548,6 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.54.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.0.tgz", @@ -1491,6 +1565,21 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@umijs/route-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@umijs/route-utils/-/route-utils-4.0.1.tgz", + "integrity": "sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==", + "dev": true + }, + "node_modules/@umijs/use-params": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@umijs/use-params/-/use-params-1.0.9.tgz", + "integrity": "sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w==", + "dev": true, + "peerDependencies": { + "react": "*" + } + }, "node_modules/@vitejs/plugin-react": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", @@ -1601,9 +1690,9 @@ } }, "node_modules/antd": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/antd/-/antd-5.2.3.tgz", - "integrity": "sha512-mUSVH4ZhzC8h3eLNyL7PWquKUmvDcWAVRZYi060MOw6uiKl0pqsB/FC8OpfwntpBlVYgGMk+y3GB0loqTMSmJA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.3.0.tgz", + "integrity": "sha512-+l8K2AQ+yvIOq9hzWtha3nG3ydFfwN6+fUmSGCqyThFrLPI2Y3IUPXdAXP4aQcYAnu1Q0sLwsTgHRvE2AXfZUw==", "dev": true, "dependencies": { "@ant-design/colors": "^7.0.0", @@ -1613,19 +1702,20 @@ "@babel/runtime": "^7.18.3", "@ctrl/tinycolor": "^3.6.0", "@rc-component/mutate-observer": "^1.0.0", - "@rc-component/tour": "~1.6.0", + "@rc-component/tour": "~1.8.0", + "@rc-component/trigger": "^1.5.0", "classnames": "^2.2.6", "copy-to-clipboard": "^3.2.0", "dayjs": "^1.11.1", "qrcode.react": "^3.1.0", - "rc-cascader": "~3.8.0", + "rc-cascader": "~3.9.0", "rc-checkbox": "~2.3.0", "rc-collapse": "~3.5.2", "rc-dialog": "~9.0.2", "rc-drawer": "~6.1.1", "rc-dropdown": "~4.0.0", "rc-field-form": "~1.27.0", - "rc-image": "~5.13.0", + "rc-image": "~5.15.2", "rc-input": "~0.2.1", "rc-input-number": "~7.4.0", "rc-mentions": "~2.0.0", @@ -1633,21 +1723,21 @@ "rc-motion": "^2.6.1", "rc-notification": "~5.0.0", "rc-pagination": "~3.2.0", - "rc-picker": "~3.1.1", + "rc-picker": "~3.2.4", "rc-progress": "~3.4.1", "rc-rate": "~2.9.0", "rc-resize-observer": "^1.2.0", "rc-segmented": "~2.1.2", - "rc-select": "~14.2.0", + "rc-select": "~14.3.0", "rc-slider": "~10.1.0", "rc-steps": "~6.0.0", "rc-switch": "~4.0.0", - "rc-table": "~7.30.2", + "rc-table": "~7.31.0", "rc-tabs": "~12.5.6", "rc-textarea": "~1.0.0", - "rc-tooltip": "~5.3.1", + "rc-tooltip": "~6.0.0", "rc-tree": "~5.7.0", - "rc-tree-select": "~5.6.0", + "rc-tree-select": "~5.7.0", "rc-trigger": "^5.3.4", "rc-upload": "~4.3.0", "rc-util": "^5.27.0", @@ -2477,9 +2567,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", - "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", + "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -2550,17 +2640,35 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "estraverse": "^4.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" } }, "node_modules/eslint-utils": { @@ -2660,6 +2768,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/eslint/node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -3287,6 +3408,15 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -3849,6 +3979,12 @@ "node": ">=0.10.0" } }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "dev": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3964,6 +4100,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/omit.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-2.0.2.tgz", + "integrity": "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==", + "dev": true + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4065,6 +4207,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", + "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -4366,15 +4514,15 @@ } }, "node_modules/rc-cascader": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.8.1.tgz", - "integrity": "sha512-VdsxbiNYmcbMqKzydODr8oehBzIvIk2ICMPu5S95b3SzuKspgkV2uSxBCUa6dsp26lUvRI/PA+k8Q00o5bsSZA==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.9.0.tgz", + "integrity": "sha512-XppS7Vd11l4oqgkIQX2RHAbsR9j+UR7IpjzWhK5wUmweY4w5OsF5RdFlxAb8oC9JsIbK4lBeGLmDWYxfi9S37g==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", "array-tree-filter": "^2.1.0", "classnames": "^2.3.1", - "rc-select": "~14.2.0", + "rc-select": "~14.3.0", "rc-tree": "~5.7.0", "rc-util": "^5.6.1" }, @@ -4482,9 +4630,9 @@ } }, "node_modules/rc-image": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-5.13.0.tgz", - "integrity": "sha512-iZTOmw5eWo2+gcrJMMcnd7SsxVHl3w5xlyCgsULUdJhJbnuI8i/AL0tVOsE7aLn9VfOh1qgDT3mC2G75/c7mqg==", + "version": "5.15.2", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-5.15.2.tgz", + "integrity": "sha512-QaeWP20v51eGyrkl24PyusTmbMk42A3vGPl7hEa15jcQjECOX36tLtvLk5sjl3vaLQpMskB8BbwiqPsN7I7aow==", "dev": true, "dependencies": { "@babel/runtime": "^7.11.2", @@ -4632,9 +4780,9 @@ } }, "node_modules/rc-picker": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-3.1.5.tgz", - "integrity": "sha512-Hh3ml+u+5mxLfl4ahVWlRGiX5+0EJrALR6tSW9yP0eea+6j+YjvjfetbvuVidViMDMweZa38dr8HTfAFLG6GFw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-3.2.4.tgz", + "integrity": "sha512-AydqPVS12u+46P6DwF0iMi4p5UAAnb64drcA/zUgNXkkB6rQhUwtYP7E75YM0WylT54F3sLhe+WttZynxtsU+Q==", "dev": true, "dependencies": { "@babel/runtime": "^7.10.1", @@ -4730,16 +4878,16 @@ } }, "node_modules/rc-select": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.2.2.tgz", - "integrity": "sha512-w+LuiYGFWgaV23PuxtdeWtXSsoxt+eCfzxu/CvRuqSRm8tn/pqvAb1xUIDAjoMMWK1FqiOW4jI/iMt7ZRG/BBg==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.3.0.tgz", + "integrity": "sha512-y+TeAKvAvfAS7WXn4nFU8xtWJ1kLC7SVBvX3UQYtfU+N3BYNpNzHw/3F1Gu34rN2YWTG4hwspwFvDuRtGXytlQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^1.4.0", "classnames": "2.x", "rc-motion": "^2.0.1", "rc-overflow": "^1.0.0", - "rc-trigger": "^5.0.4", "rc-util": "^5.16.1", "rc-virtual-list": "^3.4.13" }, @@ -4803,9 +4951,9 @@ } }, "node_modules/rc-table": { - "version": "7.30.4", - "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.30.4.tgz", - "integrity": "sha512-NXfitP7CEXAnnuxdEYZNwEMyWmWOSsv9WgiP2yI10pyy0BwomFn4aa8nX/9x832edLwuIlrYuL6ojfOK60An2w==", + "version": "7.31.0", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.31.0.tgz", + "integrity": "sha512-TiWonax1f0B/WBh4yyA0q9GyGdKluvC1z39cPoTuRDf+zsBFDyla4T8xCK8Hzx4EugPE9f9a9fCa/i7N9+SSyA==", "dev": true, "dependencies": { "@babel/runtime": "^7.10.1", @@ -4862,14 +5010,14 @@ } }, "node_modules/rc-tooltip": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-5.3.1.tgz", - "integrity": "sha512-e6H0dMD38EPaSPD2XC8dRfct27VvT2TkPdoBSuNl3RRZ5tspiY/c5xYEmGC0IrABvMBgque4Mr2SMZuliCvoiQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.0.1.tgz", + "integrity": "sha512-MdvPlsD1fDSxKp9+HjXrc/CxLmA/s11QYIh1R7aExxfodKP7CZA++DG1AjrW80F8IUdHYcR43HAm0Y2BYPelHA==", "dev": true, "dependencies": { "@babel/runtime": "^7.11.2", - "classnames": "^2.3.1", - "rc-trigger": "^5.3.1" + "@rc-component/trigger": "^1.0.4", + "classnames": "^2.3.1" }, "peerDependencies": { "react": ">=16.9.0", @@ -4897,14 +5045,14 @@ } }, "node_modules/rc-tree-select": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.6.0.tgz", - "integrity": "sha512-XG6pu0a9l6+mzhQqUYfR2VIONbe/3LjVc3wKt28k6uBMZsI1j+SSxRyt/7jWRq8Kok8jHJBQASlDg6ehr9Sp0w==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.7.0.tgz", + "integrity": "sha512-YGMpBFK9qBkgng2ZhOw7yFiL9VnjHij+uNvP+tiU/QZGdf2XcO8LHXQNUZRGAEzx4PT5lUs6d7kIfkQ9a74bqg==", "dev": true, "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", - "rc-select": "~14.2.0", + "rc-select": "~14.3.0", "rc-tree": "~5.7.0", "rc-util": "^5.16.1" }, @@ -5137,6 +5285,15 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/redux-persist": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "dev": true, + "peerDependencies": { + "redux": ">4.0.0" + } + }, "node_modules/redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", @@ -5316,14 +5473,38 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5502,6 +5683,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.0.4.tgz", + "integrity": "sha512-4GUiTjknRUVuw4MWUHR7mzJ9G/DWL+yZz/TgWDfiA0OZ9tL6qyrTkN2wPeboBpL3OJTkej3pexh3mWCnv8cFkQ==", + "dev": true, + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "engines": { + "pnpm": "7" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/tailwindcss": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", @@ -5730,6 +5926,24 @@ "punycode": "^2.1.0" } }, + "node_modules/use-json-comparison": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/use-json-comparison/-/use-json-comparison-1.0.6.tgz", + "integrity": "sha512-xPadt5yMRbEmVfOSGFSMqjjICrq7nLbfSH3rYIXsrtcuFX7PmbYDN/ku8ObBn3v8o/yZelO1OxUS5+5TI3+fUw==", + "dev": true, + "peerDependencies": { + "react": ">=16.9.0" + } + }, + "node_modules/use-media-antd-query": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/use-media-antd-query/-/use-media-antd-query-1.1.0.tgz", + "integrity": "sha512-B6kKZwNV4R+l4Rl11sWO7HqOay9alzs1Vp1b4YJqjz33YxbltBCZtt/yxXxkXN9rc1S7OeEL/GbwC30Wmqhw6Q==", + "dev": true, + "peerDependencies": { + "react": ">=16.9.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -5824,6 +6038,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 7302350..d0adeb7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,11 @@ "prettier:fix": "prettier --write \"resources/js/**/*.{js,jsx,ts,tsx,css,md,json}\"" }, "devDependencies": { + "@ant-design/pro-layout": "^7.8.3", + "@loadable/component": "^5.15.3", "@reduxjs/toolkit": "^1.9.3", + "@types/loadable__component": "^5.13.4", + "@types/nprogress": "^0.2.0", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@typescript-eslint/eslint-plugin": "^5.54.0", @@ -25,6 +29,7 @@ "eslint-plugin-react": "^7.32.1", "laravel-vite-plugin": "^0.7.2", "lodash": "^4.17.19", + "nprogress": "^0.2.0", "postcss": "^8.4.21", "prettier": "^2.8.3", "react": "^18.2.0", @@ -32,6 +37,7 @@ "react-icons": "^4.8.0", "react-redux": "^8.0.5", "react-router-dom": "^6.8.0", + "redux-persist": "^6.0.0", "sonner": "^0.1.6", "tailwindcss": "^3.2.4", "typescript": "^4.9.5", diff --git a/resources/js/admin/App.tsx b/resources/js/admin/App.tsx index 2fc3498..05c5cf0 100644 --- a/resources/js/admin/App.tsx +++ b/resources/js/admin/App.tsx @@ -1,28 +1,15 @@ -import { ConfigProvider } from 'antd'; -import { createBrowserRouter, RouterProvider } from 'react-router-dom'; -import { antdConfig } from '../shared/constants'; -import ErrorPage from './components/ErrorPage'; +import { RouterProvider } from 'react-router-dom'; +import { Toaster } from 'sonner'; +import { browserRouter } from './routes/browserRouter'; import 'antd/dist/reset.css'; import '../shared/assets/css/index.css'; -import { webRoutes } from './routes/web'; -import { Toaster } from 'sonner'; - -const router = createBrowserRouter([ - { - path: webRoutes.login.url, - element: webRoutes.login.component, - errorElement: , - }, -]); const App = () => { return ( - +
-
- -
- + +
); }; diff --git a/resources/js/admin/components/auth/AuthLayout.tsx b/resources/js/admin/components/auth/AuthLayout.tsx index 7da97c4..996d1e4 100644 --- a/resources/js/admin/components/auth/AuthLayout.tsx +++ b/resources/js/admin/components/auth/AuthLayout.tsx @@ -1,11 +1,7 @@ -import { ReactNode } from 'react'; +import { Outlet } from 'react-router'; import loginBg from '../../../shared/assets/img/login-bg.jpg'; -export type AuthLayoutProps = { - children: ReactNode; -}; - -const AuthLayout = ({ children }: AuthLayoutProps) => { +const AuthLayout = () => { return (
@@ -26,9 +22,9 @@ const AuthLayout = ({ children }: AuthLayoutProps) => {
-
+
- {children} +
diff --git a/resources/js/admin/components/auth/Login.tsx b/resources/js/admin/components/auth/Login.tsx index 9486621..2f0d4ff 100644 --- a/resources/js/admin/components/auth/Login.tsx +++ b/resources/js/admin/components/auth/Login.tsx @@ -1,14 +1,15 @@ -import AuthLayout from './AuthLayout'; import { Form, Input } from 'antd'; -import { useEffect, useState } from 'react'; +import { Fragment, useEffect, useState } from 'react'; import { handleErrorResponse, setPageTitle } from '../../../shared/utils'; -import axios from 'axios'; import { apiRoutes } from '../../routes/api'; import { useDispatch, useSelector } from 'react-redux'; import { login } from '../../store/slices/adminSlice'; import { Admin } from '../../inrterfaces/admin'; import Button from '../../../shared/components/atoms/button'; import { RootState } from '../../store'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { webRoutes } from '../../routes/web'; +import { defaultHttp } from '../../../shared/utils/http'; type FormValues = { email: string; @@ -16,8 +17,11 @@ type FormValues = { }; const Login = () => { - const admin = useSelector((state: RootState) => state.admin); const dispatch = useDispatch(); + const navigate = useNavigate(); + const location = useLocation(); + const from = location.state?.from?.pathname || webRoutes.dashboard; + const admin = useSelector((state: RootState) => state.admin); const [loading, setLoading] = useState(false); const [form] = Form.useForm(); @@ -26,13 +30,15 @@ const Login = () => { }, []); useEffect(() => { - // redirect to dashboard + if (admin) { + navigate(from, { replace: true }); + } }, [admin]); const onSubmit = (values: FormValues) => { setLoading(true); - axios + defaultHttp .post(apiRoutes.login, { email: values.email, password: values.password, @@ -55,7 +61,7 @@ const Login = () => { }; return ( - +

Admin Login

@@ -123,13 +129,14 @@ const Login = () => { className="mt-4 bg-neutral text-primary-content" block loading={loading} + size="large" htmlType={'submit'} > Login
- + ); }; diff --git a/resources/js/admin/components/dashboard/index.tsx b/resources/js/admin/components/dashboard/index.tsx index 71206b2..aaa5520 100644 --- a/resources/js/admin/components/dashboard/index.tsx +++ b/resources/js/admin/components/dashboard/index.tsx @@ -1,5 +1,5 @@ const Dashboard = () => { - return 'Dashboard'; + return
DASHBOARD
; }; export default Dashboard; diff --git a/resources/js/admin/components/ErrorPage.tsx b/resources/js/admin/components/errorPage.tsx similarity index 79% rename from resources/js/admin/components/ErrorPage.tsx rename to resources/js/admin/components/errorPage.tsx index f04264d..db8f62a 100644 --- a/resources/js/admin/components/ErrorPage.tsx +++ b/resources/js/admin/components/errorPage.tsx @@ -3,7 +3,7 @@ import { Result } from 'antd'; const ErrorPage = () => { return ( -
+
{ - return ''; -}; - -export default PrivateRoute; diff --git a/resources/js/admin/components/layout/adminRedirect.tsx b/resources/js/admin/components/layout/adminRedirect.tsx new file mode 100644 index 0000000..a1d35b3 --- /dev/null +++ b/resources/js/admin/components/layout/adminRedirect.tsx @@ -0,0 +1,14 @@ +import { useSelector } from 'react-redux'; +import { Navigate } from 'react-router-dom'; +import { webRoutes } from '../../routes/web'; +import { RootState } from '../../store'; + +const AdminRedirect = () => { + const admin = useSelector((state: RootState) => state.admin); + + return ( + + ); +}; + +export default AdminRedirect; diff --git a/resources/js/admin/components/layout/index.tsx b/resources/js/admin/components/layout/index.tsx new file mode 100644 index 0000000..59df2d7 --- /dev/null +++ b/resources/js/admin/components/layout/index.tsx @@ -0,0 +1,117 @@ +import { Outlet, useLocation, useNavigate } from 'react-router-dom'; +import { webRoutes } from '../../routes/web'; +import ProLayout, { ProLayoutProps } from '@ant-design/pro-layout'; +import logo from '../../../shared/assets/img/logo.svg'; +import { Dropdown } from 'antd'; +import { + UserOutlined, + LogoutOutlined, + QuestionCircleFilled, +} from '@ant-design/icons'; +import { useDispatch, useSelector } from 'react-redux'; +import { RootState } from '../../store'; +import { config } from '../../../shared/constants'; +import { logout } from '../../store/slices/adminSlice'; +import { memo, useEffect } from 'react'; +import { sidebar } from './sidebar'; +import http from '../../../shared/utils/http'; +import { apiRoutes } from '../../routes/api'; +import { handleErrorResponse } from '../../../shared/utils'; + +const Layout = () => { + const location = useLocation(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const admin = useSelector((state: RootState) => state.admin); + + useEffect(() => { + if (!admin) { + navigate(webRoutes.login); + } + }, [admin]); + + const defaultProps: ProLayoutProps = { + title: config.appName, + logo: logo, + fixedHeader: true, + fixSiderbar: true, + layout: 'mix', + route: { + routes: sidebar, + }, + }; + + const logoutAdmin = () => { + http + .post(apiRoutes.logout) + .then(() => { + dispatch(logout()); + }) + .catch((error) => { + handleErrorResponse(error); + }); + }; + + return ( +
+ navigate(webRoutes.dashboard)} + menuItemRender={(item, dom) => ( + { + e.preventDefault(); + item.path && navigate(item.path); + }} + href={item.path} + > + {dom} + + )} + avatarProps={{ + src: admin?.avatarUrl, + icon: , + size: 'small', + title: admin?.name.split(' ')[0], + render: (_, dom) => { + return ( + , + label: 'Logout', + onClick: () => { + logoutAdmin(); + }, + }, + ], + }} + > + {dom} + + ); + }, + }} + actionsRender={() => { + return [ + window.open(config.helpLink, '_blank')} + />, + ]; + }} + > + + +
+ ); +}; + +export default memo(Layout); diff --git a/resources/js/admin/components/layout/sidebar.tsx b/resources/js/admin/components/layout/sidebar.tsx new file mode 100644 index 0000000..8ee7c36 --- /dev/null +++ b/resources/js/admin/components/layout/sidebar.tsx @@ -0,0 +1,17 @@ +import { webRoutes } from '../../routes/web'; +import { UserOutlined, HomeOutlined } from '@ant-design/icons'; + +export const sidebar = [ + { + path: webRoutes.dashboard, + key: webRoutes.dashboard, + name: 'Dashboard', + icon: , + }, + { + path: webRoutes.users, + key: webRoutes.users, + name: 'Users', + icon: , + }, +]; diff --git a/resources/js/admin/components/loading/pageLoading.tsx b/resources/js/admin/components/loading/pageLoading.tsx new file mode 100644 index 0000000..e514570 --- /dev/null +++ b/resources/js/admin/components/loading/pageLoading.tsx @@ -0,0 +1,12 @@ +import { Spin } from 'antd'; +import { TbGridDots } from 'react-icons/tb'; + +const PageLoading = () => { + return ( +
+ } /> +
+ ); +}; + +export default PageLoading; diff --git a/resources/js/admin/components/loading/progressBar.css b/resources/js/admin/components/loading/progressBar.css new file mode 100644 index 0000000..1aaed99 --- /dev/null +++ b/resources/js/admin/components/loading/progressBar.css @@ -0,0 +1,81 @@ +/* Make clicks pass-through */ +#nprogress { + pointer-events: none; +} + +#nprogress .bar { + background: hsl(var(--p)); + + position: fixed; + z-index: 1031; + top: 0; + left: 0; + + width: 100%; + height: 2px; +} + +/* Fancy blur effect */ +#nprogress .peg { + display: block; + position: absolute; + right: 0px; + width: 100px; + height: 100%; + box-shadow: 0 0 10px hsl(var(--p)), 0 0 5px hsl(var(--p)); + opacity: 1; + + -webkit-transform: rotate(3deg) translate(0px, -4px); + -ms-transform: rotate(3deg) translate(0px, -4px); + transform: rotate(3deg) translate(0px, -4px); +} + +/* Remove these to get rid of the spinner */ +#nprogress .spinner { + display: block; + position: fixed; + z-index: 1031; + top: 15px; + right: 15px; +} + +#nprogress .spinner-icon { + width: 18px; + height: 18px; + box-sizing: border-box; + + border: solid 2px transparent; + border-top-color: hsl(var(--p)); + border-left-color: hsl(var(--p)); + border-radius: 50%; + + -webkit-animation: nprogress-spinner 400ms linear infinite; + animation: nprogress-spinner 400ms linear infinite; +} + +.nprogress-custom-parent { + overflow: hidden; + position: relative; +} + +.nprogress-custom-parent #nprogress .spinner, +.nprogress-custom-parent #nprogress .bar { + position: absolute; +} + +@-webkit-keyframes nprogress-spinner { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} +@keyframes nprogress-spinner { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/resources/js/admin/components/loading/progressBar.tsx b/resources/js/admin/components/loading/progressBar.tsx new file mode 100644 index 0000000..9f1f94e --- /dev/null +++ b/resources/js/admin/components/loading/progressBar.tsx @@ -0,0 +1,23 @@ +import { Fragment, useEffect } from 'react'; +import NProgress from 'nprogress'; +import './progressBar.css'; + +export type ProgressBarProps = { + spinner?: boolean; +}; + +const ProgressBar = ({ spinner = false }: ProgressBarProps) => { + NProgress.configure({ showSpinner: spinner }); + + useEffect(() => { + NProgress.start(); + + return () => { + NProgress.done(); + }; + }); + + return ; +}; + +export default ProgressBar; diff --git a/resources/js/admin/components/NotfoundPage.tsx b/resources/js/admin/components/notfoundPage.tsx similarity index 64% rename from resources/js/admin/components/NotfoundPage.tsx rename to resources/js/admin/components/notfoundPage.tsx index b0c45b5..9bfd8e1 100644 --- a/resources/js/admin/components/NotfoundPage.tsx +++ b/resources/js/admin/components/notfoundPage.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Result } from 'antd'; -const NotFound = () => { +const NotFoundPage = () => { return ( -
+
{ ); }; -export default NotFound; +export default NotFoundPage; diff --git a/resources/js/admin/components/users/index.tsx b/resources/js/admin/components/users/index.tsx new file mode 100644 index 0000000..bc09bc7 --- /dev/null +++ b/resources/js/admin/components/users/index.tsx @@ -0,0 +1,5 @@ +const Users = () => { + return
USERS
; +}; + +export default Users; diff --git a/resources/js/admin/main.tsx b/resources/js/admin/main.tsx index eefa0d5..2366f97 100644 --- a/resources/js/admin/main.tsx +++ b/resources/js/admin/main.tsx @@ -1,13 +1,26 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; +import persistStore from 'redux-persist/es/persistStore'; import App from './App'; import { store } from './store'; +import { PersistGate } from 'redux-persist/integration/react'; +import { ConfigProvider } from 'antd'; +import { antdConfig } from '../shared/constants'; +import PageLoading from './components/loading/pageLoading'; +import { injectStore } from '../shared/utils/http'; + +const persistor = persistStore(store); +injectStore(store); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - + + + } persistor={persistor}> + + + + ); diff --git a/resources/js/admin/routes/api.tsx b/resources/js/admin/routes/api.tsx index 07b53b6..1d18850 100644 --- a/resources/js/admin/routes/api.tsx +++ b/resources/js/admin/routes/api.tsx @@ -1,9 +1,6 @@ import { API_URL } from '../../shared/utils'; -type Routes = { - [name: string]: string; -}; - -export const apiRoutes: Routes = { +export const apiRoutes = { login: `${API_URL}/admin/login`, + logout: `${API_URL}/admin/logout`, }; diff --git a/resources/js/admin/routes/browserRouter.tsx b/resources/js/admin/routes/browserRouter.tsx new file mode 100644 index 0000000..85ad6ec --- /dev/null +++ b/resources/js/admin/routes/browserRouter.tsx @@ -0,0 +1,57 @@ +import { createBrowserRouter } from 'react-router-dom'; +import AuthLayout from '../components/auth/AuthLayout'; +import ErrorPage from '../components/errorPage'; +import Layout from '../components/layout'; +import AdminRedirect from '../components/layout/adminRedirect'; +import NotFoundPage from '../components/notfoundPage'; +import { webRoutes } from './web'; +import loadable from '@loadable/component'; +import ProgressBar from '../components/loading/progressBar'; + +const errorElement = ; +const Login = loadable(() => import('../components/auth/Login')); +const Dashboard = loadable(() => import('../components/dashboard')); +const Users = loadable(() => import('../components/users')); + +export const browserRouter = createBrowserRouter([ + { + path: webRoutes.admin, + element: , + errorElement: errorElement, + }, + + // auth routes + { + element: , + errorElement: errorElement, + children: [ + { + path: webRoutes.login, + element: } />, + }, + ], + }, + + // protected routes + { + element: , + errorElement: errorElement, + children: [ + { + path: webRoutes.dashboard, + element: } />, + }, + { + path: webRoutes.users, + element: } />, + }, + ], + }, + + // 404 + { + path: '*', + element: , + errorElement: errorElement, + }, +]); diff --git a/resources/js/admin/routes/requireAuth.tsx b/resources/js/admin/routes/requireAuth.tsx new file mode 100644 index 0000000..d9122a0 --- /dev/null +++ b/resources/js/admin/routes/requireAuth.tsx @@ -0,0 +1,21 @@ +import { useSelector } from 'react-redux'; +import { Navigate, useLocation } from 'react-router-dom'; +import { RootState } from '../store'; +import { webRoutes } from './web'; + +export type RequireAuthProps = { + children: JSX.Element; +}; + +const RequireAuth = ({ children }: RequireAuthProps) => { + const admin = useSelector((state: RootState) => state.admin); + const location = useLocation(); + + if (!admin) { + return ; + } + + return children; +}; + +export default RequireAuth; diff --git a/resources/js/admin/routes/web.tsx b/resources/js/admin/routes/web.tsx index 9ddef09..0d5325d 100644 --- a/resources/js/admin/routes/web.tsx +++ b/resources/js/admin/routes/web.tsx @@ -1,25 +1,7 @@ -import { ReactNode } from 'react'; -import Login from '../components/auth/Login'; - -type Route = { - url: string; - component: ReactNode; - authRequired: boolean; -}; - -type Routes = { - [name: string]: Route; -}; - -export const webRoutes: Routes = { - login: { - url: '/admin/login', - component: , - authRequired: false, - }, - /* dashboard: { - url: '/admin/dashboard', - component: , - authRequired: true, - }, */ +export const webRoutes = { + admin: '/admin', + login: '/admin/login', + logout: '/admin/logout', + dashboard: '/admin/dashboard', + users: '/admin/users', }; diff --git a/resources/js/admin/store/index.tsx b/resources/js/admin/store/index.tsx index ba35906..22ea485 100644 --- a/resources/js/admin/store/index.tsx +++ b/resources/js/admin/store/index.tsx @@ -1,15 +1,38 @@ import { combineReducers, configureStore } from '@reduxjs/toolkit'; import adminSlice, { AdminState } from './slices/adminSlice'; +import { + persistReducer, + FLUSH, + REHYDRATE, + PAUSE, + PERSIST, + PURGE, + REGISTER, +} from 'redux-persist'; +import storage from 'redux-persist/lib/storage'; -export interface RootState { - admin: AdminState; - // add more state slices here as needed -} +const persistConfig = { + key: 'root', + storage, +}; const rootReducer = combineReducers({ admin: adminSlice, }); +const persistedReducer = persistReducer(persistConfig, rootReducer); + export const store = configureStore({ - reducer: rootReducer, + reducer: persistedReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: { + ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], + }, + }), }); + +export type RootState = { + admin: AdminState; +}; +export type AppDispatch = typeof store.dispatch; diff --git a/resources/js/admin/store/slices/adminSlice.tsx b/resources/js/admin/store/slices/adminSlice.tsx index 8457e3a..f2a6349 100644 --- a/resources/js/admin/store/slices/adminSlice.tsx +++ b/resources/js/admin/store/slices/adminSlice.tsx @@ -1,9 +1,9 @@ import { createSlice } from '@reduxjs/toolkit'; import { Admin } from '../../inrterfaces/admin'; -export type AdminState = Admin; +export type AdminState = Admin | null; -const initialState: AdminState | null = null; +const initialState: AdminState = null; export const adminSlice = createSlice({ name: 'admin', @@ -14,8 +14,10 @@ export const adminSlice = createSlice({ return state; }, - logout: () => { - //; + logout: (state) => { + state = null; + + return state; }, }, }); diff --git a/resources/js/shared/assets/css/index.css b/resources/js/shared/assets/css/index.css index 7c6faad..f8f43b7 100644 --- a/resources/js/shared/assets/css/index.css +++ b/resources/js/shared/assets/css/index.css @@ -1,7 +1,13 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); + @tailwind base; @tailwind components; @tailwind utilities; +body { + font-family: 'Roboto', sans-serif; +} + .fade-in { opacity: 1; animation-name: fadeIn; @@ -10,6 +16,11 @@ animation-duration: 1s; } +.icon-spin { + -webkit-animation: icon-spin 2s infinite linear; + animation: icon-spin 2s infinite linear; +} + @keyframes fadeIn { 0% { opacity: 0; @@ -27,3 +38,25 @@ opacity: 1; } } + +@-webkit-keyframes icon-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes icon-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} diff --git a/resources/js/shared/constants/index.tsx b/resources/js/shared/constants/index.tsx index c1bfd03..834d665 100644 --- a/resources/js/shared/constants/index.tsx +++ b/resources/js/shared/constants/index.tsx @@ -1,13 +1,16 @@ import { ConfigProviderProps } from 'antd/es/config-provider'; -export const theme = { - colorPrimary: '#1DA74D', +export const config = { + appName: 'Pandora', + themeColor: '#1DA74D', + helpLink: 'https://github.com/arifszn/pandora/issues/new', }; export const antdConfig: ConfigProviderProps = { theme: { token: { - colorPrimary: theme.colorPrimary, + colorPrimary: config.themeColor, + fontFamily: 'Roboto', }, }, }; diff --git a/resources/js/shared/utils/http.tsx b/resources/js/shared/utils/http.tsx new file mode 100644 index 0000000..97d9010 --- /dev/null +++ b/resources/js/shared/utils/http.tsx @@ -0,0 +1,42 @@ +import { Store } from '@reduxjs/toolkit'; +import axios from 'axios'; +import { RootState } from '../../admin/store'; +import { logout } from '../../admin/store/slices/adminSlice'; + +let store: Store; + +export const injectStore = (_store: Store) => { + store = _store; +}; + +export const defaultHttp = axios.create(); +const http = axios.create(); + +http.interceptors.request.use( + (config) => { + const state: RootState = store.getState(); + const apiToken = state.admin?.token; + + if (apiToken) { + config.headers.Authorization = `Bearer ${apiToken}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +http.interceptors.response.use( + (response) => { + return response; + }, + (error) => { + if (error.response.status === 401) { + store.dispatch(logout()); + } + return Promise.reject(error); + } +); + +export default http;