started admin ui
This commit is contained in:
371
package-lock.json
generated
371
package-lock.json
generated
@@ -8,17 +8,25 @@
|
|||||||
"name": "hackaton-form",
|
"name": "hackaton-form",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@headlessui/react": "^2.2.2",
|
||||||
|
"@heroicons/react": "^2.2.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"chart.js": "^4.4.9",
|
||||||
|
"faker": "^6.6.6",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
|
"react-router-dom": "^7.5.3",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"react-toastify": "^11.0.5",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@faker-js/faker": "^9.7.0",
|
||||||
"tailwindcss": "^3.4.17"
|
"tailwindcss": "^3.4.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2462,6 +2470,105 @@
|
|||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@faker-js/faker": {
|
||||||
|
"version": "9.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.7.0.tgz",
|
||||||
|
"integrity": "sha512-aozo5vqjCmDoXLNUJarFZx2IN/GgGaogY4TMJ6so/WLZOWpSV7fvj2dmrV6sEAnUm1O7aCrhTibjpzeDFgNqbg==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fakerjs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/core": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/utils": "^0.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/dom": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.7.0",
|
||||||
|
"@floating-ui/utils": "^0.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/react": {
|
||||||
|
"version": "0.26.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz",
|
||||||
|
"integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/react-dom": "^2.1.2",
|
||||||
|
"@floating-ui/utils": "^0.2.8",
|
||||||
|
"tabbable": "^6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/react-dom": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/utils": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@headlessui/react": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-zbniWOYBQ8GHSUIOPY7BbdIn6PzUOq0z41RFrF30HbjsxG6Rrfk+6QulR8Kgf2Vwj2a/rE6i62q5vo+2gI5dJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/react": "^0.26.16",
|
||||||
|
"@react-aria/focus": "^3.17.1",
|
||||||
|
"@react-aria/interactions": "^3.21.3",
|
||||||
|
"@tanstack/react-virtual": "^3.13.6",
|
||||||
|
"use-sync-external-store": "^1.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@heroicons/react": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.13.0",
|
"version": "0.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||||
@@ -2964,6 +3071,12 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@leichtgewicht/ip-codec": {
|
"node_modules/@leichtgewicht/ip-codec": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
||||||
@@ -3094,6 +3207,103 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-aria/focus": {
|
||||||
|
"version": "3.20.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.2.tgz",
|
||||||
|
"integrity": "sha512-Q3rouk/rzoF/3TuH6FzoAIKrl+kzZi9LHmr8S5EqLAOyP9TXIKG34x2j42dZsAhrw7TbF9gA8tBKwnCNH4ZV+Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-aria/interactions": "^3.25.0",
|
||||||
|
"@react-aria/utils": "^3.28.2",
|
||||||
|
"@react-types/shared": "^3.29.0",
|
||||||
|
"@swc/helpers": "^0.5.0",
|
||||||
|
"clsx": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-aria/interactions": {
|
||||||
|
"version": "3.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz",
|
||||||
|
"integrity": "sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-aria/ssr": "^3.9.8",
|
||||||
|
"@react-aria/utils": "^3.28.2",
|
||||||
|
"@react-stately/flags": "^3.1.1",
|
||||||
|
"@react-types/shared": "^3.29.0",
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-aria/ssr": {
|
||||||
|
"version": "3.9.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
|
||||||
|
"integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-aria/utils": {
|
||||||
|
"version": "3.28.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.28.2.tgz",
|
||||||
|
"integrity": "sha512-J8CcLbvnQgiBn54eeEvQQbIOfBF3A1QizxMw9P4cl9MkeR03ug7RnjTIdJY/n2p7t59kLeAB3tqiczhcj+Oi5w==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-aria/ssr": "^3.9.8",
|
||||||
|
"@react-stately/flags": "^3.1.1",
|
||||||
|
"@react-stately/utils": "^3.10.6",
|
||||||
|
"@react-types/shared": "^3.29.0",
|
||||||
|
"@swc/helpers": "^0.5.0",
|
||||||
|
"clsx": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-stately/flags": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-XPR5gi5LfrPdhxZzdIlJDz/B5cBf63l4q6/AzNqVWFKgd0QqY5LvWJftXkklaIUpKSJkIKQb8dphuZXDtkWNqg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-stately/utils": {
|
||||||
|
"version": "3.10.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.6.tgz",
|
||||||
|
"integrity": "sha512-O76ip4InfTTzAJrg8OaZxKU4vvjMDOpfA/PGNOytiXwBbkct2ZeZwaimJ8Bt9W1bj5VsZ81/o/tW4BacbdDOMA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-types/shared": {
|
||||||
|
"version": "3.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.29.0.tgz",
|
||||||
|
"integrity": "sha512-IDQYu/AHgZimObzCFdNl1LpZvQW/xcfLt3v20sorl5qRucDVj4S9os98sVTZ4IRIBjmS+MkjqpR5E70xan7ooA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
@@ -3448,6 +3658,42 @@
|
|||||||
"url": "https://github.com/sponsors/gregberge"
|
"url": "https://github.com/sponsors/gregberge"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/helpers": {
|
||||||
|
"version": "0.5.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||||
|
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-virtual": {
|
||||||
|
"version": "3.13.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.6.tgz",
|
||||||
|
"integrity": "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/virtual-core": "3.13.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/virtual-core": {
|
||||||
|
"version": "3.13.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.6.tgz",
|
||||||
|
"integrity": "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.4.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
||||||
@@ -5541,6 +5787,18 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.4.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz",
|
||||||
|
"integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/check-types": {
|
"node_modules/check-types": {
|
||||||
"version": "11.2.3",
|
"version": "11.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz",
|
||||||
@@ -5645,6 +5903,15 @@
|
|||||||
"wrap-ansi": "^7.0.0"
|
"wrap-ansi": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/co": {
|
"node_modules/co": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||||
@@ -7986,6 +8253,12 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/faker": {
|
||||||
|
"version": "6.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/faker/-/faker-6.6.6.tgz",
|
||||||
|
"integrity": "sha512-9tCqYEDHI5RYFQigXFwF1hnCwcWCOJl/hmll0lr5D2Ljjb0o4wphb69wikeJDz5qCEzXCoPvG6ss5SDP6IfOdg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
@@ -13858,6 +14131,16 @@
|
|||||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/react-chartjs-2": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": "^4.1.1",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dev-utils": {
|
"node_modules/react-dev-utils": {
|
||||||
"version": "12.0.1",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
||||||
@@ -13996,6 +14279,54 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "7.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.3.tgz",
|
||||||
|
"integrity": "sha512-3iUDM4/fZCQ89SXlDa+Ph3MevBrozBAI655OAfWQlTm9nBR0IKlrmNwFow5lPHttbwvITZfkeeeZFP6zt3F7pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": "^1.0.1",
|
||||||
|
"set-cookie-parser": "^2.6.0",
|
||||||
|
"turbo-stream": "2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "7.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.3.tgz",
|
||||||
|
"integrity": "sha512-cK0jSaTyW4jV9SRKAItMIQfWZ/D6WEZafgHuuCb9g+SjhLolY78qc+De4w/Cz9ybjvLzShAmaIMEXt8iF1Cm+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-router": "7.5.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router/node_modules/cookie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-scripts": {
|
"node_modules/react-scripts": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
||||||
@@ -14069,6 +14400,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-toastify": {
|
||||||
|
"version": "11.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
||||||
|
"integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19",
|
||||||
|
"react-dom": "^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
@@ -14911,6 +15255,12 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-cookie-parser": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
@@ -15992,6 +16342,12 @@
|
|||||||
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/tabbable": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.17",
|
"version": "3.4.17",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
||||||
@@ -16351,6 +16707,12 @@
|
|||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
|
"node_modules/turbo-stream": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
@@ -16659,6 +17021,15 @@
|
|||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
|||||||
@@ -3,13 +3,20 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@headlessui/react": "^2.2.2",
|
||||||
|
"@heroicons/react": "^2.2.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"chart.js": "^4.4.9",
|
||||||
|
"faker": "^6.6.6",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
|
"react-router-dom": "^7.5.3",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"react-toastify": "^11.0.5",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
@@ -38,6 +45,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@faker-js/faker": "^9.7.0",
|
||||||
"tailwindcss": "^3.4.17"
|
"tailwindcss": "^3.4.17"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
194
src/App.js
194
src/App.js
@@ -1,157 +1,49 @@
|
|||||||
import SendIcon from './img/paper-plane.png';
|
import { useParams } from "react-router-dom";
|
||||||
import { useState } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
import Message from './misc/Message';
|
import { ToastContainer, toast } from 'react-toastify';
|
||||||
import SendForm from './misc/SendForm';
|
|
||||||
import Spinner from './misc/Spinner';
|
const FormComponent = React.lazy(() => import('./pages/Form'));
|
||||||
import WaitResponse from './misc/WaitResponse';
|
const DashboardComponent = React.lazy(() => import('./pages/Dashboard'));
|
||||||
import { NewRequest } from './misc/ChatGpt';
|
const LoginComponent = React.lazy(() => import('./pages/Login'));
|
||||||
import './App.css';
|
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
export default function App(args) {
|
||||||
const [Stage, setStage] = useState(0);
|
const { page } = useParams(); // Получаем параметр page из URL
|
||||||
const [Loading, setLoading] = useState(false);
|
const CurrentPage = args.page || page;
|
||||||
const [GenerationProcess, setGenerationProcess] = useState(false);
|
let content;
|
||||||
|
|
||||||
const [LoadingText, setLoadingText] = useState("");
|
switch (CurrentPage) {
|
||||||
|
case "form":
|
||||||
|
content = <FormComponent CurrentPage={CurrentPage} />;
|
||||||
|
break;
|
||||||
|
case "dashboard":
|
||||||
|
content = <DashboardComponent CurrentPage={CurrentPage} />;
|
||||||
|
break;
|
||||||
|
case "login":
|
||||||
|
content = <LoginComponent CurrentPage={CurrentPage} />;
|
||||||
|
break;
|
||||||
|
case "network":
|
||||||
|
content = <DashboardComponent CurrentPage={CurrentPage} />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
name: '',
|
|
||||||
work_place: '',
|
|
||||||
problem: '',
|
|
||||||
message: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleChange = (e) => {
|
|
||||||
const { name, value } = e.target;
|
|
||||||
setFormData((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[name]: value
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const [messages, setMessages] = useState([]);
|
|
||||||
|
|
||||||
|
|
||||||
const addMessage = (text, self = true) => {
|
|
||||||
const newMessage = {
|
|
||||||
id: Date.now(), // уникальный ключ для нового элемента
|
|
||||||
text: text,
|
|
||||||
self: self
|
|
||||||
};
|
|
||||||
|
|
||||||
setMessages(prev => [...prev, newMessage]);
|
|
||||||
};
|
|
||||||
window.addMessage = addMessage;
|
|
||||||
const handleSubmit = (e) => {
|
|
||||||
e.preventDefault(); // Останавливаем стандартную отправку формы
|
|
||||||
|
|
||||||
console.log('Отправленные данные:', formData);
|
|
||||||
setLoading(true);
|
|
||||||
NewRequest(formData, addMessage, setGenerationProcess, setStage, setLoading);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full h-full justify-center items-center dark:bg-gray-800">
|
<Suspense fallback={<div className="flex animate-[fadeIn_0.5s_ease-out] absolute inset-0 flex-col items-center justify-center z-10 dark:bg-gray-700">
|
||||||
<div className="relative flex md:h-[550px] h-full md:w-[500px] w-full flex-col justify-center md:rounded-xl bg-gray-50 p-5 text-sm/7 text-gray-800 shadow-lg dark:bg-gray-900 dark:text-gray-200">
|
<div className="w-10 h-10 border-4 border-orange-500 border-t-transparent rounded-full animate-spin" style={{ animationDuration: "0.5s" }} ></div>
|
||||||
|
</div>}>
|
||||||
<Spinner show={Loading} />
|
<ToastContainer
|
||||||
{Stage == 0 &&
|
position="top-center"
|
||||||
<div className="p-0 md:p-4">
|
autoClose={2500}
|
||||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
hideProgressBar={false}
|
||||||
Сообщить о проблеме
|
newestOnTop={false}
|
||||||
</h1>
|
closeOnClick={false}
|
||||||
<div className="space-y-6">
|
rtl={false}
|
||||||
<p className="leading-5">
|
pauseOnFocusLoss
|
||||||
Чем подробнее вы опишете ситуацию, тем быстрее мы найдём решение
|
draggable
|
||||||
</p>
|
pauseOnHover
|
||||||
<form className="space-y-3" onSubmit={handleSubmit}>
|
theme="colored"
|
||||||
<input
|
/>
|
||||||
type="text"
|
{content}
|
||||||
name="name"
|
</Suspense>
|
||||||
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
|
||||||
placeholder="Ваше имя *"
|
|
||||||
required={true}
|
|
||||||
value={formData.name}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="work_place"
|
|
||||||
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 disabled:text-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
|
||||||
defaultValue="Рабочее место 192.168.0.24"
|
|
||||||
placeholder="Адрес рабочего места *"
|
|
||||||
required={true}
|
|
||||||
disabled={false}
|
|
||||||
value={formData.work_place}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<select
|
|
||||||
name="problem"
|
|
||||||
className="mt-10 block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
|
||||||
value={formData.problem}
|
|
||||||
onChange={handleChange}
|
|
||||||
>
|
|
||||||
<option value="0">Выберите тип проблемы *</option>
|
|
||||||
<option value="1">Ошибка в работе ПО</option>
|
|
||||||
<option value="2">Проблемы с доступом/паролями</option>
|
|
||||||
<option value="3">Не работает оборудование</option>
|
|
||||||
<option value="4">Другое</option>
|
|
||||||
</select>
|
|
||||||
<textarea
|
|
||||||
name="message"
|
|
||||||
rows={4}
|
|
||||||
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
|
||||||
placeholder="Добавьте описание *"
|
|
||||||
required={true}
|
|
||||||
value={formData.message}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="text-md dark:bg-orange-600dark:focus:ring-orange-900 mb-2 w-full cursor-pointer rounded-xl bg-orange-500 px-5 py-2.5 font-bold text-white shadow-md duration-300 hover:scale-105 focus:outline-none"
|
|
||||||
>
|
|
||||||
Отправить
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{Stage == 1 &&
|
|
||||||
<>
|
|
||||||
<div className="flex flex-col gap-3 w-full h-full items-start scroll-box">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{messages.map(message => (
|
|
||||||
<Message key={message.id} self={message.self} text={message.text} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
<WaitResponse show={GenerationProcess} />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="pt-4">
|
|
||||||
<form className='flex gap-3'>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="name"
|
|
||||||
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
|
||||||
placeholder="Сообщение"
|
|
||||||
required=""
|
|
||||||
/>
|
|
||||||
<button type='submit' className='transition-transform duration-300 hover:rotate-45'>
|
|
||||||
<img src={SendIcon} className='h-6 w-6' />
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div >
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
|
||||||
152
src/dashboard/SystemMonitor.js
Normal file
152
src/dashboard/SystemMonitor.js
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import Gauge from "../misc/Gauge";
|
||||||
|
import { InformationCircleIcon, ComputerDesktopIcon, CodeBracketIcon, CpuChipIcon, ClockIcon, ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
|
||||||
|
import CpuChart from "../misc/CpuChart";
|
||||||
|
|
||||||
|
|
||||||
|
const Tabs = () => {
|
||||||
|
// Состояние для отслеживания выбранной вкладки
|
||||||
|
const [selectedTab, setSelectedTab] = useState("system");
|
||||||
|
const [Percent, setPercent] = useState(80);
|
||||||
|
window.setPercent = setPercent;
|
||||||
|
// Данные для вкладок
|
||||||
|
const tabs = [
|
||||||
|
{ id: "system", label: "Система" },
|
||||||
|
{ id: "deviceManager", label: "Диспетчер устройств" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Функция для обработки клика по вкладке
|
||||||
|
const handleTabClick = (tabId) => {
|
||||||
|
setSelectedTab(tabId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-center md:justify-start">
|
||||||
|
{/* Контейнер для вкладок */}
|
||||||
|
<div className="flex p-2 bg-gray-100 dark:bg-gray-900 gap-2 rounded-lg shadow-sm">
|
||||||
|
{tabs.map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab.id}
|
||||||
|
onClick={() => handleTabClick(tab.id)}
|
||||||
|
className={`rounded-lg px-4 py-2 text-gray-900 dark:text-gray-100 ${selectedTab === tab.id
|
||||||
|
? "bg-gray-200 dark:bg-gray-800 text-orange-600 font-semibold"
|
||||||
|
: "hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-orange-600 duration-300"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
{selectedTab === "system" && (
|
||||||
|
<div className="flex gap-3 flex-wrap flex-col md:flex-row dark:text-gray-100">
|
||||||
|
<div className="md:basis-3/6 w-full border border-gray-300 dark:border-gray-700 p-4 rounded-lg shadow-sm">
|
||||||
|
<h1 className="text-lg font-semibold">Информация о системе</h1>
|
||||||
|
<div className="flex flex-col gap-2 mt-3">
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ClockIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Время работы:</span>
|
||||||
|
</div>
|
||||||
|
<span>1d 15:05:10</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<InformationCircleIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Система:</span>
|
||||||
|
</div>
|
||||||
|
<span>Windows</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ComputerDesktopIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Hostname:</span>
|
||||||
|
</div>
|
||||||
|
<span className="break-normal">DESKTOP-380ABCDE</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<CodeBracketIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Версия:</span>
|
||||||
|
</div>
|
||||||
|
<span>10.0.19049</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<CpuChipIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Архитектура:</span>
|
||||||
|
</div>
|
||||||
|
<span>AMD64</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="md:basis-1/6 border border-gray-300 dark:border-gray-700 p-4 rounded-lg shadow-sm">
|
||||||
|
<h1 className="text-lg font-semibold dark:text-gray-100">Загрузка CPU</h1>
|
||||||
|
<div className=" flex flex-col items-center">
|
||||||
|
<Gauge value={20} title="%" color="green" className="mt-3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="md:basis-1/6 border border-gray-300 dark:border-gray-700 p-4 rounded-lg shadow-sm">
|
||||||
|
<h1 className="text-lg font-semibold dark:text-gray-100">Занято ОЗУ</h1>
|
||||||
|
<div className=" flex flex-col items-center">
|
||||||
|
<Gauge value={Percent} title="%" color="orange" className="mt-3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="md:basis-3/6 w-full border border-gray-300 dark:border-gray-700 p-4 rounded-lg shadow-sm">
|
||||||
|
<h1 className="text-lg font-semibold">Аппаратное обеспечение</h1>
|
||||||
|
<div className="flex flex-col gap-2 mt-3">
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<CpuChipIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Процессор:</span>
|
||||||
|
</div>
|
||||||
|
<span>AMD Ryzen 5 5600x 6-core processor</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ChevronDoubleRightIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Физические ядра:</span>
|
||||||
|
</div>
|
||||||
|
<span>6</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ChevronDoubleRightIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">Логические ядра:</span>
|
||||||
|
</div>
|
||||||
|
<span>12</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ChevronDoubleRightIcon className="h-6 text-gray-500" />
|
||||||
|
<span className="font-bold text-gray-600 dark:text-gray-400">ОЗУ:</span>
|
||||||
|
</div>
|
||||||
|
<span>32 ГБ</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="md:basis-[34.3%] w-full border border-gray-300 dark:border-gray-700 p-4 rounded-lg shadow-sm">
|
||||||
|
<h1 className="text-lg font-semibold">График загрузки CPU</h1>
|
||||||
|
<div className="flex flex-col gap-2 mt-3">
|
||||||
|
<CpuChart />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedTab === "deviceManager" && (
|
||||||
|
<p>Вот содержимое вкладки "Диспетчер устройств"</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tabs;
|
||||||
3
src/img/icon-network.svg
Normal file
3
src/img/icon-network.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M8.288 15.038a5.25 5.25 0 0 1 7.424 0M5.106 11.856c3.807-3.808 9.98-3.808 13.788 0M1.924 8.674c5.565-5.565 14.587-5.565 20.152 0M12.53 18.22l-.53.53-.53-.53a.75.75 0 0 1 1.06 0Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 376 B |
3
src/img/icon-servers.svg
Normal file
3
src/img/icon-servers.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Zm-3 6h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 571 B |
10
src/index.js
10
src/index.js
@@ -1,14 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
|
import {
|
||||||
|
RouterProvider,
|
||||||
|
} from "react-router-dom";
|
||||||
|
import {router} from "./misc/router"
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<RouterProvider router={router} />
|
||||||
<App />
|
|
||||||
</React.StrictMode>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
|||||||
52
src/misc/CpuChart.js
Normal file
52
src/misc/CpuChart.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
} from 'chart.js';
|
||||||
|
import { Line } from 'react-chartjs-2';
|
||||||
|
|
||||||
|
ChartJS.register(
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend
|
||||||
|
);
|
||||||
|
|
||||||
|
export const options = {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const labels = ['10:00',"11:00","12:00","13:00","14:00","15:00","16:00"];
|
||||||
|
|
||||||
|
export const data = {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Загрузка CPU',
|
||||||
|
data: [10,20,30,10,10,45,20],
|
||||||
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.5)',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function CpuChart() {
|
||||||
|
return <Line options={options} data={data} />;
|
||||||
|
}
|
||||||
80
src/misc/Gauge.js
Normal file
80
src/misc/Gauge.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
const CircularProgressIndicator = ({ value, color }) => {
|
||||||
|
const svgRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!svgRef.current) return;
|
||||||
|
|
||||||
|
// Получаем элемент SVG
|
||||||
|
const svg = svgRef.current;
|
||||||
|
const circle = svg.querySelector("circle");
|
||||||
|
|
||||||
|
// Рассчитываем длину окружности
|
||||||
|
const circumference = circle.getAttribute("r") * 2 * Math.PI;
|
||||||
|
|
||||||
|
// Устанавливаем начальное значение (0%)
|
||||||
|
circle.style.strokeDasharray = `${circumference} ${circumference}`;
|
||||||
|
circle.style.strokeDashoffset = circumference;
|
||||||
|
|
||||||
|
// Анимируем заполнение
|
||||||
|
setTimeout(() => {
|
||||||
|
const offset = circumference - (value / 100) * circumference;
|
||||||
|
circle.style.transition = "stroke-dashoffset 500ms ease-in-out";
|
||||||
|
circle.style.strokeDashoffset = offset;
|
||||||
|
}, 0);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
console.log(color)
|
||||||
|
switch(color){
|
||||||
|
case "green":
|
||||||
|
color = "#1eeb07"
|
||||||
|
break;
|
||||||
|
case "red":
|
||||||
|
color = "#fc4a03"
|
||||||
|
break;
|
||||||
|
case "yellow":
|
||||||
|
color = "#f0fc03"
|
||||||
|
break;
|
||||||
|
case "orange":
|
||||||
|
color = "#ff9f43"
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex items-center justify-center mt-3">
|
||||||
|
{/* SVG для кругового прогресс-бара */}
|
||||||
|
<svg
|
||||||
|
ref={svgRef}
|
||||||
|
className="w-32 h-32"
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="50"
|
||||||
|
cy="50"
|
||||||
|
r="45"
|
||||||
|
fill="transparent"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="7"
|
||||||
|
className="fill-gray-200 dark:fill-gray-700"
|
||||||
|
strokeDasharray="0 282.74"
|
||||||
|
strokeDashoffset="282.74" // Начинаем снизу
|
||||||
|
strokeLinecap="round" // Закругляем торцы
|
||||||
|
transform="rotate(90 50 50)" // Поворачиваем начало вниз
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{/* Текст в центре круга */}
|
||||||
|
<div className="absolute top-[44%] left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-3xl font-bold" style={{"color":color}}>
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-[65%] left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-lg text-gray-500 dark:text-gray-400">
|
||||||
|
%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CircularProgressIndicator;
|
||||||
20
src/misc/router.js
Normal file
20
src/misc/router.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {
|
||||||
|
createBrowserRouter
|
||||||
|
} from "react-router-dom";
|
||||||
|
|
||||||
|
import App from '../App';
|
||||||
|
|
||||||
|
export const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <App page="form"/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/dashboard",
|
||||||
|
element: <App page="dashboard"/>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/dashboard/:page",
|
||||||
|
element: <App/>,
|
||||||
|
},
|
||||||
|
]);
|
||||||
204
src/pages/Dashboard.js
Normal file
204
src/pages/Dashboard.js
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
|
||||||
|
import { Bars3Icon, BellIcon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||||
|
import { ServerStackIcon } from '@heroicons/react/16/solid'
|
||||||
|
import { WifiIcon } from '@heroicons/react/16/solid'
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import SystemMonitor from "../dashboard/SystemMonitor"
|
||||||
|
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
name: 'Administrator',
|
||||||
|
email: '@admin',
|
||||||
|
imageUrl:
|
||||||
|
'https://api.dicebear.com/9.x/thumbs/svg?seed=Amaya',
|
||||||
|
}
|
||||||
|
const navigation = [
|
||||||
|
{ name: 'Системный мониторинг', href: '/dashboard', icon: ServerStackIcon, pageName: "dashboard" },
|
||||||
|
{ name: 'Сетевой мониторинг', href: '/dashboard/network', icon: WifiIcon, pageName: "network" },
|
||||||
|
|
||||||
|
]
|
||||||
|
const userNavigation = [
|
||||||
|
{ name: 'Выйти из аккаунта', href: '#' },
|
||||||
|
]
|
||||||
|
|
||||||
|
function classNames(...classes) {
|
||||||
|
return classes.filter(Boolean).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Dashboard(args) {
|
||||||
|
|
||||||
|
let content;
|
||||||
|
|
||||||
|
switch (args.CurrentPage) {
|
||||||
|
case "dashboard":
|
||||||
|
content = <SystemMonitor/>;
|
||||||
|
break;
|
||||||
|
case "network":
|
||||||
|
content = <SystemMonitor/>;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/*
|
||||||
|
This example requires updating your template:
|
||||||
|
|
||||||
|
```
|
||||||
|
<html class="h-full bg-gray-100">
|
||||||
|
<body class="h-full">
|
||||||
|
```
|
||||||
|
*/}
|
||||||
|
<div className="min-h-full">
|
||||||
|
<Disclosure as="nav" className="bg-gray-700">
|
||||||
|
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="flex h-16 items-center justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="shrink-0">
|
||||||
|
<img
|
||||||
|
alt="Your Company"
|
||||||
|
src="https://tailwindcss.com/plus-assets/img/logos/mark.svg?color=indigo&shade=500"
|
||||||
|
className="size-8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="hidden md:block">
|
||||||
|
<div className="ml-10 flex items-baseline space-x-4">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
to={item.href}
|
||||||
|
aria-current={args.CurrentPage == item.pageName ? 'page' : undefined}
|
||||||
|
className={classNames(
|
||||||
|
args.CurrentPage == item.pageName ? 'bg-orange-600 text-white' : 'text-gray-300 hover:bg-orange-700 hover:text-white',
|
||||||
|
'rounded-md px-3 py-2 text-md font-medium',
|
||||||
|
'flex gap-2 justify-center'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon className='h-6'/>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden md:block">
|
||||||
|
<div className="ml-4 flex items-center md:ml-6">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800 focus:outline-hidden"
|
||||||
|
>
|
||||||
|
<span className="absolute -inset-1.5" />
|
||||||
|
<span className="sr-only">View notifications</span>
|
||||||
|
<BellIcon aria-hidden="true" className="size-6" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Profile dropdown */}
|
||||||
|
<Menu as="div" className="relative ml-3">
|
||||||
|
<div>
|
||||||
|
<MenuButton className="relative flex max-w-xs items-center rounded-full bg-gray-800 text-sm focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800 focus:outline-hidden">
|
||||||
|
<span className="absolute -inset-1.5" />
|
||||||
|
<span className="sr-only">Open user menu</span>
|
||||||
|
<img alt="" src={user.imageUrl} className="size-8 rounded-full" />
|
||||||
|
</MenuButton>
|
||||||
|
</div>
|
||||||
|
<MenuItems
|
||||||
|
transition
|
||||||
|
className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 transition focus:outline-hidden data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in"
|
||||||
|
>
|
||||||
|
{userNavigation.map((item) => (
|
||||||
|
<MenuItem key={item.name}>
|
||||||
|
<Link
|
||||||
|
to={item.href}
|
||||||
|
className="block px-4 py-2 text-sm text-gray-700 data-focus:bg-gray-100 data-focus:outline-hidden"
|
||||||
|
>
|
||||||
|
<item.icon className='h-6' />
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</MenuItems>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="-mr-2 flex md:hidden">
|
||||||
|
{/* Mobile menu button */}
|
||||||
|
<DisclosureButton className="group relative inline-flex items-center justify-center rounded-md bg-gray-700 p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:ring-offset-gray-800 focus:outline-hidden">
|
||||||
|
<span className="absolute -inset-0.5" />
|
||||||
|
<span className="sr-only">Open main menu</span>
|
||||||
|
<Bars3Icon aria-hidden="true" className="block size-6 group-data-open:hidden" />
|
||||||
|
<XMarkIcon aria-hidden="true" className="hidden size-6 group-data-open:block" />
|
||||||
|
</DisclosureButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DisclosurePanel
|
||||||
|
as="nav"
|
||||||
|
className={"overflow-hidden transition-all duration-300 ease-in-out animate-[fadeIn_0.5s_ease-out]"}
|
||||||
|
>
|
||||||
|
<div className="space-y-1 px-2 pt-2 pb-3 sm:px-3">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<DisclosureButton
|
||||||
|
key={item.name}
|
||||||
|
as="a"
|
||||||
|
href={item.href}
|
||||||
|
aria-current={args.CurrentPage == item.pageName ? 'page' : undefined}
|
||||||
|
className={classNames(
|
||||||
|
args.CurrentPage == item.pageName
|
||||||
|
? 'bg-gray-900 text-white'
|
||||||
|
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||||
|
'block rounded-md px-3 py-2 text-base font-medium',
|
||||||
|
'flex items-center gap-2'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon className='h-6' />
|
||||||
|
{item.name}
|
||||||
|
</DisclosureButton>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="border-t border-gray-700 pt-4 pb-3">
|
||||||
|
<div className="flex items-center px-5">
|
||||||
|
<div className="shrink-0">
|
||||||
|
<img alt="" src={user.imageUrl} className="size-10 rounded-full" />
|
||||||
|
</div>
|
||||||
|
<div className="ml-3">
|
||||||
|
<div className="text-base/5 font-medium text-white">{user.name}</div>
|
||||||
|
<div className="text-sm font-medium text-gray-400">{user.email}</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="relative ml-auto shrink-0 rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800 focus:outline-hidden"
|
||||||
|
>
|
||||||
|
<span className="absolute -inset-1.5" />
|
||||||
|
<span className="sr-only">View notifications</span>
|
||||||
|
<BellIcon aria-hidden="true" className="size-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 space-y-1 px-2">
|
||||||
|
{userNavigation.map((item) => (
|
||||||
|
<DisclosureButton
|
||||||
|
key={item.name}
|
||||||
|
as="a"
|
||||||
|
href={item.href}
|
||||||
|
className="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white"
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
{item.name}
|
||||||
|
</DisclosureButton>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DisclosurePanel>
|
||||||
|
</Disclosure>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8 dark:bg-gray-800">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
161
src/pages/Form.js
Normal file
161
src/pages/Form.js
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import SendIcon from '../img/paper-plane.png';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Message from '../misc/Message';
|
||||||
|
import SendForm from '../misc/SendForm';
|
||||||
|
import Spinner from '../misc/Spinner';
|
||||||
|
import WaitResponse from '../misc/WaitResponse';
|
||||||
|
import { NewRequest } from '../misc/ChatGpt';
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
import '../App.css';
|
||||||
|
|
||||||
|
|
||||||
|
function Form() {
|
||||||
|
const [Stage, setStage] = useState(0);
|
||||||
|
const [Loading, setLoading] = useState(false);
|
||||||
|
const [GenerationProcess, setGenerationProcess] = useState(false);
|
||||||
|
|
||||||
|
const [LoadingText, setLoadingText] = useState("");
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: 'TestName',
|
||||||
|
work_place: '192.168.0.10',
|
||||||
|
problem: '1',
|
||||||
|
message: 'Test problem'
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: value
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
|
|
||||||
|
|
||||||
|
const addMessage = (text, self = true) => {
|
||||||
|
const newMessage = {
|
||||||
|
id: Date.now(), // уникальный ключ для нового элемента
|
||||||
|
text: text,
|
||||||
|
self: self
|
||||||
|
};
|
||||||
|
|
||||||
|
setMessages(prev => [...prev, newMessage]);
|
||||||
|
};
|
||||||
|
window.addMessage = addMessage;
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault(); // Останавливаем стандартную отправку формы
|
||||||
|
|
||||||
|
console.log('Отправленные данные:', formData);
|
||||||
|
setLoading(true);
|
||||||
|
NewRequest(formData, addMessage, setGenerationProcess, setStage, setLoading);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col w-full h-full justify-center items-center dark:bg-gray-800">
|
||||||
|
<div className="relative flex md:h-[550px] h-full md:w-[500px] w-full flex-col justify-center md:rounded-xl bg-gray-50 p-5 text-sm/7 text-gray-800 shadow-lg dark:bg-gray-900 dark:text-gray-200">
|
||||||
|
|
||||||
|
<Spinner show={Loading} />
|
||||||
|
{Stage == 0 &&
|
||||||
|
<div className="p-0 md:p-4">
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
||||||
|
Сообщить о проблеме
|
||||||
|
</h1>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<p className="leading-5">
|
||||||
|
Чем подробнее вы опишете ситуацию, тем быстрее мы найдём решение
|
||||||
|
</p>
|
||||||
|
<form className="space-y-3" onSubmit={handleSubmit}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
||||||
|
placeholder="Ваше имя *"
|
||||||
|
required={true}
|
||||||
|
value={formData.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="work_place"
|
||||||
|
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 disabled:text-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
||||||
|
defaultValue="Рабочее место 192.168.0.24"
|
||||||
|
placeholder="Адрес рабочего места *"
|
||||||
|
required={true}
|
||||||
|
disabled={false}
|
||||||
|
value={formData.work_place}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
name="problem"
|
||||||
|
className="mt-10 block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
||||||
|
value={formData.problem}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<option value="0">Выберите тип проблемы *</option>
|
||||||
|
<option value="1">Ошибка в работе ПО</option>
|
||||||
|
<option value="2">Проблемы с доступом/паролями</option>
|
||||||
|
<option value="3">Не работает оборудование</option>
|
||||||
|
<option value="4">Другое</option>
|
||||||
|
</select>
|
||||||
|
<textarea
|
||||||
|
name="message"
|
||||||
|
rows={4}
|
||||||
|
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
||||||
|
placeholder="Добавьте описание *"
|
||||||
|
required={true}
|
||||||
|
value={formData.message}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="text-md dark:bg-orange-600dark:focus:ring-orange-900 mb-2 w-full cursor-pointer rounded-xl bg-orange-500 px-5 py-2.5 font-bold text-white shadow-md duration-300 hover:scale-105 focus:outline-none"
|
||||||
|
>
|
||||||
|
Отправить
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{Stage == 1 &&
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-3 w-full h-full items-start scroll-box">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{messages.map(message => (
|
||||||
|
<Message key={message.id} self={message.self} text={message.text} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
<WaitResponse show={GenerationProcess} />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="pt-4">
|
||||||
|
<form className='flex gap-3'>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
||||||
|
placeholder="Сообщение"
|
||||||
|
required=""
|
||||||
|
/>
|
||||||
|
<button type='submit' className='transition-transform duration-300 hover:rotate-45'>
|
||||||
|
<img src={SendIcon} className='h-6 w-6' />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
<div className='flex w-full justify-center text-md'>
|
||||||
|
<Link to="/dashboard/login" className='block mt-3 text-orange-500 hover:text-orange-300 duration-300'>Перейти в админ-панель</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Form;
|
||||||
93
src/pages/Login.js
Normal file
93
src/pages/Login.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import Spinner from '../misc/Spinner';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
function Login(args) {
|
||||||
|
const [Loading, setLoading] = useState(false);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
username: 'admin',
|
||||||
|
password: 'admin'
|
||||||
|
|
||||||
|
});
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: value
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault(); // Останавливаем стандартную отправку формы
|
||||||
|
|
||||||
|
console.log('Отправленные данные:', formData);
|
||||||
|
setLoading(true);
|
||||||
|
////////
|
||||||
|
setTimeout(() => {
|
||||||
|
// setLoading(false);
|
||||||
|
// toast.error('Неверный логин или пароль');
|
||||||
|
toast.success('Добро пожаловать!');
|
||||||
|
navigate("/dashboard");
|
||||||
|
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex w-full h-full justify-center items-center dark:bg-gray-800">
|
||||||
|
<div className="relative flex md:h-[550px] h-full md:w-[500px] w-full flex-col justify-center md:rounded-xl bg-gray-50 p-5 text-sm/7 text-gray-800 shadow-lg dark:bg-gray-900 dark:text-gray-200">
|
||||||
|
|
||||||
|
<Spinner show={Loading} />
|
||||||
|
|
||||||
|
<div className="p-0 md:p-4">
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
||||||
|
Авторизация
|
||||||
|
</h1>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<p className="leading-5">
|
||||||
|
Для продолжения нужно войти в аккаунт
|
||||||
|
</p>
|
||||||
|
<form className="space-y-3" onSubmit={handleSubmit}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
||||||
|
placeholder="Имя пользователя"
|
||||||
|
required={true}
|
||||||
|
value={formData.username}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
className="block w-full rounded-xl border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-orange-500 focus:ring-orange-500 disabled:text-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-orange-500 dark:focus:ring-orange-500"
|
||||||
|
placeholder="Пароль"
|
||||||
|
required={true}
|
||||||
|
disabled={false}
|
||||||
|
value={formData.password}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="text-md dark:bg-orange-600dark:focus:ring-orange-900 mb-2 w-full cursor-pointer rounded-xl bg-orange-500 px-5 py-2.5 font-bold text-white shadow-md duration-300 hover:scale-105 focus:outline-none"
|
||||||
|
>
|
||||||
|
Войти
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Login;
|
||||||
Reference in New Issue
Block a user