feat(catalog): add reviewed bulk edits and complete category duplication
This commit is contained in:
1 parent
0bedc04692
commit
55a47949f1
26 files changed
+2504
-85
No files matched your search
@@ -0,0 +1,68 @@
|
||||
"use server";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import type { BulkOfferInput } from "@/features/catalog/domain/bulk-offers";
|
||||
import {
|
||||
applyBulkOffersCommand,
|
||||
listBulkOfferDestinationsCommand,
|
||||
previewBulkOffersCommand,
|
||||
} from "@/features/catalog/server/bulk-offers";
|
||||
import { catalogFailure } from "@/features/catalog/server/errors";
|
||||
import { sendCatalogUpdate } from "@/features/catalog/server/sync-status";
|
||||
import { requirePermission } from "@/lib/admin/guard";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { logAudit } from "@/lib/services/audit";
|
||||
import { withCatalogExport } from "@/lib/services/catalog-git-queue";
|
||||
export async function previewBulkOffers(input: BulkOfferInput) {
|
||||
await requirePermission(PERMS.CATALOG_VIEW);
|
||||
try {
|
||||
return { ok: true as const, data: await previewBulkOffersCommand(input) };
|
||||
} catch (error) {
|
||||
return { ok: false as const, error: catalogFailure(error).message };
|
||||
}
|
||||
}
|
||||
export async function applyBulkOffers(
|
||||
input: BulkOfferInput,
|
||||
fingerprint: string,
|
||||
) {
|
||||
const staff = await requirePermission(PERMS.CATALOG_EDIT);
|
||||
try {
|
||||
return await withCatalogExport(async () => {
|
||||
const data = await applyBulkOffersCommand(input, fingerprint);
|
||||
if (data.changedCount > 0) {
|
||||
await logAudit({
|
||||
userId: staff.id,
|
||||
action: "catalog_offers_bulk_update",
|
||||
target: "catalog",
|
||||
after: {
|
||||
ids: input.ids,
|
||||
changes: input.changes,
|
||||
changedCount: data.changedCount,
|
||||
},
|
||||
}).catch((error) =>
|
||||
logger.error("Catalog bulk update committed; audit write failed", {
|
||||
module: "catalog",
|
||||
error,
|
||||
}),
|
||||
);
|
||||
await sendCatalogUpdate();
|
||||
revalidatePath("/admin/catalog", "layout");
|
||||
}
|
||||
return { ok: true as const, data };
|
||||
});
|
||||
} catch (error) {
|
||||
return { ok: false as const, error: catalogFailure(error).message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getBulkOfferDestinations() {
|
||||
await requirePermission(PERMS.CATALOG_VIEW);
|
||||
try {
|
||||
return {
|
||||
ok: true as const,
|
||||
data: await listBulkOfferDestinationsCommand(),
|
||||
};
|
||||
} catch (error) {
|
||||
return { ok: false as const, error: catalogFailure(error).message };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
"use server";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import type {
|
||||
DuplicateInput,
|
||||
DuplicatePreview,
|
||||
} from "@/features/catalog/domain/duplicate";
|
||||
import {
|
||||
duplicateCategoryCommand,
|
||||
duplicateDestinationsCommand,
|
||||
previewDuplicateCommand,
|
||||
} from "@/features/catalog/server/duplicate-commands";
|
||||
import { catalogFailure } from "@/features/catalog/server/errors";
|
||||
import { sendCatalogUpdate } from "@/features/catalog/server/sync-status";
|
||||
import { requirePermission } from "@/lib/admin/guard";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { withCatalogExport } from "@/lib/services/catalog-git-queue";
|
||||
import { logStaffActivity } from "@/lib/services/staff-activity";
|
||||
export async function previewCatalogDuplicate(input: DuplicateInput) {
|
||||
await requirePermission(PERMS.CATALOG_VIEW);
|
||||
try {
|
||||
return { ok: true as const, data: await previewDuplicateCommand(input) };
|
||||
} catch (error) {
|
||||
return { ok: false as const, error: catalogFailure(error).message };
|
||||
}
|
||||
}
|
||||
export async function applyCatalogDuplicate(input: DuplicatePreview) {
|
||||
const staff = await requirePermission(PERMS.CATALOG_EDIT);
|
||||
return withCatalogExport(async () => {
|
||||
let data: Awaited<ReturnType<typeof duplicateCategoryCommand>>;
|
||||
try {
|
||||
data = await duplicateCategoryCommand(input);
|
||||
} catch (error) {
|
||||
return { ok: false as const, error: catalogFailure(error).message };
|
||||
}
|
||||
const hotel = await sendCatalogUpdate();
|
||||
await logStaffActivity({
|
||||
staffId: staff.id,
|
||||
action: "catalog_page_create",
|
||||
description: `Duplicated ${input.kind} category #${input.sourceId} to #${data.id}: ${data.pages} categories, ${data.offers} offers`,
|
||||
targetType: "catalog_page",
|
||||
targetId: data.id,
|
||||
}).catch((error) => {
|
||||
logger.error("Category duplicated but audit logging failed", {
|
||||
module: "catalog",
|
||||
error,
|
||||
});
|
||||
});
|
||||
revalidatePath("/admin/catalog");
|
||||
return { ok: true as const, data: { ...data, hotel } };
|
||||
});
|
||||
}
|
||||
|
||||
export async function catalogDuplicateDestinations(
|
||||
kind: DuplicateInput["kind"],
|
||||
) {
|
||||
await requirePermission(PERMS.CATALOG_VIEW);
|
||||
try {
|
||||
return {
|
||||
ok: true as const,
|
||||
data: await duplicateDestinationsCommand(kind),
|
||||
};
|
||||
} catch (error) {
|
||||
return { ok: false as const, error: catalogFailure(error).message };
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user