diff --git a/cool_todo_manager/src/App.tsx b/cool_todo_manager/src/App.tsx index 89e9ce1..0664bbe 100644 --- a/cool_todo_manager/src/App.tsx +++ b/cool_todo_manager/src/App.tsx @@ -1,11 +1,8 @@ import { Theme } from '@radix-ui/themes'; 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 MyRoutes from './routes/routes'; -import React from 'react'; import { appStore } from './stores/AppStore'; import { Ctx } from './context'; diff --git a/cool_todo_manager/src/api/axios.ts b/cool_todo_manager/src/api/axios.ts index 7b0f02d..1bfbdd1 100644 --- a/cool_todo_manager/src/api/axios.ts +++ b/cool_todo_manager/src/api/axios.ts @@ -9,6 +9,6 @@ export const axiosBase = axios.create({ export const axiosAuth = axios.create({ baseURL: BASE_URL, headers: { - Authorization: `Bearer ${localStorage.getItem('token')}` // Maybe we will use cookies + Authorization: `Bearer ${localStorage.getItem('token')}` } }); \ No newline at end of file diff --git a/cool_todo_manager/src/api/queryClient.ts b/cool_todo_manager/src/api/queryClient.ts deleted file mode 100644 index 75e4ae8..0000000 --- a/cool_todo_manager/src/api/queryClient.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { QueryClient } from "@tanstack/react-query"; - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: 2, - }, - }, -}) - -export default queryClient; \ No newline at end of file diff --git a/cool_todo_manager/src/components/CardGroup/CardGroup.tsx b/cool_todo_manager/src/components/CardGroup/CardGroup.tsx index 21a1a78..16483a8 100644 --- a/cool_todo_manager/src/components/CardGroup/CardGroup.tsx +++ b/cool_todo_manager/src/components/CardGroup/CardGroup.tsx @@ -9,7 +9,6 @@ import AddUserToProjectDialog from '../dialogs/AddUserToProjectDialog/AddUserToP import CreateTaskDialog from '../dialogs/CreateTaskDialog/CreateTaskDialog'; import DeleteProjectDialog from '../dialogs/DeleteProjectDialog/CreateTaskDialog'; -// Импортируем наш mobx-store import { useStore } from '../../hooks/useStore'; type TCardGroup = { @@ -19,18 +18,15 @@ type TCardGroup = { function CardGroup(props: TCardGroup) { - // При монтировании (и при изменении `props.id`) грузим задачи для проекта const store = useStore(); useEffect(() => { store.mainStore.fetchTasksForGroup(props.id).catch(console.error); }, [props.id, store.mainStore]); - // Получаем задачи для данного проекта из стора (или пустой массив, если их нет) const tasks = store.mainStore.tasksByProject.get(props.id) ?? []; - const isLoading = store.mainStore.isLoading; // общее isLoading из стора + const isLoading = store.mainStore.isLoading; - // Создать задачу const createTask = (taskText: string, date: string) => { store.mainStore.createTask({ title: taskText, @@ -40,7 +36,6 @@ function CardGroup(props: TCardGroup) { }); }; - // Удалить проект (аналог deleteProjectMutation) const deleteGroup = () => { store.mainStore.deleteProject(props.id); }; diff --git a/cool_todo_manager/src/components/MainBoard/MainBoard.tsx b/cool_todo_manager/src/components/MainBoard/MainBoard.tsx index 727ca9f..9fbcf66 100644 --- a/cool_todo_manager/src/components/MainBoard/MainBoard.tsx +++ b/cool_todo_manager/src/components/MainBoard/MainBoard.tsx @@ -7,22 +7,17 @@ import CardGroup from '../CardGroup/CardGroup'; import CreateProjectDialog from '../dialogs/CreateProjectDialog/CreateProjectDialog'; import { useStore } from '../../hooks/useStore'; -type TDragResult = any; // или определите, если надо - function MainBoard() { const store = useStore(); - // Загрузка проектов при монтировании useEffect(() => { store.mainStore.fetchProjects().catch(console.error); }, []); const dragEndHandle = (result: TDragResult) => { - // Логика перетаскивания, если нужно console.log(result); }; - // Для кнопки "Create project" const createProject = (newProjectData: any) => { store.mainStore.createProject(newProjectData); }; diff --git a/cool_todo_manager/src/components/TaskCard/TaskCard.tsx b/cool_todo_manager/src/components/TaskCard/TaskCard.tsx index 19ec4e9..d69048e 100644 --- a/cool_todo_manager/src/components/TaskCard/TaskCard.tsx +++ b/cool_todo_manager/src/components/TaskCard/TaskCard.tsx @@ -2,7 +2,6 @@ import { Draggable } from '@hello-pangea/dnd'; import { Badge, Button, Card, Flex, Text } from '@radix-ui/themes'; import { observer } from 'mobx-react-lite'; import { useStore } from '../../hooks/useStore'; -import { useEffect } from 'react'; type TTaskCard = { title?: string; @@ -36,10 +35,6 @@ const TaskCard = observer((props: TTaskCard) => { // @ts-ignore console.log(`rerendered with`, store.mainStore.tasksByProject.get(2) ); - useEffect(() => { - console.log(store.mainStore.fakeData) - }, [store.mainStore.fakeData]) - return ( @@ -50,7 +45,6 @@ const TaskCard = observer((props: TTaskCard) => { {...provided.draggableProps} {...provided.dragHandleProps} > -

{store.mainStore.fakeData.toString()}

{props.title} @@ -83,11 +77,6 @@ const TaskCard = observer((props: TTaskCard) => { Completed )} - )} diff --git a/cool_todo_manager/src/components/dialogs/AddUserToProjectDialog/AddUserToProjectDialog.tsx b/cool_todo_manager/src/components/dialogs/AddUserToProjectDialog/AddUserToProjectDialog.tsx index b159fac..9ea3e8f 100644 --- a/cool_todo_manager/src/components/dialogs/AddUserToProjectDialog/AddUserToProjectDialog.tsx +++ b/cool_todo_manager/src/components/dialogs/AddUserToProjectDialog/AddUserToProjectDialog.tsx @@ -2,7 +2,6 @@ import { PropsWithChildren, useEffect, useState } from 'react'; import { observer } from 'mobx-react-lite'; import { Button, Dialog, Flex, Select } from '@radix-ui/themes'; -// Импортируем сторы import { useStore } from '../../../hooks/useStore'; type TAddUserToProjectDialog = { @@ -15,8 +14,7 @@ function AddUserToProjectDialog(props: TAddUserToProjectDialog) { const store = useStore(); - // При открытии диалога (или при ререндере) можно обновлять список пользователей - // (В RTK Query был refetch. Тут можно просто вызвать fetchAllUsers(force=true) если надо всегда свежий список) + useEffect(() => { store.authStore.fetchAllUsers(); }, []); @@ -24,7 +22,6 @@ function AddUserToProjectDialog(props: TAddUserToProjectDialog) { const handleAddUser = async () => { if (selectedUserId) { try { - // В store.mainStore метод addProjectMember(projectId: string, memberId: string/number) await store.mainStore.addProjectMember(projectId.toString(), selectedUserId.toString()); setSelectedUserId(null); } catch (err) { diff --git a/cool_todo_manager/src/stores/AppStore.ts b/cool_todo_manager/src/stores/AppStore.ts index 570995f..4ec2c0b 100644 --- a/cool_todo_manager/src/stores/AppStore.ts +++ b/cool_todo_manager/src/stores/AppStore.ts @@ -1,4 +1,4 @@ -import { makeAutoObservable, runInAction } from 'mobx'; +import { makeAutoObservable } from 'mobx'; import { MainStore } from './MainStore'; import { AuthStore } from './AuthStore'; diff --git a/cool_todo_manager/src/stores/AuthStore.ts b/cool_todo_manager/src/stores/AuthStore.ts index 2684838..947e93e 100644 --- a/cool_todo_manager/src/stores/AuthStore.ts +++ b/cool_todo_manager/src/stores/AuthStore.ts @@ -2,18 +2,13 @@ import { makeAutoObservable, runInAction } from 'mobx'; import axios from 'axios'; import { AppStore } from './AppStore'; -/** - * AuthStore - * Хранит логику для авторизации, регистрации и получения всех пользователей. - */ + export class AuthStore { appStore: AppStore - - // Можно также хранить здесь данные о текущем пользователе, ошибках и т.д. + isLoading = false; error: string | null = null; - // Список всех пользователей (если вам нужно его кэшировать) allUsers: any[] = []; constructor(appStore:any) { @@ -25,9 +20,6 @@ export class AuthStore { return localStorage.getItem('token') || ''; } - /** - * Логин: отправляет запрос на сервер и сохраняет токен в localStorage - */ async login(credentials: { username: string; password: string }) { this.isLoading = true; this.error = null; @@ -40,7 +32,6 @@ export class AuthStore { runInAction(() => { if (response.data.access_token) { localStorage.setItem('token', response.data.access_token); - // Если нужно перенаправить после логина: window.location.href = '/'; } this.isLoading = false; @@ -54,9 +45,6 @@ export class AuthStore { } } - /** - * Регистрация: отправляет запрос на сервер - */ async register(credentials: { username: string; password: string }) { this.isLoading = true; this.error = null; @@ -67,7 +55,6 @@ export class AuthStore { ); runInAction(() => { - // Если регистрация успешна, например, статус 201 – перенаправляем на /login if (response.status === 201) { window.location.href = '/login'; } @@ -82,11 +69,7 @@ export class AuthStore { } } - /** - * Получить всех пользователей - */ async fetchAllUsers(force = false) { - // Если уже есть пользователи и не запрошен force, просто возвращаем if (this.allUsers.length && !force) { return this.allUsers; } diff --git a/cool_todo_manager/src/stores/MainStore.ts b/cool_todo_manager/src/stores/MainStore.ts index 41fb4d4..65d370d 100644 --- a/cool_todo_manager/src/stores/MainStore.ts +++ b/cool_todo_manager/src/stores/MainStore.ts @@ -2,40 +2,18 @@ import { makeAutoObservable, runInAction } from 'mobx'; import axios from 'axios'; import { AppStore } from './AppStore'; -export type Posts = Post[] - -export interface Post { - userId: number - id: number - title: string - body: string -} - -/** - * MainStore - * Хранит логику для задач, проектов и участников проектов - */ export class MainStore { appStore: AppStore - // ----- Общие поля ----- isLoading = false; error: string | null = null; - // ----- Задачи ----- + tasks: any[] = []; - // Кэшированный список задач по ID проекта tasksByProject = new Map(); - // ----- Проекты ----- projects: any[] = []; - // Детальные данные по конкретному проекту (если нужно кэшировать) projectById = new Map(); - - fakeData: Posts = [] - - // ----- Участники проекта ----- - // Если нужно кэшировать участников по проекту projectMembersById = new Map(); constructor(appStore:any) { @@ -43,75 +21,18 @@ export class MainStore { makeAutoObservable(this); } - /** - * Получить токен из localStorage - */ + get token() { return localStorage.getItem('token') || ''; } - /** - * Заготовка для заголовков (проставляем Bearer токен) - */ + get headers() { return { Authorization: `Bearer ${this.token}`, }; } - /** - * -------------------------------------------------------------------------- - * ЗАДАЧИ - * -------------------------------------------------------------------------- - */ - - /** - * Получить все задачи (кэширование: если уже загружены, второй раз не грузим) - */ - - async fetchPosts(){ - fetch('https://jsonplaceholder.typicode.com/posts?limit=10') - .then(response => response.json()) - .then(json => { - - this.fakeData = json - console.log(this.fakeData); - - }) - } - - async fetchTasks(force = false) { - - console.error(`Aboba`); - - if (this.tasks.length && !force) { - return this.tasks; - } - this.isLoading = true; - this.error = null; - try { - const response = await axios.get('http://109.107.166.17:5000/api/tasks', { - headers: this.headers, - }); - runInAction(() => { - console.log(`This is all tasks`,response.data); - - this.tasks = response.data; - this.isLoading = false; - }); - return this.tasks; - } catch (error: any) { - runInAction(() => { - this.error = error?.response?.data?.message || 'Ошибка при загрузке задач'; - this.isLoading = false; - }); - throw error; - } - } - - /** - * Получить задачи для конкретного проекта (группы) - */ async fetchTasksForGroup(projectId: string, force = false) { if (this.tasksByProject.has(projectId) && !force) { return this.tasksByProject.get(projectId); @@ -129,8 +50,6 @@ export class MainStore { }, ); runInAction(() => { - console.log(`This is tasks for ${projectId}`,response.data); - this.tasksByProject.set(projectId, response.data); this.isLoading = false; }); @@ -144,9 +63,6 @@ export class MainStore { } } - /** - * Получить одну задачу по ID - */ async fetchTask(taskId: string) { this.isLoading = true; this.error = null; @@ -169,18 +85,17 @@ export class MainStore { } } - /** - * Создать задачу - */ async createTask(newTask: any) { try { - await axios.post( + const response = await axios.post( 'http://109.107.166.17:5000/api/tasks/create', newTask, { headers: this.headers }, ); - // Инвалидируем кэш (чтобы при следующем getTasks() данные были актуальны) - this.tasks = []; + const createdTask = response.data; + this.tasks.push(createdTask); + const oldTasks = this.tasksByProject.get(newTask.projectId) ?? []; + this.tasksByProject.set(newTask.projectId, [...oldTasks, createdTask]); } catch (error: any) { runInAction(() => { this.error = error?.response?.data?.message || 'Ошибка при создании задачи'; @@ -189,22 +104,25 @@ export class MainStore { } } - /** - * Обновить задачу - */ async updateTask(task: { id: string; status: string }) { try { - await axios.patch( + const response = await axios.patch( `http://109.107.166.17:5000/api/tasks/${task.id}`, { status: task.status }, { headers: this.headers }, ); - // Локально обновим статус в массиве tasks runInAction(() => { const index = this.tasks.findIndex((t) => t.id === task.id); if (index !== -1) { this.tasks[index].status = task.status; } + const projectId = response.data.project.id; + const oldArray = this.tasksByProject.get(projectId) || []; + const indexInMap = oldArray.findIndex((t) => t.id === +task.id); + if (indexInMap !== -1) { + oldArray[indexInMap].status = task.status; + this.tasksByProject.set(projectId, [...oldArray]); + } }); } catch (error: any) { runInAction(() => { @@ -214,9 +132,7 @@ export class MainStore { } } - /** - * Удалить задачу - */ + async deleteTask(taskId: string) { try { await axios.delete(`http://109.107.166.17:5000/api/tasks/${taskId}`, { @@ -234,15 +150,7 @@ export class MainStore { } } - /** - * -------------------------------------------------------------------------- - * ПРОЕКТЫ - * -------------------------------------------------------------------------- - */ - /** - * Получить все проекты текущего пользователя - */ async fetchProjects(force = false) { if (this.projects.length && !force) { return this.projects; @@ -269,11 +177,7 @@ export class MainStore { } } - /** - * Получить данные одного проекта по ID - */ async fetchProject(projectId: string) { - // Если хотим кэшировать отдельно проекты по id if (this.projectById.has(projectId)) { return this.projectById.get(projectId); } @@ -299,18 +203,17 @@ export class MainStore { } } - /** - * Создать проект - */ async createProject(newProject: any) { try { - await axios.post( + const response = await axios.post( 'http://109.107.166.17:5000/api/projects/create', newProject, { headers: this.headers }, ); - // Инвалидируем кэш - this.projects = []; + const created = response.data; + runInAction(() => { + this.projects.push(created); + }); } catch (error: any) { runInAction(() => { this.error = error?.response?.data?.message || 'Ошибка при создании проекта'; @@ -319,9 +222,6 @@ export class MainStore { } } - /** - * Обновить проект - */ async updateProject(id: string, projectData: any) { try { await axios.patch( @@ -329,7 +229,6 @@ export class MainStore { projectData, { headers: this.headers }, ); - // Можно либо перезагрузить список проектов, либо локально обновить this.fetchProjects(true); } catch (error: any) { runInAction(() => { @@ -339,9 +238,6 @@ export class MainStore { } } - /** - * Удалить проект - */ async deleteProject(id: string) { try { await axios.delete(`http://109.107.166.17:5000/api/projects/${id}`, { @@ -359,15 +255,6 @@ export class MainStore { } } - /** - * -------------------------------------------------------------------------- - * УЧАСТНИКИ ПРОЕКТА - * -------------------------------------------------------------------------- - */ - - /** - * Добавить участника в проект - */ async addProjectMember(projectId: string, memberId: string) { try { await axios.post( @@ -378,7 +265,6 @@ export class MainStore { }, { headers: this.headers }, ); - // Инвалидируем список участников this.projectMembersById.delete(projectId); } catch (error: any) { runInAction(() => { @@ -387,10 +273,6 @@ export class MainStore { throw error; } } - - /** - * Получить участников проекта - */ async fetchProjectMembers(projectId: string, force = false) { if (this.projectMembersById.has(projectId) && !force) { return this.projectMembersById.get(projectId); @@ -418,9 +300,6 @@ export class MainStore { } } - /** - * Удалить участника из проекта - */ async removeProjectMember(projectId: string, memberId: string) { try { await axios.delete( @@ -430,7 +309,7 @@ export class MainStore { headers: this.headers, }, ); - // Инвалидируем список участников + this.projectMembersById.delete(projectId); } catch (error: any) { runInAction(() => {