1
0
forked from baron/baron-sso

org chart 연동기능 추가

This commit is contained in:
2026-04-29 21:00:51 +09:00
parent 438f844f2b
commit 01e7b15c46
25 changed files with 5141 additions and 2940 deletions

View File

@@ -63,6 +63,13 @@ export type TenantUpdateRequest = {
config?: Record<string, unknown>;
};
export type TenantImportResult = {
created: number;
updated: number;
failed: number;
errors: string[];
};
export type ApiKeySummary = {
id: string;
name: string;
@@ -145,6 +152,36 @@ export async function deleteTenantsBulk(ids: string[]) {
});
}
export async function exportTenantsCSV() {
const response = await apiClient.get<Blob>("/v1/admin/tenants/export", {
responseType: "blob",
});
const dispositionHeader = response.headers["content-disposition"];
const disposition = Array.isArray(dispositionHeader)
? dispositionHeader[0]
: String(dispositionHeader ?? "");
const filenameMatch = disposition?.match(/filename="?([^"]+)"?/i);
return {
blob: response.data,
filename: filenameMatch?.[1] ?? "tenants.csv",
};
}
export async function importTenantsCSV(file: File) {
const formData = new FormData();
formData.append("file", file);
const { data } = await apiClient.post<TenantImportResult>(
"/v1/admin/tenants/import",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
},
);
return data;
}
export async function approveTenant(tenantId: string) {
const { data } = await apiClient.post<TenantSummary>(
`/v1/admin/tenants/${tenantId}/approve`,
@@ -266,44 +303,6 @@ export async function removeGroupMember(
);
}
export interface ImportResult {
totalRows: number;
processed: number;
userCreated: number;
userUpdated: number;
tenantCreated: number;
errors: string[];
}
export async function fetchImportProgress(
tenantId: string,
progressId: string,
) {
const { data } = await apiClient.get<{ current: number; total: number }>(
`/v1/admin/tenants/${tenantId}/organization/import/progress/${progressId}`,
);
return data;
}
export async function importOrgChart(
tenantId: string,
file: File,
progressId?: string,
) {
const formData = new FormData();
formData.append("file", file);
const url = progressId
? `/v1/admin/tenants/${tenantId}/organization/import?progressId=${progressId}`
: `/v1/admin/tenants/${tenantId}/organization/import`;
const { data } = await apiClient.post<{ data: ImportResult }>(url, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
return data.data;
}
export type GroupRole = {
tenantId: string;
tenantName: string;
@@ -412,6 +411,10 @@ export type UserCreateRequest = {
department?: string;
position?: string;
jobTitle?: string;
primaryTenantId?: string;
primaryTenantName?: string;
primaryTenantIsOwner?: boolean;
additionalAppointments?: UserAppointment[];
metadata?: Record<string, unknown>;
};
@@ -430,9 +433,22 @@ export type UserUpdateRequest = {
department?: string;
position?: string;
jobTitle?: string;
primaryTenantId?: string;
primaryTenantName?: string;
primaryTenantIsOwner?: boolean;
additionalAppointments?: UserAppointment[];
metadata?: Record<string, unknown>;
};
export type UserAppointment = {
tenantId: string;
tenantSlug?: string;
tenantName: string;
isOwner: boolean;
jobTitle?: string;
position?: string;
};
export type BulkUserItem = {
email: string;
loginId?: string;
@@ -492,19 +508,20 @@ export async function createUser(payload: UserCreateRequest) {
return data;
}
export function exportUsersCSVUrl(search?: string, tenantSlug?: string) {
const params = new URLSearchParams();
if (search) params.append("search", search);
if (tenantSlug) params.append("tenantSlug", tenantSlug);
// Get mock role from storage if exists for dev environment
const isMockRoleEnabled =
window.localStorage.getItem("X-Mock-Role-Enabled") === "true";
const mockRole = window.localStorage.getItem("X-Mock-Role");
if (isMockRoleEnabled && mockRole) params.append("x-test-role", mockRole);
const baseUrl = import.meta.env.VITE_ADMIN_API_BASE ?? "/api/v1";
return `${baseUrl}/admin/users/export?${params.toString()}`;
export async function exportUsersCSV(search?: string, tenantSlug?: string) {
const response = await apiClient.get<Blob>("/v1/admin/users/export", {
params: { search, tenantSlug },
responseType: "blob",
});
const dispositionHeader = response.headers["content-disposition"];
const disposition = Array.isArray(dispositionHeader)
? dispositionHeader[0]
: String(dispositionHeader ?? "");
const filenameMatch = disposition?.match(/filename="?([^"]+)"?/i);
return {
blob: response.data,
filename: filenameMatch?.[1] ?? "users.csv",
};
}
export async function bulkCreateUsers(users: BulkUserItem[]) {