feat(catalog): add saved packages, preview and bulk price editing

This commit is contained in:
Simo committed 2026-09-09 20:04:10 +02:00
1 parent 867113d5d4
commit 047cd9f3dd
48 files changed
+7712

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,70 @@
import { renderToStaticMarkup } from "react-dom/server";
import { expect, it, vi } from "vitest";
import type { PackageSnapshot } from "@/features/catalog/domain/packages";
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key }));
import { PackageCategoryPicker } from "./package-category-picker";
import { PackageSnapshotEditor } from "./package-snapshot-editor";
it("renders a bounded category page from a large destination list", () => {
const pages = Array.from({ length: 5000 }, (_, index) => ({
id: index + 1,
caption: `Category ${index + 1}`,
}));
const markup = renderToStaticMarkup(
<PackageCategoryPicker
pages={pages}
selected={[]}
onChange={() => {}}
multiple
/>,
);
expect((markup.match(/type="checkbox"/g) ?? []).length).toBe(25);
expect(markup).toContain("Category 25");
expect(markup).not.toContain("Category 26");
});
it("bounds offer controls and disables editing in a published or read-only snapshot", () => {
const snapshot: PackageSnapshot = {
pages: [
{
id: 10,
parentId: -1,
caption: "Rooms",
enabled: "1",
visible: "1",
iconImage: 1,
minRank: 1,
clubOnly: "0",
vipOnly: "0",
orderNum: 1,
pageLayout: "default_3x3",
},
],
offers: Array.from({ length: 500 }, (_, index) => ({
id: index + 1,
pageId: 10,
catalogName: `Offer ${index + 1}`,
itemIds: "42",
costCredits: 100,
costPoints: 0,
pointsType: 0,
amount: 1,
})),
};
const markup = renderToStaticMarkup(
<PackageSnapshotEditor
snapshot={snapshot}
disabled
revision={1}
onChange={() => {}}
/>,
);
expect(markup).toContain("Offer 25");
expect(markup).not.toContain("Offer 26");
expect(markup).toContain('<fieldset disabled=""');
expect(markup).not.toContain("selectMatching");
expect((markup.match(/aria-label="costCredits #[0-9]+"/g) ?? []).length).toBe(
25,
);
});
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,30 @@
import "server-only";
import { getTranslations } from "next-intl/server";
const messages: Record<string, string> = {
"Source categories or offers changed since this package was created. Create a fresh package to include the current data.":
"sourceChanged",
"Package changed. Reload it before saving": "saveConflict",
"Package changed. Refresh the publication preview": "publishConflict",
"Catalog changed. Refresh the publication preview": "catalogChanged",
"Catalog package not found": "missing",
"A package supports at most 200 categories including descendants":
"pageLimit",
"A package supports at most 500 offers": "offerLimit",
"Package data exceeds 8 MB safety limit": "payloadLimit",
"Package membership cannot change": "membership",
"Package hierarchy cannot change": "hierarchy",
"Package furniture references cannot change": "references",
"Package furniture reference no longer exists": "furnitureMissing",
"Package published, but audit logging failed.": "auditWarning",
"Package published, but the hotel update was not delivered.": "hotelWarning",
"Package published, but export scheduling failed.": "exportWarning",
"Package published. Refresh the page to see the latest catalog.":
"refreshWarning",
};
export async function packageMessage(message: string): Promise<string> {
const key = messages[message];
if (!key) return message;
const t = await getTranslations("pages.admin.catalogPackages.backend");
return t(key);
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff