committed by
GitHub
40 changed files with 7075 additions and 2766 deletions
@ -0,0 +1 @@ |
|||||
|
VITE_API_URL=http://localhost |
File diff suppressed because it is too large
@ -0,0 +1,22 @@ |
|||||
|
import { Button, Container, Text } from '@chakra-ui/react'; |
||||
|
import { Link } from '@tanstack/react-router'; |
||||
|
|
||||
|
const NotFound: React.FC = () => { |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Container h='100vh' |
||||
|
alignItems='stretch' |
||||
|
justifyContent='center' textAlign='center' maxW='sm' centerContent> |
||||
|
<Text fontSize='8xl' color='ui.main' fontWeight='bold' lineHeight='1' mb={4}>404</Text> |
||||
|
<Text fontSize='md'>Oops!</Text> |
||||
|
<Text fontSize='md'>Page not found.</Text> |
||||
|
<Button as={Link} to='/' color='ui.main' borderColor='ui.main' variant='outline' mt={4}>Go back</Button> |
||||
|
</Container> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default NotFound; |
||||
|
|
||||
|
|
@ -0,0 +1,33 @@ |
|||||
|
import { useQuery } from 'react-query'; |
||||
|
import { useNavigate } from '@tanstack/react-router'; |
||||
|
|
||||
|
import { Body_login_login_access_token as AccessToken, LoginService, UserOut, UsersService } from '../client'; |
||||
|
|
||||
|
const isLoggedIn = () => { |
||||
|
return localStorage.getItem('access_token') !== null; |
||||
|
}; |
||||
|
|
||||
|
const useAuth = () => { |
||||
|
const navigate = useNavigate(); |
||||
|
const { data: user, isLoading } = useQuery<UserOut | null, Error>('currentUser', UsersService.readUserMe, { |
||||
|
enabled: isLoggedIn(), |
||||
|
}); |
||||
|
|
||||
|
const login = async (data: AccessToken) => { |
||||
|
const response = await LoginService.loginAccessToken({ |
||||
|
formData: data, |
||||
|
}); |
||||
|
localStorage.setItem('access_token', response.access_token); |
||||
|
navigate({ to: '/' }); |
||||
|
}; |
||||
|
|
||||
|
const logout = () => { |
||||
|
localStorage.removeItem('access_token'); |
||||
|
navigate({ to: '/login' }); |
||||
|
}; |
||||
|
|
||||
|
return { login, logout, user, isLoading }; |
||||
|
} |
||||
|
|
||||
|
export { isLoggedIn }; |
||||
|
export default useAuth; |
@ -1,38 +0,0 @@ |
|||||
import { useUserStore } from '../store/user-store'; |
|
||||
import { Body_login_login_access_token as AccessToken, LoginService } from '../client'; |
|
||||
import { useUsersStore } from '../store/users-store'; |
|
||||
import { useItemsStore } from '../store/items-store'; |
|
||||
import { useNavigate } from 'react-router-dom'; |
|
||||
|
|
||||
const isLoggedIn = () => { |
|
||||
return localStorage.getItem('access_token') !== null; |
|
||||
}; |
|
||||
|
|
||||
const useAuth = () => { |
|
||||
const { getUser, resetUser } = useUserStore(); |
|
||||
const { resetUsers } = useUsersStore(); |
|
||||
const { resetItems } = useItemsStore(); |
|
||||
const navigate = useNavigate(); |
|
||||
|
|
||||
const login = async (data: AccessToken) => { |
|
||||
const response = await LoginService.loginAccessToken({ |
|
||||
formData: data, |
|
||||
}); |
|
||||
localStorage.setItem('access_token', response.access_token); |
|
||||
await getUser(); |
|
||||
navigate('/'); |
|
||||
}; |
|
||||
|
|
||||
const logout = () => { |
|
||||
localStorage.removeItem('access_token'); |
|
||||
resetUser(); |
|
||||
resetUsers(); |
|
||||
resetItems(); |
|
||||
navigate('/login'); |
|
||||
}; |
|
||||
|
|
||||
return { login, logout }; |
|
||||
} |
|
||||
|
|
||||
export { isLoggedIn }; |
|
||||
export default useAuth; |
|
@ -1,35 +1,33 @@ |
|||||
import React from 'react'; |
|
||||
import ReactDOM from 'react-dom/client'; |
import ReactDOM from 'react-dom/client'; |
||||
|
import { ChakraProvider } from '@chakra-ui/react'; |
||||
import { ChakraProvider } from '@chakra-ui/provider'; |
import { QueryClient, QueryClientProvider } from 'react-query'; |
||||
import { createStandaloneToast } from '@chakra-ui/toast'; |
import { RouterProvider, createRouter } from '@tanstack/react-router' |
||||
import { RouterProvider, createBrowserRouter } from 'react-router-dom'; |
import { routeTree } from './routeTree.gen' |
||||
|
|
||||
import { OpenAPI } from './client'; |
import { OpenAPI } from './client'; |
||||
import { isLoggedIn } from './hooks/useAuth'; |
|
||||
import privateRoutes from './routes/private_route'; |
|
||||
import publicRoutes from './routes/public_route'; |
|
||||
import theme from './theme'; |
import theme from './theme'; |
||||
|
import { StrictMode } from 'react'; |
||||
|
|
||||
OpenAPI.BASE = import.meta.env.VITE_API_URL; |
OpenAPI.BASE = import.meta.env.VITE_API_URL; |
||||
OpenAPI.TOKEN = async () => { |
OpenAPI.TOKEN = async () => { |
||||
return localStorage.getItem('access_token') || ''; |
return localStorage.getItem('access_token') || ''; |
||||
} |
} |
||||
|
|
||||
const router = createBrowserRouter([ |
const queryClient = new QueryClient(); |
||||
isLoggedIn() ? privateRoutes() : {}, |
|
||||
...publicRoutes(), |
|
||||
]); |
|
||||
|
|
||||
const { ToastContainer } = createStandaloneToast(); |
const router = createRouter({ routeTree }) |
||||
|
declare module '@tanstack/react-router' { |
||||
|
interface Register { |
||||
|
router: typeof router |
||||
|
} |
||||
|
} |
||||
|
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render( |
ReactDOM.createRoot(document.getElementById('root')!).render( |
||||
<React.StrictMode> |
<StrictMode> |
||||
<ChakraProvider theme={theme}> |
<ChakraProvider theme={theme}> |
||||
|
<QueryClientProvider client={queryClient}> |
||||
<RouterProvider router={router} /> |
<RouterProvider router={router} /> |
||||
<ToastContainer /> |
</QueryClientProvider> |
||||
</ChakraProvider> |
</ChakraProvider> |
||||
</React.StrictMode>, |
</StrictMode> |
||||
) |
); |
||||
|
|
@ -1,22 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import { Container, Text } from '@chakra-ui/react'; |
|
||||
|
|
||||
import { useUserStore } from '../store/user-store'; |
|
||||
|
|
||||
|
|
||||
const Dashboard: React.FC = () => { |
|
||||
const { user } = useUserStore(); |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<Container maxW='full' pt={12}> |
|
||||
<Text fontSize='2xl'>Hi, {user?.full_name || user?.email} 👋🏼</Text> |
|
||||
<Text>Welcome back, nice to see you again!</Text> |
|
||||
</Container> |
|
||||
</> |
|
||||
|
|
||||
) |
|
||||
} |
|
||||
|
|
||||
export default Dashboard; |
|
@ -1,25 +0,0 @@ |
|||||
import { Button, Container, Text } from '@chakra-ui/react'; |
|
||||
import { Link, useRouteError } from 'react-router-dom'; |
|
||||
|
|
||||
const ErrorPage: React.FC = () => { |
|
||||
const error = useRouteError(); |
|
||||
console.log(error); |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<Container h='100vh' |
|
||||
alignItems='stretch' |
|
||||
justifyContent='center' textAlign='center' maxW='xs' centerContent> |
|
||||
<Text fontSize='8xl' color='ui.main' fontWeight='bold' lineHeight='1' mb={4}>Oops!</Text> |
|
||||
<Text fontSize='md'>Houston, we have a problem.</Text> |
|
||||
<Text fontSize='md'>An unexpected error has occurred.</Text> |
|
||||
{/* <Text color='ui.danger'><i>{error.statusText || error.message}</i></Text> */} |
|
||||
<Button as={Link} to='/' color='ui.main' borderColor='ui.main' variant='outline' mt={4}>Go back to Home</Button> |
|
||||
</Container> |
|
||||
</> |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
export default ErrorPage; |
|
||||
|
|
||||
|
|
@ -1,32 +0,0 @@ |
|||||
import React, { useEffect } from 'react'; |
|
||||
|
|
||||
import { Flex } from '@chakra-ui/react'; |
|
||||
import { Outlet } from 'react-router-dom'; |
|
||||
|
|
||||
import Sidebar from '../components/Common/Sidebar'; |
|
||||
import UserMenu from '../components/Common/UserMenu'; |
|
||||
import { useUserStore } from '../store/user-store'; |
|
||||
import { isLoggedIn } from '../hooks/useAuth'; |
|
||||
|
|
||||
const Layout: React.FC = () => { |
|
||||
const { getUser } = useUserStore(); |
|
||||
|
|
||||
useEffect(() => { |
|
||||
const fetchUser = async () => { |
|
||||
if (isLoggedIn()) { |
|
||||
await getUser(); |
|
||||
} |
|
||||
}; |
|
||||
fetchUser(); |
|
||||
}, []); |
|
||||
|
|
||||
return ( |
|
||||
<Flex maxW='large' h='auto' position='relative'> |
|
||||
<Sidebar /> |
|
||||
<Outlet /> |
|
||||
<UserMenu /> |
|
||||
</Flex> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default Layout; |
|
@ -0,0 +1,118 @@ |
|||||
|
/* prettier-ignore-start */ |
||||
|
|
||||
|
/* eslint-disable */ |
||||
|
|
||||
|
// @ts-nocheck
|
||||
|
|
||||
|
// noinspection JSUnusedGlobalSymbols
|
||||
|
|
||||
|
// This file is auto-generated by TanStack Router
|
||||
|
|
||||
|
// Import Routes
|
||||
|
|
||||
|
import { Route as rootRoute } from './routes/__root' |
||||
|
import { Route as ResetPasswordImport } from './routes/reset-password' |
||||
|
import { Route as RecoverPasswordImport } from './routes/recover-password' |
||||
|
import { Route as LoginImport } from './routes/login' |
||||
|
import { Route as LayoutImport } from './routes/_layout' |
||||
|
import { Route as LayoutIndexImport } from './routes/_layout/index' |
||||
|
import { Route as LayoutSettingsImport } from './routes/_layout/settings' |
||||
|
import { Route as LayoutItemsImport } from './routes/_layout/items' |
||||
|
import { Route as LayoutAdminImport } from './routes/_layout/admin' |
||||
|
|
||||
|
// Create/Update Routes
|
||||
|
|
||||
|
const ResetPasswordRoute = ResetPasswordImport.update({ |
||||
|
path: '/reset-password', |
||||
|
getParentRoute: () => rootRoute, |
||||
|
} as any) |
||||
|
|
||||
|
const RecoverPasswordRoute = RecoverPasswordImport.update({ |
||||
|
path: '/recover-password', |
||||
|
getParentRoute: () => rootRoute, |
||||
|
} as any) |
||||
|
|
||||
|
const LoginRoute = LoginImport.update({ |
||||
|
path: '/login', |
||||
|
getParentRoute: () => rootRoute, |
||||
|
} as any) |
||||
|
|
||||
|
const LayoutRoute = LayoutImport.update({ |
||||
|
id: '/_layout', |
||||
|
getParentRoute: () => rootRoute, |
||||
|
} as any) |
||||
|
|
||||
|
const LayoutIndexRoute = LayoutIndexImport.update({ |
||||
|
path: '/', |
||||
|
getParentRoute: () => LayoutRoute, |
||||
|
} as any) |
||||
|
|
||||
|
const LayoutSettingsRoute = LayoutSettingsImport.update({ |
||||
|
path: '/settings', |
||||
|
getParentRoute: () => LayoutRoute, |
||||
|
} as any) |
||||
|
|
||||
|
const LayoutItemsRoute = LayoutItemsImport.update({ |
||||
|
path: '/items', |
||||
|
getParentRoute: () => LayoutRoute, |
||||
|
} as any) |
||||
|
|
||||
|
const LayoutAdminRoute = LayoutAdminImport.update({ |
||||
|
path: '/admin', |
||||
|
getParentRoute: () => LayoutRoute, |
||||
|
} as any) |
||||
|
|
||||
|
// Populate the FileRoutesByPath interface
|
||||
|
|
||||
|
declare module '@tanstack/react-router' { |
||||
|
interface FileRoutesByPath { |
||||
|
'/_layout': { |
||||
|
preLoaderRoute: typeof LayoutImport |
||||
|
parentRoute: typeof rootRoute |
||||
|
} |
||||
|
'/login': { |
||||
|
preLoaderRoute: typeof LoginImport |
||||
|
parentRoute: typeof rootRoute |
||||
|
} |
||||
|
'/recover-password': { |
||||
|
preLoaderRoute: typeof RecoverPasswordImport |
||||
|
parentRoute: typeof rootRoute |
||||
|
} |
||||
|
'/reset-password': { |
||||
|
preLoaderRoute: typeof ResetPasswordImport |
||||
|
parentRoute: typeof rootRoute |
||||
|
} |
||||
|
'/_layout/admin': { |
||||
|
preLoaderRoute: typeof LayoutAdminImport |
||||
|
parentRoute: typeof LayoutImport |
||||
|
} |
||||
|
'/_layout/items': { |
||||
|
preLoaderRoute: typeof LayoutItemsImport |
||||
|
parentRoute: typeof LayoutImport |
||||
|
} |
||||
|
'/_layout/settings': { |
||||
|
preLoaderRoute: typeof LayoutSettingsImport |
||||
|
parentRoute: typeof LayoutImport |
||||
|
} |
||||
|
'/_layout/': { |
||||
|
preLoaderRoute: typeof LayoutIndexImport |
||||
|
parentRoute: typeof LayoutImport |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Create and export the route tree
|
||||
|
|
||||
|
export const routeTree = rootRoute.addChildren([ |
||||
|
LayoutRoute.addChildren([ |
||||
|
LayoutAdminRoute, |
||||
|
LayoutItemsRoute, |
||||
|
LayoutSettingsRoute, |
||||
|
LayoutIndexRoute, |
||||
|
]), |
||||
|
LoginRoute, |
||||
|
RecoverPasswordRoute, |
||||
|
ResetPasswordRoute, |
||||
|
]) |
||||
|
|
||||
|
/* prettier-ignore-end */ |
@ -0,0 +1,13 @@ |
|||||
|
import { createRootRoute, Outlet } from '@tanstack/react-router' |
||||
|
import { TanStackRouterDevtools } from '@tanstack/router-devtools' |
||||
|
import NotFound from '../components/Common/NotFound' |
||||
|
|
||||
|
export const Route = createRootRoute({ |
||||
|
component: () => ( |
||||
|
<> |
||||
|
<Outlet /> |
||||
|
<TanStackRouterDevtools /> |
||||
|
</> |
||||
|
), |
||||
|
notFoundComponent: () => <NotFound />, |
||||
|
}) |
@ -0,0 +1,38 @@ |
|||||
|
import { Flex, Spinner } from '@chakra-ui/react'; |
||||
|
import { Outlet, createFileRoute, redirect } from '@tanstack/react-router'; |
||||
|
|
||||
|
import Sidebar from '../components/Common/Sidebar'; |
||||
|
import UserMenu from '../components/Common/UserMenu'; |
||||
|
import useAuth, { isLoggedIn } from '../hooks/useAuth'; |
||||
|
|
||||
|
|
||||
|
export const Route = createFileRoute('/_layout')({ |
||||
|
component: Layout, |
||||
|
beforeLoad: async () => { |
||||
|
if (!isLoggedIn()) { |
||||
|
throw redirect({ |
||||
|
to: '/login', |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
function Layout() { |
||||
|
const { isLoading } = useAuth(); |
||||
|
|
||||
|
return ( |
||||
|
<Flex maxW='large' h='auto' position='relative'> |
||||
|
<Sidebar /> |
||||
|
{isLoading ? ( |
||||
|
<Flex justify='center' align='center' height='100vh' width='full'> |
||||
|
<Spinner size='xl' color='ui.main' /> |
||||
|
</Flex> |
||||
|
) : ( |
||||
|
<Outlet /> |
||||
|
)} |
||||
|
<UserMenu /> |
||||
|
</Flex> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Layout; |
@ -0,0 +1,27 @@ |
|||||
|
|
||||
|
import { Container, Text } from '@chakra-ui/react'; |
||||
|
import { useQueryClient } from 'react-query'; |
||||
|
import { createFileRoute } from '@tanstack/react-router'; |
||||
|
|
||||
|
import { UserOut } from '../../client'; |
||||
|
|
||||
|
export const Route = createFileRoute('/_layout/')({ |
||||
|
component: Dashboard, |
||||
|
}) |
||||
|
|
||||
|
function Dashboard() { |
||||
|
const queryClient = useQueryClient(); |
||||
|
|
||||
|
const currentUser = queryClient.getQueryData<UserOut>('currentUser'); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Container maxW='full' pt={12}> |
||||
|
<Text fontSize='2xl'>Hi, {currentUser?.full_name || currentUser?.email} 👋🏼</Text> |
||||
|
<Text>Welcome back, nice to see you again!</Text> |
||||
|
</Container> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Dashboard; |
@ -1,21 +0,0 @@ |
|||||
import Admin from '../pages/Admin'; |
|
||||
import Dashboard from '../pages/Dashboard'; |
|
||||
import ErrorPage from '../pages/ErrorPage'; |
|
||||
import Items from '../pages/Items'; |
|
||||
import Layout from '../pages/Layout'; |
|
||||
import UserSettings from '../pages/UserSettings'; |
|
||||
|
|
||||
export default function privateRoutes() { |
|
||||
|
|
||||
return { |
|
||||
path: '/', |
|
||||
element: <Layout />, |
|
||||
errorElement: <ErrorPage />, |
|
||||
children: [ |
|
||||
{ path: '/', element: <Dashboard /> }, |
|
||||
{ path: 'items', element: <Items /> }, |
|
||||
{ path: 'admin', element: <Admin /> }, |
|
||||
{ path: 'settings', element: <UserSettings /> }, |
|
||||
], |
|
||||
}; |
|
||||
} |
|
@ -1,15 +0,0 @@ |
|||||
import ErrorPage from '../pages/ErrorPage'; |
|
||||
import Login from '../pages/Login'; |
|
||||
import RecoverPassword from '../pages/RecoverPassword'; |
|
||||
import ResetPassword from '../pages/ResetPassword'; |
|
||||
|
|
||||
export default function publicRoutes() { |
|
||||
return [ |
|
||||
{ path: '/login', element: <Login />, errorElement: <ErrorPage /> }, |
|
||||
{ path: 'recover-password', element: <RecoverPassword />, errorElement: <ErrorPage /> }, |
|
||||
{ path: 'reset-password', element: <ResetPassword />, errorElement: <ErrorPage /> }, |
|
||||
// TODO: complete this
|
|
||||
// { path: '*', element: <Navigate to='/login' replace /> }
|
|
||||
]; |
|
||||
} |
|
||||
|
|
@ -1,36 +0,0 @@ |
|||||
import { create } from 'zustand'; |
|
||||
import { ItemCreate, ItemOut, ItemUpdate, ItemsService } from '../client'; |
|
||||
|
|
||||
interface ItemsStore { |
|
||||
items: ItemOut[]; |
|
||||
getItems: () => Promise<void>; |
|
||||
addItem: (item: ItemCreate) => Promise<void>; |
|
||||
editItem: (id: number, item: ItemUpdate) => Promise<void>; |
|
||||
deleteItem: (id: number) => Promise<void>; |
|
||||
resetItems: () => void; |
|
||||
} |
|
||||
|
|
||||
export const useItemsStore = create<ItemsStore>((set) => ({ |
|
||||
items: [], |
|
||||
getItems: async () => { |
|
||||
const itemsResponse = await ItemsService.readItems({ skip: 0, limit: 10 }); |
|
||||
set({ items: itemsResponse.data }); |
|
||||
}, |
|
||||
addItem: async (item: ItemCreate) => { |
|
||||
const itemResponse = await ItemsService.createItem({ requestBody: item }); |
|
||||
set((state) => ({ items: [...state.items, itemResponse] })); |
|
||||
}, |
|
||||
editItem: async (id: number, item: ItemUpdate) => { |
|
||||
const itemResponse = await ItemsService.updateItem({ id: id, requestBody: item }); |
|
||||
set((state) => ({ |
|
||||
items: state.items.map((item) => (item.id === id ? itemResponse : item)) |
|
||||
})); |
|
||||
}, |
|
||||
deleteItem: async (id: number) => { |
|
||||
await ItemsService.deleteItem({ id }); |
|
||||
set((state) => ({ items: state.items.filter((item) => item.id !== id) })); |
|
||||
}, |
|
||||
resetItems: () => { |
|
||||
set({ items: [] }); |
|
||||
} |
|
||||
})); |
|
@ -1,28 +0,0 @@ |
|||||
import { create } from 'zustand'; |
|
||||
import { UpdatePassword, UserOut, UserUpdateMe, UsersService } from '../client'; |
|
||||
|
|
||||
interface UserStore { |
|
||||
user: UserOut | null; |
|
||||
getUser: () => Promise<void>; |
|
||||
editUser: (user: UserUpdateMe) => Promise<void>; |
|
||||
editPassword: (password: UpdatePassword) => Promise<void>; |
|
||||
resetUser: () => void; |
|
||||
} |
|
||||
|
|
||||
export const useUserStore = create<UserStore>((set) => ({ |
|
||||
user: null, |
|
||||
getUser: async () => { |
|
||||
const user = await UsersService.readUserMe(); |
|
||||
set({ user }); |
|
||||
}, |
|
||||
editUser: async (user: UserUpdateMe) => { |
|
||||
const updatedUser = await UsersService.updateUserMe({ requestBody: user }); |
|
||||
set((state) => ({ user: { ...state.user, ...updatedUser } })); |
|
||||
}, |
|
||||
editPassword: async (password: UpdatePassword) => { |
|
||||
await UsersService.updatePasswordMe({ requestBody: password }); |
|
||||
}, |
|
||||
resetUser: () => { |
|
||||
set({ user: null }); |
|
||||
} |
|
||||
})); |
|
@ -1,36 +0,0 @@ |
|||||
import { create } from "zustand"; |
|
||||
import { UserCreate, UserOut, UserUpdate, UsersService } from "../client"; |
|
||||
|
|
||||
interface UsersStore { |
|
||||
users: UserOut[]; |
|
||||
getUsers: () => Promise<void>; |
|
||||
addUser: (user: UserCreate) => Promise<void>; |
|
||||
editUser: (id: number, user: UserUpdate) => Promise<void>; |
|
||||
deleteUser: (id: number) => Promise<void>; |
|
||||
resetUsers: () => void; |
|
||||
} |
|
||||
|
|
||||
export const useUsersStore = create<UsersStore>((set) => ({ |
|
||||
users: [], |
|
||||
getUsers: async () => { |
|
||||
const usersResponse = await UsersService.readUsers({ skip: 0, limit: 10 }); |
|
||||
set({ users: usersResponse.data }); |
|
||||
}, |
|
||||
addUser: async (user: UserCreate) => { |
|
||||
const userResponse = await UsersService.createUser({ requestBody: user }); |
|
||||
set((state) => ({ users: [...state.users, userResponse] })); |
|
||||
}, |
|
||||
editUser: async (id: number, user: UserUpdate) => { |
|
||||
const userResponse = await UsersService.updateUser({ userId: id, requestBody: user }); |
|
||||
set((state) => ({ |
|
||||
users: state.users.map((user) => (user.id === id ? userResponse : user)) |
|
||||
})); |
|
||||
}, |
|
||||
deleteUser: async (id: number) => { |
|
||||
await UsersService.deleteUser({ userId: id }); |
|
||||
set((state) => ({ users: state.users.filter((user) => user.id !== id) })); |
|
||||
}, |
|
||||
resetUsers: () => { |
|
||||
set({ users: [] }); |
|
||||
} |
|
||||
})) |
|
@ -1,7 +1,8 @@ |
|||||
import { defineConfig } from 'vite' |
|
||||
import react from '@vitejs/plugin-react-swc' |
import react from '@vitejs/plugin-react-swc' |
||||
|
import { TanStackRouterVite } from '@tanstack/router-vite-plugin' |
||||
|
import { defineConfig } from 'vite' |
||||
|
|
||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||
export default defineConfig({ |
export default defineConfig({ |
||||
plugins: [react()], |
plugins: [react(), TanStackRouterVite()], |
||||
}) |
}) |
||||
|
Loading…
Reference in new issue