diff --git a/.env b/.env new file mode 100644 index 0000000..371506d --- /dev/null +++ b/.env @@ -0,0 +1 @@ +REACT_APP_WS_URL=localhost:8080/ws diff --git a/package-lock.json b/package-lock.json index 42b49e2..29d2d9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "browserslist": "^4.18.1", "camelcase": "^6.2.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", + "classnames": "^2.5.1", "css-loader": "^6.5.1", "css-minimizer-webpack-plugin": "^3.2.0", "dotenv": "^10.0.0", @@ -45,7 +46,6 @@ "jest-resolve": "^27.4.2", "jest-watch-typeahead": "^1.0.0", "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", "postcss-flexbugs-fixes": "^5.0.2", "postcss-loader": "^6.2.1", "postcss-normalize": "^10.0.1", @@ -58,6 +58,7 @@ "react-dom": "^18.3.1", "react-refresh": "^0.11.0", "react-router": "^6.30.0", + "react-router-dom": "^6.30.0", "resolve": "^1.20.0", "resolve-url-loader": "^4.0.0", "rxjs": "^7.8.2", @@ -73,6 +74,11 @@ "webpack-dev-server": "^4.6.0", "webpack-manifest-plugin": "^4.0.2", "workbox-webpack-plugin": "^6.4.1" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "postcss": "^8.5.3", + "sass": "^1.85.1" } }, "node_modules/@adobe/css-tools": { @@ -3211,6 +3217,316 @@ "node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -7165,6 +7481,20 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz", @@ -9763,6 +10093,13 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", @@ -12159,6 +12496,14 @@ "tslib": "^2.0.3" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz", @@ -15168,6 +15513,23 @@ "react": ">=16.8" } }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", @@ -15729,6 +16091,27 @@ "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", "license": "CC0-1.0" }, + "node_modules/sass": { + "version": "1.85.1", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.85.1.tgz", + "integrity": "sha512-Uk8WpxM5v+0cMR0XjX9KfRIacmSG86RH4DCCZjLU2rFh5tyutt9siAXJ7G+YfxQ99Q6wrRMbMlVl6KqUms71ag==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, "node_modules/sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-12.6.0.tgz", @@ -15767,6 +16150,36 @@ } } }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz", diff --git a/package.json b/package.json index 394a246..78e550e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "browserslist": "^4.18.1", "camelcase": "^6.2.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", + "classnames": "^2.5.1", "css-loader": "^6.5.1", "css-minimizer-webpack-plugin": "^3.2.0", "dotenv": "^10.0.0", @@ -40,7 +41,6 @@ "jest-resolve": "^27.4.2", "jest-watch-typeahead": "^1.0.0", "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", "postcss-flexbugs-fixes": "^5.0.2", "postcss-loader": "^6.2.1", "postcss-normalize": "^10.0.1", @@ -53,6 +53,7 @@ "react-dom": "^18.3.1", "react-refresh": "^0.11.0", "react-router": "^6.30.0", + "react-router-dom": "^6.30.0", "resolve": "^1.20.0", "resolve-url-loader": "^4.0.0", "rxjs": "^7.8.2", @@ -147,5 +148,10 @@ "presets": [ "react-app" ] + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "postcss": "^8.5.3", + "sass": "^1.85.1" } } diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e05..0000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.scss b/src/App.scss new file mode 100644 index 0000000..2c53b72 --- /dev/null +++ b/src/App.scss @@ -0,0 +1,15 @@ +.App { + text-align: center; +} +.App-logo { + animation: App-logo-spin infinite 20s linear; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/src/App.tsx b/src/App.tsx index d249ead..80df7cd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,18 @@ import React from 'react'; -import logo from './logo.svg'; -import './App.css'; -import { Switch } from 'antd'; + +import './App.scss'; +import { Outlet } from 'react-router'; + function App() { return (
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - - -
- + APP + +
+ +
); } diff --git a/src/assets/icon_logo.png b/src/assets/icon_logo.png new file mode 100644 index 0000000..1b4961b Binary files /dev/null and b/src/assets/icon_logo.png differ diff --git a/src/assets/icon_logo.svg b/src/assets/icon_logo.svg new file mode 100644 index 0000000..36496e6 --- /dev/null +++ b/src/assets/icon_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icon_pwd.svg b/src/assets/icon_pwd.svg new file mode 100644 index 0000000..a875ae3 --- /dev/null +++ b/src/assets/icon_pwd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icon_user.svg b/src/assets/icon_user.svg new file mode 100644 index 0000000..46a984b --- /dev/null +++ b/src/assets/icon_user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/img_bg.jpg b/src/assets/img_bg.jpg new file mode 100644 index 0000000..3272aff Binary files /dev/null and b/src/assets/img_bg.jpg differ diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 0000000..73f9444 --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,3 @@ +export default function Footer() { + return
Footer
+} \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..6b7e272 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,3 @@ +export default function Header() { + return
Header
+} \ No newline at end of file diff --git a/src/index.css b/src/index.css index ec2585e..17df0e7 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,7 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', diff --git a/src/index.tsx b/src/index.tsx index 032464f..a46276d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,18 +1,37 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import Login from "./pages/login/Login"; +import Home from "./pages/home/Home"; +import App from "./App"; +import reportWebVitals from "./reportWebVitals"; -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); +const router = createBrowserRouter([ + { + path: "/", + element: , + children: [ + { + path: "home", + element: , + }, + ], + }, + { + path: "/login", + element: , + }, +]); + +const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); root.render( - - - + + + ); +console.log(process.env.REACT_APP_WS_URL); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 9dfc1c0..0000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/home/Home.module.scss b/src/pages/home/Home.module.scss new file mode 100644 index 0000000..4ee680e --- /dev/null +++ b/src/pages/home/Home.module.scss @@ -0,0 +1,3 @@ +.home { + color: red; +} \ No newline at end of file diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx new file mode 100644 index 0000000..fcae07f --- /dev/null +++ b/src/pages/home/Home.tsx @@ -0,0 +1,9 @@ +import "./Home.module.scss"; + +export default function Home() { + return ( + <> +
HOME
+ + ); +} diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx new file mode 100644 index 0000000..4fb3606 --- /dev/null +++ b/src/pages/login/Login.tsx @@ -0,0 +1,47 @@ +import { Button, Form, Input } from "antd"; +import icon_user from "../../assets/icon_user.svg"; +import icon_pwd from "../../assets/icon_pwd.svg"; +import img_bg from "../../assets/img_bg.jpg"; +import img_logo from "../../assets/icon_logo.png"; +import { useNavigate } from "react-router"; + +export default function Login() { + const navigate = useNavigate(); + + const onFinish = (values: any) => { + console.log("Received values of form: ", values); + navigate('/home'); + }; + + return ( +
+
+
+ + + + } placeholder="用户名" className="w-[280px]" /> + + + + } type="password" placeholder="密码" /> + + + + + +

