diff --git a/cool_todo_manager/src/api/RTKQuery.ts b/cool_todo_manager/src/api/RTKQuery.ts index 7091df8..cd93886 100644 --- a/cool_todo_manager/src/api/RTKQuery.ts +++ b/cool_todo_manager/src/api/RTKQuery.ts @@ -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); + diff --git a/cool_todo_manager/src/components/AuthWrapper/AuthWrapper.tsx b/cool_todo_manager/src/components/AuthWrapper/AuthWrapper.tsx index ebafd16..07d3bac 100644 --- a/cool_todo_manager/src/components/AuthWrapper/AuthWrapper.tsx +++ b/cool_todo_manager/src/components/AuthWrapper/AuthWrapper.tsx @@ -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 + } - return ( -
- -
- ) + return
{props.children}
; } diff --git a/cool_todo_manager/src/components/CardGroup/CardGroup.tsx b/cool_todo_manager/src/components/CardGroup/CardGroup.tsx index f41d337..ce655b8 100644 --- a/cool_todo_manager/src/components/CardGroup/CardGroup.tsx +++ b/cool_todo_manager/src/components/CardGroup/CardGroup.tsx @@ -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 ( - + {(provided) => ( - - {localTasks.map((task, i) => ( - - ))} - {provided.placeholder} + + {props.title} + + {data && + data.map((task, i) => ( + + ))} + + + + + + + + + )} diff --git a/cool_todo_manager/src/components/CardGroup/CreateTaskDialog/CreateTaskDialog.tsx b/cool_todo_manager/src/components/CardGroup/CreateTaskDialog/CreateTaskDialog.tsx new file mode 100644 index 0000000..e9fa374 --- /dev/null +++ b/cool_todo_manager/src/components/CardGroup/CreateTaskDialog/CreateTaskDialog.tsx @@ -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 ( + + {props.children} + + + Task creation + + + + + + + + + + + + + + + + + ); +} diff --git a/cool_todo_manager/src/components/CardGroup/DeleteProjectDialog/CreateTaskDialog.tsx b/cool_todo_manager/src/components/CardGroup/DeleteProjectDialog/CreateTaskDialog.tsx new file mode 100644 index 0000000..7047c5c --- /dev/null +++ b/cool_todo_manager/src/components/CardGroup/DeleteProjectDialog/CreateTaskDialog.tsx @@ -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 ( + + {props.children} + + + Delete project? + + + + + + + + + + + + ); +} diff --git a/cool_todo_manager/src/components/MainBoard/MainBoard.tsx b/cool_todo_manager/src/components/MainBoard/MainBoard.tsx index a3573b7..31c9c93 100644 --- a/cool_todo_manager/src/components/MainBoard/MainBoard.tsx +++ b/cool_todo_manager/src/components/MainBoard/MainBoard.tsx @@ -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 ( <> - - + + + {!isLoading && + (cringe as any[]).map((item: any) => ( + + ))} + + - + + ); } diff --git a/cool_todo_manager/src/components/TaskCard/TaskCard.tsx b/cool_todo_manager/src/components/TaskCard/TaskCard.tsx index 98f44a9..490a6fe 100644 --- a/cool_todo_manager/src/components/TaskCard/TaskCard.tsx +++ b/cool_todo_manager/src/components/TaskCard/TaskCard.tsx @@ -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) { {props.title} - - - - - )} diff --git a/cool_todo_manager/src/pages/auth/LoginPage/LoginPage.tsx b/cool_todo_manager/src/pages/auth/LoginPage/LoginPage.tsx index ebb8fa0..a5a6bc5 100644 --- a/cool_todo_manager/src/pages/auth/LoginPage/LoginPage.tsx +++ b/cool_todo_manager/src/pages/auth/LoginPage/LoginPage.tsx @@ -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'; @@ -66,9 +74,15 @@ export default function LoginPage() { + + + + Register + + diff --git a/cool_todo_manager/src/pages/auth/RegisterPage/RegisterPage.tsx b/cool_todo_manager/src/pages/auth/RegisterPage/RegisterPage.tsx index 7dceaf2..21168d0 100644 --- a/cool_todo_manager/src/pages/auth/RegisterPage/RegisterPage.tsx +++ b/cool_todo_manager/src/pages/auth/RegisterPage/RegisterPage.tsx @@ -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 ( - Register - e.preventDefault()}> + + Register + + e.preventDefault()} + > Email is required @@ -42,7 +55,15 @@ export default function RegisterPage() { - + + + + + Login + + ); diff --git a/cool_todo_manager/src/routes/routes.tsx b/cool_todo_manager/src/routes/routes.tsx index 2801816..3b10cf0 100644 --- a/cool_todo_manager/src/routes/routes.tsx +++ b/cool_todo_manager/src/routes/routes.tsx @@ -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( <> - }> + + + + } + > } /> diff --git a/cool_todo_manager/src/services/mainApi.ts b/cool_todo_manager/src/services/mainApi.ts index cf5f1fe..f39a73d 100644 --- a/cool_todo_manager/src/services/mainApi.ts +++ b/cool_todo_manager/src/services/mainApi.ts @@ -30,6 +30,22 @@ export const mainApi = createApi({ ] : [{ type: 'Task', id: 'LIST' }], }), + getTasksForGroup: builder.query({ + 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({ 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;