From 49b88ba50582260d057ef5e958879abebe7f7584 Mon Sep 17 00:00:00 2001 From: Arigii Date: Tue, 10 Jun 2025 18:25:47 +0500 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20=D1=88?= =?UTF-8?q?=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=20=D0=B4=D0=BB=D1=8F=20=D1=83?= =?UTF-8?q?=D1=87=D0=B0=D1=81=D1=82=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/project_members_repository.py | 9 +- API/app/contollers/project_members_router.py | 27 + API/app/contollers/register_router.py | 26 - API/app/contollers/teams_router.py | 5 +- API/app/contollers/users_router.py | 20 +- .../infrastructure/project_members_service.py | 18 + API/app/main.py | 2 - WEB/src/api/auth/loginRequest.js | 11 +- .../project_members/createProjectMember.js | 40 + .../project_members/deleteProjectMember.js | 30 + .../deleteSingleProjectMember.js | 30 + .../project_members/getAllProjectMembers.js | 26 + .../getProjectMemberByProject.js | 24 + .../getProjectMembersByProfile.js | 24 + .../project_members/updateProjectMember.js | 52 ++ WEB/src/api/teams/downloadTeamPhotoFile.js | 27 + WEB/src/api/teams/getActiveTeam.js | 28 + WEB/src/api/teams/setActiveTeam.js | 32 + WEB/src/api/teams/uploadTeamPhoto.js | 30 + WEB/src/api/users/changeUserPassword.js | 14 +- WEB/src/api/users/registerUser.js | 21 +- WEB/src/main.js | 5 +- WEB/src/pages/AdminPage.vue | 737 +++++++++++++++++- WEB/src/pages/HomePage.vue | 48 +- WEB/src/pages/LoginPage.vue | 24 +- 25 files changed, 1209 insertions(+), 101 deletions(-) delete mode 100644 API/app/contollers/register_router.py create mode 100644 WEB/src/api/project_members/createProjectMember.js create mode 100644 WEB/src/api/project_members/deleteProjectMember.js create mode 100644 WEB/src/api/project_members/deleteSingleProjectMember.js create mode 100644 WEB/src/api/project_members/getAllProjectMembers.js create mode 100644 WEB/src/api/project_members/getProjectMemberByProject.js create mode 100644 WEB/src/api/project_members/getProjectMembersByProfile.js create mode 100644 WEB/src/api/project_members/updateProjectMember.js create mode 100644 WEB/src/api/teams/downloadTeamPhotoFile.js create mode 100644 WEB/src/api/teams/getActiveTeam.js create mode 100644 WEB/src/api/teams/setActiveTeam.js create mode 100644 WEB/src/api/teams/uploadTeamPhoto.js diff --git a/API/app/application/project_members_repository.py b/API/app/application/project_members_repository.py index 7bf226e..3798697 100644 --- a/API/app/application/project_members_repository.py +++ b/API/app/application/project_members_repository.py @@ -10,6 +10,11 @@ class ProjectMembersRepository: def __init__(self, db: AsyncSession): self.db = db + async def get_all(self) -> Sequence[ProjectMember]: + stmt = select(ProjectMember) + result = await self.db.execute(stmt) + return result.scalars().all() + async def get_by_id(self, project_member_id: int) -> Optional[ProjectMember]: stmt = select(ProjectMember).filter_by(id=project_member_id) result = await self.db.execute(stmt) @@ -43,5 +48,7 @@ class ProjectMembersRepository: for project_member in project_members: await self.db.delete(project_member) + async def delete_member(self, project_member: ProjectMember) -> ProjectMember: + await self.db.delete(project_member) await self.db.commit() - return project_members + return project_member diff --git a/API/app/contollers/project_members_router.py b/API/app/contollers/project_members_router.py index c85381d..016e705 100644 --- a/API/app/contollers/project_members_router.py +++ b/API/app/contollers/project_members_router.py @@ -11,6 +11,19 @@ from app.infrastructure.project_members_service import ProjectMembersService router = APIRouter() +@router.get( + '/', + response_model=list[ProjectMemberEntity], + summary='Get all project members', + description='Returns all project members', +) +async def get_all_project_members( + db: AsyncSession = Depends(get_db), +): + project_members_service = ProjectMembersService(db) + return await project_members_service.get_all_project_members() + + @router.get( '/by-project/{project_id}/', response_model=list[ProjectMemberEntity], @@ -84,3 +97,17 @@ async def delete_project_members( service = ProjectMembersService(db) await service.delete_project_members_by_project_id(project_id) return {"message": "All project members have been successfully deleted."} + +@router.delete( + '/member/{member_id}/', + summary='Delete a single project member by ID', + description='Deletes a specific project member by their unique ID', +) +async def delete_single_project_member( + member_id: int, + db: AsyncSession = Depends(get_db), + user=Depends(require_admin), +): + service = ProjectMembersService(db) + deleted_member = await service.delete_project_member_by_id(member_id) + return deleted_member diff --git a/API/app/contollers/register_router.py b/API/app/contollers/register_router.py deleted file mode 100644 index 6ec4a81..0000000 --- a/API/app/contollers/register_router.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Optional - -from fastapi import APIRouter, Depends -from sqlalchemy.ext.asyncio import AsyncSession - -from app.database.session import get_db -from app.domain.entities.register import RegisterEntity -from app.domain.entities.user import UserEntity -from app.infrastructure.users_service import UsersService - -router = APIRouter() - - -@router.post( - '/', - response_model=Optional[UserEntity], - summary='User Registration', - description='Performs user registration in the system', -) -async def register_user( - user_data: RegisterEntity, - db: AsyncSession = Depends(get_db) -): - users_service = UsersService(db) - user = await users_service.register_user(user_data) - return user diff --git a/API/app/contollers/teams_router.py b/API/app/contollers/teams_router.py index 536586b..969a1f5 100644 --- a/API/app/contollers/teams_router.py +++ b/API/app/contollers/teams_router.py @@ -21,7 +21,6 @@ router = APIRouter() ) async def get_all_teams( db: AsyncSession = Depends(get_db), - user=Depends(get_current_user), ): teams_service = TeamsService(db) return await teams_service.get_all_teams() @@ -33,7 +32,7 @@ async def get_all_teams( summary='Get active team', description='Returns active team', ) -async def get_all_teams( +async def get_active_team( db: AsyncSession = Depends(get_db), ): teams_service = TeamsService(db) @@ -61,7 +60,7 @@ async def create_team( summary='Make team active', description='Makes team active', ) -async def update_team( +async def set_active_team( team_id: int, db: AsyncSession = Depends(get_db), user=Depends(require_admin), diff --git a/API/app/contollers/users_router.py b/API/app/contollers/users_router.py index 6a0e6a0..1af6a19 100644 --- a/API/app/contollers/users_router.py +++ b/API/app/contollers/users_router.py @@ -1,15 +1,29 @@ from typing import Optional -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Body from sqlalchemy.ext.asyncio import AsyncSession from app.database.session import get_db +from app.domain.entities.register import RegisterEntity from app.domain.entities.user import UserEntity from app.infrastructure.dependencies import get_current_user from app.infrastructure.users_service import UsersService router = APIRouter() +@router.post( + '/', + response_model=Optional[UserEntity], + summary='User Registration', + description='Performs user registration in the system', +) +async def register_user( + user_data: RegisterEntity, + db: AsyncSession = Depends(get_db) +): + users_service = UsersService(db) + user = await users_service.register_user(user_data) + return user @router.put( '/{user_id}/', @@ -17,9 +31,9 @@ router = APIRouter() summary='Change user password', description='Change user password', ) -async def create_user( +async def change_user_password( user_id: int, - new_password: str, + new_password: str = Body(..., embed=True), # <--- ИЗМЕНЕНИЕ ЗДЕСЬ! Явно указывает, что в теле запроса db: AsyncSession = Depends(get_db), user=Depends(get_current_user), ): diff --git a/API/app/infrastructure/project_members_service.py b/API/app/infrastructure/project_members_service.py index 06e3952..bdaf80d 100644 --- a/API/app/infrastructure/project_members_service.py +++ b/API/app/infrastructure/project_members_service.py @@ -16,6 +16,13 @@ class ProjectMembersService: self.projects_repository = ProjectsRepository(db) self.profiles_repository = ProfilesRepository(db) + async def get_all_project_members(self) -> list[ProjectMemberEntity]: + project_members = await self.project_members_repository.get_all() + return [ + self.model_to_entity(project_member) + for project_member in project_members + ] + async def get_project_members_by_project_id(self, project_id: int) -> list[ProjectMemberEntity]: project_members = await self.project_members_repository.get_by_project_id(project_id) return [ @@ -107,6 +114,17 @@ class ProjectMembersService: for project_member in project_members ] + async def delete_project_member_by_id(self, member_id: int) -> Optional[ProjectMemberEntity]: + member = await self.project_members_repository.get_by_id(member_id) + if not member: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f'Project member with ID {member_id} not found' + ) + await self.project_members_repository.delete_member(member) + return self.model_to_entity(member) + + @staticmethod def model_to_entity(project: ProjectMember) -> ProjectMemberEntity: return ProjectMemberEntity( diff --git a/API/app/main.py b/API/app/main.py index 55181ac..f241ec6 100644 --- a/API/app/main.py +++ b/API/app/main.py @@ -7,7 +7,6 @@ from app.contollers.profiles_router import router as profiles_router from app.contollers.project_files_router import router as project_files_router from app.contollers.project_members_router import router as project_members_router from app.contollers.projects_router import router as projects_router -from app.contollers.register_router import router as register_router from app.contollers.rss_router import router as rss_router from app.contollers.teams_router import router as team_router from app.contollers.users_router import router as users_router @@ -35,7 +34,6 @@ def start_app(): api_app.include_router(project_members_router, prefix=f'{settings.PREFIX}/project_members', tags=['project_members']) api_app.include_router(projects_router, prefix=f'{settings.PREFIX}/projects', tags=['projects']) - api_app.include_router(register_router, prefix=f'{settings.PREFIX}/register', tags=['register']) api_app.include_router(rss_router, prefix=f'{settings.PREFIX}/rss', tags=['rss_router']) api_app.include_router(team_router, prefix=f'{settings.PREFIX}/teams', tags=['teams']) api_app.include_router(users_router, prefix=f'{settings.PREFIX}/users', tags=['users']) diff --git a/WEB/src/api/auth/loginRequest.js b/WEB/src/api/auth/loginRequest.js index f49d27d..9a148b5 100644 --- a/WEB/src/api/auth/loginRequest.js +++ b/WEB/src/api/auth/loginRequest.js @@ -11,12 +11,13 @@ const loginUser = async (loginData) => { return { access_token, user_id }; } catch (error) { - if (error.response?.status === 401) { - throw new Error("Неверное имя пользователя или пароль"); + if (error.response) { + if (error.response.status === 403) { + throw new Error("Неверное имя пользователя или пароль"); + } } - - throw new Error(error.message); + throw new Error(error.message || "Произошла неизвестная ошибка при входе."); } }; -export default loginUser; +export default loginUser; \ No newline at end of file diff --git a/WEB/src/api/project_members/createProjectMember.js b/WEB/src/api/project_members/createProjectMember.js new file mode 100644 index 0000000..b3df079 --- /dev/null +++ b/WEB/src/api/project_members/createProjectMember.js @@ -0,0 +1,40 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + + +const createProjectMember = async (projectMemberData) => { + try { + const token = localStorage.getItem("access_token"); + + if (!projectMemberData.project_id) { + throw new Error("Отсутствует ID проекта для создания участника проекта."); + } + + const requestPayload = [projectMemberData]; + + const response = await axios.post( + `${CONFIG.BASE_URL}/project_members/${projectMemberData.project_id}/`, + requestPayload, + { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + } + ); + + + return response.data; // Бэкенд возвращает список, но мы ожидаем один созданный объект + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа для создания участников проекта (401): Требуется авторизация."); + } else if (error.response?.status === 403) { + throw new Error("Доступ запрещён (403): Недостаточно прав (требуются права администратора)."); + } else if (error.response?.status === 400) { + throw new Error(`Ошибка запроса: ${error.response.data.detail || error.message}`); + } + throw new Error(`Ошибка при создании участников проекта: ${error.message}`); + } +}; + +export default createProjectMember; \ No newline at end of file diff --git a/WEB/src/api/project_members/deleteProjectMember.js b/WEB/src/api/project_members/deleteProjectMember.js new file mode 100644 index 0000000..4ac8fa6 --- /dev/null +++ b/WEB/src/api/project_members/deleteProjectMember.js @@ -0,0 +1,30 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const deleteProjectMember = async (projectId) => { + try { + const token = localStorage.getItem("access_token"); + const response = await axios.delete( + `${CONFIG.BASE_URL}/project_members/${projectId}/`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа для удаления участников проекта (401): Требуется авторизация."); + } else if (error.response?.status === 403) { + throw new Error("Доступ запрещён (403): Недостаточно прав."); + } else if (error.response?.status === 405) { + throw new Error("Ошибка метода (405): Сервер не разрешает DELETE для этого ресурса. Проверьте конфигурацию API и соответствие URL."); + } else if (error.response?.status === 400 || error.response?.status === 422) { + throw new Error(`Ошибка запроса: ${error.response.data.detail || error.message}`); + } + throw new Error(`Ошибка при удалении участников проекта: ${error.message}`); + } +}; + +export default deleteProjectMember; \ No newline at end of file diff --git a/WEB/src/api/project_members/deleteSingleProjectMember.js b/WEB/src/api/project_members/deleteSingleProjectMember.js new file mode 100644 index 0000000..0f07399 --- /dev/null +++ b/WEB/src/api/project_members/deleteSingleProjectMember.js @@ -0,0 +1,30 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const deleteSingleProjectMember = async (memberId) => { + try { + const token = localStorage.getItem("access_token"); + const response = await axios.delete( + `${CONFIG.BASE_URL}/project_members/member/${memberId}/`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа для удаления участника проекта (401): Требуется авторизация."); + } else if (error.response?.status === 403) { + throw new Error("Доступ запрещён (403): Недостаточно прав."); + } else if (error.response?.status === 404) { // Добавлено 404, если участник не найден + throw new Error("Участник проекта не найден (404)."); + } else if (error.response?.status === 400 || error.response?.status === 422) { + throw new Error(`Ошибка запроса: ${error.response.data.detail || error.message}`); + } + throw new Error(`Ошибка при удалении участника проекта: ${error.message}`); + } +}; + +export default deleteSingleProjectMember; \ No newline at end of file diff --git a/WEB/src/api/project_members/getAllProjectMembers.js b/WEB/src/api/project_members/getAllProjectMembers.js new file mode 100644 index 0000000..2ba6a08 --- /dev/null +++ b/WEB/src/api/project_members/getAllProjectMembers.js @@ -0,0 +1,26 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const getAllProjectMembers = async () => { + try { + const token = localStorage.getItem("access_token"); // Получаем токен из localStorage + const response = await axios.get(`${CONFIG.BASE_URL}/project_members/`, { // Запрос к эндпоинту членов проекта + headers: { + Authorization: `Bearer ${token}`, // Отправляем токен для авторизации + }, + }); + + return response.data; // Возвращаем полученные данные + } catch (error) { + // Обработка ошибок в зависимости от статуса ответа + if (error.response?.status === 401) { + throw new Error("Нет доступа к членам проекта (401). Требуется авторизация."); + } else if (error.response?.status === 403) { + throw new new Error("Доступ запрещён (403). У вас нет прав для просмотра членов проекта."); + } + // В случае других ошибок, выбрасываем стандартную ошибку + throw new Error(error.message || "Неизвестная ошибка при получении членов проекта."); + } +}; + +export default getAllProjectMembers; \ No newline at end of file diff --git a/WEB/src/api/project_members/getProjectMemberByProject.js b/WEB/src/api/project_members/getProjectMemberByProject.js new file mode 100644 index 0000000..4d742a9 --- /dev/null +++ b/WEB/src/api/project_members/getProjectMemberByProject.js @@ -0,0 +1,24 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const getProjectMembersByProject = async (projectId) => { + try { + const token = localStorage.getItem("access_token"); + const response = await axios.get(`${CONFIG.BASE_URL}/project_members/by-project/${projectId}/`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа к участникам проекта (401): Требуется авторизация."); + } else if (error.response?.status === 403) { + throw new Error("Доступ запрещён (403): Недостаточно прав."); + } + throw new Error(`Ошибка при получении участников проекта: ${error.message}`); + } +}; + +export default getProjectMembersByProject; \ No newline at end of file diff --git a/WEB/src/api/project_members/getProjectMembersByProfile.js b/WEB/src/api/project_members/getProjectMembersByProfile.js new file mode 100644 index 0000000..4b99dee --- /dev/null +++ b/WEB/src/api/project_members/getProjectMembersByProfile.js @@ -0,0 +1,24 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const getProjectMembersByProfile = async (profileId) => { + try { + const token = localStorage.getItem("access_token"); + const response = await axios.get(`${CONFIG.BASE_URL}/project_members/by-profile/${profileId}/`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа к данным профиля (401): Требуется авторизация."); + } else if (error.response?.status === 403) { + throw new Error("Доступ запрещён (403): Недостаточно прав."); + } + throw new Error(`Ошибка при получении участников проекта по профилю: ${error.message}`); + } +}; + +export default getProjectMembersByProfile; \ No newline at end of file diff --git a/WEB/src/api/project_members/updateProjectMember.js b/WEB/src/api/project_members/updateProjectMember.js new file mode 100644 index 0000000..26636ac --- /dev/null +++ b/WEB/src/api/project_members/updateProjectMember.js @@ -0,0 +1,52 @@ +// @/api/project_members/updateProjectMember.js +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +// Эта функция теперь будет принимать project_id и ПОЛНЫЙ СПИСОК projectMembersData +// для данного проекта. +const updateProjectMember = async (projectId, projectMembersData) => { // Изменено: теперь принимает project_id и список + try { + const token = localStorage.getItem("access_token"); + + // Убедимся, что project_id присутствует + if (!projectId) { + throw new Error("Отсутствует ID проекта для обновления списка участников."); + } + // Убедимся, что projectMembersData - это массив + if (!Array.isArray(projectMembersData)) { + throw new Error("Данные для обновления участников проекта должны быть массивом."); + } + + // Бэкенд ожидает, что каждый объект в списке также будет содержать project_id, + // и что этот project_id будет совпадать с projectId из URL. + const payloadWithProjectIds = projectMembersData.map(member => ({ + ...member, + project_id: projectId // Убедимся, что project_id установлен в каждом объекте + })); + + const response = await axios.put( + `${CONFIG.BASE_URL}/project_members/${projectId}/`, // URL теперь использует project_id + payloadWithProjectIds, // Отправляем полный список с project_id в каждом элементе + { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + } + ); + + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа для обновления участников проекта (401): Требуется авторизация."); + } else if (error.response?.status === 403) { + throw new Error("Доступ запрещён (403): Недостаточно прав."); + } else if (error.response?.status === 400 || error.response?.status === 422) { + console.error("Backend Error Detail:", error.response.data); // Выводим детали ошибки бэкенда + throw new Error(`Ошибка запроса при обновлении: ${error.response.data.detail || error.message}`); + } + throw new Error(`Ошибка при обновлении участников проекта: ${error.message}`); + } +}; + +export default updateProjectMember; \ No newline at end of file diff --git a/WEB/src/api/teams/downloadTeamPhotoFile.js b/WEB/src/api/teams/downloadTeamPhotoFile.js new file mode 100644 index 0000000..9e776c0 --- /dev/null +++ b/WEB/src/api/teams/downloadTeamPhotoFile.js @@ -0,0 +1,27 @@ +import axios from 'axios' +import CONFIG from '@/core/config.js' + +const downloadTeamPhotoFile = async (teamId) => { + try { + const token = localStorage.getItem('access_token') + + const response = await axios.get( + `${CONFIG.BASE_URL}/teams/${teamId}/file/`, + { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}` + }, + responseType: 'blob' + } + ) + + return response.data + } catch (error) { + const errorMessage = error.response?.data?.detail || error.message + console.error(`Ошибка загрузки файла логотипа команды с ID ${teamId}:`, errorMessage) + throw new Error(`Не удалось загрузить файл логотипа команды: ${errorMessage}`) + } +} + +export default downloadTeamPhotoFile \ No newline at end of file diff --git a/WEB/src/api/teams/getActiveTeam.js b/WEB/src/api/teams/getActiveTeam.js new file mode 100644 index 0000000..545da0b --- /dev/null +++ b/WEB/src/api/teams/getActiveTeam.js @@ -0,0 +1,28 @@ +import axios from 'axios'; +import CONFIG from '@/core/config.js'; + +const getActiveTeam = async () => { + try { + const token = localStorage.getItem('access_token'); + const response = await axios.get( + `${CONFIG.BASE_URL}/teams/active/`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error('Нет доступа для получения активной команды (401): Требуется авторизация.'); + } else if (error.response?.status === 403) { + throw new Error('Доступ запрещён (403): Недостаточно прав.'); + } else if (error.response?.status === 404) { + return null; + } + throw new Error(`Ошибка при получении активной команды: ${error.message}`); + } +}; + +export default getActiveTeam; \ No newline at end of file diff --git a/WEB/src/api/teams/setActiveTeam.js b/WEB/src/api/teams/setActiveTeam.js new file mode 100644 index 0000000..4555e24 --- /dev/null +++ b/WEB/src/api/teams/setActiveTeam.js @@ -0,0 +1,32 @@ +import axios from 'axios'; +import CONFIG from '@/core/config.js'; + +const setActiveTeam = async (teamId) => { + try { + const token = localStorage.getItem('access_token'); + const response = await axios.put( + `${CONFIG.BASE_URL}/teams/${teamId}/set-active/`, + {}, + { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + } + ); + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error('Нет доступа для установки активной команды (401): Требуется авторизация.'); + } else if (error.response?.status === 403) { + throw new Error('Доступ запрещён (403): Недостаточно прав (требуются права администратора).'); + } else if (error.response?.status === 400) { + throw new Error(`Ошибка запроса: ${error.response.data.detail || error.message}`); + } else if (error.response?.status === 404) { + throw new Error(`Команда с ID ${teamId} не найдена.`); + } + throw new Error(`Ошибка при установке активной команды: ${error.message}`); + } +}; + +export default setActiveTeam; \ No newline at end of file diff --git a/WEB/src/api/teams/uploadTeamPhoto.js b/WEB/src/api/teams/uploadTeamPhoto.js new file mode 100644 index 0000000..1d3b829 --- /dev/null +++ b/WEB/src/api/teams/uploadTeamPhoto.js @@ -0,0 +1,30 @@ +import axios from 'axios' +import CONFIG from '@/core/config.js' + +const uploadTeamPhoto = async (teamId, file) => { + try { + const token = localStorage.getItem('access_token') + + const formData = new FormData() + formData.append('file', file) + + const response = await axios.post( + `${CONFIG.BASE_URL}/teams/${teamId}/upload/`, + formData, + { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + } + } + ) + + return response.data + } catch (error) { + const errorMessage = error.response?.data?.detail || error.message + console.error(`Ошибка загрузки фотографии для команды ${teamId}:`, errorMessage) + throw new Error(`Не удалось загрузить фотографию команды: ${errorMessage}`) + } +} + +export default uploadTeamPhoto \ No newline at end of file diff --git a/WEB/src/api/users/changeUserPassword.js b/WEB/src/api/users/changeUserPassword.js index c53c041..ae25683 100644 --- a/WEB/src/api/users/changeUserPassword.js +++ b/WEB/src/api/users/changeUserPassword.js @@ -1,14 +1,15 @@ import axios from "axios"; import CONFIG from "@/core/config.js"; -const changeUserPassword = async (userId, newPasswordData) => { +const changeUserPassword = async (userId, newPassword) => { try { const token = localStorage.getItem("access_token"); - const response = await axios.patch( - `${CONFIG.BASE_URL}/users/${userId}/password`, - newPasswordData, + const response = await axios.put( + `${CONFIG.BASE_URL}/users/${userId}/`, + { new_password: newPassword }, { headers: { + 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, } @@ -19,6 +20,11 @@ const changeUserPassword = async (userId, newPasswordData) => { throw new Error(error.response.data.detail); } else if (error.response?.status === 403) { throw new Error("Доступ запрещён (403)"); + } else if (error.response?.status === 422) { + const errorMessage = error.response.data.detail ? + error.response.data.detail.map(err => `${err.loc.join('.')} - ${err.msg}`).join('; ') : + 'Неизвестная ошибка валидации'; + throw new Error(`Ошибка валидации данных (422): ${errorMessage}`); } throw new Error(error.message); } diff --git a/WEB/src/api/users/registerUser.js b/WEB/src/api/users/registerUser.js index 462f85e..b75bfe7 100644 --- a/WEB/src/api/users/registerUser.js +++ b/WEB/src/api/users/registerUser.js @@ -1,18 +1,23 @@ -import axios from "axios"; -import CONFIG from "@/core/config.js"; +import axios from 'axios'; +import CONFIG from '@/core/config.js'; const registerUser = async (userData) => { try { + const token = localStorage.getItem('access_token'); const response = await axios.post( - `${CONFIG.BASE_URL}/users/register`, - userData + `${CONFIG.BASE_URL}/users/`, + userData, + { + withCredentials: true, + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + } + } ); return response.data; } catch (error) { - if (error.response?.status === 400) { - throw new Error(error.response.data.detail); - } - throw new Error(error.message); + throw new Error(error.response?.data?.detail || error.message); } }; diff --git a/WEB/src/main.js b/WEB/src/main.js index ab77447..2924eda 100644 --- a/WEB/src/main.js +++ b/WEB/src/main.js @@ -40,7 +40,8 @@ import { QItemLabel, QItem, QImg, - QFile + QFile, + QSelect } from 'quasar' @@ -60,7 +61,7 @@ app.use(Quasar, { QSeparator, QCardActions, QDialog, QIcon, QSpace, QAvatar, QTooltip, QBanner, QSlideTransition, QToggle, QList, QSpinnerDots, QCarouselSlide, QCarousel, - QItemSection, QItemLabel, QItem, QImg, QFile + QItemSection, QItemLabel, QItem, QImg, QFile, QSelect }, directives: { Ripple diff --git a/WEB/src/pages/AdminPage.vue b/WEB/src/pages/AdminPage.vue index 80f6cc5..c24d64c 100644 --- a/WEB/src/pages/AdminPage.vue +++ b/WEB/src/pages/AdminPage.vue @@ -15,6 +15,7 @@ + @@ -27,7 +28,14 @@ + + +
+
+ +
+ +
+
+ @@ -121,7 +151,7 @@ Редактирование команды {{ dialogData.title || '' }} -