import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { api, authProbeRetry, unwrap } from "../api/client"; import { qk } from "../api/queryKeys"; import type { AdminMe, AnnouncementUpdate, AnnouncementWrite, MatchUpdate, } from "../domain/types"; import { uploadFile } from "../lib/upload"; export function useAdminMe() { return useQuery({ queryKey: qk.adminMe, queryFn: async (): Promise => { const r = await api.GET("/api/admin/me"); if (r.response.status === 401 || r.response.status === 403) return null; return unwrap(r); }, retry: authProbeRetry, }); } export function useAdminLogin() { const qc = useQueryClient(); return useMutation({ mutationFn: async (body: { username: string; password: string }) => unwrap(await api.POST("/api/admin/auth/login", { body })), onSuccess: (me) => qc.setQueryData(qk.adminMe, me), }); } export function useAdminLogout() { const qc = useQueryClient(); return useMutation({ mutationFn: async () => unwrap(await api.POST("/api/admin/auth/logout")), onSuccess: () => qc.setQueryData(qk.adminMe, null), }); } export function useAdminUsers(query?: string) { return useQuery({ queryKey: [...qk.adminUsers, query ?? ""], queryFn: async () => unwrap( await api.GET("/api/admin/users", { params: { query: query ? { query } : {} }, }), ), }); } export function useAdminGroups() { return useQuery({ queryKey: qk.adminGroups, queryFn: async () => unwrap(await api.GET("/api/admin/groups")), }); } export interface AdminMatchFilters { group_id?: number | null; user_id?: number | null; faction_id?: number | null; } export function useAdminMatches(filters: AdminMatchFilters = {}) { const query: Record = {}; if (filters.group_id != null) query.group_id = filters.group_id; if (filters.user_id != null) query.user_id = filters.user_id; if (filters.faction_id != null) query.faction_id = filters.faction_id; return useQuery({ queryKey: [...qk.adminMatches, query], queryFn: async () => unwrap(await api.GET("/api/admin/matches", { params: { query } })), }); } export function useAdminMatch(matchId: number | null) { return useQuery({ queryKey: ["adminMatch", matchId], enabled: matchId != null, queryFn: async () => unwrap( await api.GET("/api/admin/matches/{match_id}", { params: { path: { match_id: matchId as number } }, }), ), }); } // ─── Медиа партии (админ правит в любой момент) ────────────────────────────── export function useAdminMatchAttachments(matchId: number | null) { return useQuery({ queryKey: ["adminMatchAttachments", matchId], enabled: matchId != null, queryFn: async () => unwrap( await api.GET("/api/admin/matches/{match_id}/attachments", { params: { path: { match_id: matchId as number } }, }), ), }); } export function useAdminUploadAttachment(matchId: number) { const qc = useQueryClient(); return useMutation({ mutationFn: async (file: File) => uploadFile(`/api/admin/matches/${matchId}/attachments`, file), onSuccess: () => qc.invalidateQueries({ queryKey: ["adminMatchAttachments", matchId] }), }); } export function useAdminDeleteAttachment(matchId: number) { const qc = useQueryClient(); return useMutation({ mutationFn: async (attachmentId: number) => unwrap( await api.DELETE("/api/admin/matches/{match_id}/attachments/{attachment_id}", { params: { path: { match_id: matchId, attachment_id: attachmentId } }, }), ), onSuccess: () => qc.invalidateQueries({ queryKey: ["adminMatchAttachments", matchId] }), }); } export function useAdminUpdateMatch() { const qc = useQueryClient(); return useMutation({ mutationFn: async (args: { matchId: number; body: MatchUpdate }) => unwrap( await api.PATCH("/api/admin/matches/{match_id}", { params: { path: { match_id: args.matchId } }, body: args.body, }), ), onSuccess: (_d, args) => { qc.invalidateQueries({ queryKey: qk.adminMatches }); qc.invalidateQueries({ queryKey: ["adminMatch", args.matchId] }); }, }); } export function useAdminFactions() { return useQuery({ queryKey: ["adminFactions"], queryFn: async () => unwrap(await api.GET("/api/admin/factions")), }); } export function useAdminRenameFaction() { const qc = useQueryClient(); return useMutation({ mutationFn: async (args: { factionId: number; name_ru: string }) => unwrap( await api.PATCH("/api/admin/factions/{faction_id}", { params: { path: { faction_id: args.factionId } }, body: { name_ru: args.name_ru }, }), ), onSuccess: () => qc.invalidateQueries({ queryKey: ["adminFactions"] }), }); } export function useAdminLogs(filters?: { action?: string; entity_type?: string }) { return useQuery({ queryKey: [...qk.adminLogs, filters?.action ?? "", filters?.entity_type ?? ""], queryFn: async () => unwrap( await api.GET("/api/admin/audit-logs", { params: { query: { ...(filters?.action ? { action: filters.action } : {}), ...(filters?.entity_type ? { entity_type: filters.entity_type } : {}), }, }, }), ), }); } export function useAdminDeleteMatch() { const qc = useQueryClient(); return useMutation({ mutationFn: async (matchId: number) => unwrap( await api.DELETE("/api/admin/matches/{match_id}", { params: { path: { match_id: matchId } }, }), ), onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminMatches }), }); } export function useAdminUpdateUser() { const qc = useQueryClient(); return useMutation({ mutationFn: async (args: { userId: number; nickname?: string; is_active?: boolean }) => unwrap( await api.PATCH("/api/admin/users/{user_id}", { params: { path: { user_id: args.userId } }, body: { nickname: args.nickname, is_active: args.is_active }, }), ), onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminUsers }), }); } // Новый пароль игроку — когда он забыл свой (почту приложение не хранит). export function useAdminSetPassword() { const qc = useQueryClient(); return useMutation({ mutationFn: async (args: { userId: number; password: string }) => unwrap( await api.PUT("/api/admin/users/{user_id}/password", { params: { path: { user_id: args.userId } }, body: { new_password: args.password }, }), ), onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminUsers }), }); } // ─── Ачивки (редактор; выдача игрокам — на будущее) ────────────────────────── interface AchievementInput { name?: string; description?: string; condition?: string; } export function useAdminAchievements() { return useQuery({ queryKey: qk.adminAchievements, queryFn: async () => unwrap(await api.GET("/api/admin/achievements")), }); } export function useCreateAchievement() { const qc = useQueryClient(); return useMutation({ mutationFn: async (body: AchievementInput & { name: string }) => unwrap(await api.POST("/api/admin/achievements", { body })), onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminAchievements }), }); } export function useUpdateAchievement() { const qc = useQueryClient(); return useMutation({ mutationFn: async (args: { slug: string; body: AchievementInput }) => unwrap( await api.PATCH("/api/admin/achievements/{slug}", { params: { path: { slug: args.slug } }, body: args.body, }), ), onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminAchievements }), }); } export function useDeleteAchievement() { const qc = useQueryClient(); return useMutation({ mutationFn: async (slug: string) => unwrap( await api.DELETE("/api/admin/achievements/{slug}", { params: { path: { slug } }, }), ), onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminAchievements }), }); } // Иконка ачивки — multipart отдельным fetch (как аватар), с CSRF из cookie. export function useUploadAchievementIcon() { const qc = useQueryClient(); return useMutation({ // Иконку шлём оригиналом: ресайз в JPEG убил бы прозрачность герба. mutationFn: async ({ slug, file }: { slug: string; file: File }) => uploadFile(`/api/admin/achievements/${slug}/icon`, file, { method: "PUT", maxSide: null, }), onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminAchievements }), }); } // ─── Объявления ────────────────────────────────────────────────────────────── export function useAdminAnnouncements() { return useQuery({ queryKey: qk.adminAnnouncements, queryFn: async () => unwrap(await api.GET("/api/admin/announcements")), }); } function useAnnouncementMutation(fn: (args: A) => Promise) { const qc = useQueryClient(); return useMutation({ mutationFn: fn, onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminAnnouncements }), }); } export function useAdminCreateAnnouncement() { return useAnnouncementMutation(async (body: AnnouncementWrite) => unwrap(await api.POST("/api/admin/announcements", { body })), ); } export function useAdminUpdateAnnouncement() { return useAnnouncementMutation(async (args: { id: number; body: AnnouncementUpdate }) => unwrap( await api.PUT("/api/admin/announcements/{announcement_id}", { params: { path: { announcement_id: args.id } }, body: args.body, }), ), ); } export function useAdminStopAnnouncement() { return useAnnouncementMutation(async (id: number) => unwrap( await api.POST("/api/admin/announcements/{announcement_id}/stop", { params: { path: { announcement_id: id } }, }), ), ); } export function useAdminDeleteAnnouncement() { return useAnnouncementMutation(async (id: number) => unwrap( await api.DELETE("/api/admin/announcements/{announcement_id}", { params: { path: { announcement_id: id } }, }), ), ); }