From 44d7cfcc6600d4abd4bc85ba2fa1e8f9c1889356 Mon Sep 17 00:00:00 2001 From: joker7391 Date: Wed, 5 Jun 2024 19:41:25 +0800 Subject: [PATCH 1/5] Initial Commit --- package.json | 5 +- src/App.css | 52 -------- src/App.tsx | 39 +----- src/components/update/Modal/index.tsx | 67 ---------- src/components/update/Modal/modal.css | 87 ------------ src/components/update/Progress/index.tsx | 22 --- src/components/update/Progress/progress.css | 21 --- src/components/update/README.md | 49 ------- src/components/update/README.zh-CN.md | 51 ------- src/components/update/index.tsx | 132 ------------------ src/components/update/update.css | 24 ---- src/demos/ipc.ts | 4 - src/demos/node.ts | 8 -- src/index.css | 91 ------------- src/main.tsx | 20 +-- src/page/Login.tsx | 141 ++++++++++++++++++++ tsconfig.json | 4 +- 17 files changed, 162 insertions(+), 655 deletions(-) delete mode 100644 src/components/update/Modal/index.tsx delete mode 100644 src/components/update/Modal/modal.css delete mode 100644 src/components/update/Progress/index.tsx delete mode 100644 src/components/update/Progress/progress.css delete mode 100644 src/components/update/README.md delete mode 100644 src/components/update/README.zh-CN.md delete mode 100644 src/components/update/index.tsx delete mode 100644 src/components/update/update.css delete mode 100644 src/demos/ipc.ts delete mode 100644 src/demos/node.ts create mode 100644 src/page/Login.tsx diff --git a/package.json b/package.json index 00b09473..bae8db6b 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,10 @@ "e2e": "playwright test" }, "dependencies": { - "electron-updater": "^6.1.8" + "@mui/material": "^5.15.19", + "@mui/styled-engine-sc": "^6.0.0-alpha.18", + "electron-updater": "^6.1.8", + "styled-components": "^6.1.11" }, "devDependencies": { "@playwright/test": "^1.42.1", diff --git a/src/App.css b/src/App.css index d02bb4ba..e69de29b 100644 --- a/src/App.css +++ b/src/App.css @@ -1,52 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo-box { - position: relative; - height: 9em; -} - -.logo { - position: absolute; - left: calc(50% - 4.5em); - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - .logo.electron { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} - -.flex-center { - display: flex; - align-items: center; - justify-content: center; -} diff --git a/src/App.tsx b/src/App.tsx index c5462d1d..befdcee3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,38 +1,13 @@ -import { useState } from 'react' -import UpdateElectron from '@/components/update' -import logoVite from './assets/logo-vite.svg' -import logoElectron from './assets/logo-electron.svg' -import './App.css' +import { useState } from "react"; +import "./App.css"; function App() { - const [count, setCount] = useState(0) + const [count, setCount] = useState(0); return ( -
-
- - Electron + Vite logo - Electron + Vite logo - -
-

Electron + Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Electron + Vite logo to learn more -

-
- Place static files into the/public folder Node logo -
- - +
+

Hello

- ) + ); } -export default App \ No newline at end of file +export default App; diff --git a/src/components/update/Modal/index.tsx b/src/components/update/Modal/index.tsx deleted file mode 100644 index 934f1a2b..00000000 --- a/src/components/update/Modal/index.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, { ReactNode } from 'react' -import { createPortal } from 'react-dom' -import './modal.css' - -const ModalTemplate: React.FC void - onOk?: () => void - width?: number -}>> = props => { - const { - title, - children, - footer, - cancelText = 'Cancel', - okText = 'OK', - onCancel, - onOk, - width = 530, - } = props - - return ( -
-
-
-
-
-
{title}
- - - - - - -
-
{children}
- {typeof footer !== 'undefined' ? ( -
- - -
- ) : footer} -
-
-
- ) -} - -const Modal = (props: Parameters[0] & { open: boolean }) => { - const { open, ...omit } = props - - return createPortal( - open ? ModalTemplate(omit) : null, - document.body, - ) -} - -export default Modal diff --git a/src/components/update/Modal/modal.css b/src/components/update/Modal/modal.css deleted file mode 100644 index d52cacba..00000000 --- a/src/components/update/Modal/modal.css +++ /dev/null @@ -1,87 +0,0 @@ -.update-modal { - --primary-color: rgb(224, 30, 90); - - .update-modal__mask { - width: 100vw; - height: 100vh; - position: fixed; - left: 0; - top: 0; - z-index: 9; - background: rgba(0, 0, 0, 0.45); - } - - .update-modal__warp { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 19; - } - - .update-modal__content { - box-shadow: 0 0 10px -4px rgb(130, 86, 208); - overflow: hidden; - border-radius: 4px; - - .content__header { - display: flex; - line-height: 38px; - background-color: var(--primary-color); - - .content__header-text { - font-weight: bold; - width: 0; - flex-grow: 1; - } - } - - .update-modal--close { - width: 30px; - height: 30px; - margin: 4px; - line-height: 34px; - text-align: center; - cursor: pointer; - - svg { - width: 17px; - height: 17px; - } - } - - .content__body { - padding: 10px; - background-color: #fff; - color: #333; - } - - .content__footer { - padding: 10px; - background-color: #fff; - display: flex; - justify-content: flex-end; - - button { - padding: 7px 11px; - background-color: var(--primary-color); - font-size: 14px; - margin-left: 10px; - - &:first-child { - margin-left: 0; - } - } - } - } - - .icon { - padding: 0 15px; - width: 20px; - fill: currentColor; - - &:hover { - color: rgba(0, 0, 0, 0.4); - } - } -} \ No newline at end of file diff --git a/src/components/update/Progress/index.tsx b/src/components/update/Progress/index.tsx deleted file mode 100644 index 1701dc57..00000000 --- a/src/components/update/Progress/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react' -import './progress.css' - -const Progress: React.FC> = props => { - const { percent = 0 } = props - - return ( -
-
-
-
- {(percent ?? 0).toString().substring(0, 4)}% -
- ) -} - -export default Progress diff --git a/src/components/update/Progress/progress.css b/src/components/update/Progress/progress.css deleted file mode 100644 index 2cef7c1b..00000000 --- a/src/components/update/Progress/progress.css +++ /dev/null @@ -1,21 +0,0 @@ -.update-progress { - display: flex; - align-items: center; - - .update-progress-pr { - border: 1px solid #000; - border-radius: 3px; - height: 6px; - width: 300px; - } - - .update-progress-rate { - height: 6px; - border-radius: 3px; - background-image: linear-gradient(to right, rgb(130, 86, 208) 0%, var(--primary-color) 100%) - } - - .update-progress-num { - margin: 0 10px; - } -} \ No newline at end of file diff --git a/src/components/update/README.md b/src/components/update/README.md deleted file mode 100644 index d119a117..00000000 --- a/src/components/update/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# electron-updater - -English | [简体中文](README.zh-CN.md) - -> Use `electron-updater` to realize the update detection, download and installation of the electric program. - -```sh -npm i electron-updater -``` - -### Main logic - -1. ##### Configuration of the update the service address and update information script: - - Add a `publish` field to `electron-builder.json5` for configuring the update address and which strategy to use as the update service. - - ``` json5 - { - "publish": { - "provider": "generic", - "channel": "latest", - "url": "https://foo.com/" - } - } - ``` - - For more information, please refer to : [electron-builder.json5...](https://github.com/electron-vite/electron-vite-react/blob/2f2880a9f19de50ff14a0785b32a4d5427477e55/electron-builder.json5#L38) - -2. ##### The update logic of Electron: - - - Checking if an update is available; - - Checking the version of the software on the server; - - Checking if an update is available; - - Downloading the new version of the software from the server (when an update is available); - - Installation method; - - For more information, please refer to : [update...](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) - -3. ##### Updating UI pages in Electron: - - The main function is to provide a UI page for users to trigger the update logic mentioned in (2.) above. Users can click on the page to trigger different update functions in Electron. - - For more information, please refer to : [components/update...](https://github.com/electron-vite/electron-vite-react/blob/main/src/components/update/index.tsx) - ---- - -Here it is recommended to trigger updates through user actions (in this project, Electron update function is triggered after the user clicks on the "Check for updates" button). - -For more information on using `electron-updater` for Electron updates, please refer to the documentation : [auto-update](https://www.electron.build/.html) diff --git a/src/components/update/README.zh-CN.md b/src/components/update/README.zh-CN.md deleted file mode 100644 index f2c65fcd..00000000 --- a/src/components/update/README.zh-CN.md +++ /dev/null @@ -1,51 +0,0 @@ -# electron-auto-update - -[English](README.md) | 简体中文 - -使用`electron-updater`实现electron程序的更新检测、下载和安装等功能。 - -```sh -npm i electron-updater -``` - -### 主要逻辑 - -1. ##### 更新地址、更新信息脚本的配置 - - 在`electron-builder.json5`添加`publish`字段,用来配置更新地址和使用哪种策略作为更新服务 - - ``` json5 - { - "publish": { - "provider": "generic", // 提供者、提供商 - "channel": "latest", // 生成yml文件的名称 - "url": "https://foo.com/" //更新地址 - } - } - ``` - -更多见 : [electron-builder.json5...](xxx) - -2. ##### Electron更新逻辑 - - - 检测更新是否可用; - - - 检测服务端的软件版本; - - - 检测更新是否可用; - - - 下载服务端新版软件(当更新可用); - - 安装方式; - - 更多见 : [update...](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) - -3. ##### Electron更新UI页面 - - 主要功能是:用户触发上述(2.)更新逻辑的UI页面。用户可以通过点击页面触发electron更新的不同功能。 - 更多见 : [components/update.ts...](https://github.com/electron-vite/electron-vite-react/tree/main/src/components/update/index.tsx) - ---- - -这里建议更新触发以用户操作触发(本项目的以用户点击 **更新检测** 后触发electron更新功能) - -关于更多使用`electron-updater`进行electron更新,见文档:[auto-update](https://www.electron.build/.html) diff --git a/src/components/update/index.tsx b/src/components/update/index.tsx deleted file mode 100644 index 2f73940e..00000000 --- a/src/components/update/index.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import type { ProgressInfo } from 'electron-updater' -import { useCallback, useEffect, useState } from 'react' -import Modal from '@/components/update/Modal' -import Progress from '@/components/update/Progress' -import './update.css' - -const Update = () => { - const [checking, setChecking] = useState(false) - const [updateAvailable, setUpdateAvailable] = useState(false) - const [versionInfo, setVersionInfo] = useState() - const [updateError, setUpdateError] = useState() - const [progressInfo, setProgressInfo] = useState>() - const [modalOpen, setModalOpen] = useState(false) - const [modalBtn, setModalBtn] = useState<{ - cancelText?: string - okText?: string - onCancel?: () => void - onOk?: () => void - }>({ - onCancel: () => setModalOpen(false), - onOk: () => window.ipcRenderer.invoke('start-download'), - }) - - const checkUpdate = async () => { - setChecking(true) - /** - * @type {import('electron-updater').UpdateCheckResult | null | { message: string, error: Error }} - */ - const result = await window.ipcRenderer.invoke('check-update') - setProgressInfo({ percent: 0 }) - setChecking(false) - setModalOpen(true) - if (result?.error) { - setUpdateAvailable(false) - setUpdateError(result?.error) - } - } - - const onUpdateCanAvailable = useCallback((_event: Electron.IpcRendererEvent, arg1: VersionInfo) => { - setVersionInfo(arg1) - setUpdateError(undefined) - // Can be update - if (arg1.update) { - setModalBtn(state => ({ - ...state, - cancelText: 'Cancel', - okText: 'Update', - onOk: () => window.ipcRenderer.invoke('start-download'), - })) - setUpdateAvailable(true) - } else { - setUpdateAvailable(false) - } - }, []) - - const onUpdateError = useCallback((_event: Electron.IpcRendererEvent, arg1: ErrorType) => { - setUpdateAvailable(false) - setUpdateError(arg1) - }, []) - - const onDownloadProgress = useCallback((_event: Electron.IpcRendererEvent, arg1: ProgressInfo) => { - setProgressInfo(arg1) - }, []) - - const onUpdateDownloaded = useCallback((_event: Electron.IpcRendererEvent, ...args: any[]) => { - setProgressInfo({ percent: 100 }) - setModalBtn(state => ({ - ...state, - cancelText: 'Later', - okText: 'Install now', - onOk: () => window.ipcRenderer.invoke('quit-and-install'), - })) - }, []) - - useEffect(() => { - // Get version information and whether to update - window.ipcRenderer.on('update-can-available', onUpdateCanAvailable) - window.ipcRenderer.on('update-error', onUpdateError) - window.ipcRenderer.on('download-progress', onDownloadProgress) - window.ipcRenderer.on('update-downloaded', onUpdateDownloaded) - - return () => { - window.ipcRenderer.off('update-can-available', onUpdateCanAvailable) - window.ipcRenderer.off('update-error', onUpdateError) - window.ipcRenderer.off('download-progress', onDownloadProgress) - window.ipcRenderer.off('update-downloaded', onUpdateDownloaded) - } - }, []) - - return ( - <> - -
- {updateError - ? ( -
-

Error downloading the latest version.

-

{updateError.message}

-
- ) : updateAvailable - ? ( -
-
The last version is: v{versionInfo?.newVersion}
-
v{versionInfo?.version} -> v{versionInfo?.newVersion}
-
-
Update progress:
-
- -
-
-
- ) - : ( -
{JSON.stringify(versionInfo ?? {}, null, 2)}
- )} -
-
- - - ) -} - -export default Update diff --git a/src/components/update/update.css b/src/components/update/update.css deleted file mode 100644 index e7769684..00000000 --- a/src/components/update/update.css +++ /dev/null @@ -1,24 +0,0 @@ -.modal-slot { - .update-progress { - display: flex; - } - - .new-version__target, - .update__progress { - margin-left: 40px; - } - - .progress__title { - margin-right: 10px; - } - - .progress__bar { - width: 0; - flex-grow: 1; - } - - .can-not-available { - padding: 20px; - text-align: center; - } -} \ No newline at end of file diff --git a/src/demos/ipc.ts b/src/demos/ipc.ts deleted file mode 100644 index ba4daa0c..00000000 --- a/src/demos/ipc.ts +++ /dev/null @@ -1,4 +0,0 @@ - -window.ipcRenderer.on('main-process-message', (_event, ...args) => { - console.log('[Receive Main-process message]:', ...args) -}) diff --git a/src/demos/node.ts b/src/demos/node.ts deleted file mode 100644 index 277e6a34..00000000 --- a/src/demos/node.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { lstat } from 'node:fs/promises' -import { cwd } from 'node:process' - -lstat(cwd()).then(stats => { - console.log('[fs.lstat]', stats) -}).catch(err => { - console.error(err) -}) diff --git a/src/index.css b/src/index.css index f6ec8bae..b5c61c95 100644 --- a/src/index.css +++ b/src/index.css @@ -1,94 +1,3 @@ @tailwind base; @tailwind components; @tailwind utilities; - -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -code { - background-color: #1a1a1a; - padding: 2px 4px; - margin: 0 4px; - border-radius: 4px; -} - -.card { - padding: 2em; -} - -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } - code { - background-color: #f9f9f9; - } -} diff --git a/src/main.tsx b/src/main.tsx index baaff398..aade5230 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,17 +1,11 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; -import './index.css' - -import './demos/ipc' -// If you want use Node.js, the`nodeIntegration` needs to be enabled in the Main process. -// import './demos/node' - -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - , -) + +); -postMessage({ payload: 'removeLoading' }, '*') +postMessage({ payload: "removeLoading" }, "*"); diff --git a/src/page/Login.tsx b/src/page/Login.tsx new file mode 100644 index 00000000..eaaf7fac --- /dev/null +++ b/src/page/Login.tsx @@ -0,0 +1,141 @@ +import * as React from "react"; +import Avatar from "@mui/material/Avatar"; +import Button from "@mui/material/Button"; +import CssBaseline from "@mui/material/CssBaseline"; +import TextField from "@mui/material/TextField"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Checkbox from "@mui/material/Checkbox"; +import Link from "@mui/material/Link"; +import Paper from "@mui/material/Paper"; +import Box from "@mui/material/Box"; +import Grid from "@mui/material/Grid"; +import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; +import Typography from "@mui/material/Typography"; +import { createTheme, ThemeProvider } from "@mui/material/styles"; + +function Copyright(props: any) { + return ( + + {"Copyright © "} + + Your Website + {" "} + {new Date().getFullYear()} + {"."} + + ); +} + +// TODO remove, this demo shouldn't need to reset the theme. +const defaultTheme = createTheme(); + +export default function SignInSide() { + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + const data = new FormData(event.currentTarget); + console.log({ + email: data.get("email"), + password: data.get("password"), + }); + }; + + return ( + + + + + t.palette.mode === "light" + ? t.palette.grey[50] + : t.palette.grey[900], + backgroundSize: "cover", + backgroundPosition: "center", + }} + /> + + + + + + + Sign in + + + + + } + label="Remember me" + /> + + + + + Forgot password? + + + + + {"Don't have an account? Sign Up"} + + + + + + + + + + ); +} diff --git a/tsconfig.json b/tsconfig.json index c9c47659..b3bcaa79 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,9 @@ "paths": { "@/*": [ "src/*" - ] + ], + "@components": ["./src/components"], + "@shared": ["./src/shared"] }, }, "include": ["src", "electron"], From 79edcac96163130e7653d142c50ca5163afd7974 Mon Sep 17 00:00:00 2001 From: joker7391 Date: Thu, 6 Jun 2024 13:28:39 +0800 Subject: [PATCH 2/5] Update login, routes and dashboard --- package.json | 14 ++- src/App.tsx | 11 ++- src/axiosConfig.ts | 10 +++ src/components/AdvancedTable.tsx | 0 src/components/Chart.tsx | 84 +++++++++++++++++ src/components/Deposits.tsx | 27 ++++++ src/components/ListItems.tsx | 109 ++++++++++++++++++++++ src/components/Orders.tsx | 100 +++++++++++++++++++++ src/components/Title.tsx | 14 +++ src/main.tsx | 30 ++++++- src/page/Dashboard.tsx | 149 +++++++++++++++++++++++++++++++ src/page/Login.tsx | 101 ++++++++++++++++----- src/store/useStore.ts | 13 +++ 13 files changed, 626 insertions(+), 36 deletions(-) create mode 100644 src/axiosConfig.ts create mode 100644 src/components/AdvancedTable.tsx create mode 100644 src/components/Chart.tsx create mode 100644 src/components/Deposits.tsx create mode 100644 src/components/ListItems.tsx create mode 100644 src/components/Orders.tsx create mode 100644 src/components/Title.tsx create mode 100644 src/page/Dashboard.tsx create mode 100644 src/store/useStore.ts diff --git a/package.json b/package.json index bae8db6b..8e03c5c3 100644 --- a/package.json +++ b/package.json @@ -20,15 +20,23 @@ "e2e": "playwright test" }, "dependencies": { + "@emotion/styled": "^11.11.5", + "@mui/icons-material": "^5.15.19", "@mui/material": "^5.15.19", "@mui/styled-engine-sc": "^6.0.0-alpha.18", + "@mui/x-charts": "^7.6.1", + "@mui/x-tree-view": "^7.6.1", + "axios": "^1.7.2", "electron-updater": "^6.1.8", - "styled-components": "^6.1.11" + "react-query": "^3.39.3", + "react-router-dom": "^6.23.1", + "styled-components": "^6.1.11", + "zustand": "^4.5.2" }, "devDependencies": { "@playwright/test": "^1.42.1", - "@types/react": "^18.2.64", - "@types/react-dom": "^18.2.21", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.18", "electron": "^29.1.1", diff --git a/src/App.tsx b/src/App.tsx index befdcee3..58af6543 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,12 @@ -import { useState } from "react"; -import "./App.css"; +import React from "react"; +import { Outlet } from "react-router-dom"; -function App() { - const [count, setCount] = useState(0); +const App: React.FC = () => { return (
-

Hello

+
); -} +}; export default App; diff --git a/src/axiosConfig.ts b/src/axiosConfig.ts new file mode 100644 index 00000000..09da4190 --- /dev/null +++ b/src/axiosConfig.ts @@ -0,0 +1,10 @@ +import axios from 'axios'; + +const axiosInstance = axios.create({ + baseURL: 'http://180.191.51.65:9130/api', + headers: { + 'Content-Type': 'application/json', + }, +}); + +export default axiosInstance; diff --git a/src/components/AdvancedTable.tsx b/src/components/AdvancedTable.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Chart.tsx b/src/components/Chart.tsx new file mode 100644 index 00000000..f1cfccf6 --- /dev/null +++ b/src/components/Chart.tsx @@ -0,0 +1,84 @@ +import * as React from "react"; +import { useTheme } from "@mui/material/styles"; +import { LineChart, axisClasses } from "@mui/x-charts"; +import { ChartsTextStyle } from "@mui/x-charts/ChartsText"; +import Title from "./Title"; + +// Generate Sales Data +function createData( + time: string, + amount?: number +): { time: string; amount: number | null } { + return { time, amount: amount ?? null }; +} + +const data = [ + createData("00:00", 0), + createData("03:00", 300), + createData("06:00", 600), + createData("09:00", 800), + createData("12:00", 1500), + createData("15:00", 2000), + createData("18:00", 2400), + createData("21:00", 2400), + createData("24:00"), +]; + +export default function Chart() { + const theme = useTheme(); + + return ( + + Today +
+ +
+
+ ); +} diff --git a/src/components/Deposits.tsx b/src/components/Deposits.tsx new file mode 100644 index 00000000..d6abd070 --- /dev/null +++ b/src/components/Deposits.tsx @@ -0,0 +1,27 @@ +import * as React from "react"; +import Link from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import Title from "@/components/Title"; + +function preventDefault(event: React.MouseEvent) { + event.preventDefault(); +} + +export default function Deposits() { + return ( + + Recent Deposits + + $3,024.00 + + + on 15 March, 2019 + +
+ + View balance + +
+
+ ); +} diff --git a/src/components/ListItems.tsx b/src/components/ListItems.tsx new file mode 100644 index 00000000..55cccf5e --- /dev/null +++ b/src/components/ListItems.tsx @@ -0,0 +1,109 @@ +import * as React from "react"; +import ListItemButton from "@mui/material/ListItemButton"; +import ListItemIcon from "@mui/material/ListItemIcon"; +import ListItemText from "@mui/material/ListItemText"; +import ListSubheader from "@mui/material/ListSubheader"; +import DashboardIcon from "@mui/icons-material/Dashboard"; +import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; +import PeopleIcon from "@mui/icons-material/People"; +import BarChartIcon from "@mui/icons-material/BarChart"; +import LayersIcon from "@mui/icons-material/Layers"; +import AssignmentIcon from "@mui/icons-material/Assignment"; +import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; +import { TreeViewBaseItem, TreeViewItemId } from "@mui/x-tree-view/models"; + +const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ + { + id: "grid", + label: "Data Grid", + children: [ + { id: "grid-community", label: "@mui/x-data-grid" }, + { id: "grid-pro", label: "@mui/x-data-grid-pro" }, + { id: "grid-premium", label: "@mui/x-data-grid-premium" }, + ], + }, + { + id: "pickers", + label: "Date and Time Pickers", + children: [ + { id: "pickers-community", label: "@mui/x-date-pickers" }, + { id: "pickers-pro", label: "@mui/x-date-pickers-pro" }, + ], + }, + { + id: "charts", + label: "Charts", + children: [{ id: "charts-community", label: "@mui/x-charts" }], + }, + { + id: "tree-view", + label: "Tree View", + children: [{ id: "tree-view-community", label: "@mui/x-tree-view" }], + }, +]; + +export const mainListItems = ( + + + + + + {/* */} + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export const secondaryListItems = ( + + + Saved reports + + + + + + + + + + + + + + + + + + + + +); diff --git a/src/components/Orders.tsx b/src/components/Orders.tsx new file mode 100644 index 00000000..dd726e8e --- /dev/null +++ b/src/components/Orders.tsx @@ -0,0 +1,100 @@ +import * as React from "react"; +import Link from "@mui/material/Link"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Title from "./Title"; + +// Generate Order Data +function createData( + id: number, + date: string, + name: string, + shipTo: string, + paymentMethod: string, + amount: number +) { + return { id, date, name, shipTo, paymentMethod, amount }; +} + +const rows = [ + createData( + 0, + "16 Mar, 2019", + "Elvis Presley", + "Tupelo, MS", + "VISA ⠀•••• 3719", + 312.44 + ), + createData( + 1, + "16 Mar, 2019", + "Paul McCartney", + "London, UK", + "VISA ⠀•••• 2574", + 866.99 + ), + createData( + 2, + "16 Mar, 2019", + "Tom Scholz", + "Boston, MA", + "MC ⠀•••• 1253", + 100.81 + ), + createData( + 3, + "16 Mar, 2019", + "Michael Jackson", + "Gary, IN", + "AMEX ⠀•••• 2000", + 654.39 + ), + createData( + 4, + "15 Mar, 2019", + "Bruce Springsteen", + "Long Branch, NJ", + "VISA ⠀•••• 5919", + 212.79 + ), +]; + +function preventDefault(event: React.MouseEvent) { + event.preventDefault(); +} + +export default function Orders() { + return ( + + Recent Orders + + + + Date + Name + Ship To + Payment Method + Sale Amount + + + + {rows.map((row) => ( + + {row.date} + {row.name} + {row.shipTo} + {row.paymentMethod} + {`$${row.amount}`} + + ))} + +
+ + See more orders + +
+ ); +} diff --git a/src/components/Title.tsx b/src/components/Title.tsx new file mode 100644 index 00000000..c9109cd6 --- /dev/null +++ b/src/components/Title.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import Typography from "@mui/material/Typography"; + +interface TitleProps { + children?: React.ReactNode; +} + +export default function Title(props: TitleProps) { + return ( + + {props.children} + + ); +} diff --git a/src/main.tsx b/src/main.tsx index aade5230..b7a81b83 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,11 +1,35 @@ import React from "react"; import ReactDOM from "react-dom/client"; +import { QueryClient, QueryClientProvider } from "react-query"; import App from "./App"; +import "./index.css"; +import { createHashRouter, RouterProvider } from "react-router-dom"; +import Login from "./page/Login"; +import Dashboard from "./page/Dashboard"; + +const router = createHashRouter([ + { + path: "/", + element: , + children: [ + { + path: "dashboard", + element: , + }, + { + path: "/", + element: , + }, + ], + }, +]); + +const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + + + ); - -postMessage({ payload: "removeLoading" }, "*"); diff --git a/src/page/Dashboard.tsx b/src/page/Dashboard.tsx new file mode 100644 index 00000000..c4f9dacb --- /dev/null +++ b/src/page/Dashboard.tsx @@ -0,0 +1,149 @@ +import * as React from "react"; +import { styled, createTheme, ThemeProvider } from "@mui/material/styles"; +import CssBaseline from "@mui/material/CssBaseline"; +import MuiDrawer from "@mui/material/Drawer"; +import Box from "@mui/material/Box"; +import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar"; +import Toolbar from "@mui/material/Toolbar"; +import List from "@mui/material/List"; +import Typography from "@mui/material/Typography"; +import Divider from "@mui/material/Divider"; +import IconButton from "@mui/material/IconButton"; +import Badge from "@mui/material/Badge"; +import Container from "@mui/material/Container"; +import Grid from "@mui/material/Grid"; +import Paper from "@mui/material/Paper"; +import Link from "@mui/material/Link"; +import MenuIcon from "@mui/icons-material/Menu"; +// import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; +import NotificationsIcon from "@mui/icons-material/Notifications"; +import { mainListItems, secondaryListItems } from "@/components/ListItems"; +import Chart from "@/components/Chart"; +import Deposits from "@/components/Deposits"; +import Orders from "@/components/Orders"; + +function Copyright(props: any) { + return ( + + {"Copyright © "} + + Your Website + {" "} + {new Date().getFullYear()} + {"."} + + ); +} + +const drawerWidth: number = 240; + +interface AppBarProps extends MuiAppBarProps { + open?: boolean; +} + +const AppBar = styled(MuiAppBar, { + shouldForwardProp: (prop) => prop !== "open", +})(({ theme, open }) => ({ + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), +})); + +const Drawer = styled(MuiDrawer, { + shouldForwardProp: (prop) => prop !== "open", +})(({ theme, open }) => ({ + "& .MuiDrawer-paper": { + position: "relative", + whiteSpace: "nowrap", + width: drawerWidth, + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + boxSizing: "border-box", + ...(!open && { + overflowX: "hidden", + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + width: theme.spacing(7), + [theme.breakpoints.up("sm")]: { + width: theme.spacing(9), + }, + }), + }, +})); + +const defaultTheme = createTheme(); + +export default function Dashboard() { + const [open, setOpen] = React.useState(true); + const toggleDrawer = () => { + setOpen(!open); + }; + + return ( + + + + + + + + + + Dashboard + + + + + + + + + + + + + {mainListItems} + + {secondaryListItems} + + + + + ); +} diff --git a/src/page/Login.tsx b/src/page/Login.tsx index eaaf7fac..00a6c565 100644 --- a/src/page/Login.tsx +++ b/src/page/Login.tsx @@ -1,17 +1,53 @@ -import * as React from "react"; -import Avatar from "@mui/material/Avatar"; -import Button from "@mui/material/Button"; -import CssBaseline from "@mui/material/CssBaseline"; -import TextField from "@mui/material/TextField"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Checkbox from "@mui/material/Checkbox"; -import Link from "@mui/material/Link"; -import Paper from "@mui/material/Paper"; -import Box from "@mui/material/Box"; -import Grid from "@mui/material/Grid"; +import React from "react"; +import { Outlet } from "react-router-dom"; +import { + Avatar, + Button, + CssBaseline, + TextField, + FormControlLabel, + Checkbox, + Link, + Paper, + Box, + Grid, + Typography, +} from "@mui/material"; import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; -import Typography from "@mui/material/Typography"; import { createTheme, ThemeProvider } from "@mui/material/styles"; +import { useMutation } from "react-query"; +import { useNavigate } from "react-router-dom"; +import useAuthStore from "../store/useStore"; + +const defaultTheme = createTheme(); + +interface LoginResponse { + code: number; + status: string; + payload: { + BearerToken: string; + }; +} + +const loginUser = async (credentials: { + usrcde: string; + usrpwd: string; +}): Promise => { + const response = await fetch("http://localhost:8080/api/user-ess/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(credentials), + }); + + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.statusText}`); + } + + const data = await response.json(); + return data; +}; function Copyright(props: any) { return ( @@ -31,17 +67,28 @@ function Copyright(props: any) { ); } -// TODO remove, this demo shouldn't need to reset the theme. -const defaultTheme = createTheme(); +export default function Login() { + const [error, setError] = React.useState(""); + const setUser = useAuthStore((state) => state.setUser); + const navigate = useNavigate(); + + const mutation = useMutation(loginUser, { + onSuccess: (data) => { + setUser(data.payload.BearerToken); + navigate("/dashboard"); + }, + onError: (err) => { + setError("Login failed. Please check your credentials and try again."); + }, + }); -export default function SignInSide() { const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); const data = new FormData(event.currentTarget); - console.log({ - email: data.get("email"), - password: data.get("password"), - }); + const usrcde = data.get("username") as string; + const usrpwd = data.get("password") as string; + setError(""); + mutation.mutate({ usrcde, usrpwd }); }; return ( @@ -91,10 +138,10 @@ export default function SignInSide() { margin="normal" required fullWidth - id="email" - label="Email Address" - name="email" - autoComplete="email" + id="username" + label="Username" + name="username" + autoComplete="username" autoFocus /> - Sign In + {mutation.isLoading ? "Signing in..." : "Sign In"} + {error && ( + + {error} + + )} diff --git a/src/store/useStore.ts b/src/store/useStore.ts new file mode 100644 index 00000000..55fd55fb --- /dev/null +++ b/src/store/useStore.ts @@ -0,0 +1,13 @@ +import {create} from 'zustand'; + +interface AuthState { + user: any | null; + setUser: (user: any) => void; +} + +const useAuthStore = create((set) => ({ + user: null, + setUser: (user) => set({ user }), +})); + +export default useAuthStore; From c174fafa8c092b4efa90298781219a7f4c360cad Mon Sep 17 00:00:00 2001 From: joker7391 Date: Thu, 6 Jun 2024 15:52:10 +0800 Subject: [PATCH 3/5] Updated and Dashboard --- package.json | 1 + src/api/userLogin.ts | 27 +++++++++ src/assets/SidebarData.tsx | 56 ++++++++++++++++++ src/components/ItemList.tsx | 98 +++++++++++++++++++++++++++++++ src/components/ListItems.tsx | 109 ----------------------------------- src/page/Dashboard.tsx | 43 ++++++-------- src/page/Login.tsx | 40 ++----------- 7 files changed, 205 insertions(+), 169 deletions(-) create mode 100644 src/api/userLogin.ts create mode 100644 src/assets/SidebarData.tsx create mode 100644 src/components/ItemList.tsx delete mode 100644 src/components/ListItems.tsx diff --git a/package.json b/package.json index 8e03c5c3..1efd19bc 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@emotion/styled": "^11.11.5", + "@material-ui/core": "^4.12.4", "@mui/icons-material": "^5.15.19", "@mui/material": "^5.15.19", "@mui/styled-engine-sc": "^6.0.0-alpha.18", diff --git a/src/api/userLogin.ts b/src/api/userLogin.ts new file mode 100644 index 00000000..42982ddc --- /dev/null +++ b/src/api/userLogin.ts @@ -0,0 +1,27 @@ +export interface LoginResponse { + code: number; + status: string; + payload: { + BearerToken: string; + }; + } + +export const loginUser = async (credentials: { + usrcde: string; + usrpwd: string; + }): Promise => { + const response = await fetch("http://localhost:8080/api/user-ess/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(credentials), + }); + + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.statusText}`); + } + + const data = await response.json(); + return data; + }; \ No newline at end of file diff --git a/src/assets/SidebarData.tsx b/src/assets/SidebarData.tsx new file mode 100644 index 00000000..52ad5020 --- /dev/null +++ b/src/assets/SidebarData.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import DashboardIcon from "@mui/icons-material/Dashboard"; +import StarBorder from "@mui/icons-material/StarBorder"; +import EmojiPeopleIcon from "@mui/icons-material/EmojiPeople"; +import PaymentIcon from "@mui/icons-material/Payment"; +import ReportIcon from "@mui/icons-material/Report"; + +interface SidebarItem { + id: number; + label: string; + icon: React.ReactNode; + items?: SidebarItem[]; +} + +export const sidebarData: SidebarItem[] = [ + { + id: 1, + label: "Dashboard", + icon: , + items: [ + { id: 11, label: "Overview", icon: }, + { id: 12, label: "Stats", icon: }, + { id: 13, label: "Reports", icon: }, + ], + }, + { + id: 2, + label: "Employees", + icon: , + items: [ + { id: 21, label: "All Employees", icon: }, + { id: 22, label: "Add Employee", icon: }, + { id: 23, label: "Manage Employees", icon: }, + ], + }, + { + id: 3, + label: "Payroll", + icon: , + items: [ + { id: 31, label: "Monthly Payroll", icon: }, + { id: 32, label: "Annual Payroll", icon: }, + { id: 33, label: "Payroll Settings", icon: }, + ], + }, + { + id: 4, + label: "Reports", + icon: , + items: [ + { id: 41, label: "Payroll Reports", icon: }, + { id: 42, label: "Employee Reports", icon: }, + { id: 43, label: "Tax Reports", icon: }, + ], + }, +]; diff --git a/src/components/ItemList.tsx b/src/components/ItemList.tsx new file mode 100644 index 00000000..088f6fe0 --- /dev/null +++ b/src/components/ItemList.tsx @@ -0,0 +1,98 @@ +import React, { useState } from "react"; +import { styled } from "@mui/system"; +import List from "@mui/material/List"; +import ListItem from "@mui/material/ListItem"; +import ListItemIcon from "@mui/material/ListItemIcon"; +import ListItemText from "@mui/material/ListItemText"; +import Collapse from "@mui/material/Collapse"; +import ExpandLess from "@mui/icons-material/ExpandLess"; +import ExpandMore from "@mui/icons-material/ExpandMore"; +import { sidebarData } from "@/assets/SidebarData"; +import { Box } from "@mui/material"; + +const Nested = styled(ListItem)(({ theme }) => ({ + paddingLeft: theme.spacing(4), +})); + +const NestedSecondLevel = styled(ListItem)(({ theme }) => ({ + paddingLeft: theme.spacing(8), +})); + +export function ItemList() { + const [open, setOpen] = useState(null); + const [openSecondLevel, setOpenSecondLevel] = useState(null); + + const handleClick = (item: string) => { + setOpen(open === item ? null : item); + }; + + const handleClickSecondLevel = (item: string) => { + setOpenSecondLevel(openSecondLevel === item ? null : item); + }; + + const handleMouseLeave = () => { + setOpen(null); + setOpenSecondLevel(null); + }; + + return ( + + {sidebarData.map((side) => ( + + handleClick(side.label)}> + {side.icon} + + {open === side.label ? : } + + + + + {side.items?.map((subItem) => ( + handleClickSecondLevel(subItem.label)} + > + {subItem.icon} + + {subItem.items ? ( + openSecondLevel === subItem.label ? ( + + ) : ( + + ) + ) : null} + + ))} + + {side.items?.map((subItem) => + subItem.items ? ( + + + {subItem.items.map((nestedItem) => ( + + {nestedItem.icon} + + + ))} + + + ) : null + )} + + + + ))} + + ); +} diff --git a/src/components/ListItems.tsx b/src/components/ListItems.tsx deleted file mode 100644 index 55cccf5e..00000000 --- a/src/components/ListItems.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import * as React from "react"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import ListItemText from "@mui/material/ListItemText"; -import ListSubheader from "@mui/material/ListSubheader"; -import DashboardIcon from "@mui/icons-material/Dashboard"; -import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; -import PeopleIcon from "@mui/icons-material/People"; -import BarChartIcon from "@mui/icons-material/BarChart"; -import LayersIcon from "@mui/icons-material/Layers"; -import AssignmentIcon from "@mui/icons-material/Assignment"; -import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; -import { TreeViewBaseItem, TreeViewItemId } from "@mui/x-tree-view/models"; - -const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ - { - id: "grid", - label: "Data Grid", - children: [ - { id: "grid-community", label: "@mui/x-data-grid" }, - { id: "grid-pro", label: "@mui/x-data-grid-pro" }, - { id: "grid-premium", label: "@mui/x-data-grid-premium" }, - ], - }, - { - id: "pickers", - label: "Date and Time Pickers", - children: [ - { id: "pickers-community", label: "@mui/x-date-pickers" }, - { id: "pickers-pro", label: "@mui/x-date-pickers-pro" }, - ], - }, - { - id: "charts", - label: "Charts", - children: [{ id: "charts-community", label: "@mui/x-charts" }], - }, - { - id: "tree-view", - label: "Tree View", - children: [{ id: "tree-view-community", label: "@mui/x-tree-view" }], - }, -]; - -export const mainListItems = ( - - - - - - {/* */} - - - - - - - - - - - - - - - - - - - - - - - - - - - -); - -export const secondaryListItems = ( - - - Saved reports - - - - - - - - - - - - - - - - - - - - -); diff --git a/src/page/Dashboard.tsx b/src/page/Dashboard.tsx index c4f9dacb..cd150277 100644 --- a/src/page/Dashboard.tsx +++ b/src/page/Dashboard.tsx @@ -10,17 +10,10 @@ import Typography from "@mui/material/Typography"; import Divider from "@mui/material/Divider"; import IconButton from "@mui/material/IconButton"; import Badge from "@mui/material/Badge"; -import Container from "@mui/material/Container"; -import Grid from "@mui/material/Grid"; -import Paper from "@mui/material/Paper"; import Link from "@mui/material/Link"; -import MenuIcon from "@mui/icons-material/Menu"; -// import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import NotificationsIcon from "@mui/icons-material/Notifications"; -import { mainListItems, secondaryListItems } from "@/components/ListItems"; -import Chart from "@/components/Chart"; -import Deposits from "@/components/Deposits"; -import Orders from "@/components/Orders"; + +import { ItemList } from "@/components/ItemList"; function Copyright(props: any) { return ( @@ -86,10 +79,18 @@ const defaultTheme = createTheme(); export default function Dashboard() { const [open, setOpen] = React.useState(true); - const toggleDrawer = () => { - setOpen(!open); + + const handleMouseEnter = () => { + if (!open) { + setOpen(true); + } }; + const handleMouseLeave = () => { + if (open) { + setOpen(false); + } + }; return ( @@ -100,17 +101,6 @@ export default function Dashboard() { pr: "24px", // keep right padding when drawer closed }} > - - - - - {mainListItems} + + - {secondaryListItems} diff --git a/src/page/Login.tsx b/src/page/Login.tsx index 00a6c565..4e657ba1 100644 --- a/src/page/Login.tsx +++ b/src/page/Login.tsx @@ -5,8 +5,6 @@ import { Button, CssBaseline, TextField, - FormControlLabel, - Checkbox, Link, Paper, Box, @@ -18,37 +16,9 @@ import { createTheme, ThemeProvider } from "@mui/material/styles"; import { useMutation } from "react-query"; import { useNavigate } from "react-router-dom"; import useAuthStore from "../store/useStore"; - +import { loginUser, LoginResponse } from "@/api/userLogin"; const defaultTheme = createTheme(); -interface LoginResponse { - code: number; - status: string; - payload: { - BearerToken: string; - }; -} - -const loginUser = async (credentials: { - usrcde: string; - usrpwd: string; -}): Promise => { - const response = await fetch("http://localhost:8080/api/user-ess/login", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(credentials), - }); - - if (!response.ok) { - throw new Error(`Network response was not ok: ${response.statusText}`); - } - - const data = await response.json(); - return data; -}; - function Copyright(props: any) { return ( - } label="Remember me" - /> + /> */} {error && ( {error} )} - {/* - - - Forgot password? - - - - - {"Don't have an account? Sign Up"} - - - */} diff --git a/src/store/useStore.ts b/src/store/useStore.ts index 55fd55fb..7f8c7ec9 100644 --- a/src/store/useStore.ts +++ b/src/store/useStore.ts @@ -1,13 +1,50 @@ -import {create} from 'zustand'; +import { create } from 'zustand'; interface AuthState { - user: any | null; - setUser: (user: any) => void; + token: string | null; + menus: any[]; + setUser: (token: string) => void; + fetchMenus: () => void; } const useAuthStore = create((set) => ({ - user: null, - setUser: (user) => set({ user }), + token: null, + menus: [], + setUser: (token) => set({ token }), + fetchMenus: async () => { + const token = getToken(); + if (!token) { + throw new Error('No token found'); + } + + const response = await fetch('http://180.191.51.65:9130/api/menus', { + headers: { + 'Authorization': `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error('Failed to fetch menus'); + } + + const data = await response.json(); + set({ menus: data.payload }); + }, })); +const getToken = () => { + try { + const tokenString = localStorage.getItem('token'); + if (!tokenString) { + return null; + } + const token = JSON.parse(tokenString); + return token?.BearerToken || null; + } catch (error) { + console.error('Error parsing token from localStorage', error); + return null; + } +}; + + export default useAuthStore;