turash/bugulma/frontend/hooks/api/useAdminAPI.ts

666 lines
19 KiB
TypeScript

/**
* Admin API Hooks
* React Query hooks for admin operations
*/
import type {
CreateAnnouncementRequest,
CreatePageRequest,
CreateUserRequest,
UpdatePageRequest,
UpdateUserRequest
} from '@/services/admin-api';
import * as adminApi from '@/services/admin-api';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
// ============================================================================
// Query Keys
// ============================================================================
export const adminKeys = {
all: ['admin'] as const,
dashboard: {
all: ['admin', 'dashboard'] as const,
stats: () => [...adminKeys.dashboard.all, 'stats'] as const,
},
analytics: {
all: ['admin', 'analytics'] as const,
organizations: () => [...adminKeys.analytics.all, 'organizations'] as const,
users: () => [...adminKeys.analytics.all, 'users'] as const,
matching: () => [...adminKeys.analytics.all, 'matching'] as const,
},
system: {
all: ['admin', 'system'] as const,
health: () => [...adminKeys.system.all, 'health'] as const,
},
users: {
all: ['admin', 'users'] as const,
lists: (params?: unknown) => [...adminKeys.users.all, 'list', params] as const,
detail: (id: string) => [...adminKeys.users.all, 'detail', id] as const,
stats: () => [...adminKeys.users.all, 'stats'] as const,
activity: (id: string, params?: unknown) => [...adminKeys.users.all, 'activity', id, params] as const,
},
organizations: {
all: ['admin', 'organizations'] as const,
verificationQueue: (params?: unknown) => [...adminKeys.organizations.all, 'verification-queue', params] as const,
stats: () => [...adminKeys.organizations.all, 'stats'] as const,
},
i18n: {
all: ['admin', 'i18n'] as const,
translationKeys: (locale: string) => [...adminKeys.i18n.all, 'translation-keys', locale] as const,
},
content: {
all: ['admin', 'content'] as const,
pages: {
all: ['admin', 'content', 'pages'] as const,
lists: () => [...adminKeys.content.pages.all, 'list'] as const,
detail: (id: string) => [...adminKeys.content.pages.all, 'detail', id] as const,
},
announcements: {
all: ['admin', 'content', 'announcements'] as const,
lists: (params?: unknown) => [...adminKeys.content.announcements.all, 'list', params] as const,
detail: (id: string) => [...adminKeys.content.announcements.all, 'detail', id] as const,
},
media: {
all: ['admin', 'content', 'media'] as const,
lists: (params?: unknown) => [...adminKeys.content.media.all, 'list', params] as const,
detail: (id: string) => [...adminKeys.content.media.all, 'detail', id] as const,
},
},
};
// ============================================================================
// Dashboard & Statistics
// ============================================================================
/**
* Get dashboard statistics
*/
export function useDashboardStats() {
return useQuery({
queryKey: adminKeys.dashboard.stats(),
queryFn: () => adminApi.getDashboardStats(),
staleTime: 30000, // 30 seconds
retry: (failureCount, error: any) => {
// Don't retry on 403 (Forbidden) or 401 (Unauthorized) errors
if (error?.status === 403 || error?.status === 401) {
return false;
}
// Retry up to 2 times for other errors
return failureCount < 2;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
}
/**
* Get organization statistics
*/
export function useOrganizationStats() {
return useQuery({
queryKey: adminKeys.analytics.organizations(),
queryFn: () => adminApi.getOrganizationStats(),
staleTime: 60000, // 1 minute
});
}
/**
* Get user activity statistics
*/
export function useUserActivityStats() {
return useQuery({
queryKey: adminKeys.analytics.users(),
queryFn: () => adminApi.getUserActivityStats(),
staleTime: 60000, // 1 minute
});
}
/**
* Get matching statistics
*/
export function useMatchingStats() {
return useQuery({
queryKey: adminKeys.analytics.matching(),
queryFn: () => adminApi.getMatchingStats(),
staleTime: 60000, // 1 minute
});
}
/**
* Get system health
*/
export function useSystemHealth() {
return useQuery({
queryKey: adminKeys.system.health(),
queryFn: () => adminApi.getSystemHealth(),
staleTime: 10000, // 10 seconds
refetchInterval: 30000, // Refetch every 30 seconds
});
}
// ============================================================================
// User Management
// ============================================================================
/**
* List users
*/
export function useUsers(params?: {
role?: string;
isActive?: boolean;
search?: string;
limit?: number;
offset?: number;
}) {
return useQuery({
queryKey: adminKeys.users.lists(params),
queryFn: () => adminApi.listUsers(params),
});
}
/**
* Get user by ID
*/
export function useUser(id: string | null | undefined) {
return useQuery({
queryKey: adminKeys.users.detail(id!),
queryFn: () => adminApi.getUser(id!),
enabled: !!id,
});
}
/**
* Create user mutation
*/
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (request: CreateUserRequest) => adminApi.createUser(request),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.users.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.users.stats() });
},
});
}
/**
* Update user mutation
*/
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, request }: { id: string; request: UpdateUserRequest }) =>
adminApi.updateUser(id, request),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.users.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.users.detail(variables.id) });
queryClient.invalidateQueries({ queryKey: adminKeys.users.stats() });
},
});
}
/**
* Update user role mutation
*/
export function useUpdateUserRole() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, role }: { id: string; role: string }) => adminApi.updateUserRole(id, role),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.users.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.users.detail(variables.id) });
},
});
}
/**
* Update user permissions mutation
*/
export function useUpdateUserPermissions() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, permissions }: { id: string; permissions: string[] }) =>
adminApi.updateUserPermissions(id, permissions),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.users.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.users.detail(variables.id) });
},
});
}
/**
* Deactivate user mutation
*/
export function useDeactivateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => adminApi.deactivateUser(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.users.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.users.stats() });
},
});
}
/**
* Get user activity log
*/
export function useUserActivity(id: string | null | undefined, params?: { limit?: number; offset?: number }) {
return useQuery({
queryKey: adminKeys.users.activity(id!, params),
queryFn: () => adminApi.getUserActivity(id!, params),
enabled: !!id,
});
}
/**
* Get user statistics
*/
export function useUserStats() {
return useQuery({
queryKey: adminKeys.users.stats(),
queryFn: () => adminApi.getUserStats(),
staleTime: 60000, // 1 minute
});
}
// ============================================================================
// Organization Verification
// ============================================================================
/**
* Get verification queue
*/
export function useVerificationQueue(params?: {
status?: string;
dataType?: string;
organizationId?: string;
}) {
return useQuery({
queryKey: adminKeys.organizations.verificationQueue(params),
queryFn: () => adminApi.getVerificationQueue(params),
staleTime: 30000, // 30 seconds
});
}
/**
* Verify organization mutation
*/
export function useVerifyOrganization() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, notes }: { id: string; notes?: string }) => adminApi.verifyOrganization(id, notes),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.organizations.verificationQueue() });
queryClient.invalidateQueries({ queryKey: adminKeys.organizations.stats() });
queryClient.invalidateQueries({ queryKey: ['organizations'] });
},
});
}
/**
* Reject verification mutation
*/
export function useRejectVerification() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, reason, notes }: { id: string; reason: string; notes?: string }) =>
adminApi.rejectVerification(id, reason, notes),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.organizations.verificationQueue() });
},
});
}
/**
* Bulk verify organizations mutation
*/
export function useBulkVerifyOrganizations() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (organizationIds: string[]) => adminApi.bulkVerifyOrganizations(organizationIds),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.organizations.verificationQueue() });
queryClient.invalidateQueries({ queryKey: adminKeys.organizations.stats() });
queryClient.invalidateQueries({ queryKey: ['organizations'] });
},
});
}
/**
* Get organization statistics (admin)
*/
export function useOrganizationStatsAdmin() {
return useQuery({
queryKey: adminKeys.organizations.stats(),
queryFn: () => adminApi.getOrganizationStatsAdmin(),
staleTime: 60000, // 1 minute
});
}
// ============================================================================
// Localization Management
// ============================================================================
/**
* Get translation keys for a locale
*/
export function useTranslationKeys(locale: string) {
return useQuery({
queryKey: adminKeys.i18n.translationKeys(locale),
queryFn: () => adminApi.getTranslationKeys(locale),
enabled: !!locale,
});
}
/**
* Update UI translation mutation
*/
export function useUpdateUITranslation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ locale, key, value }: { locale: string; key: string; value: string }) =>
adminApi.updateUITranslation(locale, key, value),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.i18n.translationKeys(variables.locale) });
},
});
}
/**
* Bulk update UI translations mutation
*/
export function useBulkUpdateUITranslations() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (updates: Array<{ locale: string; key: string; value: string }>) =>
adminApi.bulkUpdateUITranslations(updates),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.i18n.all });
},
});
}
/**
* Auto-translate missing keys mutation
*/
export function useAutoTranslateMissing() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ sourceLocale, targetLocale }: { sourceLocale: string; targetLocale: string }) =>
adminApi.autoTranslateMissing(sourceLocale, targetLocale),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.i18n.translationKeys(variables.targetLocale) });
},
});
}
/**
* Update data translation mutation
*/
export function useUpdateDataTranslation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
entityType,
entityID,
field,
locale,
value,
}: {
entityType: string;
entityID: string;
field: string;
locale: string;
value: string;
}) => adminApi.updateDataTranslation(entityType, entityID, field, locale, value),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.i18n.all });
},
});
}
// ============================================================================
// Content Management
// ============================================================================
/**
* List static pages
*/
export function usePages() {
return useQuery({
queryKey: adminKeys.content.pages.lists(),
queryFn: () => adminApi.listPages(),
retry: (failureCount, error: any) => {
// Don't retry on 403 (Forbidden) or 401 (Unauthorized) errors
if (error?.status === 403 || error?.status === 401) {
return false;
}
// Retry up to 2 times for other errors
return failureCount < 2;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
}
/**
* Get page by ID
*/
export function usePage(id: string | null | undefined) {
return useQuery({
queryKey: adminKeys.content.pages.detail(id!),
queryFn: () => adminApi.getPage(id!),
enabled: !!id,
});
}
/**
* Create page mutation
*/
export function useCreatePage() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (request: CreatePageRequest) => adminApi.createPage(request),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.pages.lists() });
},
});
}
/**
* Update page mutation
*/
export function useUpdatePage() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, request }: { id: string; request: UpdatePageRequest }) =>
adminApi.updatePage(id, request),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.pages.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.content.pages.detail(variables.id) });
},
});
}
/**
* Delete page mutation
*/
export function useDeletePage() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => adminApi.deletePage(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.pages.lists() });
},
});
}
/**
* Publish page mutation
*/
export function usePublishPage() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => adminApi.publishPage(id),
onSuccess: (_, id) => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.pages.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.content.pages.detail(id) });
},
});
}
/**
* List announcements
*/
export function useAnnouncements(params?: { isActive?: boolean; priority?: string }) {
return useQuery({
queryKey: adminKeys.content.announcements.lists(params),
queryFn: () => adminApi.listAnnouncements(params),
retry: (failureCount, error: any) => {
// Don't retry on 403 (Forbidden) or 401 (Unauthorized) errors
if (error?.status === 403 || error?.status === 401) {
return false;
}
// Retry up to 2 times for other errors
return failureCount < 2;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
}
/**
* Get announcement by ID
*/
export function useAnnouncement(id: string | null | undefined) {
return useQuery({
queryKey: adminKeys.content.announcements.detail(id!),
queryFn: () => adminApi.getAnnouncement(id!),
enabled: !!id,
});
}
/**
* Create announcement mutation
*/
export function useCreateAnnouncement() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (request: CreateAnnouncementRequest) => adminApi.createAnnouncement(request),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.announcements.lists() });
},
});
}
/**
* Update announcement mutation
*/
export function useUpdateAnnouncement() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, request }: { id: string; request: Partial<CreateAnnouncementRequest> }) =>
adminApi.updateAnnouncement(id, request),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.announcements.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.content.announcements.detail(variables.id) });
},
});
}
/**
* Delete announcement mutation
*/
export function useDeleteAnnouncement() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => adminApi.deleteAnnouncement(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.announcements.lists() });
},
});
}
/**
* List media assets
*/
export function useMediaAssets(params?: { type?: string; tags?: string }) {
return useQuery({
queryKey: adminKeys.content.media.lists(params),
queryFn: () => adminApi.listMediaAssets(params),
retry: (failureCount, error: any) => {
// Don't retry on 403 (Forbidden) or 401 (Unauthorized) errors
if (error?.status === 403 || error?.status === 401) {
return false;
}
// Retry up to 2 times for other errors
return failureCount < 2;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
}
/**
* Get media asset by ID
*/
export function useMediaAsset(id: string | null | undefined) {
return useQuery({
queryKey: adminKeys.content.media.detail(id!),
queryFn: () => adminApi.getMediaAsset(id!),
enabled: !!id,
});
}
/**
* Create media asset mutation
*/
export function useCreateMediaAsset() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (request: {
filename: string;
originalName: string;
url: string;
type: string;
mimeType?: string;
size?: number;
width?: number;
height?: number;
duration?: number;
altText?: string;
tags?: string[];
}) => adminApi.createMediaAsset(request),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.media.lists() });
},
});
}
/**
* Update media asset mutation
*/
export function useUpdateMediaAsset() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, request }: { id: string; request: { altText?: string; tags?: string[] } }) =>
adminApi.updateMediaAsset(id, request),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.media.lists() });
queryClient.invalidateQueries({ queryKey: adminKeys.content.media.detail(variables.id) });
},
});
}
/**
* Delete media asset mutation
*/
export function useDeleteMediaAsset() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => adminApi.deleteMediaAsset(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: adminKeys.content.media.lists() });
},
});
}