typed api

This commit is contained in:
nvrl committed 2026-04-26 03:14:57 +02:00
1 parent 5219a66722
commit fa37f8eb79
61 files changed
+2961 -2578

No files matched your search

@@ -0,0 +1,91 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
const invalidateAdmin = (qc: ReturnType<typeof useQueryClient>) => {
qc.invalidateQueries({ queryKey: ['adminStats'] });
qc.invalidateQueries({ queryKey: ['adminUsers'] });
qc.invalidateQueries({ queryKey: ['adminJobs'] });
qc.invalidateQueries({ queryKey: ['adminConfig'] });
};
export const useUpdateAdminConfig = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ key, value }: { key: string; value: unknown }) =>
api.patch('admin-custom/config/', { [key]: value }),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['adminConfig'] });
},
});
};
export const useTriggerJob = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: number) => api.post(`admin-custom/${id}/trigger_job/`),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['adminJobs'] });
},
});
};
export const useUpdateJobSchedule = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: number; data: Record<string, unknown> }) =>
api.patch(`admin-custom/${id}/update_job_schedule/`, data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['adminJobs'] });
},
});
};
export const useActivateBanner = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: number) => api.post(`banners/${id}/activate/`),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['banners'] });
qc.invalidateQueries({ queryKey: ['activeBanner'] });
},
});
};
export const useToggleBannerEnabled = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: number) => api.post(`banners/${id}/toggle_enabled/`),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['banners'] });
},
});
};
export const useSendTestEmail = () =>
useMutation({
mutationFn: () => api.post('admin-custom/send_test_email/'),
});
export const useToggleUserActive = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, isActive }: { id: string | number; isActive: boolean }) =>
api.patch(`admin-custom/${id}/user_detail/`, { is_active: !isActive }),
onSuccess: (_, vars) => {
qc.invalidateQueries({ queryKey: ['adminUserDetail', String(vars.id)] });
qc.invalidateQueries({ queryKey: ['adminUsers'] });
},
});
};
export const useDeleteUser = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: string | number) => api.delete(`admin-custom/${id}/user_detail/`),
onSuccess: (_, id) => {
qc.invalidateQueries({ queryKey: ['adminUserDetail', String(id)] });
qc.invalidateQueries({ queryKey: ['adminUsers'] });
invalidateAdmin(qc);
},
});
};
@@ -0,0 +1,25 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
import { queryKeys } from '../keys';
export interface UpdateFlavorInput {
id: string | number;
data: Record<string, unknown> | FormData;
isFormData?: boolean;
}
export const useUpdateFlavor = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, data, isFormData }: UpdateFlavorInput) =>
api.patch(
`flavors/${id}/`,
data,
isFormData ? { headers: { 'Content-Type': 'multipart/form-data' } } : undefined,
),
onSuccess: (_, vars) => {
qc.invalidateQueries({ queryKey: queryKeys.flavorDetail(vars.id) });
qc.invalidateQueries({ queryKey: queryKeys.flavorsAll() });
},
});
};
@@ -0,0 +1,28 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
import { queryKeys } from '../keys';
import type { Notification } from '../queries/useNotifications';
export const useMarkAllNotificationsRead = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: () => api.post('notifications/mark_all_read/'),
onSuccess: () => {
qc.setQueryData<Notification[]>(queryKeys.notifications(), (old) =>
old?.map((n) => ({ ...n, is_read: true })),
);
},
});
};
export const useMarkNotificationRead = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: number) => api.post(`notifications/${id}/mark_read/`),
onSuccess: (_, id) => {
qc.setQueryData<Notification[]>(queryKeys.notifications(), (old) =>
old?.map((n) => (n.id === id ? { ...n, is_read: true } : n)),
);
},
});
};
@@ -0,0 +1,70 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
import { queryKeys } from '../keys';
const invalidateRatingViews = (qc: ReturnType<typeof useQueryClient>) => {
qc.invalidateQueries({ queryKey: queryKeys.dashboard() });
qc.invalidateQueries({ queryKey: queryKeys.publicProfileAll() });
qc.invalidateQueries({ queryKey: queryKeys.flavorsAll() });
qc.invalidateQueries({ queryKey: ['flavorDetail'] });
qc.invalidateQueries({ queryKey: ['communityFeed'] });
};
export interface UpdateRatingInput {
id: number;
score: number;
comment: string | null;
}
export const useUpdateRating = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, score, comment }: UpdateRatingInput) =>
api.patch(`ratings/${id}/`, { score, comment }),
onSuccess: () => invalidateRatingViews(qc),
});
};
export const useDeleteRating = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: number) => api.delete(`ratings/${id}/`),
onSuccess: () => invalidateRatingViews(qc),
});
};
export interface CreateRatingInput {
flavor: number;
score: number;
comment?: string | null;
}
export const useCreateRating = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (input: CreateRatingInput) => api.post('ratings/', input),
onSuccess: () => invalidateRatingViews(qc),
});
};
export interface ReplyInput {
ratingId: number;
text: string;
}
export const useCreateReply = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ ratingId, text }: ReplyInput) =>
api.post(`ratings/${ratingId}/reply/`, { text }),
onSuccess: () => invalidateRatingViews(qc),
});
};
export const useDeleteReply = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (replyId: number) => api.delete(`replies/${replyId}/`),
onSuccess: () => invalidateRatingViews(qc),
});
};
@@ -0,0 +1,20 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
import { queryKeys } from '../keys';
const invalidateRatingViews = (qc: ReturnType<typeof useQueryClient>) => {
qc.invalidateQueries({ queryKey: queryKeys.dashboard() });
qc.invalidateQueries({ queryKey: queryKeys.publicProfileAll() });
qc.invalidateQueries({ queryKey: queryKeys.flavorsAll() });
qc.invalidateQueries({ queryKey: ['flavorDetail'] });
qc.invalidateQueries({ queryKey: ['communityFeed'] });
};
export const useUpdateReply = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ replyId, text }: { replyId: number; text: string }) =>
api.patch(`replies/${replyId}/`, { text }),
onSuccess: () => invalidateRatingViews(qc),
});
};
@@ -0,0 +1,62 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
const invalidateMe = (qc: ReturnType<typeof useQueryClient>) => {
qc.invalidateQueries({ queryKey: ['me'] });
qc.invalidateQueries({ queryKey: ['currentUserLite'] });
};
export const useUpdateAvatar = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (file: File) => {
const formData = new FormData();
formData.append('avatar', file);
return api.post<{ avatar: string }>('users/update_avatar/', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
},
onSuccess: () => invalidateMe(qc),
});
};
export const useUpdatePreferences = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (prefs: Record<string, unknown>) => api.patch('users/update_preferences/', prefs),
onSuccess: () => invalidateMe(qc),
});
};
export const useUpdateProfile = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (input: { username: string; email: string }) =>
api.patch<{ message?: string }>('users/update_profile/', input),
onSuccess: () => invalidateMe(qc),
});
};
export const useConfirmEmail = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (code: string) => api.post<{ email: string }>('users/confirm_email/', { code }),
onSuccess: () => invalidateMe(qc),
});
};
export const useChangePassword = () =>
useMutation({
mutationFn: (input: { old_password: string; new_password: string }) =>
api.post('users/change_password/', input),
});
export const useRequestAccountDeletion = () =>
useMutation({
mutationFn: () => api.post('users/request_account_deletion/'),
});
export const useConfirmAccountDeletion = () =>
useMutation({
mutationFn: (code: string) => api.post('users/confirm_account_deletion/', { code }),
});
@@ -0,0 +1,36 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
import { queryKeys } from '../keys';
export const useFollowToggle = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ userId, currentlyFollowing }: { userId: number; currentlyFollowing: boolean }) =>
api.post(`users/${userId}/${currentlyFollowing ? 'unfollow' : 'follow'}/`),
onSuccess: () => {
qc.invalidateQueries({ queryKey: queryKeys.publicProfileAll() });
},
});
};
export const useAddProfileComment = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ userId, text }: { userId: number; text: string }) =>
api.post(`users/${userId}/add_comment/`, { text }),
onSuccess: () => {
qc.invalidateQueries({ queryKey: queryKeys.publicProfileAll() });
},
});
};
export const useDeleteProfileComment = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ userId, commentId }: { userId: number; commentId: number }) =>
api.delete(`users/${userId}/delete_comment/${commentId}/`),
onSuccess: () => {
qc.invalidateQueries({ queryKey: queryKeys.publicProfileAll() });
},
});
};
@@ -0,0 +1,40 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../../lib/api';
const invalidateTickets = (qc: ReturnType<typeof useQueryClient>) => {
qc.invalidateQueries({ queryKey: ['tickets'] });
};
export const useCreateTicket = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (input: { subject: string; description: string }) => api.post('tickets/', input),
onSuccess: () => invalidateTickets(qc),
});
};
export const useAddTicketMessage = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ ticketId, text }: { ticketId: number; text: string }) =>
api.post(`tickets/${ticketId}/add_message/`, { text }),
onSuccess: () => invalidateTickets(qc),
});
};
export const useUpdateTicketStatus = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ ticketId, status }: { ticketId: number; status: string }) =>
api.post(`tickets/${ticketId}/update_status/`, { status }),
onSuccess: () => invalidateTickets(qc),
});
};
export const useDeleteTicket = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (ticketId: number) => api.delete(`tickets/${ticketId}/`),
onSuccess: () => invalidateTickets(qc),
});
};