first commit
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled

This commit is contained in:
2026-07-23 02:38:05 +03:00
commit 8757bec8ac
335 changed files with 61527 additions and 0 deletions

80
frontend/src/api/users.ts Normal file
View File

@@ -0,0 +1,80 @@
/**
* API-функции профиля текущего пользователя и поиска зарегистрированных
* пользователей (участники конференций, профиль).
*/
import { apiRequest } from '@/api/client'
import type { CurrentUser } from '@/api/auth'
/** Элемент результата поиска пользователей (пикер участников конференции) — без email, только для отображения/выбора по `id`. */
export interface UserSearchResult {
id: string
display_name: string
avatar_url: string | null
}
/** Поиск зарегистрированных пользователей по имени/email — для пикера участников конференции. */
export async function searchUsers(query: string, limit = 8): Promise<UserSearchResult[]> {
const params = new URLSearchParams({ q: query, limit: String(limit) })
return apiRequest<UserSearchResult[]>(`/users?${params.toString()}`)
}
/** Команда в справочнике команд (страница профиля). */
export interface TeamOption {
id: string
name: string
}
/**
* Справочник команд для страницы профиля — аутентифицированный эндпоинт,
* не зависит от тумблера `registration_team_choice` (в отличие от
* `getRegistrationOptions` в `api/auth.ts`, который гасит список команд при
* выключенном выборе команды на регистрации).
*/
export async function listTeams(): Promise<TeamOption[]> {
return apiRequest<TeamOption[]>('/teams')
}
/** Тело правки собственного профиля — ФИО и/или команда, email не редактируется. */
export interface UpdateMePayload {
name_user?: string
/** `null` — снять с команды. */
team_id?: string | null
}
/** Частично обновить собственный профиль. */
export async function updateMe(payload: UpdateMePayload): Promise<CurrentUser> {
return apiRequest<CurrentUser>('/users/me', { method: 'PATCH', body: payload })
}
/**
* Загрузить аватар текущего пользователя. 200 — полный обновлённый профиль.
* 413 — файл больше 2 МБ, 415 — недопустимый тип (разрешены jpeg/png/webp);
* оба кода обрабатываются вызывающей стороной через `ApiError.status`.
*/
export async function uploadMyAvatar(file: File): Promise<CurrentUser> {
const form = new FormData()
form.append('file', file)
return apiRequest<CurrentUser>('/users/me/avatar', { method: 'POST', body: form })
}
/** Удалить аватар текущего пользователя — 204 без тела; вызывающая сторона сама перечитывает профиль (см. `refreshUser`). */
export async function deleteMyAvatar(): Promise<void> {
await apiRequest('/users/me/avatar', { method: 'DELETE' })
}
/** Тело смены пароля текущего пользователя. */
export interface PasswordChangePayload {
current_password: string
new_password: string
}
/**
* Сменить пароль текущего пользователя. 204 без тела. 400 `invalid_current_password` —
* неверный текущий пароль, 422 — новый пароль короче 8 символов.
*
* Refresh-сессии сознательно не отзываются backend'ом (минимальный дифф —
* см. ADR-005, отзыв запланирован вместе со сбросом пароля по email в v0.1.0).
*/
export async function changePassword(payload: PasswordChangePayload): Promise<void> {
await apiRequest('/users/me/password', { method: 'POST', body: payload })
}