251208:0010 Backend & Frontend Debug
Some checks failed
Spec Validation / validate-markdown (push) Has been cancelled
Spec Validation / validate-diagrams (push) Has been cancelled
Spec Validation / check-todos (push) Has been cancelled

This commit is contained in:
2025-12-08 00:10:37 +07:00
parent 32d820ea6b
commit dcd126d704
99 changed files with 2775 additions and 1480 deletions

View File

@@ -0,0 +1,14 @@
import { useQuery } from '@tanstack/react-query';
import { auditLogService } from '@/lib/services/audit-log.service';
export const auditLogKeys = {
all: ['audit-logs'] as const,
list: (params: any) => [...auditLogKeys.all, 'list', params] as const,
};
export function useAuditLogs(params?: any) {
return useQuery({
queryKey: auditLogKeys.list(params),
queryFn: () => auditLogService.getLogs(params),
});
}

View File

@@ -0,0 +1,73 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { correspondenceService } from '@/lib/services/correspondence.service';
import { SearchCorrespondenceDto } from '@/types/dto/correspondence/search-correspondence.dto';
import { CreateCorrespondenceDto } from '@/types/dto/correspondence/create-correspondence.dto';
import { SubmitCorrespondenceDto } from '@/types/dto/correspondence/submit-correspondence.dto';
import { toast } from 'sonner';
// Keys for Query Cache
export const correspondenceKeys = {
all: ['correspondences'] as const,
lists: () => [...correspondenceKeys.all, 'list'] as const,
list: (params: SearchCorrespondenceDto) => [...correspondenceKeys.lists(), params] as const,
details: () => [...correspondenceKeys.all, 'detail'] as const,
detail: (id: number | string) => [...correspondenceKeys.details(), id] as const,
};
// --- Queries ---
export function useCorrespondences(params: SearchCorrespondenceDto) {
return useQuery({
queryKey: correspondenceKeys.list(params),
queryFn: () => correspondenceService.getAll(params),
placeholderData: (previousData) => previousData, // Keep previous data while fetching new page
});
}
export function useCorrespondence(id: number | string) {
return useQuery({
queryKey: correspondenceKeys.detail(id),
queryFn: () => correspondenceService.getById(id),
enabled: !!id,
});
}
// --- Mutations ---
export function useCreateCorrespondence() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateCorrespondenceDto) => correspondenceService.create(data),
onSuccess: () => {
toast.success('Correspondence created successfully');
queryClient.invalidateQueries({ queryKey: correspondenceKeys.lists() });
},
onError: (error: any) => {
toast.error('Failed to create correspondence', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useSubmitCorrespondence() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: number; data: SubmitCorrespondenceDto }) =>
correspondenceService.submit(id, data),
onSuccess: (_, { id }) => {
toast.success('Correspondence submitted successfully');
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(id) });
queryClient.invalidateQueries({ queryKey: correspondenceKeys.lists() });
},
onError: (error: any) => {
toast.error('Failed to submit correspondence', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
// Add more mutations as needed (update, delete, etc.)

View File

@@ -0,0 +1,33 @@
import { useQuery } from '@tanstack/react-query';
import { dashboardService } from '@/lib/services/dashboard.service';
export const dashboardKeys = {
all: ['dashboard'] as const,
stats: () => [...dashboardKeys.all, 'stats'] as const,
activity: () => [...dashboardKeys.all, 'activity'] as const,
pending: () => [...dashboardKeys.all, 'pending'] as const,
};
export function useDashboardStats() {
return useQuery({
queryKey: dashboardKeys.stats(),
queryFn: dashboardService.getStats,
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
export function useRecentActivity() {
return useQuery({
queryKey: dashboardKeys.activity(),
queryFn: dashboardService.getRecentActivity,
staleTime: 1 * 60 * 1000,
});
}
export function usePendingTasks() {
return useQuery({
queryKey: dashboardKeys.pending(),
queryFn: dashboardService.getPendingTasks,
staleTime: 2 * 60 * 1000,
});
}

View File

@@ -0,0 +1,73 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { contractDrawingService } from '@/lib/services/contract-drawing.service';
import { shopDrawingService } from '@/lib/services/shop-drawing.service';
import { SearchContractDrawingDto, CreateContractDrawingDto } from '@/types/dto/drawing/contract-drawing.dto';
import { SearchShopDrawingDto, CreateShopDrawingDto } from '@/types/dto/drawing/shop-drawing.dto';
import { toast } from 'sonner';
type DrawingType = 'CONTRACT' | 'SHOP';
export const drawingKeys = {
all: ['drawings'] as const,
lists: () => [...drawingKeys.all, 'list'] as const,
list: (type: DrawingType, params: any) => [...drawingKeys.lists(), type, params] as const,
details: () => [...drawingKeys.all, 'detail'] as const,
detail: (type: DrawingType, id: number | string) => [...drawingKeys.details(), type, id] as const,
};
// --- Queries ---
export function useDrawings(type: DrawingType, params: any) {
return useQuery({
queryKey: drawingKeys.list(type, params),
queryFn: async () => {
if (type === 'CONTRACT') {
return contractDrawingService.getAll(params as SearchContractDrawingDto);
} else {
return shopDrawingService.getAll(params as SearchShopDrawingDto);
}
},
placeholderData: (previousData) => previousData,
});
}
export function useDrawing(type: DrawingType, id: number | string) {
return useQuery({
queryKey: drawingKeys.detail(type, id),
queryFn: async () => {
if (type === 'CONTRACT') {
return contractDrawingService.getById(id);
} else {
return shopDrawingService.getById(id);
}
},
enabled: !!id,
});
}
// --- Mutations ---
export function useCreateDrawing(type: DrawingType) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (data: any) => {
if (type === 'CONTRACT') {
return contractDrawingService.create(data as CreateContractDrawingDto);
} else {
return shopDrawingService.create(data as CreateShopDrawingDto);
}
},
onSuccess: () => {
toast.success(`${type === 'CONTRACT' ? 'Contract' : 'Shop'} Drawing uploaded successfully`);
queryClient.invalidateQueries({ queryKey: drawingKeys.lists() });
},
onError: (error: any) => {
toast.error('Failed to upload drawing', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
// You can add useCreateShopDrawingRevision logic here if needed separate

View File

@@ -0,0 +1,25 @@
import { useQuery } from '@tanstack/react-query';
import { masterDataService } from '@/lib/services/master-data.service';
export const masterDataKeys = {
all: ['masterData'] as const,
organizations: () => [...masterDataKeys.all, 'organizations'] as const,
correspondenceTypes: () => [...masterDataKeys.all, 'correspondenceTypes'] as const,
disciplines: (contractId?: number) => [...masterDataKeys.all, 'disciplines', contractId] as const,
};
export function useOrganizations() {
return useQuery({
queryKey: masterDataKeys.organizations(),
queryFn: () => masterDataService.getOrganizations(),
});
}
export function useDisciplines(contractId?: number) {
return useQuery({
queryKey: masterDataKeys.disciplines(contractId),
queryFn: () => masterDataService.getDisciplines(contractId),
});
}
// Add other master data hooks as needed

View File

@@ -0,0 +1,31 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { notificationService } from '@/lib/services/notification.service';
import { toast } from 'sonner';
export const notificationKeys = {
all: ['notifications'] as const,
unread: () => [...notificationKeys.all, 'unread'] as const,
};
export function useNotifications() {
return useQuery({
queryKey: notificationKeys.unread(),
queryFn: notificationService.getUnread,
refetchInterval: 60 * 1000, // Poll every 1 minute
staleTime: 30 * 1000,
});
}
export function useMarkNotificationRead() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: notificationService.markAsRead,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: notificationKeys.unread() });
},
onError: () => {
toast.error("Failed to mark notification as read");
}
});
}

89
frontend/hooks/use-rfa.ts Normal file
View File

@@ -0,0 +1,89 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { rfaService } from '@/lib/services/rfa.service';
import { SearchRfaDto, CreateRfaDto, UpdateRfaDto } from '@/types/dto/rfa/rfa.dto';
import { WorkflowActionDto } from '@/lib/services/rfa.service';
import { toast } from 'sonner';
// Keys
export const rfaKeys = {
all: ['rfas'] as const,
lists: () => [...rfaKeys.all, 'list'] as const,
list: (params: SearchRfaDto) => [...rfaKeys.lists(), params] as const,
details: () => [...rfaKeys.all, 'detail'] as const,
detail: (id: number | string) => [...rfaKeys.details(), id] as const,
};
// --- Queries ---
export function useRFAs(params: SearchRfaDto) {
return useQuery({
queryKey: rfaKeys.list(params),
queryFn: () => rfaService.getAll(params),
placeholderData: (previousData) => previousData,
});
}
export function useRFA(id: number | string) {
return useQuery({
queryKey: rfaKeys.detail(id),
queryFn: () => rfaService.getById(id),
enabled: !!id,
});
}
// --- Mutations ---
export function useCreateRFA() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateRfaDto) => rfaService.create(data),
onSuccess: () => {
toast.success('RFA created successfully');
queryClient.invalidateQueries({ queryKey: rfaKeys.lists() });
},
onError: (error: any) => {
toast.error('Failed to create RFA', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useUpdateRFA() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: number | string; data: UpdateRfaDto }) =>
rfaService.update(id, data),
onSuccess: (_, { id }) => {
toast.success('RFA updated successfully');
queryClient.invalidateQueries({ queryKey: rfaKeys.detail(id) });
queryClient.invalidateQueries({ queryKey: rfaKeys.lists() });
},
onError: (error: any) => {
toast.error('Failed to update RFA', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useProcessRFA() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: number | string; data: WorkflowActionDto }) =>
rfaService.processWorkflow(id, data),
onSuccess: (_, { id }) => {
toast.success('Workflow status updated successfully');
queryClient.invalidateQueries({ queryKey: rfaKeys.detail(id) });
queryClient.invalidateQueries({ queryKey: rfaKeys.lists() });
},
onError: (error: any) => {
toast.error('Failed to process workflow', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}

View File

@@ -0,0 +1,27 @@
import { useQuery } from '@tanstack/react-query';
import { searchService } from '@/lib/services/search.service';
import { SearchQueryDto } from '@/types/dto/search/search-query.dto';
export const searchKeys = {
all: ['search'] as const,
results: (query: SearchQueryDto) => [...searchKeys.all, 'results', query] as const,
suggestions: (query: string) => [...searchKeys.all, 'suggestions', query] as const,
};
export function useSearch(query: SearchQueryDto) {
return useQuery({
queryKey: searchKeys.results(query),
queryFn: () => searchService.search(query),
enabled: !!query.q || Object.keys(query).length > 0,
placeholderData: (previousData) => previousData,
});
}
export function useSearchSuggestions(query: string) {
return useQuery({
queryKey: searchKeys.suggestions(query),
queryFn: () => searchService.suggest(query),
enabled: query.length > 2,
staleTime: 60 * 1000, // Cache suggestions for 1 minute
});
}

View File

@@ -0,0 +1,65 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { userService } from '@/lib/services/user.service';
import { CreateUserDto, UpdateUserDto, SearchUserDto } from '@/types/user'; // Ensure types exist
import { toast } from 'sonner';
export const userKeys = {
all: ['users'] as const,
list: (params: any) => [...userKeys.all, 'list', params] as const,
detail: (id: number) => [...userKeys.all, 'detail', id] as const,
};
export function useUsers(params?: SearchUserDto) {
return useQuery({
queryKey: userKeys.list(params),
queryFn: () => userService.getAll(params),
});
}
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateUserDto) => userService.create(data),
onSuccess: () => {
toast.success("User created successfully");
queryClient.invalidateQueries({ queryKey: userKeys.all });
},
onError: (error: any) => {
toast.error("Failed to create user", {
description: error.response?.data?.message || "Unknown error"
});
}
});
}
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: number; data: UpdateUserDto }) => userService.update(id, data),
onSuccess: () => {
toast.success("User updated successfully");
queryClient.invalidateQueries({ queryKey: userKeys.all });
},
onError: (error: any) => {
toast.error("Failed to update user", {
description: error.response?.data?.message || "Unknown error"
});
}
});
}
export function useDeleteUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: number) => userService.delete(id),
onSuccess: () => {
toast.success("User deleted successfully");
queryClient.invalidateQueries({ queryKey: userKeys.all });
},
onError: (error: any) => {
toast.error("Failed to delete user", {
description: error.response?.data?.message || "Unknown error"
});
}
});
}