mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
666 lines
19 KiB
TypeScript
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() });
|
|
},
|
|
});
|
|
}
|
|
|