260324:2133 Refactor correspondence & rfa
CI / CD Pipeline / build (push) Failing after 17m3s
CI / CD Pipeline / deploy (push) Has been skipped

This commit is contained in:
admin
2026-03-24 21:33:59 +07:00
parent 42fc9fa502
commit aa82b890a5
42 changed files with 2617 additions and 233 deletions
@@ -0,0 +1,107 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import { createTestQueryClient } from '@/lib/test-utils';
import {
useCirculationsByCorrespondence,
circulationKeys,
} from '../use-circulation';
import { circulationService } from '@/lib/services/circulation.service';
vi.mock('@/lib/services/circulation.service', () => ({
circulationService: {
getByCorrespondenceUuid: vi.fn(),
},
}));
describe('use-circulation hooks', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('circulationKeys', () => {
it('should generate correct cache keys', () => {
expect(circulationKeys.all).toEqual(['circulations']);
expect(circulationKeys.byCorrespondence('uuid-abc')).toEqual([
'circulations',
'byCorrespondence',
'uuid-abc',
]);
});
});
describe('useCirculationsByCorrespondence', () => {
it('should fetch circulations for a correspondence UUID', async () => {
const mockData = {
data: [
{
uuid: 'circ-uuid-1',
circulationNo: 'CIR-001',
subject: 'Review Document',
statusCode: 'OPEN',
routings: [],
},
],
meta: { total: 1, page: 1, limit: 50 },
};
vi.mocked(circulationService.getByCorrespondenceUuid).mockResolvedValue(mockData);
const { wrapper } = createTestQueryClient();
const { result } = renderHook(
() => useCirculationsByCorrespondence('corr-uuid-1'),
{ wrapper }
);
await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});
expect(circulationService.getByCorrespondenceUuid).toHaveBeenCalledWith('corr-uuid-1');
expect(result.current.data).toEqual(mockData);
});
it('should not fetch when correspondenceUuid is empty', () => {
const { wrapper } = createTestQueryClient();
const { result } = renderHook(
() => useCirculationsByCorrespondence(''),
{ wrapper }
);
expect(result.current.fetchStatus).toBe('idle');
expect(circulationService.getByCorrespondenceUuid).not.toHaveBeenCalled();
});
it('should handle fetch error gracefully', async () => {
vi.mocked(circulationService.getByCorrespondenceUuid).mockRejectedValue(
new Error('Network error')
);
const { wrapper } = createTestQueryClient();
const { result } = renderHook(
() => useCirculationsByCorrespondence('corr-uuid-error'),
{ wrapper }
);
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
});
it('should use the correct query key', () => {
vi.mocked(circulationService.getByCorrespondenceUuid).mockResolvedValue([]);
const { wrapper } = createTestQueryClient();
const { result } = renderHook(
() => useCirculationsByCorrespondence('test-uuid'),
{ wrapper }
);
expect(result.current.status).toBeDefined();
expect(circulationKeys.byCorrespondence('test-uuid')).toEqual([
'circulations',
'byCorrespondence',
'test-uuid',
]);
});
});
});
+15
View File
@@ -0,0 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import { circulationService } from '@/lib/services/circulation.service';
export const circulationKeys = {
all: ['circulations'] as const,
byCorrespondence: (uuid: string) => ['circulations', 'byCorrespondence', uuid] as const,
};
export function useCirculationsByCorrespondence(correspondenceUuid: string) {
return useQuery({
queryKey: circulationKeys.byCorrespondence(correspondenceUuid),
queryFn: () => circulationService.getByCorrespondenceUuid(correspondenceUuid),
enabled: !!correspondenceUuid,
});
}
+107
View File
@@ -91,6 +91,25 @@ export function useDeleteCorrespondence() {
});
}
export function useCancelCorrespondence() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ uuid, reason }: { uuid: string; reason: string }) =>
correspondenceService.cancel(uuid, reason),
onSuccess: (_, { uuid }) => {
toast.success('Correspondence cancelled successfully');
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(uuid) });
queryClient.invalidateQueries({ queryKey: correspondenceKeys.lists() });
},
onError: (error: ApiError) => {
toast.error('Failed to cancel correspondence', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useSubmitCorrespondence() {
const queryClient = useQueryClient();
@@ -110,6 +129,94 @@ export function useSubmitCorrespondence() {
});
}
export function useCorrespondenceTags(uuid: string) {
return useQuery({
queryKey: [...correspondenceKeys.detail(uuid), 'tags'] as const,
queryFn: () => correspondenceService.getTags(uuid),
enabled: !!uuid,
});
}
export function useAddTag() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ uuid, tagId }: { uuid: string; tagId: number }) =>
correspondenceService.addTag(uuid, tagId),
onSuccess: (_, { uuid }) => {
toast.success('Tag added');
queryClient.invalidateQueries({ queryKey: [...correspondenceKeys.detail(uuid), 'tags'] });
},
onError: (error: ApiError) => {
toast.error('Failed to add tag', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useRemoveTag() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ uuid, tagId }: { uuid: string; tagId: number }) =>
correspondenceService.removeTag(uuid, tagId),
onSuccess: (_, { uuid }) => {
toast.success('Tag removed');
queryClient.invalidateQueries({ queryKey: [...correspondenceKeys.detail(uuid), 'tags'] });
},
onError: (error: ApiError) => {
toast.error('Failed to remove tag', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useReferences(uuid: string) {
return useQuery({
queryKey: [...correspondenceKeys.detail(uuid), 'references'] as const,
queryFn: () => correspondenceService.getReferences(uuid),
enabled: !!uuid,
});
}
export function useAddReference() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ uuid, targetUuid }: { uuid: string; targetUuid: string }) =>
correspondenceService.addReference(uuid, { targetUuid }),
onSuccess: (_, { uuid }) => {
toast.success('Reference added successfully');
queryClient.invalidateQueries({ queryKey: [...correspondenceKeys.detail(uuid), 'references'] });
},
onError: (error: ApiError) => {
toast.error('Failed to add reference', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useRemoveReference() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ uuid, targetUuid }: { uuid: string; targetUuid: string }) =>
correspondenceService.removeReference(uuid, targetUuid),
onSuccess: (_, { uuid }) => {
toast.success('Reference removed');
queryClient.invalidateQueries({ queryKey: [...correspondenceKeys.detail(uuid), 'references'] });
},
onError: (error: ApiError) => {
toast.error('Failed to remove reference', {
description: error.response?.data?.message || 'Something went wrong',
});
},
});
}
export function useProcessWorkflow() {
const queryClient = useQueryClient();