feat: add configuration module for managing branches, divisions, and customers

- Introduced a new Configuration module with routes for managing branches, divisions, and customers.
- Implemented UI components for creating, editing, and viewing branch details, including general information, location, and working schedule.
- Added validation schemas for branch data and integrated language support for English and Indonesian.
- Developed comprehensive unit tests for the branches remote data service and transformer to ensure functionality and reliability.

This commit enhances the application by providing a structured approach to configuration management, improving user experience and data handling.
This commit is contained in:
shancheas
2026-08-25 22:56:07 +07:00
parent 44b0ef0168
commit 73d84f3874
92 changed files with 3271 additions and 78 deletions
@@ -0,0 +1,132 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { AxiosInstance } from '@repo/core-api/http-client';
import { BaseDataTransformer } from '@repo/core-api/data-services';
import { TrackGoRemoteDataServices } from './trackgo-remote-data-services';
interface SampleEntity {
id?: string;
name: string;
code: string;
}
class SampleTransformer extends BaseDataTransformer<SampleEntity> {
transformToEntity(dto: SampleEntity): SampleEntity {
return { ...dto };
}
}
class SampleRemoteDataServices extends TrackGoRemoteDataServices<SampleEntity> {}
function createMockHttpClient(): AxiosInstance {
return {
request: vi.fn().mockResolvedValue({ data: {}, status: 200 }),
defaults: {} as AxiosInstance['defaults'],
interceptors: {
request: { use: vi.fn(), eject: vi.fn(), clear: vi.fn() },
response: { use: vi.fn(), eject: vi.fn(), clear: vi.fn() },
},
getUri: vi.fn(),
get: vi.fn(),
delete: vi.fn(),
head: vi.fn(),
options: vi.fn(),
post: vi.fn(),
put: vi.fn(),
patch: vi.fn(),
postForm: vi.fn(),
putForm: vi.fn(),
patchForm: vi.fn(),
} as unknown as AxiosInstance;
}
const unwrappedDetail = {
id: 'row-1',
name: 'Jakarta',
code: 'JKT',
};
describe('TrackGoRemoteDataServices', () => {
let httpClient: AxiosInstance;
let service: SampleRemoteDataServices;
beforeEach(() => {
httpClient = createMockHttpClient();
service = new SampleRemoteDataServices(httpClient, {
apiUrl: '/divisions',
moduleKey: 'CONFIGURATION.DIVISION',
transformer: new SampleTransformer(),
});
});
it('wraps an unwrapped getOne body as { data: entity }', async () => {
vi.mocked(httpClient.request).mockResolvedValueOnce({ data: unwrappedDetail, status: 200 });
const result = await service.getOne('row-1');
expect(result.data).toEqual({
data: expect.objectContaining({ id: 'row-1', code: 'JKT' }),
});
expect(httpClient.request).toHaveBeenCalledWith(
expect.objectContaining({ url: '/divisions/row-1', method: 'GET' }),
);
});
it('uses PATCH when editing', async () => {
await service.edit('row-1', { name: 'Jakarta', code: 'JKT' });
expect(httpClient.request).toHaveBeenCalledWith(
expect.objectContaining({
url: '/divisions/row-1',
method: 'PATCH',
}),
);
});
it('activates via PATCH /:id/status with status active', async () => {
await service.activate('row-1');
expect(httpClient.request).toHaveBeenCalledWith(
expect.objectContaining({
url: '/divisions/row-1/status',
method: 'PATCH',
data: { status: 'active' },
}),
);
});
it('deactivates via PATCH /:id/status with status archived', async () => {
await service.deactivate('row-1');
expect(httpClient.request).toHaveBeenCalledWith(
expect.objectContaining({
url: '/divisions/row-1/status',
method: 'PATCH',
data: { status: 'archived' },
}),
);
});
it('bulk-deletes via POST /bulk-delete', async () => {
await service.batchDelete(['row-1', 'row-2']);
expect(httpClient.request).toHaveBeenCalledWith(
expect.objectContaining({
url: '/divisions/bulk-delete',
method: 'POST',
data: expect.objectContaining({ ids: ['row-1', 'row-2'] }),
}),
);
});
it('bulk-activates via POST /bulk-status', async () => {
await service.batchActivate(['row-1']);
expect(httpClient.request).toHaveBeenCalledWith(
expect.objectContaining({
url: '/divisions/bulk-status',
method: 'POST',
data: { ids: ['row-1'], status: 'active' },
}),
);
});
});