CHINA LOGISTICS

+
+
+
+ ); +} diff --git a/src/services/httpRequest.ts b/src/services/httpRequest.ts new file mode 100644 index 0000000..46ed9f7 --- /dev/null +++ b/src/services/httpRequest.ts @@ -0,0 +1,64 @@ +import { Subject } from "rxjs"; + +export interface BaseResponse { + success: boolean; + code: string; + msg: string; + data: T; +} + +type HttpReqParam = { + url: string; + method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE"; + params?: Record; + encode?: "form" | "json"; // 入参编码类型 + headers?: Record; +}; + +export type ApiException = "invalidToken" | "serverError"; + +const exceptionSub = new Subject(); +export const exceptionOb = exceptionSub.asObservable(); + +function extHandle(res: BaseResponse) { + if (res.code === "A0230") { + // 访问令牌无效或已过期 + exceptionSub.next("invalidToken"); + } + return { + ...res, + success: res.code === "00000", + }; +} + +export default async function httpRequest({ url, method = "GET", params = {}, encode = "json", headers = {} }: HttpReqParam) { + const token = sessionStorage.getItem("token"); + if (token) { + headers = { Authorization: token, ...headers }; + } + if (method === "GET") { + const query = urlEncode(params); + const _url = query ? url + "?" + query : url; + const res = await fetch(_url, { headers }); + return res.json().then(res => extHandle(res) as T); + } else { + const body = encode === "json" ? JSON.stringify(params) : urlEncode(params); + const _headers = + encode === "json" + ? { "Content-Type": "application/json; charset=utf-8", ...headers } + : { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", ...headers }; + const res = await fetch(url, { method, headers: _headers, body }); + return res.json().then(res => extHandle(res) as T); + } +} +export function urlEncode(params?: Record) { + let query = ""; + if (params && Object.keys(params).length > 0) { + const qs = []; + for (let attr in params) { + qs.push(`${attr}=${encodeURIComponent(params[attr])}`); + } + query = qs.join("&"); + } + return query; +} diff --git a/src/services/socket.ts b/src/services/socket.ts new file mode 100644 index 0000000..82c1b3c --- /dev/null +++ b/src/services/socket.ts @@ -0,0 +1,111 @@ +import { Subject } from "rxjs"; + +export type SocketState = 'open' | 'close' | 'error' + +class WebSocketClient { + private ws: WebSocket | null = null; + private url: string; + private reconnectAttempts: number = -1; + private maxReconnectAttempts: number = 5; + private reconnectInterval: number = 3000; + + readonly dataOb = new Subject() + readonly stateOb = new Subject() + + constructor(url: string) { + this.url = url; + } + + // 连接 WebSocket + connect(): void { + try { + // WebSocket.CONNECTING (0) WebSocket.OPEN (1) + if (this.ws && this.ws.readyState <= 1) { + // 已连接 + console.log(`${this.url} 正在连接或已连接,无需重复连接`); + } else { + this.ws = new WebSocket(this.url); + this.bindEvents(); + } + } catch (error) { + console.error("WebSocket 连接失败:", error); + this.reconnect(); + } + } + + // 绑定事件 + private bindEvents(): void { + if (!this.ws) return; + + // 连接建立时的处理 + this.ws.onopen = () => { + console.log("WebSocket 连接已建立"); + this.reconnectAttempts = -1; // 重置重连次数 + this.stateOb.next('open') + }; + + // 接收消息的处理 + this.ws.onmessage = (event: MessageEvent) => { + try { + const data = JSON.parse(event.data); + // console.log('🚀 ~ WebSocketClient ~ bindEvents ~ data:', data) + this.dataOb.next(data) + } catch (error) { + console.error("消息解析错误:", error); + } + }; + + this.ws.onclose = () => { + this.stateOb.next('close') + console.log("WebSocket 连接已关闭"); + this.reconnect(); + }; + + this.ws.onerror = error => { + this.stateOb.next('error') + console.error("WebSocket 错误:", error); + }; + } + + // 重连机制 + private reconnect(): void { + if (this.reconnectAttempts === -1) { + this.reconnectAttempts = 0; + } + if (this.reconnectAttempts >= this.maxReconnectAttempts) { + console.log("达到最大重连次数,停止重连"); + this.reconnectAttempts = -1; + return; + } + + setTimeout(() => { + console.log(`尝试第 ${this.reconnectAttempts + 1} 次重连...`); + this.reconnectAttempts++; + this.connect(); + }, this.reconnectInterval); + } + + // 关闭连接 + disconnect(): void { + if (this.ws) { + this.ws.close(); + this.ws = null; + } + } +} + +const urlSocketMap = new Map(); + +// 导出 WebSocket 客户端 +export const createWebSocket = (url: string): WebSocketClient => { + if (urlSocketMap.has(url)) { + return urlSocketMap.get(url)!; + } else { + const client = new WebSocketClient(url); + urlSocketMap.set(url, client); + return client; + } +}; + + +export const sharedWsUrl = `ws://${process.env.REACT_APP_WS_URL}`; \ No newline at end of file diff --git a/src/styles/mixins.scss b/src/styles/mixins.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..1c1eb9a --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,5 @@ +export function formatRemainTime(seconds: number) { + const min = Math.floor(seconds / 60).toFixed(); + const sec = (seconds % 60).toFixed(); + return min.padStart(2, "0") + ":" + sec.padStart(2, "0"); +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..3555d90 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,15 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./src/**/*.{js,jsx,ts,tsx}"], + theme: { + colors: { + primary: "#1989fa", + white: "#FFF", + title: "#646566", + text: "#333", + warn: "#f05a28", + }, + extend: {}, + }, + plugins: [], +};