labelService.ts 9.38 KB
import { createApiClient } from "../lib/apiClient";
import type {
  LabelCreateInput,
  LabelBatchCreateInput,
  LabelBatchCreateResult,
  LabelDto,
  LabelGetListInput,
  LabelUpdateInput,
  PagedResultDto,
} from "../types/label";

const api = createApiClient({
  getToken: () => {
    try {
      return localStorage.getItem("access_token") ?? localStorage.getItem("token") ?? null;
    } catch {
      return null;
    }
  },
});

const PATH = "/label";

function parseIdList(v: unknown): string[] | undefined {
  if (!Array.isArray(v)) return undefined;
  return [...new Set(v.map((x) => String(x).trim()).filter(Boolean))];
}

export function normalizeLabelDto(raw: unknown): LabelDto {
  const r = (raw && typeof raw === "object" ? raw : {}) as Record<string, unknown>;
  const regionIds = parseIdList(r.regionIds ?? r.RegionIds);
  const groupIds = parseIdList(r.groupIds ?? r.GroupIds);
  const mergedRegionIds = [...new Set([...(regionIds ?? []), ...(groupIds ?? [])])];
  const appliedRaw = r.appliedRegionType ?? r.AppliedRegionType;
  const appliedRegionType =
    typeof appliedRaw === "string" && appliedRaw.trim()
      ? appliedRaw.trim().toUpperCase()
      : mergedRegionIds.length > 0
        ? "SPECIFIED"
        : null;
  const regionRaw = r.region ?? r.Region;
  const locationIds = parseIdList(r.locationIds ?? r.LocationIds) ?? [];
  const locationIdRaw = r.locationId ?? r.LocationId;
  const locationId =
    locationIdRaw != null && String(locationIdRaw).trim()
      ? String(locationIdRaw).trim()
      : locationIds[0] ?? null;
  const mergedLocationIds = [
    ...new Set([
      ...(locationId ? [locationId] : []),
      ...locationIds,
    ]),
  ];
  return {
    ...(r as object),
    id: String(r.id ?? r.Id ?? r.labelCode ?? r.LabelCode ?? ""),
    locationId,
    locationIds: mergedLocationIds.length ? mergedLocationIds : locationIds,
    regionIds: mergedRegionIds.length ? mergedRegionIds : regionIds,
    groupIds: mergedRegionIds.length ? mergedRegionIds : groupIds,
    appliedRegionType: appliedRegionType as LabelDto["appliedRegionType"],
    region: typeof regionRaw === "string" ? regionRaw : null,
  } as LabelDto;
}

function locationBodyFromInput(input: LabelCreateInput | LabelUpdateInput): Record<string, unknown> {
  const locationIds = [
    ...new Set((input.locationIds ?? []).map((x) => String(x).trim()).filter(Boolean)),
  ];
  const body: Record<string, unknown> = {};
  if (locationIds.length) {
    body.locationIds = locationIds;
    body.locationId = locationIds[0];
  }
  return body;
}

function scopeBodyFromInput(input: LabelCreateInput | LabelUpdateInput): Record<string, unknown> {
  const regionIds = [
    ...new Set(
      [...(input.regionIds ?? []), ...(input.groupIds ?? [])]
        .map((x) => String(x).trim())
        .filter(Boolean),
    ),
  ];
  const applied =
    String(input.appliedRegionType ?? "").trim().toUpperCase() ||
    (regionIds.length > 0 ? "SPECIFIED" : "");
  const body: Record<string, unknown> = {};
  if (applied === "ALL" || applied === "SPECIFIED") {
    body.appliedRegionType = applied;
  }
  if (regionIds.length) {
    body.regionIds = regionIds;
    body.groupIds = regionIds;
  } else if (applied === "ALL") {
    body.regionIds = [];
    body.groupIds = [];
  }
  return body;
}

export async function getLabels(input: LabelGetListInput, signal?: AbortSignal): Promise<PagedResultDto<LabelDto>> {
  const res = await api.requestJson<PagedResultDto<LabelDto> & { items?: unknown[]; Items?: unknown[] }>({
    path: PATH,
    method: "GET",
    query: {
      SkipCount: input.skipCount,
      MaxResultCount: input.maxResultCount,
      Sorting: input.sorting,
      Keyword: input.keyword,
      PartnerId: input.partnerId,
      GroupId: input.groupId,
      LocationId: input.locationId,
      ProductId: input.productId,
      LabelCategoryId: input.labelCategoryId,
      LabelTypeId: input.labelTypeId,
      TemplateCode: input.templateCode,
      State: input.state,
    },
    signal,
  });
  const itemsRaw = res.items ?? res.Items ?? [];
  const items = (Array.isArray(itemsRaw) ? itemsRaw : []).map((x) => normalizeLabelDto(x));
  return {
    ...res,
    items,
    totalCount: res.totalCount ?? (res as { TotalCount?: number }).TotalCount ?? items.length,
  };
}

export async function getLabel(labelCode: string, signal?: AbortSignal): Promise<LabelDto> {
  const raw = await api.requestJson<unknown>({
    path: `${PATH}/${encodeURIComponent(labelCode)}`,
    method: "GET",
    signal,
  });
  return normalizeLabelDto(raw);
}

