uchill/front_material/api/profile.ts

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;
}