added spa

This commit is contained in:
Max 2025-02-22 15:09:28 +03:00
parent 8fd29f043d
commit 62617bfb6f
12 changed files with 236 additions and 29 deletions

View File

@ -9,6 +9,7 @@
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@hello-pangea/dnd": "^18.0.1", "@hello-pangea/dnd": "^18.0.1",
"@radix-ui/react-form": "^0.1.2",
"@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-icons": "^1.3.2",
"@radix-ui/themes": "^3.2.0", "@radix-ui/themes": "^3.2.0",
"@tailwindcss/vite": "^4.0.6", "@tailwindcss/vite": "^4.0.6",
@ -18,6 +19,7 @@
"jotai": "^2.12.0", "jotai": "^2.12.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-router-dom": "^6.26.2",
"tailwindcss": "^4.0.6" "tailwindcss": "^4.0.6"
}, },
"devDependencies": { "devDependencies": {
@ -2545,6 +2547,15 @@
} }
} }
}, },
"node_modules/@remix-run/router": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
"integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.34.7", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz",
@ -5323,6 +5334,38 @@
} }
} }
}, },
"node_modules/react-router": {
"version": "6.26.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz",
"integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.19.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.26.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz",
"integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.19.2",
"react-router": "6.26.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-style-singleton": { "node_modules/react-style-singleton": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",

View File

@ -11,6 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@hello-pangea/dnd": "^18.0.1", "@hello-pangea/dnd": "^18.0.1",
"@radix-ui/react-form": "^0.1.2",
"@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-icons": "^1.3.2",
"@radix-ui/themes": "^3.2.0", "@radix-ui/themes": "^3.2.0",
"@tailwindcss/vite": "^4.0.6", "@tailwindcss/vite": "^4.0.6",
@ -20,6 +21,7 @@
"jotai": "^2.12.0", "jotai": "^2.12.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-router-dom": "^6.26.2",
"tailwindcss": "^4.0.6" "tailwindcss": "^4.0.6"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,14 +1,21 @@
import { Theme } from '@radix-ui/themes'; import { Theme } from '@radix-ui/themes';
import '@radix-ui/themes/styles.css'; import '@radix-ui/themes/styles.css';
import { QueryClientProvider } from '@tanstack/react-query';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import queryClient from './api/queryClient';
import './App.css'; import './App.css';
import MainBoard from './components/MainBoard/MainBoard'; import MyRoutes from './routes/routes';
const router = createBrowserRouter(MyRoutes);
function App() { function App() {
return ( return (
<Theme accentColor="amber" grayColor="gray"> <Theme className='size-full' accentColor="amber" grayColor="gray">
<MainBoard /> <QueryClientProvider client={queryClient}>
{/* <ThemePanel /> */} {/* <ThemePanel /> */}
</Theme> <RouterProvider router={router} />
</QueryClientProvider>
</Theme>
); );
} }

View File

@ -0,0 +1,14 @@
import axios from "axios";
const BASE_URL = 'http://localhost:4567';
export const axiosBase = axios.create({
baseURL: BASE_URL,
});
export const axiosAuth = axios.create({
baseURL: BASE_URL,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}` // Maybe we will use cookies
}
});

View File

@ -0,0 +1,11 @@
import { QueryClient } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 2,
},
},
})
export default queryClient;

View File

@ -8,29 +8,26 @@ const tasks = [
]; ];
type TCardGroup = { type TCardGroup = {
id: string id: string;
} };
export default function CardGroup(props: TCardGroup) { export default function CardGroup(props: TCardGroup) {
return ( return (
<Droppable droppableId={props.id}> <Droppable droppableId={props.id}>
{(provided) => ( {(provided) => (
<Box <Box ref={provided.innerRef} {...provided.droppableProps}>
ref={provided.innerRef} {tasks.map((task, i) => (
{...provided.droppableProps} <TaskCard
> key={task.id}
{tasks.map((task, i) => ( id={task.id.toString() + props.id}
<TaskCard title={task.title}
key={task.id} description={task.description}
id={task.id.toString() + props.id} index={i}
title={task.title} />
description={task.description} ))}
index={i} {provided.placeholder}
/> </Box>
))} )}
{provided.placeholder} </Droppable>
</Box>
)}
</Droppable>
); );
} }

View File

@ -1 +1,5 @@
@import 'tailwindcss'; @import 'tailwindcss';
#root {
height: 100vh;
}

View File

@ -1,5 +1,5 @@
import { createRoot } from "react-dom/client"; import { createRoot } from 'react-dom/client';
import App from "./App.tsx"; import App from './App.tsx';
import "./index.css"; import './index.css';
createRoot(document.getElementById("root")!).render(<App />); createRoot(document.getElementById('root')!).render(<App />);

View File

@ -0,0 +1,52 @@
import { Button, Card, Heading, Text, TextField } from '@radix-ui/themes';
import { Form } from 'radix-ui';
export default function LoginPage() {
return (
<>
<Card className="absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2 w-fit">
<Heading size="4" className='text-center !mb-2'>Login</Heading>
<Form.Root className="flex flex-col gap-4" onSubmit={e => e.preventDefault()}>
<Form.Field name="email">
<Form.Message match={'valueMissing'}>
<Text>Email is required</Text>
</Form.Message>
<Form.Message match={'typeMismatch'}>
<Text>Email is not valid</Text>
</Form.Message>
<Form.Control asChild>
<TextField.Root type="email" placeholder="Email">
<TextField.Slot />
</TextField.Root>
</Form.Control>
</Form.Field>
<Form.Field name="password">
<Form.Message match="valueMissing">
<Text>Password is required</Text>
</Form.Message>
<Form.Control asChild>
<TextField.Root
type="password"
placeholder="Password"
>
<TextField.Slot />
</TextField.Root>
</Form.Control>
</Form.Field>
<Button
// onClick={(e) => e.preventDefault()}
className="mt-4"
>
Sign In
</Button>
</Form.Root>
</Card>
</>
);
}

View File

@ -0,0 +1,49 @@
import { Button, Card, Heading, Text, TextField } from '@radix-ui/themes';
import { Form } from 'radix-ui';
export default function RegisterPage() {
return (
<Card className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-fit">
<Heading size="4" className='text-center !mb-2'>Register</Heading>
<Form.Root className="flex flex-col gap-4" onSubmit={e => e.preventDefault()}>
<Form.Field name="email">
<Form.Message match="valueMissing">
<Text>Email is required</Text>
</Form.Message>
<Form.Message match="typeMismatch">
<Text>Email is not valid</Text>
</Form.Message>
<Form.Control asChild>
<TextField.Root
type="email"
placeholder="Email"
required
>
<TextField.Slot />
</TextField.Root>
</Form.Control>
</Form.Field>
<Form.Field name="password">
<Form.Message match="valueMissing">
<Text>Password is required</Text>
</Form.Message>
<Form.Control asChild>
<TextField.Root
type="password"
placeholder="Password"
required
>
<TextField.Slot />
</TextField.Root>
</Form.Control>
</Form.Field>
<Button onClick={e => e.preventDefault()} className="mt-4">Register</Button>
</Form.Root>
</Card>
);
}

View File

@ -0,0 +1,10 @@
import { Box } from '@radix-ui/themes'
import { Outlet } from 'react-router-dom'
export default function MainPage() {
return (
<Box className='size-full'>
<Outlet />
</Box>
)
}

View File

@ -0,0 +1,18 @@
import { createRoutesFromElements, Route } from 'react-router-dom';
import MainBoard from '../components/MainBoard/MainBoard';
import LoginPage from '../pages/auth/LoginPage/LoginPage';
import RegisterPage from '../pages/auth/RegisterPage/RegisterPage';
import MainPage from '../pages/main/MainPage';
const MyRoutes = createRoutesFromElements(
<>
<Route path="/" element={<MainPage />}>
<Route index element={<MainBoard />} />
</Route>
<Route path="login" element={<LoginPage />} />
<Route path="register" element={<RegisterPage />} />
</>,
);
export default MyRoutes;