ignore folder

This commit is contained in:
2026-04-21 06:30:48 +07:00
commit ca00b36f19
70 changed files with 3871 additions and 0 deletions

59
services/api.ts Normal file
View File

@ -0,0 +1,59 @@
import axios from "axios";
import { useAuthStore } from "@/store/authStore";
import { useTenantStore } from "@/store/tenantStore";
import { useLocaleStore } from "@/store/uiStore";
import { useApiStore } from "@/store/uiStore";
export const apiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:9191",
timeout: 25000
});
apiClient.interceptors.request.use((config) => {
const auth = useAuthStore.getState();
const tenantId = useTenantStore.getState().tenantId || auth.tenantId;
const locale = useLocaleStore.getState().locale;
if (auth.accessToken) {
config.headers.Authorization = `${auth.tokenType ?? "Bearer"} ${auth.accessToken}`;
}
if (tenantId) {
config.headers["X-Tenant-Id"] = tenantId;
}
if (locale) {
config.headers["Accept-Language"] = locale === "id" ? "id-ID" : "en-US";
}
return config;
});
apiClient.interceptors.response.use(
(response) => response,
(error) => {
const status = error?.response?.status;
const message = error?.response?.data?.message || error.message;
const ui = useApiStore.getState();
if (status === 401) {
ui.addToast(message || "Unauthorized", "error");
useAuthStore.getState().clearAuth();
if (typeof window !== "undefined") {
window.location.href = "/(auth)/login";
}
}
if (status === 403) {
ui.addToast(message || "Forbidden", "error");
}
return Promise.reject(new Error(message ?? "Request failed"));
}
);
export async function unwrap<T>(promise: ReturnType<typeof apiClient.request>): Promise<T> {
const response = await promise;
const payload = response.data;
if (payload && typeof payload.success === "boolean" && payload.success === false) {
throw new Error(payload.message ?? "Business error");
}
return payload.data as T;
}

10
services/audit.ts Normal file
View File

@ -0,0 +1,10 @@
import { ApiResponse, AuditItem } from "@/types/api";
import { apiClient } from "./api";
export async function getAudit(limit = 50): Promise<AuditItem[]> {
const response = await apiClient.get<ApiResponse<AuditItem[]>>("/api/audit", {
params: { limit }
});
if (!response.data.success) throw new Error(response.data.message);
return response.data.data;
}

41
services/auth.ts Normal file
View File

@ -0,0 +1,41 @@
import { ApiResponse, LoginRequest, LoginResponseData, UserProfile, TenantContextResponse } from "@/types/api";
import { apiClient, unwrap } from "./api";
export async function login(request: LoginRequest, tenantId: string): Promise<LoginResponseData> {
const response = await apiClient.post<ApiResponse<LoginResponseData>>(
"/api/auth/login",
request,
{
headers: { "X-Tenant-Id": tenantId }
}
);
const payload = response.data;
if (!payload.success) {
throw new Error(payload.message);
}
return payload.data;
}
export async function refreshToken(refreshToken: string) {
const response = await apiClient.post<ApiResponse<LoginResponseData>>("/api/auth/refresh", {
refreshToken
});
const payload = response.data;
if (!payload.success) throw new Error(payload.message);
return payload.data;
}
export async function logout() {
await unwrap<null>(apiClient.post<ApiResponse<null>>("/api/auth/logout"));
}
export async function getCurrentUser(): Promise<ApiResponse<UserProfile>> {
const response = await apiClient.get<ApiResponse<UserProfile>>("/api/users/me");
return response.data;
}
export async function getTenantContext(): Promise<TenantContextResponse> {
const response = await apiClient.get<ApiResponse<TenantContextResponse>>("/api/tenant/context");
if (!response.data.success) throw new Error(response.data.message);
return response.data.data;
}

15
services/modules.ts Normal file
View File

