From 546192b96d4ca6906257de3b9d84c4c8e9f709e5 Mon Sep 17 00:00:00 2001 From: Mikan <72257910+Mikan-DS@users.noreply.github.com> Date: Mon, 2 Jun 2025 19:39:54 +0300 Subject: [PATCH] Doc fetch --- src/API/hooks.ts | 92 +++++++++++++++++++- src/API/users.ts | 8 ++ src/App.tsx | 10 ++- src/layouts/DashboardLayout.tsx | 11 ++- src/pages/DashboardPage.tsx | 3 - src/pages/dashboards/DashboardPage.tsx | 70 +++++++++++++++ src/pages/dashboards/DataPage.tsx | 47 ++++++++++ src/pages/{ => surveys}/ProfessionPage.tsx | 8 +- src/pages/{ => surveys}/SurveyPage.tsx | 8 +- src/pages/{ => surveys}/SurveyResultPage.tsx | 8 +- src/types/common.ts | 6 ++ 11 files changed, 244 insertions(+), 27 deletions(-) delete mode 100644 src/pages/DashboardPage.tsx create mode 100644 src/pages/dashboards/DashboardPage.tsx create mode 100644 src/pages/dashboards/DataPage.tsx rename src/pages/{ => surveys}/ProfessionPage.tsx (87%) rename src/pages/{ => surveys}/SurveyPage.tsx (96%) rename src/pages/{ => surveys}/SurveyResultPage.tsx (86%) diff --git a/src/API/hooks.ts b/src/API/hooks.ts index 426c91b..9b30192 100644 --- a/src/API/hooks.ts +++ b/src/API/hooks.ts @@ -1,8 +1,8 @@ -import {PageControl} from "../types/common.ts"; +import {FieldInfo, PageControl} from "../types/common.ts"; import {useEffect, useState} from "react"; import {ApiResponse, PageResponse} from "../types/api.ts"; import {getFetch} from "./common.ts"; -import {createQueryString} from "../utils/common.ts"; +import {createQueryString, hostUrl} from "../utils/common.ts"; export function useDataPage(application:string, endpoint: string, params:{[key:string]:string|number}={}, needLoad:boolean=true):PageControl { const [data, setData] = useState(null); @@ -137,3 +137,91 @@ export function useCachedData(application: string, endpoint: string): CachedD } } +/** + * Хук для получения документации swagger JSON и извлечения структур. + * @param applicationName Название модуля/приложения. + * @param endpointName Название эндпоинта. + * @returns Массив структур без ID с полями: название, тип, title, format. + */ +export function useApiDocumentation( + applicationName: string, + endpointName: string +): { schema: FieldInfo[] | null; loading: boolean; error: Error | null } { + const [schema, setSchema] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + async function fetchDocumentation() { + try { + setLoading(true); + const response = await fetch(hostUrl('/docs/swaggerjson/')); + if (!response.ok) { + throw new Error(`Failed to fetch documentation: ${response.statusText}`); + } + const data = await response.json(); + + // Найти путь по applicationName и endpointName + const pathKey = `/${applicationName}/${endpointName}/`; + const pathItem = data.paths?.[pathKey]; + if (!pathItem) { + throw new Error(`Path ${pathKey} not found`); + } + + + const getMethod = pathItem.get; + if (!getMethod || !getMethod.responses?.['200']) { + throw new Error(`GET method or response 200 not found for ${pathKey}`); + } + + + + const schemaRef = getMethod.responses["200"].schema?.properties?.results?.items?.['$ref']; + if (!schemaRef) { + throw new Error(`$ref not found in response schema for ${pathKey}`); + } + + console.log(schemaRef.match(/#\/definitions\/(\w+)/)) + + // Получить название определения + const defNameMatch = schemaRef.match(/#\/definitions\/(\w+)/); + if (!defNameMatch || defNameMatch.length < 2) { + throw new Error(`Invalid $ref format: ${schemaRef}`); + } + const defName = defNameMatch[1]; + + const definitions = data.definitions; + if (!definitions || !definitions[defName]) { + throw new Error(`Definition ${defName} not found`); + } + + const defProps = definitions[defName].properties; + if (!defProps) { + throw new Error(`Properties for ${defName} not found`); + } + + // Собрать поля, исключая id + const result: FieldInfo[] = Object.entries(defProps) + .filter((prop) => prop[0] !== 'id') + .map(([name, prop]) => { + return { + name, + type: prop.type || '', + title: prop.title || '', + format: prop.format || undefined, + }; + }); + setSchema(result); + } catch (err) { + setError(err as Error); + } finally { + setLoading(false); + } + } + + fetchDocumentation().then(); + }, [applicationName, endpointName]); + + return { schema, loading, error }; +} + diff --git a/src/API/users.ts b/src/API/users.ts index 5ebdbef..e4f4713 100644 --- a/src/API/users.ts +++ b/src/API/users.ts @@ -1,6 +1,7 @@ import { TokenObtainPair, TokenRefresh, TokenVerify, JwtTokenResponse } from '../types/users'; import {apiUrl} from "../utils/common.ts"; +import {postFetch} from "./common.ts"; export async function login(data: TokenObtainPair): Promise { @@ -44,3 +45,10 @@ export async function verifyToken(data: TokenVerify): Promise { return response.ok; } + +export async function changePassword(data: { currentPassword: string; newPassword: string }) { + const response = await postFetch("/users/auth/users/set_password/", {new_password: data.newPassword, re_new_password: data.newPassword, current_password: data.currentPassword}); + if (!response.success) { + throw new Error('Не удалось изменить пароль'); + } +} diff --git a/src/App.tsx b/src/App.tsx index 4a7cda1..76eeb75 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,16 @@ // App.tsx import {BrowserRouter as Router, Routes, Route, Navigate} from 'react-router-dom'; import {MainPage} from "./pages/MainPage.tsx"; -import {SurveyPage} from "./pages/SurveyPage.tsx"; -import {DashboardPage} from "./pages/DashboardPage.tsx"; -import { SurveyResultPage } from './pages/SurveyResultPage.tsx'; -import { ProfessionPage } from './pages/ProfessionPage.tsx'; +import {SurveyPage} from "./pages/surveys/SurveyPage.tsx"; +import {DashboardPage} from "./pages/dashboards/DashboardPage.tsx"; +import { SurveyResultPage } from './pages/surveys/SurveyResultPage.tsx'; +import { ProfessionPage } from './pages/surveys/ProfessionPage.tsx'; import React from "react"; import {LoginPage} from "./pages/LoginPage.tsx"; import {UserProvider} from "./utils/users/UserProvider.tsx"; import {DashboardLayout} from "./layouts/DashboardLayout.tsx"; import {MainLayout} from "./layouts/MainLayout.tsx"; +import {DataPage} from "./pages/dashboards/DataPage.tsx"; export default function App() { @@ -23,6 +24,7 @@ export default function App() { }> } /> + } /> diff --git a/src/layouts/DashboardLayout.tsx b/src/layouts/DashboardLayout.tsx index fe3e288..f256885 100644 --- a/src/layouts/DashboardLayout.tsx +++ b/src/layouts/DashboardLayout.tsx @@ -28,12 +28,11 @@ export function DashboardLayout() {

Pro-Fi

{user.roleTitle || "Роль не опознана"}

{/* Отображаем текущую роль пользователя */} diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx deleted file mode 100644 index 59e4964..0000000 --- a/src/pages/DashboardPage.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export function DashboardPage() { - return null; -} \ No newline at end of file diff --git a/src/pages/dashboards/DashboardPage.tsx b/src/pages/dashboards/DashboardPage.tsx new file mode 100644 index 0000000..a8c2ade --- /dev/null +++ b/src/pages/dashboards/DashboardPage.tsx @@ -0,0 +1,70 @@ +// pages/DashboardPage.tsx +import React, { useState } from 'react'; +import {useUser} from "../../utils/users/UseUser.ts"; +import {changePassword} from "../../API/users.ts"; + + +export function DashboardPage() { + // Получаем данные о пользователе + const userContext = useUser(); + const user = userContext?.user; + + // Состояние для управления модальным окном + const [isModalOpen, setModalOpen] = useState(false); + const [currentPassword, setCurrentPassword] = useState(''); + const [newPassword, setNewPassword] = useState(''); + + // Обработчик смены пароля + const handleChangePassword = async () => { + try { + await changePassword({ currentPassword, newPassword }); + alert('Пароль успешно изменен!'); + setModalOpen(false); // Закрыть модальное окно после успешной смены пароля + } catch (error) { + alert('Ошибка при смене пароля: ' + error.message); + } + }; + + return ( +
+
+ С возвращением, {user?.last_name} {user?.first_name} {user?.middle_name}! +
+
+ Вы вошли как: {user?.roleTitle} +
+ + { + !isModalOpen && + + } + + {isModalOpen && ( +
+
+ + + + +
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/pages/dashboards/DataPage.tsx b/src/pages/dashboards/DataPage.tsx new file mode 100644 index 0000000..60a9888 --- /dev/null +++ b/src/pages/dashboards/DataPage.tsx @@ -0,0 +1,47 @@ +import { useParams } from 'react-router-dom'; +import {useApiDocumentation} from "../../API/hooks.ts"; + +export function DataPage() { + const { applicationName, endpointName } = useParams<{ applicationName: string; endpointName: string }>(); + + const {schema} = useApiDocumentation(applicationName || "", endpointName || ""); + + console.log(schema) + + const handleExport = () => { + // Пока пусто + }; + + const handleAdd = () => { + // Пока пусто + }; + + const handleFilters = () => { + // Пока пусто + }; + + return ( +
+
+ + + +
+
+ + + + + + + + + + + {/* Тут могут быть строки данных */} + +
Header 1Header 2Header 3Header 4
+
+
+ ); +} \ No newline at end of file diff --git a/src/pages/ProfessionPage.tsx b/src/pages/surveys/ProfessionPage.tsx similarity index 87% rename from src/pages/ProfessionPage.tsx rename to src/pages/surveys/ProfessionPage.tsx index 787cdf9..c551025 100644 --- a/src/pages/ProfessionPage.tsx +++ b/src/pages/surveys/ProfessionPage.tsx @@ -1,9 +1,9 @@ import {Link, useParams} from 'react-router-dom'; -import { Institution, Profession, Specialty } from '../types/survey'; +import { Institution, Profession, Specialty } from '../../types/survey.ts'; import ReactMarkdown from 'react-markdown'; -import { LoadingData } from '../components/LoadingData'; -import { LoadingList } from '../components/LoadingList'; -import {useCachedData, useData, useDataPage} from "../API/hooks.ts"; +import { LoadingData } from '../../components/LoadingData.tsx'; +import { LoadingList } from '../../components/LoadingList.tsx'; +import {useCachedData, useData, useDataPage} from "../../API/hooks.ts"; diff --git a/src/pages/SurveyPage.tsx b/src/pages/surveys/SurveyPage.tsx similarity index 96% rename from src/pages/SurveyPage.tsx rename to src/pages/surveys/SurveyPage.tsx index 2ce821d..deab071 100644 --- a/src/pages/SurveyPage.tsx +++ b/src/pages/surveys/SurveyPage.tsx @@ -7,10 +7,10 @@ import { AgreementQuestion, QuestionType, -} from "../types/survey"; -import { LoadingData } from "../components/LoadingData"; -import { LoadingList } from "../components/LoadingList"; -import {useData, useDataPage} from "../API/hooks.ts"; +} from "../../types/survey.ts"; +import { LoadingData } from "../../components/LoadingData.tsx"; +import { LoadingList } from "../../components/LoadingList.tsx"; +import {useData, useDataPage} from "../../API/hooks.ts"; export function SurveyPage() { const { surveyId } = useParams<{ surveyId: string }>(); diff --git a/src/pages/SurveyResultPage.tsx b/src/pages/surveys/SurveyResultPage.tsx similarity index 86% rename from src/pages/SurveyResultPage.tsx rename to src/pages/surveys/SurveyResultPage.tsx index 9affb35..449aa1d 100644 --- a/src/pages/SurveyResultPage.tsx +++ b/src/pages/surveys/SurveyResultPage.tsx @@ -1,9 +1,9 @@ import { Link, useParams } from 'react-router-dom'; -import { Profession, ScoreVariable, Survey } from '../types/survey'; +import { Profession, ScoreVariable, Survey } from '../../types/survey.ts'; import ReactMarkdown from 'react-markdown'; -import { LoadingData } from '../components/LoadingData'; -import { LoadingList } from '../components/LoadingList'; -import {useData, useDataPage} from "../API/hooks.ts"; +import { LoadingData } from '../../components/LoadingData.tsx'; +import { LoadingList } from '../../components/LoadingList.tsx'; +import {useData, useDataPage} from "../../API/hooks.ts"; export function SurveyResultPage() { const { scoreVariableId } = useParams<{ scoreVariableId: string }>(); diff --git a/src/types/common.ts b/src/types/common.ts index 031906b..457c486 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -11,3 +11,9 @@ export interface PageControl{ previousPage(): void; } +export interface FieldInfo { + name: string; + type: string; + title: string; + format?: string; +} \ No newline at end of file