This commit is contained in:
2026-08-16 18:36:56 +02:00
parent d981147ff2
commit a94a5aaad7
26 changed files with 904 additions and 507 deletions
+37 -11
View File
@@ -1,7 +1,7 @@
/**
* Admin orgs UI client — users + companies directory, staff roles, plan assign,
* clone-catalog, and Fix A1 (`POST …/fix-catalog`).
* Flash Fix A1: result.prompts / hashes / categories → flash.admin.fixA1Success.
* clone-catalog, and Sync A1 (`POST …/sync-a1`; fix-catalog is a compat alias).
* Flash Sync A1: result.prompts / hashes / categories / dump_* → flash.admin.syncA1Success.
* Contract: docs/admin-roles-support/04-contract.md · Docs: 10-admin-orgs-ui.md
*/
import { api, ApiError } from "$lib/api";
@@ -22,6 +22,8 @@ export const ADMIN_CLONE_CATALOG_PATH = (companyId: string) =>
`${ADMIN_COMPANIES_PATH}/${encodeURIComponent(companyId)}/clone-catalog`;
export const ADMIN_FIX_CATALOG_PATH = (companyId: string) =>
`${ADMIN_COMPANIES_PATH}/${encodeURIComponent(companyId)}/fix-catalog`;
export const ADMIN_SYNC_A1_PATH = (companyId: string) =>
`${ADMIN_COMPANIES_PATH}/${encodeURIComponent(companyId)}/sync-a1`;
export const ADMIN_STAFF_ROLE_PATH = (userId: string) =>
`/api/admin/users/${encodeURIComponent(userId)}/staff-role`;
@@ -230,13 +232,13 @@ export async function cloneAdminCompanyCatalog(
});
}
export type FixCatalogResult = {
export type SyncA1Result = {
company_id: string;
company_name: string;
a1_cohort?: boolean;
category_attribute_orphans_removed?: number;
category_attribute_links?: number;
/** Alias of category_prompts_updated for flash.admin.fixA1Success {prompts}. */
/** Alias of category_prompts_updated for flash.admin.syncA1Success {prompts}. */
prompts?: number;
category_prompts_updated?: number;
product_enhance_languages?: number;
@@ -258,23 +260,36 @@ export type FixCatalogResult = {
products_scanned?: number;
reprocess_needed_count?: number;
reprocess_sample_raw_product_ids?: string[];
dump_found?: boolean;
dump_path?: string;
dump_skipped_reason?: string;
dump_pairs?: number;
dump_mapped_updated?: number;
dump_processed_updated?: number;
dump_status?: string;
};
export type FixCatalogResponse = {
export type SyncA1Response = {
status: string;
result: FixCatalogResult;
result: SyncA1Result;
note?: string;
};
/** In-place Fix A1 / catalog hygiene. Never clears catalog; no mass reprocess. */
export async function fixAdminCompanyCatalog(
/** @deprecated Use SyncA1Result — kept for type aliases during UI rename. */
export type FixCatalogResult = SyncA1Result;
/** @deprecated Use SyncA1Response */
export type FixCatalogResponse = SyncA1Response;
/** Sync A1: dump category backfill (when dump on API host) + Fix hygiene. No mass reprocess. */
export async function syncAdminCompanyA1(
companyId: string,
opts?: { backfillCategories?: boolean; reprocessSampleLimit?: number }
): Promise<FixCatalogResponse> {
opts?: { backfillCategories?: boolean; reprocessSampleLimit?: number; skipDumpBackfill?: boolean }
): Promise<SyncA1Response> {
const body: {
confirm: true;
backfill_categories?: boolean;
reprocess_sample_limit?: number;
skip_dump_backfill?: boolean;
} = { confirm: true };
if (opts?.backfillCategories === false) {
body.backfill_categories = false;
@@ -282,12 +297,23 @@ export async function fixAdminCompanyCatalog(
if (typeof opts?.reprocessSampleLimit === "number") {
body.reprocess_sample_limit = opts.reprocessSampleLimit;
}
return api<FixCatalogResponse>(ADMIN_FIX_CATALOG_PATH(companyId), {
if (opts?.skipDumpBackfill) {
body.skip_dump_backfill = true;
}
return api<SyncA1Response>(ADMIN_SYNC_A1_PATH(companyId), {
method: "POST",
body
});
}
/** Compat alias — same as syncAdminCompanyA1 (fix-catalog route). */
export async function fixAdminCompanyCatalog(
companyId: string,
opts?: { backfillCategories?: boolean; reprocessSampleLimit?: number }
): Promise<SyncA1Response> {
return syncAdminCompanyA1(companyId, opts);
}
export function isStaffRoleApiUnavailable(err: unknown): boolean {
return err instanceof ApiError && (err.status === 404 || err.status === 501);
}