@ -0,0 +1,15 @@
import { ApiResponse, ModuleItem, ToggleModuleRequest } from "@/types/api";
import { apiClient } from "./api";
export async function getModules(): Promise<ModuleItem[]> {
const response = await apiClient.get<ApiResponse<ModuleItem[]>>("/api/modules");
if (!response.data.success) throw new Error(response.data.message);
return response.data.data;
}
export async function toggleModule(code: string, payload: ToggleModuleRequest): Promise<ModuleItem> {
const response = await apiClient.post<ApiResponse<ModuleItem>>(`/api/modules/${code}/toggle`, payload);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}

24
services/roles.ts Normal file
View File

@ -0,0 +1,24 @@
import { ApiResponse, RoleCreateRequest, UpdateRolePermissionRequest, WorkflowRequestItem } from "@/types/api";
import { apiClient } from "./api";
export async function createRoleRequest(payload: RoleCreateRequest): Promise<WorkflowRequestItem> {
const response = await apiClient.post<ApiResponse<WorkflowRequestItem>>(
"/api/roles/management/requests/create",
payload
);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}
export async function updateRolePermissionRequest(
payload: UpdateRolePermissionRequest
): Promise<WorkflowRequestItem> {
const response = await apiClient.post<ApiResponse<WorkflowRequestItem>>(
"/api/roles/management/requests/update-permissions",
payload
);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}

8
services/tenant.ts Normal file
View File

@ -0,0 +1,8 @@
import { ApiResponse, TenantContextResponse } from "@/types/api";
import { apiClient } from "./api";
export async function getTenantContext(): Promise<TenantContextResponse> {
const response = await apiClient.get<ApiResponse<TenantContextResponse>>("/api/tenant/context");
if (!response.data.success) throw new Error(response.data.message);
return response.data.data;
}

22
services/users.ts Normal file
View File

@ -0,0 +1,22 @@
import { ApiResponse, UpdateUserRolesRequest, UserCreateRequest, WorkflowRequestItem, WorkflowCreateRequest } from "@/types/api";
import { apiClient, unwrap } from "./api";
export async function createUserRequest(payload: UserCreateRequest): Promise<WorkflowRequestItem> {
const response = await apiClient.post<ApiResponse<WorkflowRequestItem>>(
"/api/users/management/requests/create",
payload
);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}
export async function updateUserRolesRequest(payload: UpdateUserRolesRequest): Promise<WorkflowRequestItem> {
const response = await apiClient.post<ApiResponse<WorkflowRequestItem>>(
"/api/users/management/requests/update-roles",
payload
);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}

43
services/workflow.ts Normal file
View File

@ -0,0 +1,43 @@
import {
ApiResponse,
WorkflowActionPayload,
WorkflowCreateRequest,
WorkflowFilters,
WorkflowRequestItem
} from "@/types/api";
import { apiClient } from "./api";
export async function getWorkflowRequests(filters: WorkflowFilters = {}): Promise<WorkflowRequestItem[]> {
const response = await apiClient.get<ApiResponse<WorkflowRequestItem[]>>("/api/workflow/requests", {
params: filters
});
if (!response.data.success) throw new Error(response.data.message);
return response.data.data;
}
export async function createWorkflowRequest(payload: WorkflowCreateRequest): Promise<WorkflowRequestItem> {
const response = await apiClient.post<ApiResponse<WorkflowRequestItem>>("/api/workflow/request", payload);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}
export async function approveWorkflowRequest(id: string, payload: WorkflowActionPayload): Promise<WorkflowRequestItem> {
const response = await apiClient.post<ApiResponse<WorkflowRequestItem>>(
`/api/workflow/${id}/approve`,
payload
);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}
export async function rejectWorkflowRequest(id: string, payload: WorkflowActionPayload): Promise<WorkflowRequestItem> {
const response = await apiClient.post<ApiResponse<WorkflowRequestItem>>(
`/api/workflow/${id}/reject`,
payload
);
const data = response.data;
if (!data.success) throw new Error(data.message);
return data.data;
}