факит

This commit is contained in:
Mikan
2025-06-02 16:43:01 +03:00
parent 4f10dd93c5
commit 6ede1ca15f
13 changed files with 720 additions and 17 deletions

241
StaticHelper/source/docs.md Normal file
View File

@@ -0,0 +1,241 @@
# Требования к коду
Используется библиотека `react-router-dom` и `react=markdown`.
Все компоненты должны быть функциональными.
props должны быть сразу распакованы.
export без default
Пример:
```tsx
export function ProfessionPage() {
const {professionId} = useParams<{ professionId: string }>();
}
```
Используйте современные практики React (например, хуки).
При создании новых компонентов добавьте комментарии.
Если для задачи необходимы новые типы, интерфейсы или вспомогательные компоненты - создавай их в соответвующих файлах.
# Документария
## Существующие типы
`types/api.ts`
```ts
export interface ApiResponse {
body?: object,
success: boolean
}
export interface PageResponse<T>{
count: number;
next?: string;
previous?: string;
results: T[]
}
```
`types/common.ts`
```ts
export interface UniqueItem{
id: number;
}
export interface PageControl<T>{
items: T[] | null;
hasNext: boolean;
hasPrevious: boolean;
nextPage(): void;
previousPage(): void;
}
```
`types/survey.ts`
```ts
import {UniqueItem} from "./common.ts";
export interface Survey extends UniqueItem{
title: string;
description: string;
}
interface Statement extends UniqueItem{
text: string;
score_variable: number;
}
export enum QuestionType{
QuestionPair,
AgreementQuestion
}
export interface Question extends UniqueItem{
questionType: QuestionType
}
export interface QuestionPair extends Question{
statements: Statement[]
}
export interface AgreementQuestion extends Question{
question: string;
score_variable: number;
}
export interface ScoreVariable {
id: string;
name: string;
title: string;
description: string;
survey: number;
}
export interface Profession extends UniqueItem {
name: string;
description: string;
}
export interface Specialty extends UniqueItem {
name: string;
institution: number;
link?: string;
}
export interface Institution extends UniqueItem {
name: string;
}
```
`types/users.ts`
```ts
import {UniqueItem} from "./common.ts";
export type UserContextType = {
user: Account | undefined;
login: (credentials: TokenObtainPair) => Promise<void>;
logout: () => void;
};
export interface ContactData{
first_name: string;
last_name?: string;
middle_name?: string;
phone?: string;
email?:string;
}
interface Role extends UniqueItem{
title: string;
}
export interface Account extends UniqueItem, ContactData{
login: string;
role?: Role;
is_staff: boolean;
is_active: boolean;
}
export interface TokenObtainPair {
login: string;
password: string;
}
export interface TokenRefresh {
refresh: string;
}
export interface TokenVerify {
token: string;
}
export interface JwtTokenResponse {
access: string;
refresh: string;
}
```
## Способы запросов
Вместо fetch запросы должны проходить при помощи специальных функций/функции
`API/common.ts`
```ts
async function getFetch(path: string) // Get method
async function postFetch(
path: string,
body: object | FormData
) // Post method
async function patchFetch(
path: string,
body: object | FormData
) // Patch method
async function deleteFetch(path: string) // Delete method
```
## Хуки
### Для загрузки данных
Для упрощения работы с api созданы следующие хуки
`API/hooks.ts`
```ts
function useDataPage<T>(application:string, endpoint: string, params:{[key:string]:string|number}={}, needLoad:boolean=true):PageControl<T>
function useData<T>(application:string, endpoint: string, id: string|number, needLoad:boolean=true): T|null|undefined
function useCachedData<T>(application: string, endpoint: string): (id: string | number, needLoad: boolean = true)=> T|undefined|null
```
- application - Название модуля
- endpoint - Название объекта над которым идёт работа
- id - id искомого объекта
- params - дополнительные параметры (например фильтрация)
- needLoad - указывает нужно ли загружать список/объект прямо сейчас
- null - загрузка еще не происходила
- undefined - не найдено
- T (объект)
### Для ленивой загрузки элементов
`components/LoadingData.tsx`
```tsx
function LoadingData({data, children}: {data: object|null|undefined, children: (ReactElement | null | undefined | boolean)[]})
```
Для загрузки отдельного элемента.
Если data
- null - Возвращает <div className={"loading"}>Идёт загрузка...</div>
- undefined - <Link className={"not-found"} to={"/"}>404 НЕ НАЙДЕНО</Link>
- иначе возвращает children
`components/LoadingList.tsx`
```tsx
function LoadingList({data, listElement}: {data: UniqueItem[]|null, listElement(e:UniqueItem, i:number):unknown})
```
Для загрузки списка. Если data не null - то возвращает элементы результаты `listElement`
### Примеры
```tsx
export function ProfessionPage() {
const { professionId } = useParams<{ professionId: string }>();
const profession = useData<Profession>('surveys', 'professions', professionId || 0);
const specialties = useDataPage<Specialty>('surveys', 'specialties', { profession: professionId || 0 }, !!profession);
const institutions = useCachedData<Institution>("surveys", "institutions");
return (
<div>
<LoadingData data={profession}>
<h1>{profession?.name}</h1>
<div className="description">
<ReactMarkdown>{profession?.description || ''}</ReactMarkdown>
</div>
<div className="specialties-block">
{specialties.items != null ? <h2>Где можно обучиться</h2>:null}
<LoadingList
data={specialties.items}
listElement={specialty => (
<SpecialtyBlock
key={specialty.id}
specialty={specialty as Specialty}
institution={institutions((specialty as Specialty).institution)}
/>
)}
/>
</div>
</LoadingData>
</div>
);
}
```
### Авторизация
Везде доступен хук `useUser():UserContextType | undefined `

