119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
/**
|
|
* API для профиля и настроек
|
|
*/
|
|
|
|
import apiClient from '@/lib/api-client';
|
|
import type { User } from './auth';
|
|
|
|
export interface CityOption {
|
|
name: string;
|
|
timezone?: string;
|
|
region?: string;
|
|
full_name?: string;
|
|
}
|
|
|
|
export interface MentorHomeworkAISettings {
|
|
ai_trust_draft?: boolean;
|
|
ai_trust_publish?: boolean;
|
|
}
|
|
|
|
export interface ProfileSettings {
|
|
preferences: {
|
|
timezone?: string;
|
|
country?: string;
|
|
city?: string;
|
|
};
|
|
notifications: {
|
|
email_notifications?: boolean;
|
|
telegram_notifications?: boolean;
|
|
in_app_notifications?: boolean;
|
|
};
|
|
/** Только для ментора: доверие AI при проверке ДЗ */
|
|
mentor_homework_ai?: MentorHomeworkAISettings;
|
|
}
|
|
|
|
export async function getProfileSettings(): Promise<ProfileSettings> {
|
|
const response = await apiClient.get<ProfileSettings>('/profile/settings/');
|
|
return response.data;
|
|
}
|
|
|
|
export async function updateProfileSettings(data: Partial<ProfileSettings>): Promise<void> {
|
|
await apiClient.patch('/profile/update_settings/', data);
|
|
}
|
|
|
|
export async function searchCitiesFromCSV(query: string, limit = 50): Promise<CityOption[]> {
|
|
try {
|
|
const params = new URLSearchParams();
|
|
params.set('q', query);
|
|
params.set('limit', limit.toString());
|
|
const response = await apiClient.get<CityOption[]>(`/profile/cities/search/?${params.toString()}`);
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export interface UpdateProfileData {
|
|
first_name?: string;
|
|
last_name?: string;
|
|
phone?: string;
|
|
email?: string;
|
|
bio?: string;
|
|
avatar?: File | null;
|
|
}
|
|
|
|
/** Кэш аватаров — не делаем повторных запросов в течение сессии */
|
|
const avatarUrlCache = new Map<string, { url: string | null; timestamp: number }>();
|
|
const AVATAR_CACHE_TTL = 5 * 60 * 1000; // 5 минут
|
|
|
|
/** URL аватара пользователя по ID (для плейсхолдера в видеоконференции). GET /api/users/<id>/avatar_url/ */
|
|
export async function getAvatarUrl(userId: number | string): Promise<string | null> {
|
|
const key = String(userId);
|
|
const cached = avatarUrlCache.get(key);
|
|
if (cached && Date.now() - cached.timestamp < AVATAR_CACHE_TTL) {
|
|
return cached.url;
|
|
}
|
|
try {
|
|
const response = await apiClient.get<{ avatar_url: string | null }>(`/users/${userId}/avatar_url/`);
|
|
const url = response.data?.avatar_url ?? null;
|
|
avatarUrlCache.set(key, { url, timestamp: Date.now() });
|
|
return url;
|
|
} catch {
|
|
avatarUrlCache.set(key, { url: null, timestamp: Date.now() });
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Загрузить аватар из Telegram (требуется привязанный Telegram). */
|
|
export async function loadTelegramAvatar(): Promise<User> {
|
|
const response = await apiClient.post<{ success?: boolean; user: User }>('/profile/load_telegram_avatar/');
|
|
const data = response.data as { success?: boolean; user?: User };
|
|
return data?.user ?? (data as unknown as User);
|
|
}
|
|
|
|
export async function updateProfile(data: UpdateProfileData): Promise<User> {
|
|
if (data.avatar !== undefined && data.avatar !== null && data.avatar instanceof File) {
|
|
const formData = new FormData();
|
|
if (data.first_name) formData.append('first_name', data.first_name);
|
|
if (data.last_name) formData.append('last_name', data.last_name);
|
|
if (data.phone) formData.append('phone', data.phone);
|
|
if (data.email) formData.append('email', data.email);
|
|
if (data.bio) formData.append('bio', data.bio);
|
|
formData.append('avatar', data.avatar);
|
|
const res = await apiClient.getInstance().patch<User>('/profile/update_profile/', formData);
|
|
return res.data;
|
|
}
|
|
if (data.avatar === null) {
|
|
const formData = new FormData();
|
|
if (data.first_name) formData.append('first_name', data.first_name);
|
|
if (data.last_name) formData.append('last_name', data.last_name);
|
|
if (data.phone) formData.append('phone', data.phone);
|
|
if (data.email) formData.append('email', data.email);
|
|
formData.append('avatar', '');
|
|
const res = await apiClient.getInstance().patch<User>('/profile/update_profile/', formData);
|
|
return res.data;
|
|
}
|
|
const response = await apiClient.patch<User>('/profile/update_profile/', data);
|
|
return response.data;
|
|
}
|