refactor: modularize road management modules

This commit is contained in:
2026-06-17 13:31:25 +05:30
parent 9bb740e446
commit 182d4ac293
41 changed files with 2386 additions and 1894 deletions

View File

@@ -1,28 +1,31 @@
import type { AuthResponseData, LoginPayload, MeResponse, PermissionResponse } from '@/types';
import { API_ROUTES } from '@/constants/apiRoutes';
import axiosClient, { axiosAuth } from '../axios/axios';
export const authService = {
login: async (payload: LoginPayload): Promise<AuthResponseData> => {
const response = await axiosAuth.post<AuthResponseData>('api/auth/login', payload, {
const response = await axiosAuth.post<AuthResponseData>(API_ROUTES.AUTH.LOGIN, payload, {
withCredentials: true,
});
return response.data;
},
refresh: async (): Promise<AuthResponseData> => {
const response = await axiosAuth.post<AuthResponseData>('api/auth/refresh', {}, {
const response = await axiosAuth.post<AuthResponseData>(API_ROUTES.AUTH.REFRESH, {}, {
withCredentials: true,
});
return response.data;
},
logout: async (): Promise<void> => {
await axiosAuth.post('api/auth/logout', {}, { withCredentials: true });
await axiosAuth.post(API_ROUTES.AUTH.LOGOUT, {}, { withCredentials: true });
},
me: async (): Promise<MeResponse> => {
const response = await axiosClient.get<MeResponse>('api/auth/me');
const response = await axiosClient.get<MeResponse>(API_ROUTES.AUTH.ME);
return response.data;
},
permissions: async (): Promise<PermissionResponse> => {
const response = await axiosClient.get<PermissionResponse>('api/permissions/my-permissions');
const response = await axiosClient.get<PermissionResponse>(
API_ROUTES.PERMISSIONS.MY_PERMISSIONS,
);
return response.data;
},
};

View File

@@ -1,4 +1,5 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import {
Chainage,
ChainageCreate,
@@ -7,8 +8,6 @@ import {
PaginationParams,
} from '@/types';
const CHAINAGES_ENDPOINT = 'biz/api/v1/chainages';
/**
* Chainage Service
*/
@@ -19,7 +18,7 @@ export const chainageService = {
getChainages: async (params?: PaginationParams): Promise<PaginatedResponse<Chainage>> => {
const skip = params?.skip ?? 0;
const limit = params?.limit ?? 100;
const response = await axiosClient.get<PaginatedResponse<Chainage>>(CHAINAGES_ENDPOINT, {
const response = await axiosClient.get<PaginatedResponse<Chainage>>(API_ROUTES.CHAINAGES.BASE, {
params: { skip, limit },
});
return response.data;
@@ -34,7 +33,7 @@ export const chainageService = {
): Promise<PaginatedResponse<Chainage>> => {
const skip = params?.skip ?? 0;
const limit = params?.limit ?? 100;
const response = await axiosClient.get<PaginatedResponse<Chainage>>(CHAINAGES_ENDPOINT, {
const response = await axiosClient.get<PaginatedResponse<Chainage>>(API_ROUTES.CHAINAGES.BASE, {
params: { package_id: packageId, skip, limit },
});
return response.data;
@@ -44,7 +43,7 @@ export const chainageService = {
* Create a new chainage
*/
createChainage: async (data: ChainageCreate): Promise<Chainage> => {
const response = await axiosClient.post<Chainage>(CHAINAGES_ENDPOINT, data);
const response = await axiosClient.post<Chainage>(API_ROUTES.CHAINAGES.BASE, data);
return response.data;
},
@@ -52,7 +51,7 @@ export const chainageService = {
* Update an existing chainage
*/
updateChainage: async (chainageId: string, data: ChainageUpdate): Promise<Chainage> => {
const response = await axiosClient.put<Chainage>(`${CHAINAGES_ENDPOINT}/${chainageId}`, data);
const response = await axiosClient.put<Chainage>(API_ROUTES.CHAINAGES.DETAIL(chainageId), data);
return response.data;
},
@@ -61,7 +60,7 @@ export const chainageService = {
*/
deleteChainage: async (chainageId: string): Promise<{ message: string }> => {
const response = await axiosClient.delete<{ message: string }>(
`${CHAINAGES_ENDPOINT}/${chainageId}`,
API_ROUTES.CHAINAGES.DETAIL(chainageId),
);
return response.data;
},

View File

@@ -1,8 +1,7 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import type { Client, ClientListParams, ClientListResponse, ClientRequest } from '@/types';
const CLIENTS_ENDPOINT = 'api/clients';
function toClientPayload(payload: ClientRequest) {
return {
name: payload.name,
@@ -21,7 +20,7 @@ function toClientPayload(payload: ClientRequest) {
export const clientService = {
getClients: async (params?: ClientListParams): Promise<ClientListResponse> => {
const response = await axiosClient.get<ClientListResponse>(CLIENTS_ENDPOINT, {
const response = await axiosClient.get<ClientListResponse>(API_ROUTES.CLIENTS.BASE, {
params: {
skip: params?.skip ?? 0,
limit: params?.limit ?? 10,
@@ -38,20 +37,20 @@ export const clientService = {
},
getClientById: async (id: number): Promise<Client> => {
const response = await axiosClient.get<Client>(`${CLIENTS_ENDPOINT}/${id}`);
const response = await axiosClient.get<Client>(API_ROUTES.CLIENTS.DETAIL(id));
return response.data;
},
saveClient: async (payload: ClientRequest): Promise<Client> => {
const requestPayload = toClientPayload(payload);
const response = payload.id
? await axiosClient.put<Client>(`${CLIENTS_ENDPOINT}/${payload.id}`, requestPayload)
: await axiosClient.post<Client>(CLIENTS_ENDPOINT, requestPayload);
? await axiosClient.put<Client>(API_ROUTES.CLIENTS.DETAIL(payload.id), requestPayload)
: await axiosClient.post<Client>(API_ROUTES.CLIENTS.BASE, requestPayload);
return response.data;
},
updateClientStatus: async (id: number, isActive: boolean): Promise<Client> => {
const response = await axiosClient.patch<Client>(`${CLIENTS_ENDPOINT}/${id}/status`, {
const response = await axiosClient.patch<Client>(API_ROUTES.CLIENTS.STATUS(id), {
is_active: isActive,
});
return response.data;

View File

@@ -1,4 +1,5 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import { Detection } from '@/types';
import { projectService } from './project.service';
@@ -31,7 +32,7 @@ export const detectionService = {
};
};
};
}>('biz/api/v1/dashboard/overview', {
}>(API_ROUTES.DASHBOARD.OVERVIEW, {
params: { project_id: project.id },
});

View File

@@ -1,11 +1,11 @@
export * from './project.service';
export * from './project-summary.service';
export * from './package.service';
export * from './auth.service';
export { chainageService } from './chainage.service';
export * from './video.service';
export * from './detection.service';
export * from './session.service';
export { projectDataService } from './project.service';
export * from './permission.service';
export * from './role.service';
export * from './user.service';

View File

@@ -1,4 +1,5 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import {
Package,
PackageCreate,
@@ -7,8 +8,6 @@ import {
PaginationParams,
} from '@/types';
const PACKAGES_ENDPOINT = 'biz/api/v1/packages';
/**
* Package Service
*/
@@ -19,7 +18,7 @@ export const packageService = {
getPackages: async (params?: PaginationParams): Promise<PaginatedResponse<Package>> => {
const skip = params?.skip ?? 0;
const limit = params?.limit ?? 100;
const response = await axiosClient.get<PaginatedResponse<Package>>(PACKAGES_ENDPOINT, {
const response = await axiosClient.get<PaginatedResponse<Package>>(API_ROUTES.PACKAGES.BASE, {
params: { skip, limit },
});
return response.data;
@@ -34,7 +33,7 @@ export const packageService = {
): Promise<PaginatedResponse<Package>> => {
const skip = params?.skip ?? 0;
const limit = params?.limit ?? 100;
const response = await axiosClient.get<PaginatedResponse<Package>>(PACKAGES_ENDPOINT, {
const response = await axiosClient.get<PaginatedResponse<Package>>(API_ROUTES.PACKAGES.BASE, {
params: { project_id: projectId, skip, limit },
});
return response.data;
@@ -44,7 +43,7 @@ export const packageService = {
* Create a new package
*/
createPackage: async (data: PackageCreate): Promise<Package> => {
const response = await axiosClient.post<Package>(PACKAGES_ENDPOINT, data);
const response = await axiosClient.post<Package>(API_ROUTES.PACKAGES.BASE, data);
return response.data;
},
@@ -52,7 +51,7 @@ export const packageService = {
* Update an existing package
*/
updatePackage: async (packageId: string, data: PackageUpdate): Promise<Package> => {
const response = await axiosClient.put<Package>(`${PACKAGES_ENDPOINT}/${packageId}`, data);
const response = await axiosClient.put<Package>(API_ROUTES.PACKAGES.DETAIL(packageId), data);
return response.data;
},
@@ -61,7 +60,7 @@ export const packageService = {
*/
deletePackage: async (packageId: string): Promise<{ message: string }> => {
const response = await axiosClient.delete<{ message: string }>(
`${PACKAGES_ENDPOINT}/${packageId}`,
API_ROUTES.PACKAGES.DETAIL(packageId),
);
return response.data;
},

View File

@@ -1,15 +1,18 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import type { PermissionResponse, PermissionTreeNode } from '@/types';
export const permissionService = {
getPermissions: async (): Promise<PermissionResponse> => {
const response = await axiosClient.get<PermissionResponse>('api/permissions/my-permissions');
const response = await axiosClient.get<PermissionResponse>(
API_ROUTES.PERMISSIONS.MY_PERMISSIONS,
);
return response.data;
},
getOrganizationPermissionTree: async (): Promise<PermissionTreeNode[]> => {
const response = await axiosClient.get<PermissionTreeNode[]>(
'api/permissions/organization-tree',
API_ROUTES.PERMISSIONS.ORGANIZATION_TREE,
);
return response.data;
},

View File

@@ -1,8 +1,7 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import type { Plan, PlanListParams, PlanListResponse, PlanRequest } from '@/types';
const PLANS_ENDPOINT = 'api/superadmin/plans';
function toPlanPayload(payload: PlanRequest) {
return {
name: payload.name,
@@ -23,7 +22,7 @@ function toPlanPayload(payload: PlanRequest) {
export const planService = {
getPlans: async (params?: PlanListParams): Promise<PlanListResponse> => {
const response = await axiosClient.get<PlanListResponse>(PLANS_ENDPOINT, {
const response = await axiosClient.get<PlanListResponse>(API_ROUTES.PLANS.BASE, {
params: {
skip: params?.skip ?? 0,
limit: params?.limit ?? 10,
@@ -42,13 +41,13 @@ export const planService = {
savePlan: async (payload: PlanRequest): Promise<Plan> => {
const requestPayload = toPlanPayload(payload);
const response = payload.id
? await axiosClient.put<Plan>(`${PLANS_ENDPOINT}/${payload.id}`, requestPayload)
: await axiosClient.post<Plan>(PLANS_ENDPOINT, requestPayload);
? await axiosClient.put<Plan>(API_ROUTES.PLANS.DETAIL(payload.id), requestPayload)
: await axiosClient.post<Plan>(API_ROUTES.PLANS.BASE, requestPayload);
return response.data;
},
updatePlanStatus: async (id: number, isActive: boolean): Promise<Plan> => {
const response = await axiosClient.patch<Plan>(`${PLANS_ENDPOINT}/${id}/status`, {
const response = await axiosClient.patch<Plan>(API_ROUTES.PLANS.STATUS(id), {
is_active: isActive,
});
return response.data;

View File

@@ -0,0 +1,52 @@
import { API_ROUTES } from '@/constants/apiRoutes';
import axiosClient from '../axios/axios';
export const projectSummaryService = {
getProjectSummary: async <T = unknown>(projectId: string): Promise<T> => {
const response = await axiosClient.get<T>(API_ROUTES.DASHBOARD.OVERVIEW, {
params: { project_id: projectId },
});
return response.data;
},
getProjectSummaryByVideo: async <T = unknown>(
projectId: string,
videoId: string,
): Promise<T> => {
const response = await axiosClient.get<T>(API_ROUTES.DASHBOARD.OVERVIEW, {
params: { project_id: projectId, video_id: videoId },
});
return response.data;
},
};
export const projectDataService = {
extractDetections(
projectSummary: any,
selectedPackageId?: string | null,
selectedChainageId?: string | null,
): any[] {
if (!projectSummary) return [];
const detections: any[] = [];
const packagesToProcess =
selectedPackageId && selectedPackageId !== 'all'
? { [selectedPackageId]: projectSummary.packages[selectedPackageId] }
: projectSummary.packages || {};
for (const pkg of Object.values(packagesToProcess)) {
const chainagesToProcess =
selectedChainageId && selectedChainageId !== 'all'
? { [selectedChainageId]: (pkg as any).chainages[selectedChainageId] }
: (pkg as any).chainages || {};
for (const chainage of Object.values(chainagesToProcess)) {
if (!chainage) continue;
detections.push(...((chainage as any).detections || []));
}
}
return detections;
},
};

View File

@@ -1,3 +1,4 @@
import { API_ROUTES } from '@/constants/apiRoutes';
import axiosClient from '../axios/axios';
import {
Project,
@@ -7,9 +8,6 @@ import {
PaginationParams,
} from '@/types';
const PROJECTS_ENDPOINT = 'biz/api/v1/projects';
const PROJECT_SUMMARY_ENDPOINT = 'biz/api/v1/dashboard/overview';
/**
* Project Service
*/
@@ -20,7 +18,7 @@ export const projectService = {
getProjects: async (params?: PaginationParams): Promise<PaginatedResponse<Project>> => {
const skip = params?.skip ?? 0;
const limit = params?.limit ?? 100;
const response = await axiosClient.get<PaginatedResponse<Project>>(PROJECTS_ENDPOINT, {
const response = await axiosClient.get<PaginatedResponse<Project>>(API_ROUTES.PROJECTS.BASE, {
params: { skip, limit },
});
return response.data;
@@ -30,7 +28,7 @@ export const projectService = {
* Create a new project
*/
createProject: async (data: ProjectCreate): Promise<Project> => {
const response = await axiosClient.post<Project>(PROJECTS_ENDPOINT, data);
const response = await axiosClient.post<Project>(API_ROUTES.PROJECTS.BASE, data);
return response.data;
},
@@ -38,7 +36,7 @@ export const projectService = {
* Update an existing project
*/
updateProject: async (projectId: string, data: ProjectUpdate): Promise<Project> => {
const response = await axiosClient.put<Project>(`${PROJECTS_ENDPOINT}/${projectId}`, data);
const response = await axiosClient.put<Project>(API_ROUTES.PROJECTS.DETAIL(projectId), data);
return response.data;
},
@@ -47,66 +45,8 @@ export const projectService = {
*/
deleteProject: async (projectId: string): Promise<{ message: string }> => {
const response = await axiosClient.delete<{ message: string }>(
`${PROJECTS_ENDPOINT}/${projectId}`,
API_ROUTES.PROJECTS.DETAIL(projectId),
);
return response.data;
},
/**
* Fetch project summary (detections across packages and chainages)
*/
getProjectSummary: async (projectId: string): Promise<any> => {
const response = await axiosClient.get(PROJECT_SUMMARY_ENDPOINT, {
params: { project_id: projectId },
});
return response.data;
},
/**
* Fetch project summary filtered by video ID
*/
getProjectSummaryByVideo: async (projectId: string, videoId: string): Promise<any> => {
const response = await axiosClient.get(PROJECT_SUMMARY_ENDPOINT, {
params: { project_id: projectId, video_id: videoId },
});
return response.data;
},
};
/**
* Service to handle data extraction from project summaries
* Moved from legacy project-service.ts
*/
export const projectDataService = {
/**
* Extracts all detections from a project summary, optionally filtered by package and chainage
*/
extractDetections(
projectSummary: any,
selectedPackageId?: string | null,
selectedChainageId?: string | null,
): any[] {
if (!projectSummary) return [];
const detections: any[] = [];
const packagesToProcess =
selectedPackageId && selectedPackageId !== 'all'
? { [selectedPackageId]: projectSummary.packages[selectedPackageId] }
: projectSummary.packages || {};
for (const [pkgName, pkg] of Object.entries(packagesToProcess)) {
const chainagesToProcess =
selectedChainageId && selectedChainageId !== 'all'
? { [selectedChainageId]: (pkg as any).chainages[selectedChainageId] }
: (pkg as any).chainages || {};
for (const [chnName, chn] of Object.entries(chainagesToProcess)) {
if (!chn) continue;
const chainageDetections = (chn as any).detections || [];
detections.push(...chainageDetections);
}
}
return detections;
},
};

View File

@@ -1,11 +1,10 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import type { Role, RoleListParams, RoleListResponse, RoleRequest } from '@/types';
const ROLES_ENDPOINT = 'api/roles';
export const roleService = {
getRoles: async (params?: RoleListParams): Promise<RoleListResponse> => {
const response = await axiosClient.get<RoleListResponse>(ROLES_ENDPOINT, {
const response = await axiosClient.get<RoleListResponse>(API_ROUTES.ROLES.BASE, {
params: {
skip: params?.skip ?? 0,
limit: params?.limit ?? 10,
@@ -22,19 +21,19 @@ export const roleService = {
},
getRoleById: async (id: number): Promise<Role> => {
const response = await axiosClient.get<Role>(`${ROLES_ENDPOINT}/${id}`);
const response = await axiosClient.get<Role>(API_ROUTES.ROLES.DETAIL(id));
return response.data;
},
saveRole: async (payload: RoleRequest): Promise<Role> => {
const response = payload.id
? await axiosClient.put<Role>(ROLES_ENDPOINT, payload)
: await axiosClient.post<Role>(ROLES_ENDPOINT, payload);
? await axiosClient.put<Role>(API_ROUTES.ROLES.BASE, payload)
: await axiosClient.post<Role>(API_ROUTES.ROLES.BASE, payload);
return response.data;
},
updateRoleStatus: async (id: number, isActive: boolean): Promise<Role> => {
const response = await axiosClient.patch<Role>(`${ROLES_ENDPOINT}/${id}/status`, {
const response = await axiosClient.patch<Role>(API_ROUTES.ROLES.STATUS(id), {
is_active: isActive,
});
return response.data;

View File

@@ -1,4 +1,5 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import type {
Tenant,
TenantCreateRequest,
@@ -7,8 +8,6 @@ import type {
TenantUpdateRequest,
} from '@/types';
const TENANTS_ENDPOINT = 'api/superadmin/tenants';
function toCreatePayload(payload: TenantCreateRequest) {
return {
name: payload.name,
@@ -39,7 +38,7 @@ function toUpdatePayload(payload: TenantUpdateRequest) {
export const tenantService = {
getTenants: async (params?: TenantListParams): Promise<TenantListResponse> => {
const response = await axiosClient.get<TenantListResponse>(TENANTS_ENDPOINT, {
const response = await axiosClient.get<TenantListResponse>(API_ROUTES.TENANTS.BASE, {
params: {
skip: params?.skip ?? 0,
limit: params?.limit ?? 10,
@@ -53,19 +52,22 @@ export const tenantService = {
},
createTenant: async (payload: TenantCreateRequest): Promise<Tenant> => {
const response = await axiosClient.post<Tenant>(TENANTS_ENDPOINT, toCreatePayload(payload));
const response = await axiosClient.post<Tenant>(
API_ROUTES.TENANTS.BASE,
toCreatePayload(payload),
);
return response.data;
},
updateTenant: async (payload: TenantUpdateRequest): Promise<Tenant> => {
const response = await axiosClient.put<Tenant>(
`${TENANTS_ENDPOINT}/${payload.id}`,
API_ROUTES.TENANTS.DETAIL(payload.id),
toUpdatePayload(payload),
);
return response.data;
},
deleteTenant: async (id: number): Promise<void> => {
await axiosClient.delete(`${TENANTS_ENDPOINT}/${id}`);
await axiosClient.delete(API_ROUTES.TENANTS.DETAIL(id));
},
};

View File

@@ -1,11 +1,10 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import type { AdministrationUser, UserListParams, UserListResponse, UserRequest } from '@/types';
const USERS_ENDPOINT = 'api/users';
export const userService = {
getUsers: async (params?: UserListParams): Promise<UserListResponse> => {
const response = await axiosClient.get<UserListResponse>(USERS_ENDPOINT, {
const response = await axiosClient.get<UserListResponse>(API_ROUTES.USERS.BASE, {
params: {
skip: params?.skip ?? 0,
limit: params?.limit ?? 10,
@@ -24,8 +23,8 @@ export const userService = {
saveUser: async (payload: UserRequest): Promise<AdministrationUser> => {
const response = payload.id
? await axiosClient.put<AdministrationUser>(USERS_ENDPOINT, payload)
: await axiosClient.post<AdministrationUser>(USERS_ENDPOINT, payload);
? await axiosClient.put<AdministrationUser>(API_ROUTES.USERS.BASE, payload)
: await axiosClient.post<AdministrationUser>(API_ROUTES.USERS.BASE, payload);
return response.data;
},
@@ -33,7 +32,7 @@ export const userService = {
id: number,
status: 'active' | 'inactive',
): Promise<AdministrationUser> => {
const response = await axiosClient.patch<AdministrationUser>(`${USERS_ENDPOINT}/${id}/status`, {
const response = await axiosClient.patch<AdministrationUser>(API_ROUTES.USERS.STATUS(id), {
status,
});
return response.data;

View File

@@ -1,4 +1,5 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
import { Video, PaginationParams } from '@/types';
/**
@@ -27,7 +28,7 @@ export const videoService = {
total_detections?: number;
};
}>;
}>(`/videos`, {
}>(API_ROUTES.VIDEOS.LIST, {
params: { skip, limit },
});
@@ -53,7 +54,7 @@ export const videoService = {
* Upload a video for processing
*/
uploadVideo: async (formData: FormData): Promise<any> => {
const response = await axiosClient.post('/upload', formData, {
const response = await axiosClient.post(API_ROUTES.VIDEOS.UPLOAD, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
@@ -65,7 +66,7 @@ export const videoService = {
* Get processing status of a video
*/
getVideoStatus: async (videoId: string): Promise<any> => {
const response = await axiosClient.get(`/status/${videoId}`);
const response = await axiosClient.get(API_ROUTES.VIDEOS.STATUS(videoId));
return response.data;
},
@@ -73,7 +74,7 @@ export const videoService = {
* Get analysis results for a video
*/
getVideoResults: async (videoId: string): Promise<any> => {
const response = await axiosClient.get(`/results/${videoId}`);
const response = await axiosClient.get(API_ROUTES.VIDEOS.RESULTS(videoId));
return response.data;
},
};