View File

@@ -0,0 +1,14 @@
Ты Senior разработчик React.
Твоя задача - писать код под задачи (отвечай ТОЛЬКО кодом).
Я могу кидать тебе просто код или информацию. Если я не прошу писать код - отвечай только "Понял".
Код, по возможности, только тот который необходимо внести в проект. Может быть работа сразу над несколькими файлами
Шаблон ответа кодом:
`Путь и название файла`
```ts
// Код который должен быть в этом файле
```

View File

View File

@@ -0,0 +1,95 @@
`API/users.ts`
```ts
export async function login(data: TokenObtainPair): Promise<JwtTokenResponse | null>
export async function refreshToken(data: TokenRefresh): Promise<JwtTokenResponse | null>
export async function verifyToken(data: TokenVerify): Promise<boolean>
```
`utils/users/UserProvider.tsx`
```tsx
export function UserProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<Account | undefined>(() => {
const storedUser = localStorage.getItem('user');
return storedUser ? JSON.parse(storedUser) : undefined;
});
const [tokens, setTokens] = useState<JwtTokenResponse | undefined>(() => {
const storedTokens = localStorage.getItem('tokens');
return storedTokens ? JSON.parse(storedTokens) : undefined;
});
useEffect(() => {
if (tokens?.access) {
localStorage.setItem('tokens', JSON.stringify(tokens));
} else {
localStorage.removeItem('tokens');
}
}, [tokens]);
useEffect(() => {
if (user) {
localStorage.setItem('user', JSON.stringify(user));
} else {
localStorage.removeItem('user');
}
}, [user]);
const handleLogin = async (credentials: TokenObtainPair) => {
const tokens = await apiLogin(credentials);
if (tokens) {
setTokens(tokens);
// Здесь можно добавить запрос для получения данных пользователя
setUser({ id: 1, login: credentials.login, is_staff: false, is_active: true } as Account); // TODO
}
};
const handleLogout = () => {
setUser(undefined);
setTokens(undefined);
};
const validateToken = async () => {
if (tokens?.access) {
const isValid = await verifyToken({ token: tokens.access });
if (!isValid && tokens.refresh) {
const newTokens = await refreshToken({ refresh: tokens.refresh });
if (newTokens) {
setTokens(newTokens);
} else {
handleLogout();
}
} else if (!isValid) {
handleLogout();
}
}
};
useEffect(() => {
validateToken().then();
}, []);
return (
<UserContext.Provider value={{ user, login: handleLogin, logout: handleLogout }}>
{children}
</UserContext.Provider>
);
}
```
В частности обрати внимание на эту функцию
```ts
const handleLogin = async (credentials: TokenObtainPair) => {
const tokens = await apiLogin(credentials);
if (tokens) {
setTokens(tokens);
// Здесь можно добавить запрос для получения данных пользователя
setUser({ id: 1, login: credentials.login, is_staff: false, is_active: true } as Account); // TODO
}
};
```
Воспользуйся эндпоинтами /users/auth/users/me/ для получения id текущего пользователя