export async function createLabel(input: LabelCreateInput): Promise<LabelDto> {
  const raw = await api.requestJson<unknown>({
    path: PATH,
    method: "POST",
    body: {
      labelCode: String(input.labelCode ?? "").trim() || null,
      labelName: input.labelName,
      templateCode: input.templateCode,
      ...locationBodyFromInput(input),
      labelCategoryId: input.labelCategoryId,
      labelTypeId: String(input.labelTypeId ?? "").trim() || null,
      productIds: input.productIds,
      labelInfoJson: input.labelInfoJson,
      state: input.state ?? true,
      ...scopeBodyFromInput(input),
    },
  });
  return normalizeLabelDto(raw);
}

function normalizeBatchCreateResult(raw: unknown): LabelBatchCreateResult {
  const r = (raw && typeof raw === "object" ? raw : {}) as Record<string, unknown>;
  const successItemsRaw = r.successItems ?? r.SuccessItems;
  const errorsRaw = r.errors ?? r.Errors;
  const successItems = Array.isArray(successItemsRaw)
    ? successItemsRaw.map((x) => {
        const it = (x && typeof x === "object" ? x : {}) as Record<string, unknown>;
        return {
          rowNumber: typeof (it.rowNumber ?? it.RowNumber) === "number" ? (it.rowNumber ?? it.RowNumber) as number : null,
          labelCode: it.labelCode != null || it.LabelCode != null ? String(it.labelCode ?? it.LabelCode) : null,
          labelName: it.labelName != null || it.LabelName != null ? String(it.labelName ?? it.LabelName) : null,
        };
      })
    : [];
  const errors = Array.isArray(errorsRaw)
    ? errorsRaw.map((x) => {
        const it = (x && typeof x === "object" ? x : {}) as Record<string, unknown>;
        return {
          rowNumber: typeof (it.rowNumber ?? it.RowNumber) === "number" ? (it.rowNumber ?? it.RowNumber) as number : null,
          labelName: it.labelName != null || it.LabelName != null ? String(it.labelName ?? it.LabelName) : null,
          message: it.message != null || it.Message != null ? String(it.message ?? it.Message) : null,
        };
      })
    : [];
  return {
    successCount: Number(r.successCount ?? r.SuccessCount ?? successItems.length) || 0,
    failCount: Number(r.failCount ?? r.FailCount ?? errors.length) || 0,
    successItems,
    errors,
  };
}

export async function batchCreateLabels(input: LabelBatchCreateInput): Promise<LabelBatchCreateResult> {
  const raw = await api.requestJson<unknown>({
    path: `${PATH}/batch-create`,
    method: "POST",
    body: {
      templateCode: input.templateCode,
      saveTemplateProductDefaults: input.saveTemplateProductDefaults ?? true,
      items: input.items.map((item) => {
        const locationIds = [
          ...new Set((item.locationIds ?? []).map((x) => String(x).trim()).filter(Boolean)),
        ];
        const regionIds = [
          ...new Set(
            [...(item.regionIds ?? []), ...(item.groupIds ?? [])]
              .map((x) => String(x).trim())
              .filter(Boolean),
          ),
        ];
        const partnerIds = [
          ...new Set(
            [
              ...(item.partnerId ? [item.partnerId] : []),
              ...(item.partnerIds ?? []),
            ]
              .map((x) => String(x).trim())
              .filter(Boolean),
          ),
        ];
        return {
          labelCode: String(item.labelCode ?? "").trim() || null,
          labelName: item.labelName,
          labelCategoryId: item.labelCategoryId,
          labelTypeId: String(item.labelTypeId ?? "").trim() || null,
          productIds: item.productIds,
          locationIds,
          locationId: item.locationId ?? locationIds[0] ?? null,
          appliedRegionType: item.appliedRegionType,
          regionIds,
          groupIds: regionIds,
          partnerId: item.partnerId ?? partnerIds[0] ?? null,
          partnerIds,
          labelInfoJson: item.labelInfoJson ?? null,
          state: item.state ?? true,
          templateDefaultValues: item.templateDefaultValues ?? {},
          templateDataValues: item.templateDataValues ?? {},
          templateDateOffsets: item.templateDateOffsets ?? {},
          nutritionByElementId: item.nutritionByElementId ?? {},
        };
      }),
    },
  });
  return normalizeBatchCreateResult(raw);
}

export async function updateLabel(labelCode: string, input: LabelUpdateInput): Promise<LabelDto> {
  const raw = await api.requestJson<unknown>({
    path: `${PATH}/${encodeURIComponent(labelCode)}`,
    method: "PUT",
    body: {
      labelName: input.labelName,
      templateCode: input.templateCode,
      ...locationBodyFromInput(input),
      labelCategoryId: input.labelCategoryId,
      labelTypeId: input.labelTypeId,
      productIds: input.productIds,
      labelInfoJson: input.labelInfoJson,
      state: input.state ?? true,
      ...scopeBodyFromInput(input),
    },
  });
  return normalizeLabelDto(raw);
}

export async function deleteLabel(labelCode: string): Promise<void> {
  await api.requestJson<unknown>({
    path: `${PATH}/${encodeURIComponent(labelCode)}`,
    method: "DELETE",
  });
}