forked from VinokurovVE/tests
DashboardLayout changes, refactoring, useSWR
This commit is contained in:
273
frontend_reactjs/package-lock.json
generated
273
frontend_reactjs/package-lock.json
generated
@ -13,7 +13,9 @@
|
|||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/material": "^5.15.20",
|
||||||
|
"@mui/x-charts": "^7.7.1",
|
||||||
"@mui/x-data-grid": "^7.7.1",
|
"@mui/x-data-grid": "^7.7.1",
|
||||||
|
"@tanstack/react-query": "^5.45.1",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
@ -21,6 +23,7 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.52.0",
|
"react-hook-form": "^7.52.0",
|
||||||
"react-router-dom": "^6.23.1",
|
"react-router-dom": "^6.23.1",
|
||||||
|
"swr": "^2.2.5",
|
||||||
"zod": "^3.23.8",
|
"zod": "^3.23.8",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
@ -2886,6 +2889,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-charts": {
|
||||||
|
"version": "7.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.7.1.tgz",
|
||||||
|
"integrity": "sha512-qUvkGGxBukHXLUqoTUs2rJZz1t+gK0P+bZzQWFbxN93XQyAhrjrOU8VN0x+G7nO3qcXrC2P6s0nevms7ZRSDrA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.24.7",
|
||||||
|
"@mui/base": "^5.0.0-beta.40",
|
||||||
|
"@mui/system": "^5.15.20",
|
||||||
|
"@mui/utils": "^5.15.20",
|
||||||
|
"@react-spring/rafz": "^9.7.3",
|
||||||
|
"@react-spring/web": "^9.7.3",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"d3-color": "^3.1.0",
|
||||||
|
"d3-delaunay": "^6.0.4",
|
||||||
|
"d3-interpolate": "^3.0.1",
|
||||||
|
"d3-scale": "^4.0.2",
|
||||||
|
"d3-shape": "^3.2.0",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.9.0",
|
||||||
|
"@emotion/styled": "^11.8.1",
|
||||||
|
"@mui/material": "^5.15.14",
|
||||||
|
"react": "^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/x-data-grid": {
|
"node_modules/@mui/x-data-grid": {
|
||||||
"version": "7.7.1",
|
"version": "7.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.7.1.tgz",
|
||||||
@ -2965,6 +3006,71 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-spring/animated": {
|
||||||
|
"version": "9.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz",
|
||||||
|
"integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-spring/shared": "~9.7.3",
|
||||||
|
"@react-spring/types": "~9.7.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-spring/core": {
|
||||||
|
"version": "9.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz",
|
||||||
|
"integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-spring/animated": "~9.7.3",
|
||||||
|
"@react-spring/shared": "~9.7.3",
|
||||||
|
"@react-spring/types": "~9.7.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/react-spring/donate"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-spring/rafz": {
|
||||||
|
"version": "9.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz",
|
||||||
|
"integrity": "sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@react-spring/shared": {
|
||||||
|
"version": "9.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz",
|
||||||
|
"integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-spring/types": "~9.7.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-spring/types": {
|
||||||
|
"version": "9.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz",
|
||||||
|
"integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw=="
|
||||||
|
},
|
||||||
|
"node_modules/@react-spring/web": {
|
||||||
|
"version": "9.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz",
|
||||||
|
"integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-spring/animated": "~9.7.3",
|
||||||
|
"@react-spring/core": "~9.7.3",
|
||||||
|
"@react-spring/shared": "~9.7.3",
|
||||||
|
"@react-spring/types": "~9.7.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@remix-run/router": {
|
"node_modules/@remix-run/router": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
|
||||||
@ -3475,6 +3581,30 @@
|
|||||||
"@swc/counter": "^0.1.3"
|
"@swc/counter": "^0.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/query-core": {
|
||||||
|
"version": "5.45.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.45.0.tgz",
|
||||||
|
"integrity": "sha512-RVfIZQmFUTdjhSAAblvueimfngYyfN6HlwaJUPK71PKd7yi43Vs1S/rdimmZedPWX/WGppcq/U1HOj7O7FwYxw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-query": {
|
||||||
|
"version": "5.45.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.45.1.tgz",
|
||||||
|
"integrity": "sha512-mYYfJujKg2kxmkRRjA6nn4YKG3ITsKuH22f1kteJ5IuVQqgKUgbaSQfYwVP0gBS05mhwxO03HVpD0t7BMN7WOA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-core": "5.45.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||||
@ -4209,6 +4339,11 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/client-only": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
|
||||||
|
},
|
||||||
"node_modules/clsx": {
|
"node_modules/clsx": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
@ -4351,6 +4486,111 @@
|
|||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/d3-array": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||||
|
"dependencies": {
|
||||||
|
"internmap": "1 - 2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-color": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-delaunay": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||||
|
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
||||||
|
"dependencies": {
|
||||||
|
"delaunator": "5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-format": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-interpolate": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-color": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-path": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-scale": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "2.10.0 - 3",
|
||||||
|
"d3-format": "1 - 3",
|
||||||
|
"d3-interpolate": "1.2.0 - 3",
|
||||||
|
"d3-time": "2.1.1 - 3",
|
||||||
|
"d3-time-format": "2 - 4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-shape": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-path": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-time": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "2 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-time-format": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-time": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/data-view-buffer": {
|
"node_modules/data-view-buffer": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
|
||||||
@ -4467,6 +4707,14 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delaunator": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||||
|
"dependencies": {
|
||||||
|
"robust-predicates": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@ -5556,6 +5804,14 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/internmap": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-array-buffer": {
|
"node_modules/is-array-buffer": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
|
||||||
@ -7016,6 +7272,11 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/robust-predicates": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
|
||||||
@ -7570,6 +7831,18 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/swr": {
|
||||||
|
"version": "2.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz",
|
||||||
|
"integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==",
|
||||||
|
"dependencies": {
|
||||||
|
"client-only": "^0.0.1",
|
||||||
|
"use-sync-external-store": "^1.2.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.4",
|
"version": "3.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz",
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/material": "^5.15.20",
|
||||||
|
"@mui/x-charts": "^7.7.1",
|
||||||
"@mui/x-data-grid": "^7.7.1",
|
"@mui/x-data-grid": "^7.7.1",
|
||||||
|
"@tanstack/react-query": "^5.45.1",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
@ -23,6 +25,7 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.52.0",
|
"react-hook-form": "^7.52.0",
|
||||||
"react-router-dom": "^6.23.1",
|
"react-router-dom": "^6.23.1",
|
||||||
|
"swr": "^2.2.5",
|
||||||
"zod": "^3.23.8",
|
"zod": "^3.23.8",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
|
96
frontend_reactjs/src/components/AccountMenu.tsx
Normal file
96
frontend_reactjs/src/components/AccountMenu.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
import PersonAdd from '@mui/icons-material/PersonAdd';
|
||||||
|
import Settings from '@mui/icons-material/Settings';
|
||||||
|
import Logout from '@mui/icons-material/Logout';
|
||||||
|
|
||||||
|
export default function AccountMenu() {
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', textAlign: 'center' }}>
|
||||||
|
<Tooltip title="Account settings">
|
||||||
|
<IconButton
|
||||||
|
onClick={handleClick}
|
||||||
|
size="small"
|
||||||
|
sx={{ ml: 2 }}
|
||||||
|
aria-controls={open ? 'account-menu' : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={open ? 'true' : undefined}
|
||||||
|
>
|
||||||
|
<Avatar sx={{ width: 32, height: 32 }}></Avatar>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
id="account-menu"
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
onClick={handleClose}
|
||||||
|
PaperProps={{
|
||||||
|
elevation: 0,
|
||||||
|
sx: {
|
||||||
|
overflow: 'visible',
|
||||||
|
filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
|
||||||
|
mt: 1.5,
|
||||||
|
'& .MuiAvatar-root': {
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
ml: -0.5,
|
||||||
|
mr: 1,
|
||||||
|
},
|
||||||
|
'&::before': {
|
||||||
|
content: '""',
|
||||||
|
display: 'block',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
right: 14,
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
transform: 'translateY(-50%) rotate(45deg)',
|
||||||
|
zIndex: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||||
|
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={handleClose}>
|
||||||
|
<Avatar /> Профиль
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<MenuItem onClick={handleClose}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Settings fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
Настройки
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleClose}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Logout fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
Выход
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
@ -6,9 +6,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function DataTable(props: Props) {
|
export default function DataTable(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ flexGrow: 1, height: "100%", width: '100%' }}>
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
style={{ width: "100%", height: "100%" }}
|
||||||
rows={props.rows}
|
rows={props.rows}
|
||||||
columns={props.columns}
|
columns={props.columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
@ -18,7 +19,9 @@ export default function DataTable(props: Props) {
|
|||||||
}}
|
}}
|
||||||
pageSizeOptions={[10, 20, 50, 100]}
|
pageSizeOptions={[10, 20, 50, 100]}
|
||||||
checkboxSelection
|
checkboxSelection
|
||||||
|
|
||||||
|
disableRowSelectionOnClick
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
11
frontend_reactjs/src/components/modals/CreateRoleModal.tsx
Normal file
11
frontend_reactjs/src/components/modals/CreateRoleModal.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const CreateRoleModal = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateRoleModal
|
141
frontend_reactjs/src/components/modals/CreateUserModal.tsx
Normal file
141
frontend_reactjs/src/components/modals/CreateUserModal.tsx
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import { Box, Button, Modal, TextField, Typography } from '@mui/material'
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
|
import { ApiResponse } from '../../interfaces/auth';
|
||||||
|
import UserService from '../../services/UserService';
|
||||||
|
import { CreateUserFormData } from '../../interfaces/user';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
open: boolean;
|
||||||
|
setOpen: (state: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
position: 'absolute' as 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: 400,
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
boxShadow: 24,
|
||||||
|
borderRadius: 2,
|
||||||
|
p: 4,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "8px"
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CreateUserModal({
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
}: Props) {
|
||||||
|
const { register, handleSubmit, formState: { errors } } = useForm<CreateUserFormData>({
|
||||||
|
defaultValues: {
|
||||||
|
email: '',
|
||||||
|
login: '',
|
||||||
|
phone: '',
|
||||||
|
name: '',
|
||||||
|
surname: '',
|
||||||
|
is_active: true,
|
||||||
|
password: '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit: SubmitHandler<CreateUserFormData> = async (data) => {
|
||||||
|
try {
|
||||||
|
const response: AxiosResponse<ApiResponse> = await UserService.createUser(data)
|
||||||
|
console.log(response.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
aria-labelledby="modal-modal-title"
|
||||||
|
aria-describedby="modal-modal-description"
|
||||||
|
>
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
|
||||||
|
<Box sx={style}>
|
||||||
|
<Typography variant="h6" component="h6" gutterBottom>
|
||||||
|
Создание пользователя
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
label="Email"
|
||||||
|
required
|
||||||
|
{...register('email', { required: 'Email обязателен' })}
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={errors.email?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
label="Логин"
|
||||||
|
required
|
||||||
|
{...register('login', { required: 'Логин обязателен' })}
|
||||||
|
error={!!errors.login}
|
||||||
|
helperText={errors.login?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
label="Телефон"
|
||||||
|
{...register('phone')}
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={errors.phone?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
label="Имя"
|
||||||
|
{...register('name')}
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={errors.name?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
label="Фамилия"
|
||||||
|
{...register('surname')}
|
||||||
|
error={!!errors.surname}
|
||||||
|
helperText={errors.surname?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
type="password"
|
||||||
|
label="Пароль"
|
||||||
|
required
|
||||||
|
{...register('password', { required: 'Пароль обязателен' })}
|
||||||
|
error={!!errors.password}
|
||||||
|
helperText={errors.password?.message}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "8px"
|
||||||
|
}}>
|
||||||
|
<Button type="submit" variant="contained" color="primary">
|
||||||
|
Добавить пользователя
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
23
frontend_reactjs/src/hooks/swrHooks.ts
Normal file
23
frontend_reactjs/src/hooks/swrHooks.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import useSWR from "swr";
|
||||||
|
import RoleService from "../services/RoleService";
|
||||||
|
import UserService from "../services/UserService";
|
||||||
|
|
||||||
|
export function useRoles() {
|
||||||
|
const { data, error, isLoading } = useSWR(`/auth/roles`, RoleService.getRoles)
|
||||||
|
|
||||||
|
return {
|
||||||
|
roles: data?.data,
|
||||||
|
isLoading,
|
||||||
|
isError: error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUsers() {
|
||||||
|
const { data, error, isLoading } = useSWR(`/auth/user`, UserService.getUsers)
|
||||||
|
|
||||||
|
return {
|
||||||
|
users: data?.data,
|
||||||
|
isLoading,
|
||||||
|
isError: error
|
||||||
|
}
|
||||||
|
}
|
@ -16,23 +16,25 @@ export interface UserCreds extends User {
|
|||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface Role {
|
||||||
|
name: string;
|
||||||
|
description?: string | null;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoleCreate {
|
||||||
|
name: string;
|
||||||
|
description?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AuthState {
|
export interface AuthState {
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
token: string | null;
|
token: string | null;
|
||||||
userData: UserData | {};
|
userData: UserData | {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SignUpFormData {
|
export interface LoginFormData {
|
||||||
email: string;
|
|
||||||
login: string;
|
|
||||||
phone: string;
|
|
||||||
name: string;
|
|
||||||
surname: string;
|
|
||||||
is_active: boolean;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SignInFormData {
|
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
grant_type: string;
|
grant_type: string;
|
||||||
|
9
frontend_reactjs/src/interfaces/user.ts
Normal file
9
frontend_reactjs/src/interfaces/user.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export interface CreateUserFormData {
|
||||||
|
email: string;
|
||||||
|
login: string;
|
||||||
|
phone: string;
|
||||||
|
name: string;
|
||||||
|
surname: string;
|
||||||
|
is_active: boolean;
|
||||||
|
password: string;
|
||||||
|
}
|
@ -1,53 +1,76 @@
|
|||||||
// Layout for dashboard with responsive drawer
|
|
||||||
|
|
||||||
import { Link, NavLink, Navigate, Outlet, useLocation, useNavigate } from "react-router-dom"
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import AppBar from '@mui/material/AppBar';
|
import { styled, createTheme, ThemeProvider } from '@mui/material/styles';
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
import Divider from '@mui/material/Divider';
|
import MuiDrawer from '@mui/material/Drawer';
|
||||||
import Drawer from '@mui/material/Drawer';
|
import Box from '@mui/material/Box';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
|
||||||
import List from '@mui/material/List';
|
|
||||||
import ListItem from '@mui/material/ListItem';
|
|
||||||
import ListItemButton from '@mui/material/ListItemButton';
|
|
||||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
|
||||||
import ListItemText from '@mui/material/ListItemText';
|
|
||||||
import MenuIcon from '@mui/icons-material/Menu';
|
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
|
import List from '@mui/material/List';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Api, ExitToApp, Home, People, Settings, Shield } from "@mui/icons-material";
|
import Divider from '@mui/material/Divider';
|
||||||
import { getUserData, useAuthStore } from "../store/auth";
|
import IconButton from '@mui/material/IconButton';
|
||||||
import { UserData } from "../interfaces/auth";
|
import Badge from '@mui/material/Badge';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
|
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
||||||
|
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||||
|
import { AccountCircle, Api, ExitToApp, Home, People, Settings, Shield } from '@mui/icons-material';
|
||||||
|
import { ListItem, ListItemButton, ListItemIcon, ListItemText, colors } from '@mui/material';
|
||||||
|
import { Outlet, useNavigate } from 'react-router-dom';
|
||||||
|
import { UserData } from '../interfaces/auth';
|
||||||
|
import { getUserData, useAuthStore } from '../store/auth';
|
||||||
|
import { Theme, useTheme } from '@emotion/react';
|
||||||
|
import AccountMenu from '../components/AccountMenu';
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth: number = 240;
|
||||||
|
|
||||||
export default function DashboardLayout() {
|
interface AppBarProps extends MuiAppBarProps {
|
||||||
const authStore = useAuthStore();
|
open?: boolean;
|
||||||
const [userData, setUserData] = React.useState<UserData>();
|
|
||||||
|
|
||||||
const location = useLocation()
|
|
||||||
|
|
||||||
const navigate = useNavigate()
|
|
||||||
|
|
||||||
//const { window } = props;
|
|
||||||
const [mobileOpen, setMobileOpen] = React.useState(false);
|
|
||||||
const [isClosing, setIsClosing] = React.useState(false);
|
|
||||||
|
|
||||||
const handleDrawerClose = () => {
|
|
||||||
setIsClosing(true);
|
|
||||||
setMobileOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrawerTransitionEnd = () => {
|
|
||||||
setIsClosing(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrawerToggle = () => {
|
|
||||||
if (!isClosing) {
|
|
||||||
setMobileOpen(!mobileOpen);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const AppBar = styled(MuiAppBar, {
|
||||||
|
shouldForwardProp: (prop) => prop !== 'open',
|
||||||
|
})<AppBarProps>(({ theme, open }) => ({
|
||||||
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
|
transition: theme.transitions.create(['width', 'margin'], {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
|
}),
|
||||||
|
...(open && {
|
||||||
|
marginLeft: drawerWidth,
|
||||||
|
width: `calc(100% - ${drawerWidth}px)`,
|
||||||
|
transition: theme.transitions.create(['width', 'margin'], {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
|
||||||
|
({ theme, open }) => ({
|
||||||
|
'& .MuiDrawer-paper': {
|
||||||
|
position: 'relative',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
width: drawerWidth,
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
...(!open && {
|
||||||
|
overflowX: 'hidden',
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
|
}),
|
||||||
|
width: theme.spacing(7),
|
||||||
|
[theme.breakpoints.up('sm')]: {
|
||||||
|
width: theme.spacing(9),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const pages = [
|
const pages = [
|
||||||
{
|
{
|
||||||
@ -72,70 +95,27 @@ export default function DashboardLayout() {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const misc = [
|
export default function DashboardLayout() {
|
||||||
{
|
const theme = useTheme()
|
||||||
label: "Настройки",
|
const innerTheme = createTheme(theme)
|
||||||
path: "/settings",
|
|
||||||
icon: <Settings />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Выход",
|
|
||||||
path: "/signOut",
|
|
||||||
icon: <ExitToApp />
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const drawer = (
|
const [open, setOpen] = React.useState(true);
|
||||||
<div>
|
const toggleDrawer = () => {
|
||||||
<Toolbar>
|
setOpen(!open);
|
||||||
<Box>
|
};
|
||||||
<Typography>{userData?.name} {userData?.surname}</Typography>
|
|
||||||
<Divider />
|
|
||||||
<Typography variant="caption">{userData?.login}</Typography>
|
|
||||||
</Box>
|
|
||||||
</Toolbar>
|
|
||||||
|
|
||||||
<Divider />
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
<List>
|
const navigate = useNavigate()
|
||||||
{pages.map((item, index) => (
|
|
||||||
<ListItem key={index} disablePadding>
|
|
||||||
<ListItemButton
|
|
||||||
onClick={() => {
|
|
||||||
navigate(item.path)
|
|
||||||
}}
|
|
||||||
selected={location.pathname === item.path}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
{item.icon}
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={item.label} />
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<Divider />
|
const getPageTitle = () => {
|
||||||
|
const currentPath = location.pathname;
|
||||||
|
const allPages = [...pages];
|
||||||
|
const currentPage = allPages.find(page => page.path === currentPath);
|
||||||
|
return currentPage ? currentPage.label : "Dashboard";
|
||||||
|
};
|
||||||
|
|
||||||
<List>
|
const [userData, setUserData] = React.useState<UserData>();
|
||||||
{misc.map((item, index) => (
|
|
||||||
<ListItem key={index} disablePadding>
|
|
||||||
<ListItemButton
|
|
||||||
onClick={() => {
|
|
||||||
navigate(item.path)
|
|
||||||
}}
|
|
||||||
selected={location.pathname === item.path}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
{item.icon}
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={item.label} />
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (authStore) {
|
if (authStore) {
|
||||||
@ -146,87 +126,112 @@ export default function DashboardLayout() {
|
|||||||
}
|
}
|
||||||
}, [authStore])
|
}, [authStore])
|
||||||
|
|
||||||
const getPageTitle = () => {
|
|
||||||
const currentPath = location.pathname;
|
|
||||||
const allPages = [...pages, ...misc];
|
|
||||||
const currentPage = allPages.find(page => page.path === currentPath);
|
|
||||||
return currentPage ? currentPage.label : "Dashboard";
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<ThemeProvider theme={innerTheme}>
|
||||||
display: 'flex',
|
<Box sx={{ display: 'flex' }}>
|
||||||
flexGrow: 1,
|
|
||||||
height: "100vh"
|
|
||||||
}}>
|
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<AppBar
|
<AppBar position="absolute" open={open}>
|
||||||
position="fixed"
|
<Toolbar
|
||||||
sx={{
|
sx={{
|
||||||
width: { sm: `calc(100% - ${drawerWidth}px)` },
|
pr: '24px', // keep right padding when drawer closed
|
||||||
ml: { sm: `${drawerWidth}px` },
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toolbar>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
|
edge="start"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
aria-label="open drawer"
|
aria-label="open drawer"
|
||||||
edge="start"
|
onClick={toggleDrawer}
|
||||||
onClick={handleDrawerToggle}
|
sx={{
|
||||||
sx={{ mr: 2, display: { sm: 'none' } }}
|
marginRight: '36px',
|
||||||
|
...(open && { display: 'none' }),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" noWrap component="div">
|
|
||||||
|
<Typography
|
||||||
|
component="h1"
|
||||||
|
variant="h6"
|
||||||
|
color="inherit"
|
||||||
|
noWrap
|
||||||
|
sx={{ flexGrow: 1 }}
|
||||||
|
>
|
||||||
{getPageTitle()}
|
{getPageTitle()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
<Box sx={{ display: "flex", gap: "8px" }}>
|
||||||
|
<Box>
|
||||||
|
<Typography>{userData?.name} {userData?.surname}</Typography>
|
||||||
|
<Divider />
|
||||||
|
<Typography variant="caption">{userData?.login}</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* <IconButton color="inherit">
|
||||||
|
<Badge badgeContent={0} color="secondary">
|
||||||
|
<AccountCircle />
|
||||||
|
</Badge>
|
||||||
|
</IconButton> */}
|
||||||
|
|
||||||
|
<AccountMenu/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
<Drawer variant="permanent" open={open}>
|
||||||
<Box
|
<Toolbar
|
||||||
component="nav"
|
|
||||||
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
|
|
||||||
aria-label="mailbox folders"
|
|
||||||
>
|
|
||||||
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
|
|
||||||
<Drawer
|
|
||||||
variant="temporary"
|
|
||||||
open={mobileOpen}
|
|
||||||
onTransitionEnd={handleDrawerTransitionEnd}
|
|
||||||
onClose={handleDrawerClose}
|
|
||||||
ModalProps={{
|
|
||||||
keepMounted: true, // Better open performance on mobile.
|
|
||||||
}}
|
|
||||||
sx={{
|
sx={{
|
||||||
display: { xs: 'block', sm: 'none' },
|
display: 'flex',
|
||||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
px: [1],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{drawer}
|
<Box display="flex" justifyContent={'space-between'} width={"100%"}>
|
||||||
</Drawer>
|
<IconButton onClick={toggleDrawer}>
|
||||||
<Drawer
|
<ChevronLeftIcon />
|
||||||
variant="permanent"
|
</IconButton>
|
||||||
sx={{
|
|
||||||
display: { xs: 'none', sm: 'block' },
|
|
||||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
|
||||||
}}
|
|
||||||
open
|
|
||||||
>
|
|
||||||
{drawer}
|
|
||||||
</Drawer>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
</Toolbar>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<List component="nav">
|
||||||
|
{pages.map((item, index) => (
|
||||||
|
<ListItem key={index} disablePadding>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() => {
|
||||||
|
navigate(item.path)
|
||||||
|
}}
|
||||||
|
style={{ background: location.pathname === item.path ? innerTheme.palette.action.selected : "transparent" }}
|
||||||
|
selected={location.pathname === item.path}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
{item.icon}
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary={item.label} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Drawer>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
component="main"
|
component="main"
|
||||||
sx={{
|
sx={{
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.mode === 'light'
|
||||||
|
? theme.palette.grey[100]
|
||||||
|
: theme.palette.grey[900],
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
p: 3,
|
maxHeight: "100vh",
|
||||||
width: { sm: `calc(100% - ${drawerWidth}px)` }
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
|
<Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
252
frontend_reactjs/src/layouts/DashboardLayoutResponsive.tsx
Normal file
252
frontend_reactjs/src/layouts/DashboardLayoutResponsive.tsx
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// Layout for dashboard with responsive drawer
|
||||||
|
|
||||||
|
import { Link, NavLink, Navigate, Outlet, useLocation, useNavigate } from "react-router-dom"
|
||||||
|
import * as React from 'react';
|
||||||
|
import AppBar from '@mui/material/AppBar';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import Drawer from '@mui/material/Drawer';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import List from '@mui/material/List';
|
||||||
|
import ListItem from '@mui/material/ListItem';
|
||||||
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { Api, ExitToApp, Home, People, Settings, Shield } from "@mui/icons-material";
|
||||||
|
import { getUserData, useAuthStore } from "../store/auth";
|
||||||
|
import { UserData } from "../interfaces/auth";
|
||||||
|
|
||||||
|
const drawerWidth = 240;
|
||||||
|
|
||||||
|
export default function DashboardLayoutResponsive() {
|
||||||
|
const [open, setOpen] = React.useState(true);
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const [userData, setUserData] = React.useState<UserData>();
|
||||||
|
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
//const { window } = props;
|
||||||
|
const [mobileOpen, setMobileOpen] = React.useState(false);
|
||||||
|
const [isClosing, setIsClosing] = React.useState(false);
|
||||||
|
|
||||||
|
const handleDrawerClose = () => {
|
||||||
|
setIsClosing(true);
|
||||||
|
setMobileOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrawerTransitionEnd = () => {
|
||||||
|
setIsClosing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrawerToggle = () => {
|
||||||
|
if (!isClosing) {
|
||||||
|
setMobileOpen(!mobileOpen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const pages = [
|
||||||
|
{
|
||||||
|
label: "Главная",
|
||||||
|
path: "/",
|
||||||
|
icon: <Home />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Пользователи",
|
||||||
|
path: "/user",
|
||||||
|
icon: <People />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Роли",
|
||||||
|
path: "/role",
|
||||||
|
icon: <Shield />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "API Test",
|
||||||
|
path: "/api-test",
|
||||||
|
icon: <Api />
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const misc = [
|
||||||
|
{
|
||||||
|
label: "Настройки",
|
||||||
|
path: "/settings",
|
||||||
|
icon: <Settings />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Выход",
|
||||||
|
path: "/signOut",
|
||||||
|
icon: <ExitToApp />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const getPageTitle = () => {
|
||||||
|
const currentPath = location.pathname;
|
||||||
|
const allPages = [...pages, ...misc];
|
||||||
|
const currentPage = allPages.find(page => page.path === currentPath);
|
||||||
|
return currentPage ? currentPage.label : "Dashboard";
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleDrawer = () => {
|
||||||
|
setOpen(!open);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawer = (
|
||||||
|
<div>
|
||||||
|
<Toolbar>
|
||||||
|
<Box display="flex" justifyContent={'space-between'} width={"100%"}>
|
||||||
|
<Box>
|
||||||
|
<Typography>{userData?.name} {userData?.surname}</Typography>
|
||||||
|
<Divider />
|
||||||
|
<Typography variant="caption">{userData?.login}</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
aria-label="open drawer"
|
||||||
|
onClick={toggleDrawer}
|
||||||
|
sx={{
|
||||||
|
...(open && { display: 'none' }),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Toolbar>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<List>
|
||||||
|
{pages.map((item, index) => (
|
||||||
|
<ListItem key={index} disablePadding>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() => {
|
||||||
|
navigate(item.path)
|
||||||
|
}}
|
||||||
|
selected={location.pathname === item.path}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
{item.icon}
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary={item.label} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<List>
|
||||||
|
{misc.map((item, index) => (
|
||||||
|
<ListItem key={index} disablePadding>
|
||||||
|
<ListItemButton
|
||||||
|
onClick={() => {
|
||||||
|
navigate(item.path)
|
||||||
|
}}
|
||||||
|
selected={location.pathname === item.path}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
{item.icon}
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary={item.label} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (authStore) {
|
||||||
|
const stored = getUserData()
|
||||||
|
if (stored) {
|
||||||
|
setUserData(stored)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [authStore])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexGrow: 1,
|
||||||
|
height: "100vh"
|
||||||
|
}}>
|
||||||
|
<CssBaseline />
|
||||||
|
<AppBar
|
||||||
|
position="fixed"
|
||||||
|
sx={{
|
||||||
|
width: { sm: `calc(100% - ${drawerWidth}px)` },
|
||||||
|
ml: { sm: `${drawerWidth}px` },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton
|
||||||
|
color="inherit"
|
||||||
|
aria-label="open drawer"
|
||||||
|
edge="start"
|
||||||
|
onClick={handleDrawerToggle}
|
||||||
|
sx={{ mr: 2, display: { sm: 'none' } }}
|
||||||
|
>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="h6" noWrap component="div">
|
||||||
|
{getPageTitle()}
|
||||||
|
</Typography>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
component="nav"
|
||||||
|
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
|
||||||
|
aria-label="mailbox folders"
|
||||||
|
>
|
||||||
|
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
|
||||||
|
<Drawer
|
||||||
|
variant="temporary"
|
||||||
|
open={mobileOpen}
|
||||||
|
onTransitionEnd={handleDrawerTransitionEnd}
|
||||||
|
onClose={handleDrawerClose}
|
||||||
|
ModalProps={{
|
||||||
|
keepMounted: true, // Better open performance on mobile.
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
display: { xs: 'block', sm: 'none' },
|
||||||
|
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{drawer}
|
||||||
|
</Drawer>
|
||||||
|
<Drawer
|
||||||
|
variant="permanent"
|
||||||
|
sx={{
|
||||||
|
display: { xs: 'none', sm: 'block' },
|
||||||
|
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||||
|
}}
|
||||||
|
open
|
||||||
|
>
|
||||||
|
{drawer}
|
||||||
|
</Drawer>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
p: 3,
|
||||||
|
width: { sm: `calc(100% - ${drawerWidth}px)` }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Toolbar />
|
||||||
|
<Outlet />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -6,6 +6,9 @@ import { registerSW } from 'virtual:pwa-register'
|
|||||||
import { ThemeProvider } from '@emotion/react'
|
import { ThemeProvider } from '@emotion/react'
|
||||||
import { createTheme } from '@mui/material'
|
import { createTheme } from '@mui/material'
|
||||||
import { ruRU } from '@mui/material/locale'
|
import { ruRU } from '@mui/material/locale'
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const theme = createTheme(
|
const theme = createTheme(
|
||||||
{
|
{
|
||||||
@ -30,7 +33,9 @@ const updateSW = registerSW({
|
|||||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
<App />
|
<App />
|
||||||
|
</QueryClientProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
)
|
)
|
||||||
|
@ -1,43 +1,32 @@
|
|||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { Button } from "@mui/material"
|
import { Button, Typography } from "@mui/material"
|
||||||
import DataTable from "../components/DataTable"
|
|
||||||
import { GridColDef } from "@mui/x-data-grid"
|
|
||||||
import UserService from "../services/UserService"
|
|
||||||
|
|
||||||
export default function ApiTest() {
|
export default function ApiTest() {
|
||||||
const [users, setUsers] = useState<any>(null)
|
const [data, setData] = useState<any>(null)
|
||||||
|
|
||||||
const getUsers = async () => {
|
function getRealtimeData(data: any) {
|
||||||
await UserService.getUsers().then(response => {
|
setData(data)
|
||||||
setUsers(response.data)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns: GridColDef[] = [
|
useEffect(() => {
|
||||||
{ field: 'id', headerName: 'ID', type: "number", width: 70 },
|
const sse = new EventSource(`${import.meta.env.VITE_API_SSE_URL}/stream`)
|
||||||
{ field: 'email', headerName: 'Email', width: 130 },
|
|
||||||
{ field: 'login', headerName: 'Логин', width: 130 },
|
sse.onmessage = e => getRealtimeData(e.data)
|
||||||
{ field: 'phone', headerName: 'Телефон', width: 90 },
|
|
||||||
{ field: 'name', headerName: 'Имя', width: 90 },
|
sse.onerror = () => {
|
||||||
{ field: 'surname', headerName: 'Фамилия', width: 90 },
|
sse.close()
|
||||||
{ field: 'is_active', headerName: 'Активен', type: "boolean", width: 90 },
|
}
|
||||||
{
|
|
||||||
field: 'role_id',
|
return () => {
|
||||||
headerName: 'Роль',
|
sse.close()
|
||||||
valueGetter: (value, row) => `${value}`,
|
}
|
||||||
width: 90
|
}, [])
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={() => getUsers()}>
|
<Typography>
|
||||||
Get users
|
{JSON.stringify(data)}
|
||||||
</Button>
|
</Typography>
|
||||||
|
|
||||||
{users &&
|
|
||||||
<DataTable rows={users} columns={columns}/>
|
|
||||||
}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -32,7 +32,8 @@ function Roles() {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
gap: '16px'
|
gap: '16px',
|
||||||
|
flexGrow: 1
|
||||||
}}>
|
}}>
|
||||||
<Button onClick={() => console.log("TODO: Add")}>
|
<Button onClick={() => console.log("TODO: Add")}>
|
||||||
Добавить роль
|
Добавить роль
|
||||||
|
@ -1,43 +1,62 @@
|
|||||||
import { useEffect, useState } from "react"
|
import { Box, Button, CircularProgress } from "@mui/material"
|
||||||
import { Box, Button } from "@mui/material"
|
|
||||||
import DataTable from "../components/DataTable"
|
import DataTable from "../components/DataTable"
|
||||||
import { GridColDef } from "@mui/x-data-grid"
|
import { GridColDef } from "@mui/x-data-grid"
|
||||||
import UserService from "../services/UserService"
|
import { useRoles, useUsers } from "../hooks/swrHooks"
|
||||||
|
import { Role } from "../interfaces/auth"
|
||||||
|
import { useState } from "react"
|
||||||
|
import CreateUserModal from "../components/modals/CreateUserModal"
|
||||||
|
|
||||||
export default function Users() {
|
export default function Users() {
|
||||||
const [users, setUsers] = useState<any>(null)
|
const { users, isError, isLoading } = useUsers()
|
||||||
|
|
||||||
const getUsers = async () => {
|
const { roles } = useRoles()
|
||||||
await UserService.getUsers().then(response => {
|
|
||||||
setUsers(response.data)
|
const [open, setOpen] = useState(false)
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns: GridColDef[] = [
|
const columns: GridColDef[] = [
|
||||||
{ field: 'id', headerName: 'ID', type: "number", width: 70 },
|
{ field: 'id', headerName: 'ID', type: "number", width: 70 },
|
||||||
{ field: 'email', headerName: 'Email', width: 130 },
|
{ field: 'email', headerName: 'Email', width: 130, editable: true},
|
||||||
{ field: 'login', headerName: 'Логин', width: 130 },
|
{ field: 'login', headerName: 'Логин', width: 130, editable: true },
|
||||||
{ field: 'phone', headerName: 'Телефон', width: 90 },
|
{ field: 'phone', headerName: 'Телефон', width: 90, editable: true },
|
||||||
{ field: 'name', headerName: 'Имя', width: 90 },
|
{ field: 'name', headerName: 'Имя', width: 90, editable: true },
|
||||||
{ field: 'surname', headerName: 'Фамилия', width: 90 },
|
{ field: 'surname', headerName: 'Фамилия', width: 90, editable: true },
|
||||||
{ field: 'is_active', headerName: 'Активен', type: "boolean", width: 90 },
|
{ field: 'is_active', headerName: 'Активен', type: "boolean", width: 90, editable: true},
|
||||||
{
|
{
|
||||||
field: 'role_id',
|
field: 'role_id',
|
||||||
headerName: 'Роль',
|
headerName: 'Роль',
|
||||||
valueGetter: (value, row) => `${value}`,
|
valueGetter: (value, row) => {
|
||||||
|
if (roles) {
|
||||||
|
const roleName = roles.find((role: Role) => role.id === value).name
|
||||||
|
return roleName
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
width: 90
|
width: 90
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (isError) return <div>Произошла ошибка при получении данных.</div>
|
||||||
|
if (isLoading) return <CircularProgress />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box sx={{
|
||||||
<Button onClick={() => getUsers()}>
|
display: "flex",
|
||||||
Get users
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
gap: "16px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button onClick={() => setOpen(true)}>
|
||||||
|
Добавить пользователя
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{users &&
|
<CreateUserModal
|
||||||
|
open={open}
|
||||||
|
setOpen={setOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
<DataTable rows={users} columns={columns} />
|
<DataTable rows={users} columns={columns} />
|
||||||
}
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,14 +1,14 @@
|
|||||||
import { useForm, SubmitHandler } from 'react-hook-form';
|
import { useForm, SubmitHandler } from 'react-hook-form';
|
||||||
import { TextField, Button, Container, Typography, Box } from '@mui/material';
|
import { TextField, Button, Container, Typography, Box } from '@mui/material';
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { SignInFormData, ApiResponse } from '../../interfaces/auth';
|
import { ApiResponse, LoginFormData } from '../../interfaces/auth';
|
||||||
import { login, setUserData } from '../../store/auth';
|
import { login, setUserData } from '../../store/auth';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import AuthService from '../../services/AuthService';
|
import AuthService from '../../services/AuthService';
|
||||||
import UserService from '../../services/UserService';
|
import UserService from '../../services/UserService';
|
||||||
|
|
||||||
const SignIn = () => {
|
const SignIn = () => {
|
||||||
const { register, handleSubmit, formState: { errors } } = useForm<SignInFormData>({
|
const { register, handleSubmit, formState: { errors } } = useForm<LoginFormData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
@ -21,10 +21,10 @@ const SignIn = () => {
|
|||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const onSubmit: SubmitHandler<SignInFormData> = async (data) => {
|
const onSubmit: SubmitHandler<LoginFormData> = async (data) => {
|
||||||
const formBody = new URLSearchParams();
|
const formBody = new URLSearchParams();
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
formBody.append(key, data[key as keyof SignInFormData] as string);
|
formBody.append(key, data[key as keyof LoginFormData] as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { useForm, SubmitHandler } from 'react-hook-form';
|
import { useForm, SubmitHandler } from 'react-hook-form';
|
||||||
import { TextField, Button, Container, Typography, Box } from '@mui/material';
|
import { TextField, Button, Container, Typography, Box } from '@mui/material';
|
||||||
import axios, { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { SignUpFormData, ApiResponse } from '../../interfaces/auth';
|
import { ApiResponse } from '../../interfaces/auth';
|
||||||
import axiosInstance from '../../http/axiosInstance';
|
|
||||||
import UserService from '../../services/UserService';
|
import UserService from '../../services/UserService';
|
||||||
|
import { CreateUserFormData } from '../../interfaces/user';
|
||||||
|
|
||||||
const SignUp = () => {
|
const SignUp = () => {
|
||||||
const { register, handleSubmit, formState: { errors } } = useForm<SignUpFormData>({
|
const { register, handleSubmit, formState: { errors } } = useForm<CreateUserFormData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
email: '',
|
email: '',
|
||||||
login: '',
|
login: '',
|
||||||
@ -19,7 +19,7 @@ const SignUp = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const onSubmit: SubmitHandler<SignUpFormData> = async (data) => {
|
const onSubmit: SubmitHandler<CreateUserFormData> = async (data) => {
|
||||||
try {
|
try {
|
||||||
const response: AxiosResponse<ApiResponse> = await UserService.createUser(data)
|
const response: AxiosResponse<ApiResponse> = await UserService.createUser(data)
|
||||||
console.log('Успешная регистрация:', response.data);
|
console.log('Успешная регистрация:', response.data);
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import axiosInstance from "../http/axiosInstance";
|
import axiosInstance from "../http/axiosInstance";
|
||||||
|
import { Role, RoleCreate } from "../interfaces/auth";
|
||||||
|
|
||||||
export default class RoleService {
|
export default class RoleService {
|
||||||
static async getRoles() {
|
static async getRoles() {
|
||||||
return await axiosInstance.get(`/auth/roles`)
|
return await axiosInstance.get(`/auth/roles`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async createRole(data: RoleCreate) {
|
||||||
|
return await axiosInstance.post(`/auth/roles/`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static async getRoleById(id: number) {
|
static async getRoleById(id: number) {
|
||||||
return await axiosInstance.get(`/auth/roles/${id}`)
|
return await axiosInstance.get(`/auth/roles/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static async deleteRole(id: number) {
|
||||||
|
// return await axiosInstance.delete(`/auth/roles/${id}`)
|
||||||
|
// }
|
||||||
}
|
}
|
@ -1022,7 +1022,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz"
|
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz"
|
||||||
integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
|
integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
|
||||||
|
|
||||||
"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.11.4", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0":
|
"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.11.4", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0", "@emotion/react@^11.9.0":
|
||||||
version "11.11.4"
|
version "11.11.4"
|
||||||
resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz"
|
resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz"
|
||||||
integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==
|
integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==
|
||||||
@ -1052,7 +1052,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz"
|
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz"
|
||||||
integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
|
integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
|
||||||
|
|
||||||
"@emotion/styled@^11.11.5", "@emotion/styled@^11.3.0":
|
"@emotion/styled@^11.11.5", "@emotion/styled@^11.3.0", "@emotion/styled@^11.8.1":
|
||||||
version "11.11.5"
|
version "11.11.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz"
|
resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz"
|
||||||
integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==
|
integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==
|
||||||
@ -1219,7 +1219,7 @@
|
|||||||
"@jridgewell/resolve-uri" "^3.1.0"
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
"@mui/base@5.0.0-beta.40":
|
"@mui/base@^5.0.0-beta.40", "@mui/base@5.0.0-beta.40":
|
||||||
version "5.0.0-beta.40"
|
version "5.0.0-beta.40"
|
||||||
resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz"
|
resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz"
|
||||||
integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==
|
integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==
|
||||||
@ -1310,6 +1310,25 @@
|
|||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^18.2.0"
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/x-charts@^7.7.1":
|
||||||
|
version "7.7.1"
|
||||||
|
resolved "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.7.1.tgz"
|
||||||
|
integrity sha512-qUvkGGxBukHXLUqoTUs2rJZz1t+gK0P+bZzQWFbxN93XQyAhrjrOU8VN0x+G7nO3qcXrC2P6s0nevms7ZRSDrA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.24.7"
|
||||||
|
"@mui/base" "^5.0.0-beta.40"
|
||||||
|
"@mui/system" "^5.15.20"
|
||||||
|
"@mui/utils" "^5.15.20"
|
||||||
|
"@react-spring/rafz" "^9.7.3"
|
||||||
|
"@react-spring/web" "^9.7.3"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
d3-color "^3.1.0"
|
||||||
|
d3-delaunay "^6.0.4"
|
||||||
|
d3-interpolate "^3.0.1"
|
||||||
|
d3-scale "^4.0.2"
|
||||||
|
d3-shape "^3.2.0"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/x-data-grid@^7.7.1":
|
"@mui/x-data-grid@^7.7.1":
|
||||||
version "7.7.1"
|
version "7.7.1"
|
||||||
resolved "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.7.1.tgz"
|
resolved "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.7.1.tgz"
|
||||||
@ -1353,6 +1372,50 @@
|
|||||||
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz"
|
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz"
|
||||||
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
||||||
|
|
||||||
|
"@react-spring/animated@~9.7.3":
|
||||||
|
version "9.7.3"
|
||||||
|
resolved "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz"
|
||||||
|
integrity sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==
|
||||||
|
dependencies:
|
||||||
|
"@react-spring/shared" "~9.7.3"
|
||||||
|
"@react-spring/types" "~9.7.3"
|
||||||
|
|
||||||
|
"@react-spring/core@~9.7.3":
|
||||||
|
version "9.7.3"
|
||||||
|
resolved "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz"
|
||||||
|
integrity sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==
|
||||||
|
dependencies:
|
||||||
|
"@react-spring/animated" "~9.7.3"
|
||||||
|
"@react-spring/shared" "~9.7.3"
|
||||||
|
"@react-spring/types" "~9.7.3"
|
||||||
|
|
||||||
|
"@react-spring/rafz@^9.7.3":
|
||||||
|
version "9.7.3"
|
||||||
|
resolved "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz"
|
||||||
|
integrity sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ==
|
||||||
|
|
||||||
|
"@react-spring/shared@~9.7.3":
|
||||||
|
version "9.7.3"
|
||||||
|
resolved "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz"
|
||||||
|
integrity sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==
|
||||||
|
dependencies:
|
||||||
|
"@react-spring/types" "~9.7.3"
|
||||||
|
|
||||||
|
"@react-spring/types@~9.7.3":
|
||||||
|
version "9.7.3"
|
||||||
|
resolved "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz"
|
||||||
|
integrity sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==
|
||||||
|
|
||||||
|
"@react-spring/web@^9.7.3":
|
||||||
|
version "9.7.3"
|
||||||
|
resolved "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz"
|
||||||
|
integrity sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==
|
||||||
|
dependencies:
|
||||||
|
"@react-spring/animated" "~9.7.3"
|
||||||
|
"@react-spring/core" "~9.7.3"
|
||||||
|
"@react-spring/shared" "~9.7.3"
|
||||||
|
"@react-spring/types" "~9.7.3"
|
||||||
|
|
||||||
"@remix-run/router@1.16.1":
|
"@remix-run/router@1.16.1":
|
||||||
version "1.16.1"
|
version "1.16.1"
|
||||||
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz"
|
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz"
|
||||||
@ -1464,6 +1527,18 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@swc/counter" "^0.1.3"
|
"@swc/counter" "^0.1.3"
|
||||||
|
|
||||||
|
"@tanstack/query-core@5.45.0":
|
||||||
|
version "5.45.0"
|
||||||
|
resolved "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.45.0.tgz"
|
||||||
|
integrity sha512-RVfIZQmFUTdjhSAAblvueimfngYyfN6HlwaJUPK71PKd7yi43Vs1S/rdimmZedPWX/WGppcq/U1HOj7O7FwYxw==
|
||||||
|
|
||||||
|
"@tanstack/react-query@^5.45.1":
|
||||||
|
version "5.45.1"
|
||||||
|
resolved "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.45.1.tgz"
|
||||||
|
integrity sha512-mYYfJujKg2kxmkRRjA6nn4YKG3ITsKuH22f1kteJ5IuVQqgKUgbaSQfYwVP0gBS05mhwxO03HVpD0t7BMN7WOA==
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/query-core" "5.45.0"
|
||||||
|
|
||||||
"@types/estree@^1.0.0", "@types/estree@1.0.5":
|
"@types/estree@^1.0.0", "@types/estree@1.0.5":
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
|
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
|
||||||
@ -1904,6 +1979,11 @@ chokidar@^3.5.3:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
client-only@^0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
|
||||||
|
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||||
|
|
||||||
clsx@^2.1.0, clsx@^2.1.1:
|
clsx@^2.1.0, clsx@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
|
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
|
||||||
@ -2012,6 +2092,74 @@ csstype@^3.0.2, csstype@^3.1.3:
|
|||||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
|
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
|
||||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||||
|
|
||||||
|
"d3-array@2 - 3", "d3-array@2.10.0 - 3":
|
||||||
|
version "3.2.4"
|
||||||
|
resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz"
|
||||||
|
integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
|
||||||
|
dependencies:
|
||||||
|
internmap "1 - 2"
|
||||||
|
|
||||||
|
d3-color@^3.1.0, "d3-color@1 - 3":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz"
|
||||||
|
integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
|
||||||
|
|
||||||
|
d3-delaunay@^6.0.4:
|
||||||
|
version "6.0.4"
|
||||||
|
resolved "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz"
|
||||||
|
integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==
|
||||||
|
dependencies:
|
||||||
|
delaunator "5"
|
||||||
|
|
||||||
|
"d3-format@1 - 3":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz"
|
||||||
|
integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
|
||||||
|
|
||||||
|
d3-interpolate@^3.0.1, "d3-interpolate@1.2.0 - 3":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz"
|
||||||
|
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
|
||||||
|
dependencies:
|
||||||
|
d3-color "1 - 3"
|
||||||
|
|
||||||
|
d3-path@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz"
|
||||||
|
integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
|
||||||
|
|
||||||
|
d3-scale@^4.0.2:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz"
|
||||||
|
integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
|
||||||
|
dependencies:
|
||||||
|
d3-array "2.10.0 - 3"
|
||||||
|
d3-format "1 - 3"
|
||||||
|
d3-interpolate "1.2.0 - 3"
|
||||||
|
d3-time "2.1.1 - 3"
|
||||||
|
d3-time-format "2 - 4"
|
||||||
|
|
||||||
|
d3-shape@^3.2.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz"
|
||||||
|
integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
|
||||||
|
dependencies:
|
||||||
|
d3-path "^3.1.0"
|
||||||
|
|
||||||
|
"d3-time-format@2 - 4":
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz"
|
||||||
|
integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
|
||||||
|
dependencies:
|
||||||
|
d3-time "1 - 3"
|
||||||
|
|
||||||
|
"d3-time@1 - 3", "d3-time@2.1.1 - 3":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz"
|
||||||
|
integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
|
||||||
|
dependencies:
|
||||||
|
d3-array "2 - 3"
|
||||||
|
|
||||||
data-view-buffer@^1.0.1:
|
data-view-buffer@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz"
|
||||||
@ -2074,6 +2222,13 @@ define-properties@^1.2.0, define-properties@^1.2.1:
|
|||||||
has-property-descriptors "^1.0.0"
|
has-property-descriptors "^1.0.0"
|
||||||
object-keys "^1.1.1"
|
object-keys "^1.1.1"
|
||||||
|
|
||||||
|
delaunator@5:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz"
|
||||||
|
integrity sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==
|
||||||
|
dependencies:
|
||||||
|
robust-predicates "^3.0.2"
|
||||||
|
|
||||||
delayed-stream@~1.0.0:
|
delayed-stream@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
|
||||||
@ -2759,6 +2914,11 @@ internal-slot@^1.0.7:
|
|||||||
hasown "^2.0.0"
|
hasown "^2.0.0"
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
|
"internmap@1 - 2":
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz"
|
||||||
|
integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
|
||||||
|
|
||||||
is-array-buffer@^3.0.4:
|
is-array-buffer@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz"
|
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz"
|
||||||
@ -3463,7 +3623,7 @@ randombytes@^2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "^5.1.0"
|
safe-buffer "^5.1.0"
|
||||||
|
|
||||||
"react-dom@^17.0.0 || ^18.0.0", react-dom@^18.2.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0:
|
"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^18.2.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0:
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
|
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
|
||||||
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
|
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
|
||||||
@ -3516,7 +3676,7 @@ react-transition-group@^4.4.5:
|
|||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
"react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.2.0, react@^18.3.1, react@>=16.6.0, react@>=16.8, react@>=16.8.0:
|
"react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.2.0, react@^18.3.1, react@>=16.6.0, react@>=16.8, react@>=16.8.0:
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
|
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
|
||||||
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
|
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
|
||||||
@ -3626,6 +3786,11 @@ rimraf@^3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
|
robust-predicates@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz"
|
||||||
|
integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==
|
||||||
|
|
||||||
"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.43.1:
|
"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.43.1:
|
||||||
version "2.79.1"
|
version "2.79.1"
|
||||||
resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz"
|
resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz"
|
||||||
@ -3957,6 +4122,14 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
|
swr@^2.2.5:
|
||||||
|
version "2.2.5"
|
||||||
|
resolved "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz"
|
||||||
|
integrity sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==
|
||||||
|
dependencies:
|
||||||
|
client-only "^0.0.1"
|
||||||
|
use-sync-external-store "^1.2.0"
|
||||||
|
|
||||||
tailwindcss@^3.4.4:
|
tailwindcss@^3.4.4:
|
||||||
version "3.4.4"
|
version "3.4.4"
|
||||||
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz"
|
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz"
|
||||||
@ -4189,7 +4362,7 @@ uri-js@^4.2.2, uri-js@^4.4.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
use-sync-external-store@1.2.0:
|
use-sync-external-store@^1.2.0, use-sync-external-store@1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
||||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||||
|
Reference in New Issue
Block a user