redux setup
This commit is contained in:
parent
e82b06b508
commit
cabb8a9b27
@ -1,7 +1,22 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { configureStore, isRejectedWithValue, Middleware } from '@reduxjs/toolkit';
|
||||
import { setupListeners } from '@reduxjs/toolkit/query';
|
||||
import { authApi, mainApi } from '../services/mainApi';
|
||||
|
||||
const loggerMiddleware: Middleware = (_store) => (next) => (action) => {
|
||||
console.log('dispatching', action);
|
||||
if(isRejectedWithValue(action)) {
|
||||
// @ts-ignore
|
||||
const statusCode = action.payload.status;
|
||||
if(statusCode === 401) {
|
||||
console.log('Unauthorized, redirecting to login page');
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
let result = next(action);
|
||||
return result;
|
||||
};
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
[mainApi.reducerPath]: mainApi.reducer,
|
||||
@ -9,8 +24,10 @@ export const store = configureStore({
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware()
|
||||
.prepend(loggerMiddleware)
|
||||
.concat(mainApi.middleware)
|
||||
.concat(authApi.middleware),
|
||||
});
|
||||
|
||||
setupListeners(store.dispatch);
|
||||
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
|
||||
export default function AuthWrapper() {
|
||||
const navigate = useNavigate()
|
||||
export default function AuthWrapper(props: PropsWithChildren) {
|
||||
|
||||
if(!localStorage.getItem('token')) {
|
||||
navigate('/login')
|
||||
if (!localStorage.getItem('token')) {
|
||||
console.log('No token found, redirecting to login');
|
||||
return <Navigate to={'/login'} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
return <div>{props.children}</div>;
|
||||
}
|
||||
|
||||
@ -1,34 +1,69 @@
|
||||
import { Droppable } from '@hello-pangea/dnd';
|
||||
import { Box } from '@radix-ui/themes';
|
||||
import { useState } from 'react';
|
||||
import { Box, Button, Flex, Text } from '@radix-ui/themes';
|
||||
import {
|
||||
useCreateTaskMutation,
|
||||
useDeleteProjectMutation,
|
||||
useGetTasksForGroupQuery,
|
||||
} from '../../services/mainApi';
|
||||
import TaskCard from '../TaskCard/TaskCard';
|
||||
|
||||
const tasks = [
|
||||
{ id: 1, title: 'Task 1', description: 'Description for Task 1' },
|
||||
{ id: 2, title: 'Task 2', description: 'Description for Task 2' },
|
||||
];
|
||||
import CreateTaskDialog from './CreateTaskDialog/CreateTaskDialog';
|
||||
import DeleteProjectDialog from './DeleteProjectDialog/CreateTaskDialog';
|
||||
|
||||
type TCardGroup = {
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export default function CardGroup(props: TCardGroup) {
|
||||
const [localTasks, setLocalTasks] = useState(tasks)
|
||||
const { data, isLoading } = useGetTasksForGroupQuery(props.id);
|
||||
|
||||
const [createTaskForGroup] = useCreateTaskMutation();
|
||||
const [deleteProject] = useDeleteProjectMutation();
|
||||
|
||||
const createTask = (taskText: string, date: string) => {
|
||||
createTaskForGroup({
|
||||
title: taskText,
|
||||
projectId: props.id,
|
||||
assignedUserId: 1,
|
||||
deadline: date,
|
||||
});
|
||||
};
|
||||
|
||||
const deleteGroup = () => {
|
||||
deleteProject(props.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<Droppable droppableId={props.id}>
|
||||
<Droppable droppableId={props.id.toString()}>
|
||||
{(provided) => (
|
||||
<Box ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{localTasks.map((task, i) => (
|
||||
<Box className="flex flex-col text-center rounded-lg p-4 bg-gray-200 min-w-fit">
|
||||
<Text className="mb-2">{props.title}</Text>
|
||||
<Box
|
||||
ref={provided.innerRef}
|
||||
className="bg-gray-200 min-w-full rounded-lg !flex flex-col gap-2"
|
||||
>
|
||||
{data &&
|
||||
data.map((task, i) => (
|
||||
<TaskCard
|
||||
key={task.id}
|
||||
id={task.id.toString() + props.id}
|
||||
title={task.title}
|
||||
description={task.description}
|
||||
description={task.status}
|
||||
index={i}
|
||||
status={task.status}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</Box>
|
||||
<Flex gap={'2'} className="mx-auto w-full mt-2">
|
||||
<CreateTaskDialog onClose={createTask}>
|
||||
<Button>Add Task</Button>
|
||||
</CreateTaskDialog>
|
||||
<DeleteProjectDialog onClose={deleteGroup}>
|
||||
<Button color="red" onClick={deleteGroup}>
|
||||
Delete Project
|
||||
</Button>
|
||||
</DeleteProjectDialog>
|
||||
</Flex>
|
||||
</Box>
|
||||
)}
|
||||
</Droppable>
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
import { Button, Dialog, Flex, Text, TextField } from '@radix-ui/themes';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
|
||||
type TCreateTaskDialog = {
|
||||
onClose: (text: string, timeString: string) => void;
|
||||
} & PropsWithChildren;
|
||||
|
||||
export default function CreateTaskDialog(props: TCreateTaskDialog) {
|
||||
const [text, setText] = useState('');
|
||||
const [deadline, setDeadline] = useState('');
|
||||
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger>{props.children}</Dialog.Trigger>
|
||||
|
||||
<Dialog.Content maxWidth="450px">
|
||||
<Dialog.Title>Task creation</Dialog.Title>
|
||||
|
||||
<Flex direction="column" gap="3">
|
||||
<label>
|
||||
<Text as="div" size="2" mb="1" weight="bold">
|
||||
Task description
|
||||
</Text>
|
||||
<TextField.Root
|
||||
defaultValue=""
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
placeholder="Enter task description"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<Text as="div" size="2" mb="1" weight="bold">
|
||||
Deadline
|
||||
</Text>
|
||||
<input
|
||||
type="date"
|
||||
onChange={(e) => setDeadline(e.target.value)}
|
||||
></input>
|
||||
</label>
|
||||
</Flex>
|
||||
|
||||
<Flex gap="3" mt="4" justify="end">
|
||||
<Dialog.Close>
|
||||
<Button variant="soft" color="gray">
|
||||
Cancel
|
||||
</Button>
|
||||
</Dialog.Close>
|
||||
<Dialog.Close>
|
||||
<Button
|
||||
onClick={() => {
|
||||
props.onClose(text, deadline);
|
||||
setDeadline('');
|
||||
setText('');
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</Dialog.Close>
|
||||
</Flex>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import { Button, Dialog, Flex } from '@radix-ui/themes';
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
type TCreateTaskDialog = {
|
||||
onClose: () => void;
|
||||
} & PropsWithChildren;
|
||||
|
||||
export default function DeleteProjectDialog(props: TCreateTaskDialog) {
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger>{props.children}</Dialog.Trigger>
|
||||
|
||||
<Dialog.Content maxWidth="450px">
|
||||
<Dialog.Title>Delete project?</Dialog.Title>
|
||||
|
||||
<Flex gap="3" mt="4" justify="end">
|
||||
<Dialog.Close>
|
||||
<Button variant="soft" color="gray">
|
||||
Cancel
|
||||
</Button>
|
||||
</Dialog.Close>
|
||||
<Dialog.Close>
|
||||
<Button
|
||||
color="red"
|
||||
onClick={() => {
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Dialog.Close>
|
||||
</Flex>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
);
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
import { DragDropContext } from '@hello-pangea/dnd';
|
||||
import { Button } from '@radix-ui/themes';
|
||||
import { Button, Flex, ScrollArea } from '@radix-ui/themes';
|
||||
import {
|
||||
useCreateProjectMutation,
|
||||
useCreateTaskMutation,
|
||||
useGetTasksQuery,
|
||||
useGetProjectsQuery,
|
||||
} from '../../services/mainApi';
|
||||
import CardGroup from '../CardGroup/CardGroup';
|
||||
|
||||
@ -11,26 +12,40 @@ export default function MainBoard() {
|
||||
result;
|
||||
};
|
||||
|
||||
const { data } = useGetTasksQuery();
|
||||
|
||||
const [create] = useCreateTaskMutation();
|
||||
const [createProject] = useCreateProjectMutation();
|
||||
const { data: cringe, isLoading } = useGetProjectsQuery({});
|
||||
|
||||
const onClick = () => {
|
||||
create({
|
||||
title: 'My Task 123',
|
||||
projectId: 1,
|
||||
assignedUserId: 2,
|
||||
projectId: 4,
|
||||
assignedUserId: 1,
|
||||
deadline: '2025-03-01T12:00:00Z',
|
||||
});
|
||||
};
|
||||
|
||||
const onClick1 = () => {
|
||||
createProject({
|
||||
title: 'My project test 12',
|
||||
description: 'Test desc 123 123 123',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DragDropContext onDragEnd={dragEndHandle}>
|
||||
<CardGroup id="qwhdf" />
|
||||
<CardGroup id="123fsduiyuiyi" />
|
||||
<ScrollArea scrollbars='horizontal'>
|
||||
<Flex gap={'2'} className='min-w-fit'>
|
||||
{!isLoading &&
|
||||
(cringe as any[]).map((item: any) => (
|
||||
<CardGroup id={item.id} title={item.title} />
|
||||
))}
|
||||
</Flex>
|
||||
</ScrollArea>
|
||||
</DragDropContext>
|
||||
<Button onClick={onClick}>asdasdasd </Button>
|
||||
<Button onClick={onClick}>Create task</Button>
|
||||
<Button onClick={onClick1}>Create project</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
import { DragHandleHorizontalIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Button, Card, Flex, Text } from '@radix-ui/themes';
|
||||
import { Button, Card, Flex, Text } from '@radix-ui/themes';
|
||||
|
||||
type TTaskCard = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
id?: string;
|
||||
index?: number;
|
||||
status: "todo" | "in-progress" | "completed";
|
||||
};
|
||||
|
||||
export default function TaskCard(props: TTaskCard) {
|
||||
@ -21,14 +21,6 @@ export default function TaskCard(props: TTaskCard) {
|
||||
<Flex direction="column" gap="2">
|
||||
<Text wrap="pretty">{props.title}</Text>
|
||||
<Button>Mark completed</Button>
|
||||
<Flex className="w-full !justify-center transition-transform">
|
||||
<Box
|
||||
className="px-10 cursor-grab"
|
||||
{...provided.dragHandleProps}
|
||||
>
|
||||
<DragHandleHorizontalIcon />
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
import { Button, Card, Heading, Text, TextField } from '@radix-ui/themes';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Heading,
|
||||
Link,
|
||||
Text,
|
||||
TextField,
|
||||
} from '@radix-ui/themes';
|
||||
import { Form } from 'radix-ui';
|
||||
import { useState } from 'react';
|
||||
import { useLoginMutation } from '../../../services/mainApi';
|
||||
@ -69,6 +77,12 @@ export default function LoginPage() {
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
|
||||
<Box className='w-full !flex justify-center'>
|
||||
<Link href="/register">
|
||||
<Text>Register</Text>
|
||||
</Link>
|
||||
</Box>
|
||||
</Form.Root>
|
||||
</Card>
|
||||
</>
|
||||
|
||||
@ -1,11 +1,24 @@
|
||||
import { Button, Card, Heading, Text, TextField } from '@radix-ui/themes';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Heading,
|
||||
Link,
|
||||
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()}>
|
||||
<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>
|
||||
@ -42,7 +55,15 @@ export default function RegisterPage() {
|
||||
</Form.Control>
|
||||
</Form.Field>
|
||||
|
||||
<Button onClick={e => e.preventDefault()} className="mt-4">Register</Button>
|
||||
<Button onClick={(e) => e.preventDefault()} className="mt-4">
|
||||
Register
|
||||
</Button>
|
||||
|
||||
<Box className="w-full !flex justify-center">
|
||||
<Link href="/login">
|
||||
<Text>Login</Text>
|
||||
</Link>
|
||||
</Box>
|
||||
</Form.Root>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { createRoutesFromElements, Route } from 'react-router-dom';
|
||||
import AuthWrapper from '../components/AuthWrapper/AuthWrapper';
|
||||
import MainBoard from '../components/MainBoard/MainBoard';
|
||||
import LoginPage from '../pages/auth/LoginPage/LoginPage';
|
||||
import RegisterPage from '../pages/auth/RegisterPage/RegisterPage';
|
||||
@ -6,7 +7,14 @@ import MainPage from '../pages/main/MainPage';
|
||||
|
||||
const MyRoutes = createRoutesFromElements(
|
||||
<>
|
||||
<Route path="/" element={<MainPage />}>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<AuthWrapper>
|
||||
<MainPage />
|
||||
</AuthWrapper>
|
||||
}
|
||||
>
|
||||
<Route index element={<MainBoard />} />
|
||||
</Route>
|
||||
|
||||
|
||||
@ -30,6 +30,22 @@ export const mainApi = createApi({
|
||||
]
|
||||
: [{ type: 'Task', id: 'LIST' }],
|
||||
}),
|
||||
getTasksForGroup: builder.query<any[], string>({
|
||||
query: (id: string) => ({
|
||||
url: `tasks/project/${id}`,
|
||||
method: 'GET',
|
||||
}),
|
||||
providesTags: (result) =>
|
||||
result
|
||||
? [
|
||||
...result.map((task) => ({
|
||||
type: 'Task' as const,
|
||||
id: task,
|
||||
})),
|
||||
{ type: 'Task', id: 'LIST' },
|
||||
]
|
||||
: [{ type: 'Task', id: 'LIST' }],
|
||||
}),
|
||||
getTask: builder.query<string, string>({
|
||||
query: (id) => ({
|
||||
url: `tasks/${id}`,
|
||||
@ -64,7 +80,7 @@ export const mainApi = createApi({
|
||||
// PROJECTS
|
||||
createProject: builder.mutation({
|
||||
query: (newProject) => ({
|
||||
url: 'tasks/create',
|
||||
url: 'projects/create',
|
||||
method: 'POST',
|
||||
body: newProject,
|
||||
}),
|
||||
@ -141,5 +157,5 @@ export const authApi = createApi({
|
||||
});
|
||||
|
||||
export const { useLoginMutation, useRegisterMutation } = authApi;
|
||||
export const { useGetTasksQuery, useCreateTaskMutation, useUpdateTaskMutation, useDeleteTaskMutation } = mainApi;
|
||||
export const { useGetTasksQuery, useCreateTaskMutation, useUpdateTaskMutation, useDeleteTaskMutation, useGetTasksForGroupQuery } = mainApi;
|
||||
export const { useGetProjectsQuery, useCreateProjectMutation, useUpdateProjectMutation, useDeleteProjectMutation } = mainApi;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user