feat: extend workflow brand hierarchy to five levels
This commit is contained in:
parent
7eda4720d3
commit
ea6a9a9d76
@ -92,11 +92,13 @@ import type {
|
|||||||
AgentPresetLibrarySection,
|
AgentPresetLibrarySection,
|
||||||
AgentWorkflowBrandModelConfig,
|
AgentWorkflowBrandModelConfig,
|
||||||
AgentWorkflowBrandStoreEntry,
|
AgentWorkflowBrandStoreEntry,
|
||||||
|
AgentWorkflowFourthCategoryEntry,
|
||||||
AgentWorkflowGoodsInfoApiConfig,
|
AgentWorkflowGoodsInfoApiConfig,
|
||||||
AgentWorkflowMaterialApiConfig,
|
AgentWorkflowMaterialApiConfig,
|
||||||
AgentWorkflowOptionUiConfig,
|
AgentWorkflowOptionUiConfig,
|
||||||
AgentPsdTextColorConfig,
|
AgentPsdTextColorConfig,
|
||||||
AgentWorkflowStoreEntry,
|
AgentWorkflowStoreEntry,
|
||||||
|
AgentWorkflowThirdCategoryEntry,
|
||||||
AgentWorkflowShowcaseConfig,
|
AgentWorkflowShowcaseConfig,
|
||||||
AgentWorkflowStepUiConfig,
|
AgentWorkflowStepUiConfig,
|
||||||
AgentWorkflowUiConfig,
|
AgentWorkflowUiConfig,
|
||||||
@ -465,15 +467,86 @@ function buildWorkflowInputOptionsFromBrandEntries(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uniqueThirdCategoryEntries = new Map<string, AgentWorkflowThirdCategoryEntry>();
|
||||||
|
|
||||||
|
(storeEntry.thirdCategoryEntries || []).forEach((thirdEntry, thirdIndex) => {
|
||||||
|
const categoryName = String(thirdEntry.categoryName || "").trim();
|
||||||
|
if (!categoryName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueFourthCategoryEntries = new Map<string, AgentWorkflowFourthCategoryEntry>();
|
||||||
|
(thirdEntry.fourthCategoryEntries || []).forEach((fourthEntry, fourthIndex) => {
|
||||||
|
const fourthCategoryName = String(fourthEntry.categoryName || "").trim();
|
||||||
|
if (!fourthCategoryName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueFourthCategoryEntries.set(fourthCategoryName.toLowerCase(), {
|
||||||
|
categoryName: fourthCategoryName,
|
||||||
|
fifthCategories: Array.from(
|
||||||
|
new Set(
|
||||||
|
(fourthEntry.fifthCategories || [])
|
||||||
|
.map((fifthCategory) => fifthCategory.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
sortOrder: fourthIndex,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const fourthCategoryEntries = Array.from(uniqueFourthCategoryEntries.values()).map(
|
||||||
|
(fourthEntry, fourthIndex) => ({
|
||||||
|
...fourthEntry,
|
||||||
|
sortOrder: fourthIndex,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
uniqueThirdCategoryEntries.set(categoryName.toLowerCase(), {
|
||||||
|
categoryName,
|
||||||
|
fourthCategories:
|
||||||
|
fourthCategoryEntries.length > 0
|
||||||
|
? fourthCategoryEntries.map((fourthEntry) => fourthEntry.categoryName)
|
||||||
|
: Array.from(
|
||||||
|
new Set(
|
||||||
|
(thirdEntry.fourthCategories || [])
|
||||||
|
.map((fourthCategory) => fourthCategory.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
fourthCategoryEntries,
|
||||||
|
sortOrder: thirdIndex,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const thirdCategoryEntries = Array.from(uniqueThirdCategoryEntries.values()).map((thirdEntry, thirdIndex) => ({
|
||||||
|
...thirdEntry,
|
||||||
|
fourthCategories:
|
||||||
|
(thirdEntry.fourthCategoryEntries || []).length > 0
|
||||||
|
? (thirdEntry.fourthCategoryEntries || []).map((fourthEntry) => fourthEntry.categoryName)
|
||||||
|
: Array.from(
|
||||||
|
new Set(
|
||||||
|
(thirdEntry.fourthCategories || [])
|
||||||
|
.map((fourthCategory) => fourthCategory.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
sortOrder: thirdIndex,
|
||||||
|
}));
|
||||||
|
|
||||||
uniqueStoreEntries.set(storeName.toLowerCase(), {
|
uniqueStoreEntries.set(storeName.toLowerCase(), {
|
||||||
storeName,
|
storeName,
|
||||||
thirdCategories: Array.from(
|
thirdCategories:
|
||||||
new Set(
|
thirdCategoryEntries.length > 0
|
||||||
(storeEntry.thirdCategories || [])
|
? thirdCategoryEntries.map((thirdEntry) => thirdEntry.categoryName)
|
||||||
.map((thirdCategory) => thirdCategory.trim())
|
: Array.from(
|
||||||
.filter(Boolean)
|
new Set(
|
||||||
)
|
(storeEntry.thirdCategories || [])
|
||||||
),
|
.map((thirdCategory) => thirdCategory.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
thirdCategoryEntries,
|
||||||
sortOrder: storeIndex,
|
sortOrder: storeIndex,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -513,10 +586,51 @@ function buildWorkflowInputOptionsFromBrandEntries(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
fourthCategories: Array.from(
|
||||||
|
new Set(
|
||||||
|
normalizedEntries.flatMap((entry) =>
|
||||||
|
entry.storeEntries.flatMap((storeEntry) =>
|
||||||
|
(storeEntry.thirdCategoryEntries || []).flatMap(
|
||||||
|
(thirdEntry) => thirdEntry.fourthCategories || []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
fifthCategories: Array.from(
|
||||||
|
new Set(
|
||||||
|
normalizedEntries.flatMap((entry) =>
|
||||||
|
entry.storeEntries.flatMap((storeEntry) =>
|
||||||
|
(storeEntry.thirdCategoryEntries || []).flatMap((thirdEntry) =>
|
||||||
|
(thirdEntry.fourthCategoryEntries || []).flatMap(
|
||||||
|
(fourthEntry) => fourthEntry.fifthCategories || []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
priceBands: normalizedPriceBands,
|
priceBands: normalizedPriceBands,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createStoreDraftKey(brandName: string, storeName: string) {
|
||||||
|
return `${brandName}::${storeName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createThirdCategoryDraftKey(brandName: string, storeName: string, thirdCategory: string) {
|
||||||
|
return `${brandName}::${storeName}::${thirdCategory}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFourthCategoryDraftKey(
|
||||||
|
brandName: string,
|
||||||
|
storeName: string,
|
||||||
|
thirdCategory: string,
|
||||||
|
fourthCategory: string
|
||||||
|
) {
|
||||||
|
return `${brandName}::${storeName}::${thirdCategory}::${fourthCategory}`;
|
||||||
|
}
|
||||||
|
|
||||||
interface AgentWorkflowSettingsClientProps {
|
interface AgentWorkflowSettingsClientProps {
|
||||||
backHref?: string;
|
backHref?: string;
|
||||||
backLabel?: string;
|
backLabel?: string;
|
||||||
@ -573,6 +687,8 @@ export function AgentWorkflowSettingsClient({
|
|||||||
const [priceBandDraft, setPriceBandDraft] = useState("");
|
const [priceBandDraft, setPriceBandDraft] = useState("");
|
||||||
const [storeDrafts, setStoreDrafts] = useState<Record<string, string>>({});
|
const [storeDrafts, setStoreDrafts] = useState<Record<string, string>>({});
|
||||||
const [thirdCategoryDrafts, setThirdCategoryDrafts] = useState<Record<string, string>>({});
|
const [thirdCategoryDrafts, setThirdCategoryDrafts] = useState<Record<string, string>>({});
|
||||||
|
const [fourthCategoryDrafts, setFourthCategoryDrafts] = useState<Record<string, string>>({});
|
||||||
|
const [fifthCategoryDrafts, setFifthCategoryDrafts] = useState<Record<string, string>>({});
|
||||||
const [blockedOptionSearches, setBlockedOptionSearches] = useState<Record<string, string>>({});
|
const [blockedOptionSearches, setBlockedOptionSearches] = useState<Record<string, string>>({});
|
||||||
const fileInputRefs = useRef<Record<string, HTMLInputElement | null>>({});
|
const fileInputRefs = useRef<Record<string, HTMLInputElement | null>>({});
|
||||||
|
|
||||||
@ -717,6 +833,24 @@ export function AgentWorkflowSettingsClient({
|
|||||||
});
|
});
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
setFourthCategoryDrafts((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setFifthCategoryDrafts((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddStoreToBrand = (brandName: string) => {
|
const handleAddStoreToBrand = (brandName: string) => {
|
||||||
@ -752,6 +886,7 @@ export function AgentWorkflowSettingsClient({
|
|||||||
{
|
{
|
||||||
storeName: nextValue,
|
storeName: nextValue,
|
||||||
thirdCategories: [],
|
thirdCategories: [],
|
||||||
|
thirdCategoryEntries: [],
|
||||||
sortOrder: (entry.storeEntries || []).length,
|
sortOrder: (entry.storeEntries || []).length,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -780,13 +915,35 @@ export function AgentWorkflowSettingsClient({
|
|||||||
);
|
);
|
||||||
setThirdCategoryDrafts((prev) => {
|
setThirdCategoryDrafts((prev) => {
|
||||||
const next = { ...prev };
|
const next = { ...prev };
|
||||||
delete next[`${brandName}::${storeName}`];
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::${storeName}`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setFourthCategoryDrafts((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::${storeName}`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setFifthCategoryDrafts((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::${storeName}`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddThirdCategoryToStore = (brandName: string, storeName: string) => {
|
const handleAddThirdCategoryToStore = (brandName: string, storeName: string) => {
|
||||||
const draftKey = `${brandName}::${storeName}`;
|
const draftKey = createStoreDraftKey(brandName, storeName);
|
||||||
const nextValue = normalizeInputOptionValue(thirdCategoryDrafts[draftKey] || "");
|
const nextValue = normalizeInputOptionValue(thirdCategoryDrafts[draftKey] || "");
|
||||||
if (!nextValue) {
|
if (!nextValue) {
|
||||||
toast.error("请输入三级分类名称");
|
toast.error("请输入三级分类名称");
|
||||||
@ -798,6 +955,9 @@ export function AgentWorkflowSettingsClient({
|
|||||||
(storeEntry) => storeEntry.storeName === storeName
|
(storeEntry) => storeEntry.storeName === storeName
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
|
(targetStoreEntry?.thirdCategoryEntries || []).some(
|
||||||
|
(thirdEntry) => thirdEntry.categoryName.toLowerCase() === nextValue.toLowerCase()
|
||||||
|
) ||
|
||||||
targetStoreEntry?.thirdCategories.some(
|
targetStoreEntry?.thirdCategories.some(
|
||||||
(thirdCategory) => thirdCategory.toLowerCase() === nextValue.toLowerCase()
|
(thirdCategory) => thirdCategory.toLowerCase() === nextValue.toLowerCase()
|
||||||
)
|
)
|
||||||
@ -816,6 +976,18 @@ export function AgentWorkflowSettingsClient({
|
|||||||
? {
|
? {
|
||||||
...storeEntry,
|
...storeEntry,
|
||||||
thirdCategories: [...(storeEntry.thirdCategories || []), nextValue],
|
thirdCategories: [...(storeEntry.thirdCategories || []), nextValue],
|
||||||
|
thirdCategoryEntries: [
|
||||||
|
...((storeEntry.thirdCategoryEntries || []).map((thirdEntry, thirdIndex) => ({
|
||||||
|
...thirdEntry,
|
||||||
|
sortOrder: thirdIndex,
|
||||||
|
})) || []),
|
||||||
|
{
|
||||||
|
categoryName: nextValue,
|
||||||
|
fourthCategories: [],
|
||||||
|
fourthCategoryEntries: [],
|
||||||
|
sortOrder: (storeEntry.thirdCategoryEntries || []).length,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
: storeEntry
|
: storeEntry
|
||||||
),
|
),
|
||||||
@ -845,6 +1017,259 @@ export function AgentWorkflowSettingsClient({
|
|||||||
thirdCategories: (storeEntry.thirdCategories || []).filter(
|
thirdCategories: (storeEntry.thirdCategories || []).filter(
|
||||||
(item) => item !== thirdCategory
|
(item) => item !== thirdCategory
|
||||||
),
|
),
|
||||||
|
thirdCategoryEntries: (storeEntry.thirdCategoryEntries || []).filter(
|
||||||
|
(item) => item.categoryName !== thirdCategory
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: storeEntry
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setFourthCategoryDrafts((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::${storeName}::${thirdCategory}`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setFifthCategoryDrafts((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::${storeName}::${thirdCategory}`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddFourthCategoryToThirdCategory = (
|
||||||
|
brandName: string,
|
||||||
|
storeName: string,
|
||||||
|
thirdCategory: string
|
||||||
|
) => {
|
||||||
|
const draftKey = createThirdCategoryDraftKey(brandName, storeName, thirdCategory);
|
||||||
|
const nextValue = normalizeInputOptionValue(fourthCategoryDrafts[draftKey] || "");
|
||||||
|
if (!nextValue) {
|
||||||
|
toast.error("请输入四级分类名称");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetEntry = brandStoreEntries.find((entry) => entry.brandName === brandName);
|
||||||
|
const targetStoreEntry = targetEntry?.storeEntries?.find(
|
||||||
|
(storeEntry) => storeEntry.storeName === storeName
|
||||||
|
);
|
||||||
|
const targetThirdEntry = targetStoreEntry?.thirdCategoryEntries?.find(
|
||||||
|
(thirdEntry) => thirdEntry.categoryName === thirdCategory
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
(targetThirdEntry?.fourthCategoryEntries || []).some(
|
||||||
|
(fourthEntry) => fourthEntry.categoryName.toLowerCase() === nextValue.toLowerCase()
|
||||||
|
) ||
|
||||||
|
(targetThirdEntry?.fourthCategories || []).some(
|
||||||
|
(fourthCategory) => fourthCategory.toLowerCase() === nextValue.toLowerCase()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
toast.error("该四级分类已存在");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInputOptions(
|
||||||
|
brandStoreEntries.map((entry) => {
|
||||||
|
if (entry.brandName !== brandName) return entry;
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
storeEntries: (entry.storeEntries || []).map((storeEntry) =>
|
||||||
|
storeEntry.storeName === storeName
|
||||||
|
? {
|
||||||
|
...storeEntry,
|
||||||
|
thirdCategoryEntries: (storeEntry.thirdCategoryEntries || []).map((thirdEntry) =>
|
||||||
|
thirdEntry.categoryName === thirdCategory
|
||||||
|
? {
|
||||||
|
...thirdEntry,
|
||||||
|
fourthCategories: [...(thirdEntry.fourthCategories || []), nextValue],
|
||||||
|
fourthCategoryEntries: [
|
||||||
|
...((thirdEntry.fourthCategoryEntries || []).map((fourthEntry, fourthIndex) => ({
|
||||||
|
...fourthEntry,
|
||||||
|
sortOrder: fourthIndex,
|
||||||
|
})) || []),
|
||||||
|
{
|
||||||
|
categoryName: nextValue,
|
||||||
|
fifthCategories: [],
|
||||||
|
sortOrder: (thirdEntry.fourthCategoryEntries || []).length,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: thirdEntry
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: storeEntry
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setFourthCategoryDrafts((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[draftKey]: "",
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveFourthCategoryFromThirdCategory = (
|
||||||
|
brandName: string,
|
||||||
|
storeName: string,
|
||||||
|
thirdCategory: string,
|
||||||
|
fourthCategory: string
|
||||||
|
) => {
|
||||||
|
updateInputOptions(
|
||||||
|
brandStoreEntries.map((entry) => {
|
||||||
|
if (entry.brandName !== brandName) return entry;
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
storeEntries: (entry.storeEntries || []).map((storeEntry) =>
|
||||||
|
storeEntry.storeName === storeName
|
||||||
|
? {
|
||||||
|
...storeEntry,
|
||||||
|
thirdCategoryEntries: (storeEntry.thirdCategoryEntries || []).map((thirdEntry) =>
|
||||||
|
thirdEntry.categoryName === thirdCategory
|
||||||
|
? {
|
||||||
|
...thirdEntry,
|
||||||
|
fourthCategories: (thirdEntry.fourthCategories || []).filter(
|
||||||
|
(item) => item !== fourthCategory
|
||||||
|
),
|
||||||
|
fourthCategoryEntries: (thirdEntry.fourthCategoryEntries || []).filter(
|
||||||
|
(item) => item.categoryName !== fourthCategory
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: thirdEntry
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: storeEntry
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setFifthCategoryDrafts((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
Object.keys(next).forEach((key) => {
|
||||||
|
if (key.startsWith(`${brandName}::${storeName}::${thirdCategory}::${fourthCategory}`)) {
|
||||||
|
delete next[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddFifthCategoryToFourthCategory = (
|
||||||
|
brandName: string,
|
||||||
|
storeName: string,
|
||||||
|
thirdCategory: string,
|
||||||
|
fourthCategory: string
|
||||||
|
) => {
|
||||||
|
const draftKey = createFourthCategoryDraftKey(
|
||||||
|
brandName,
|
||||||
|
storeName,
|
||||||
|
thirdCategory,
|
||||||
|
fourthCategory
|
||||||
|
);
|
||||||
|
const nextValue = normalizeInputOptionValue(fifthCategoryDrafts[draftKey] || "");
|
||||||
|
if (!nextValue) {
|
||||||
|
toast.error("请输入五级分类名称");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetEntry = brandStoreEntries.find((entry) => entry.brandName === brandName);
|
||||||
|
const targetStoreEntry = targetEntry?.storeEntries?.find(
|
||||||
|
(storeEntry) => storeEntry.storeName === storeName
|
||||||
|
);
|
||||||
|
const targetThirdEntry = targetStoreEntry?.thirdCategoryEntries?.find(
|
||||||
|
(thirdEntry) => thirdEntry.categoryName === thirdCategory
|
||||||
|
);
|
||||||
|
const targetFourthEntry = targetThirdEntry?.fourthCategoryEntries?.find(
|
||||||
|
(fourthEntry) => fourthEntry.categoryName === fourthCategory
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
targetFourthEntry?.fifthCategories.some(
|
||||||
|
(fifthCategory) => fifthCategory.toLowerCase() === nextValue.toLowerCase()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
toast.error("该五级分类已存在");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInputOptions(
|
||||||
|
brandStoreEntries.map((entry) => {
|
||||||
|
if (entry.brandName !== brandName) return entry;
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
storeEntries: (entry.storeEntries || []).map((storeEntry) =>
|
||||||
|
storeEntry.storeName === storeName
|
||||||
|
? {
|
||||||
|
...storeEntry,
|
||||||
|
thirdCategoryEntries: (storeEntry.thirdCategoryEntries || []).map((thirdEntry) =>
|
||||||
|
thirdEntry.categoryName === thirdCategory
|
||||||
|
? {
|
||||||
|
...thirdEntry,
|
||||||
|
fourthCategoryEntries: (thirdEntry.fourthCategoryEntries || []).map(
|
||||||
|
(fourthEntry) =>
|
||||||
|
fourthEntry.categoryName === fourthCategory
|
||||||
|
? {
|
||||||
|
...fourthEntry,
|
||||||
|
fifthCategories: [...(fourthEntry.fifthCategories || []), nextValue],
|
||||||
|
}
|
||||||
|
: fourthEntry
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: thirdEntry
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: storeEntry
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setFifthCategoryDrafts((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[draftKey]: "",
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveFifthCategoryFromFourthCategory = (
|
||||||
|
brandName: string,
|
||||||
|
storeName: string,
|
||||||
|
thirdCategory: string,
|
||||||
|
fourthCategory: string,
|
||||||
|
fifthCategory: string
|
||||||
|
) => {
|
||||||
|
updateInputOptions(
|
||||||
|
brandStoreEntries.map((entry) => {
|
||||||
|
if (entry.brandName !== brandName) return entry;
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
storeEntries: (entry.storeEntries || []).map((storeEntry) =>
|
||||||
|
storeEntry.storeName === storeName
|
||||||
|
? {
|
||||||
|
...storeEntry,
|
||||||
|
thirdCategoryEntries: (storeEntry.thirdCategoryEntries || []).map((thirdEntry) =>
|
||||||
|
thirdEntry.categoryName === thirdCategory
|
||||||
|
? {
|
||||||
|
...thirdEntry,
|
||||||
|
fourthCategoryEntries: (thirdEntry.fourthCategoryEntries || []).map(
|
||||||
|
(fourthEntry) =>
|
||||||
|
fourthEntry.categoryName === fourthCategory
|
||||||
|
? {
|
||||||
|
...fourthEntry,
|
||||||
|
fifthCategories: (fourthEntry.fifthCategories || []).filter(
|
||||||
|
(item) => item !== fifthCategory
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: fourthEntry
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: thirdEntry
|
||||||
|
),
|
||||||
}
|
}
|
||||||
: storeEntry
|
: storeEntry
|
||||||
),
|
),
|
||||||
@ -2726,7 +3151,7 @@ export function AgentWorkflowSettingsClient({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<CardTitle className="text-base">基础下拉选项</CardTitle>
|
<CardTitle className="text-base">基础下拉选项</CardTitle>
|
||||||
<CardDescription>配置品牌、店铺、三级分类的层级关系,以及第一页可选的价格带区间</CardDescription>
|
<CardDescription>配置品牌、店铺到五级分类的层级关系,以及第一页可选的价格带区间</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@ -2841,7 +3266,7 @@ export function AgentWorkflowSettingsClient({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{(storeEntry.thirdCategories || []).length} 个三级分类
|
{(storeEntry.thirdCategoryEntries || []).length} 个三级分类
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -2883,32 +3308,214 @@ export function AgentWorkflowSettingsClient({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(storeEntry.thirdCategories || []).length > 0 ? (
|
{(storeEntry.thirdCategoryEntries || []).length > 0 ? (
|
||||||
<div className="mt-3 flex flex-wrap gap-2">
|
<div className="mt-3 space-y-3">
|
||||||
{(storeEntry.thirdCategories || []).map(
|
{(storeEntry.thirdCategoryEntries || []).map((thirdEntry) => {
|
||||||
(thirdCategory) => (
|
const fourthDraftKey = createThirdCategoryDraftKey(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName
|
||||||
|
);
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${entry.brandName}-${storeEntry.storeName}-${thirdCategory}`}
|
key={`${entry.brandName}-${storeEntry.storeName}-${thirdEntry.categoryName}`}
|
||||||
className="inline-flex items-center gap-2 rounded-full border bg-background px-3 py-1.5 text-sm"
|
className="rounded-xl border bg-background/80 p-3"
|
||||||
>
|
>
|
||||||
<span>{thirdCategory}</span>
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
<button
|
<div className="flex items-center gap-2">
|
||||||
type="button"
|
<div className="inline-flex items-center gap-2 rounded-full border bg-background px-3 py-1.5 text-sm font-medium">
|
||||||
onClick={() =>
|
<span>{thirdEntry.categoryName}</span>
|
||||||
handleRemoveThirdCategoryFromStore(
|
<button
|
||||||
entry.brandName,
|
type="button"
|
||||||
storeEntry.storeName,
|
onClick={() =>
|
||||||
thirdCategory
|
handleRemoveThirdCategoryFromStore(
|
||||||
)
|
entry.brandName,
|
||||||
}
|
storeEntry.storeName,
|
||||||
className="rounded-full p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-destructive"
|
thirdEntry.categoryName
|
||||||
aria-label={`删除三级分类 ${thirdCategory}`}
|
)
|
||||||
>
|
}
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
className="rounded-full p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-destructive"
|
||||||
</button>
|
aria-label={`删除三级分类 ${thirdEntry.categoryName}`}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{(thirdEntry.fourthCategoryEntries || []).length} 个四级分类
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 rounded-xl border bg-muted/20 p-3">
|
||||||
|
<div className="flex flex-col gap-2 sm:flex-row">
|
||||||
|
<Input
|
||||||
|
value={fourthCategoryDrafts[fourthDraftKey] || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFourthCategoryDrafts((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[fourthDraftKey]: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
handleAddFourthCategoryToThirdCategory(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder={`给三级分类“${thirdEntry.categoryName}”添加四级分类`}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
handleAddFourthCategoryToThirdCategory(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="shrink-0"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
添加四级分类
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(thirdEntry.fourthCategoryEntries || []).length > 0 ? (
|
||||||
|
<div className="mt-3 space-y-3">
|
||||||
|
{(thirdEntry.fourthCategoryEntries || []).map(
|
||||||
|
(fourthEntry) => {
|
||||||
|
const fifthDraftKey = createFourthCategoryDraftKey(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName,
|
||||||
|
fourthEntry.categoryName
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${entry.brandName}-${storeEntry.storeName}-${thirdEntry.categoryName}-${fourthEntry.categoryName}`}
|
||||||
|
className="rounded-xl border bg-background/80 p-3"
|
||||||
|
>
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="inline-flex items-center gap-2 rounded-full border bg-background px-3 py-1.5 text-sm font-medium">
|
||||||
|
<span>{fourthEntry.categoryName}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
handleRemoveFourthCategoryFromThirdCategory(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName,
|
||||||
|
fourthEntry.categoryName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="rounded-full p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-destructive"
|
||||||
|
aria-label={`删除四级分类 ${fourthEntry.categoryName}`}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{(fourthEntry.fifthCategories || []).length} 个五级分类
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 rounded-xl border bg-muted/20 p-3">
|
||||||
|
<div className="flex flex-col gap-2 sm:flex-row">
|
||||||
|
<Input
|
||||||
|
value={fifthCategoryDrafts[fifthDraftKey] || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFifthCategoryDrafts((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[fifthDraftKey]: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
handleAddFifthCategoryToFourthCategory(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName,
|
||||||
|
fourthEntry.categoryName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder={`给四级分类“${fourthEntry.categoryName}”添加五级分类`}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
handleAddFifthCategoryToFourthCategory(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName,
|
||||||
|
fourthEntry.categoryName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="shrink-0"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
添加五级分类
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(fourthEntry.fifthCategories || []).length > 0 ? (
|
||||||
|
<div className="mt-3 flex flex-wrap gap-2">
|
||||||
|
{(fourthEntry.fifthCategories || []).map(
|
||||||
|
(fifthCategory) => (
|
||||||
|
<div
|
||||||
|
key={`${entry.brandName}-${storeEntry.storeName}-${thirdEntry.categoryName}-${fourthEntry.categoryName}-${fifthCategory}`}
|
||||||
|
className="inline-flex items-center gap-2 rounded-full border bg-background px-3 py-1.5 text-sm"
|
||||||
|
>
|
||||||
|
<span>{fifthCategory}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
handleRemoveFifthCategoryFromFourthCategory(
|
||||||
|
entry.brandName,
|
||||||
|
storeEntry.storeName,
|
||||||
|
thirdEntry.categoryName,
|
||||||
|
fourthEntry.categoryName,
|
||||||
|
fifthCategory
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="rounded-full p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-destructive"
|
||||||
|
aria-label={`删除五级分类 ${fifthCategory}`}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-3 rounded-lg border border-dashed bg-background/70 px-3 py-4 text-sm text-muted-foreground">
|
||||||
|
这个四级分类下还没有添加任何五级分类。
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-3 rounded-lg border border-dashed bg-background/70 px-3 py-4 text-sm text-muted-foreground">
|
||||||
|
这个三级分类下还没有添加任何四级分类。
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
)}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mt-3 rounded-lg border border-dashed bg-background/70 px-3 py-4 text-sm text-muted-foreground">
|
<div className="mt-3 rounded-lg border border-dashed bg-background/70 px-3 py-4 text-sm text-muted-foreground">
|
||||||
@ -2931,7 +3538,7 @@ export function AgentWorkflowSettingsClient({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mt-3 rounded-lg border border-dashed bg-background/70 px-3 py-4 text-sm text-muted-foreground">
|
<div className="mt-3 rounded-lg border border-dashed bg-background/70 px-3 py-4 text-sm text-muted-foreground">
|
||||||
还没有添加品牌。先添加品牌,再在每个品牌下继续添加店铺和三级分类。
|
还没有添加品牌。先添加品牌,再在每个品牌下继续添加店铺以及三级到五级分类。
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -731,6 +731,8 @@ const updateSchema = z.object({
|
|||||||
brands: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
brands: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
||||||
stores: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
stores: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
||||||
thirdCategories: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
thirdCategories: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
||||||
|
fourthCategories: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
||||||
|
fifthCategories: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
||||||
priceBands: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
priceBands: z.array(z.string().trim().min(1).max(64)).max(300).optional(),
|
||||||
brandStoreEntries: z
|
brandStoreEntries: z
|
||||||
.array(
|
.array(
|
||||||
@ -745,6 +747,32 @@ const updateSchema = z.object({
|
|||||||
.array(z.string().trim().min(1).max(64))
|
.array(z.string().trim().min(1).max(64))
|
||||||
.max(300)
|
.max(300)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
thirdCategoryEntries: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
categoryName: z.string().trim().min(1).max(64),
|
||||||
|
fourthCategories: z
|
||||||
|
.array(z.string().trim().min(1).max(64))
|
||||||
|
.max(300)
|
||||||
|
.optional(),
|
||||||
|
fourthCategoryEntries: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
categoryName: z.string().trim().min(1).max(64),
|
||||||
|
fifthCategories: z
|
||||||
|
.array(z.string().trim().min(1).max(64))
|
||||||
|
.max(300)
|
||||||
|
.optional(),
|
||||||
|
sortOrder: z.number().min(0).max(999).optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.max(300)
|
||||||
|
.optional(),
|
||||||
|
sortOrder: z.number().min(0).max(999).optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.max(300)
|
||||||
|
.optional(),
|
||||||
sortOrder: z.number().min(0).max(999).optional(),
|
sortOrder: z.number().min(0).max(999).optional(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@ -153,6 +153,8 @@ function hasMeaningfulWorkflowState(
|
|||||||
String(inputs?.brand || "").trim() ||
|
String(inputs?.brand || "").trim() ||
|
||||||
String(inputs?.store || "").trim() ||
|
String(inputs?.store || "").trim() ||
|
||||||
String(inputs?.thirdCategory || "").trim() ||
|
String(inputs?.thirdCategory || "").trim() ||
|
||||||
|
String(inputs?.fourthCategory || "").trim() ||
|
||||||
|
String(inputs?.fifthCategory || "").trim() ||
|
||||||
String(inputs?.priceBand || "").trim()
|
String(inputs?.priceBand || "").trim()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -322,6 +324,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
brand: "",
|
brand: "",
|
||||||
store: "",
|
store: "",
|
||||||
thirdCategory: "",
|
thirdCategory: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
priceBand: "",
|
priceBand: "",
|
||||||
});
|
});
|
||||||
const [workflowMaterials, setWorkflowMaterials] = useState<AgentWorkflowMaterialSelections>(
|
const [workflowMaterials, setWorkflowMaterials] = useState<AgentWorkflowMaterialSelections>(
|
||||||
@ -489,6 +493,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
brand: workflowInputs.brand.trim(),
|
brand: workflowInputs.brand.trim(),
|
||||||
store: workflowInputs.store.trim(),
|
store: workflowInputs.store.trim(),
|
||||||
thirdCategory: workflowInputs.thirdCategory.trim(),
|
thirdCategory: workflowInputs.thirdCategory.trim(),
|
||||||
|
fourthCategory: workflowInputs.fourthCategory.trim(),
|
||||||
|
fifthCategory: workflowInputs.fifthCategory.trim(),
|
||||||
priceBand: workflowInputs.priceBand.trim(),
|
priceBand: workflowInputs.priceBand.trim(),
|
||||||
},
|
},
|
||||||
materials: workflowMaterials,
|
materials: workflowMaterials,
|
||||||
@ -535,6 +541,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
brand: "",
|
brand: "",
|
||||||
store: "",
|
store: "",
|
||||||
thirdCategory: "",
|
thirdCategory: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
priceBand: "",
|
priceBand: "",
|
||||||
});
|
});
|
||||||
setWorkflowMaterials(createEmptyAgentWorkflowMaterialSelections());
|
setWorkflowMaterials(createEmptyAgentWorkflowMaterialSelections());
|
||||||
@ -567,6 +575,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
brand: String(persistedWorkflow.inputs?.brand || "").trim(),
|
brand: String(persistedWorkflow.inputs?.brand || "").trim(),
|
||||||
store: String(persistedWorkflow.inputs?.store || "").trim(),
|
store: String(persistedWorkflow.inputs?.store || "").trim(),
|
||||||
thirdCategory: String(persistedWorkflow.inputs?.thirdCategory || "").trim(),
|
thirdCategory: String(persistedWorkflow.inputs?.thirdCategory || "").trim(),
|
||||||
|
fourthCategory: String(persistedWorkflow.inputs?.fourthCategory || "").trim(),
|
||||||
|
fifthCategory: String(persistedWorkflow.inputs?.fifthCategory || "").trim(),
|
||||||
priceBand: String(persistedWorkflow.inputs?.priceBand || "").trim(),
|
priceBand: String(persistedWorkflow.inputs?.priceBand || "").trim(),
|
||||||
});
|
});
|
||||||
setWorkflowMaterials(
|
setWorkflowMaterials(
|
||||||
@ -676,6 +686,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
brand: workflowInputs.brand.trim(),
|
brand: workflowInputs.brand.trim(),
|
||||||
store: workflowInputs.store.trim(),
|
store: workflowInputs.store.trim(),
|
||||||
thirdCategory: workflowInputs.thirdCategory.trim(),
|
thirdCategory: workflowInputs.thirdCategory.trim(),
|
||||||
|
fourthCategory: workflowInputs.fourthCategory.trim(),
|
||||||
|
fifthCategory: workflowInputs.fifthCategory.trim(),
|
||||||
priceBand: workflowInputs.priceBand.trim(),
|
priceBand: workflowInputs.priceBand.trim(),
|
||||||
},
|
},
|
||||||
materials: workflowMaterials,
|
materials: workflowMaterials,
|
||||||
@ -763,6 +775,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
brand: String(parsed.inputs?.brand || "").trim(),
|
brand: String(parsed.inputs?.brand || "").trim(),
|
||||||
store: String(parsed.inputs?.store || "").trim(),
|
store: String(parsed.inputs?.store || "").trim(),
|
||||||
thirdCategory: String(parsed.inputs?.thirdCategory || "").trim(),
|
thirdCategory: String(parsed.inputs?.thirdCategory || "").trim(),
|
||||||
|
fourthCategory: String(parsed.inputs?.fourthCategory || "").trim(),
|
||||||
|
fifthCategory: String(parsed.inputs?.fifthCategory || "").trim(),
|
||||||
priceBand: String(parsed.inputs?.priceBand || "").trim(),
|
priceBand: String(parsed.inputs?.priceBand || "").trim(),
|
||||||
});
|
});
|
||||||
setWorkflowMaterials(normalizeAgentWorkflowMaterialSelections(parsed.materials));
|
setWorkflowMaterials(normalizeAgentWorkflowMaterialSelections(parsed.materials));
|
||||||
@ -890,6 +904,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
|||||||
brand: String(persistedWorkflow.inputs?.brand || "").trim(),
|
brand: String(persistedWorkflow.inputs?.brand || "").trim(),
|
||||||
store: String(persistedWorkflow.inputs?.store || "").trim(),
|
store: String(persistedWorkflow.inputs?.store || "").trim(),
|
||||||
thirdCategory: String(persistedWorkflow.inputs?.thirdCategory || "").trim(),
|
thirdCategory: String(persistedWorkflow.inputs?.thirdCategory || "").trim(),
|
||||||
|
fourthCategory: String(persistedWorkflow.inputs?.fourthCategory || "").trim(),
|
||||||
|
fifthCategory: String(persistedWorkflow.inputs?.fifthCategory || "").trim(),
|
||||||
priceBand: String(persistedWorkflow.inputs?.priceBand || "").trim(),
|
priceBand: String(persistedWorkflow.inputs?.priceBand || "").trim(),
|
||||||
});
|
});
|
||||||
setWorkflowMaterials(normalizeAgentWorkflowMaterialSelections(persistedWorkflow.materials));
|
setWorkflowMaterials(normalizeAgentWorkflowMaterialSelections(persistedWorkflow.materials));
|
||||||
|
|||||||
@ -9,10 +9,10 @@ import {
|
|||||||
createEmptyAgentWorkflowMaterialSelections,
|
createEmptyAgentWorkflowMaterialSelections,
|
||||||
normalizeAgentWorkflowMaterialSelections,
|
normalizeAgentWorkflowMaterialSelections,
|
||||||
} from "@/lib/agents/workflow-material-config";
|
} from "@/lib/agents/workflow-material-config";
|
||||||
|
import { buildAgentWorkflowComposerContext } from "@/lib/agents/workflow-composer";
|
||||||
import type {
|
import type {
|
||||||
AgentWorkflowMaterialSelections,
|
AgentWorkflowMaterialSelections,
|
||||||
AgentWorkflowStepKey,
|
AgentWorkflowStepKey,
|
||||||
AgentWorkflowComposerContext,
|
|
||||||
} from "@/types/agent";
|
} from "@/types/agent";
|
||||||
|
|
||||||
interface AgentPageDarkProps {
|
interface AgentPageDarkProps {
|
||||||
@ -38,6 +38,8 @@ export function AgentPageDark({
|
|||||||
brand: "",
|
brand: "",
|
||||||
store: "",
|
store: "",
|
||||||
thirdCategory: "",
|
thirdCategory: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
priceBand: "",
|
priceBand: "",
|
||||||
});
|
});
|
||||||
const [workflowMaterials, setWorkflowMaterials] = useState<AgentWorkflowMaterialSelections>(
|
const [workflowMaterials, setWorkflowMaterials] = useState<AgentWorkflowMaterialSelections>(
|
||||||
@ -56,24 +58,12 @@ export function AgentPageDark({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Build workflow context
|
// Build workflow context
|
||||||
const workflowContext: AgentWorkflowComposerContext | null = {
|
const workflowContext = buildAgentWorkflowComposerContext({
|
||||||
|
config: workflowConfig,
|
||||||
inputs: workflowInputs,
|
inputs: workflowInputs,
|
||||||
materials: workflowMaterials,
|
materials: workflowMaterials,
|
||||||
selections: Object.entries(workflowSelections).map(([stepKey, optionKey]) => {
|
selections: workflowSelections,
|
||||||
const step = workflowConfig?.steps?.find((s: any) => s.key === stepKey);
|
});
|
||||||
const option = step?.options?.find((o: any) => o.key === optionKey);
|
|
||||||
return {
|
|
||||||
stepKey,
|
|
||||||
stepTitle: step?.title || "",
|
|
||||||
optionKey: optionKey || "",
|
|
||||||
optionName: option?.buttonName || "",
|
|
||||||
promptFragment: option?.promptFragment || "",
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
referenceImages: [],
|
|
||||||
summaryText: "",
|
|
||||||
promptText: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-black min-h-screen">
|
<div className="bg-black min-h-screen">
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import { cn } from "@/lib/utils";
|
|||||||
import {
|
import {
|
||||||
defaultAgentWorkflowUiConfig,
|
defaultAgentWorkflowUiConfig,
|
||||||
getAgentWorkflowBrandStoreEntries,
|
getAgentWorkflowBrandStoreEntries,
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth,
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory,
|
||||||
getAgentWorkflowPriceBandOptions,
|
getAgentWorkflowPriceBandOptions,
|
||||||
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
||||||
getAgentWorkflowStoreOptionsByBrand,
|
getAgentWorkflowStoreOptionsByBrand,
|
||||||
@ -33,6 +35,8 @@ export interface AgentWorkflowInputs {
|
|||||||
brand: string;
|
brand: string;
|
||||||
store: string;
|
store: string;
|
||||||
thirdCategory: string;
|
thirdCategory: string;
|
||||||
|
fourthCategory: string;
|
||||||
|
fifthCategory: string;
|
||||||
priceBand: string;
|
priceBand: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +113,27 @@ export function AgentWorkflowPanel({
|
|||||||
}),
|
}),
|
||||||
[config, inputs.brand, inputs.store]
|
[config, inputs.brand, inputs.store]
|
||||||
);
|
);
|
||||||
|
const fourthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory]
|
||||||
|
);
|
||||||
|
const fifthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
fourthCategory: inputs.fourthCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory, inputs.fourthCategory]
|
||||||
|
);
|
||||||
const priceBandOptions = useMemo(
|
const priceBandOptions = useMemo(
|
||||||
() => getAgentWorkflowPriceBandOptions(config),
|
() => getAgentWorkflowPriceBandOptions(config),
|
||||||
[config]
|
[config]
|
||||||
@ -198,6 +223,8 @@ export function AgentWorkflowPanel({
|
|||||||
brand: "",
|
brand: "",
|
||||||
store: "",
|
store: "",
|
||||||
thirdCategory: "",
|
thirdCategory: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
priceBand: "",
|
priceBand: "",
|
||||||
});
|
});
|
||||||
onMaterialsChange({
|
onMaterialsChange({
|
||||||
@ -217,7 +244,7 @@ export function AgentWorkflowPanel({
|
|||||||
<div className="space-y-5 p-4">
|
<div className="space-y-5 p-4">
|
||||||
<section className="space-y-3">
|
<section className="space-y-3">
|
||||||
<div className="text-sm font-medium">基础配置</div>
|
<div className="text-sm font-medium">基础配置</div>
|
||||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4">
|
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6">
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
value={inputs.brand}
|
value={inputs.brand}
|
||||||
options={brandOptions}
|
options={brandOptions}
|
||||||
@ -236,6 +263,8 @@ export function AgentWorkflowPanel({
|
|||||||
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -254,6 +283,8 @@ export function AgentWorkflowPanel({
|
|||||||
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -264,7 +295,44 @@ export function AgentWorkflowPanel({
|
|||||||
searchPlaceholder="输入三级分类筛选"
|
searchPlaceholder="输入三级分类筛选"
|
||||||
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
||||||
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
||||||
onValueChange={(value) => onInputsChange({ ...inputs, thirdCategory: value })}
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
thirdCategory: value,
|
||||||
|
fourthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.thirdCategory.trim().toLowerCase()
|
||||||
|
? inputs.fourthCategory
|
||||||
|
: "",
|
||||||
|
fifthCategory: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fourthCategory}
|
||||||
|
options={fourthCategoryOptions}
|
||||||
|
placeholder="选择四级分类"
|
||||||
|
searchPlaceholder="输入四级分类筛选"
|
||||||
|
emptyText={inputs.thirdCategory ? "当前三级分类下暂无四级分类" : "请先选择三级分类"}
|
||||||
|
disabled={!inputs.thirdCategory || fourthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
fourthCategory: value,
|
||||||
|
fifthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.fourthCategory.trim().toLowerCase()
|
||||||
|
? inputs.fifthCategory
|
||||||
|
: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fifthCategory}
|
||||||
|
options={fifthCategoryOptions}
|
||||||
|
placeholder="选择五级分类"
|
||||||
|
searchPlaceholder="输入五级分类筛选"
|
||||||
|
emptyText={inputs.fourthCategory ? "当前四级分类下暂无五级分类" : "请先选择四级分类"}
|
||||||
|
disabled={!inputs.fourthCategory || fifthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) => onInputsChange({ ...inputs, fifthCategory: value })}
|
||||||
/>
|
/>
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
value={inputs.priceBand}
|
value={inputs.priceBand}
|
||||||
@ -357,6 +425,8 @@ export function AgentWorkflowPanel({
|
|||||||
inputs.brand ||
|
inputs.brand ||
|
||||||
inputs.store ||
|
inputs.store ||
|
||||||
inputs.thirdCategory ||
|
inputs.thirdCategory ||
|
||||||
|
inputs.fourthCategory ||
|
||||||
|
inputs.fifthCategory ||
|
||||||
inputs.priceBand ||
|
inputs.priceBand ||
|
||||||
materials.primary.selectedImageIds.length > 0 ||
|
materials.primary.selectedImageIds.length > 0 ||
|
||||||
materials.secondary.selectedImageIds.length > 0 ? (
|
materials.secondary.selectedImageIds.length > 0 ? (
|
||||||
@ -376,6 +446,12 @@ export function AgentWorkflowPanel({
|
|||||||
{inputs.thirdCategory ? (
|
{inputs.thirdCategory ? (
|
||||||
<span className="rounded-full bg-background px-2 py-1">三级分类:{inputs.thirdCategory}</span>
|
<span className="rounded-full bg-background px-2 py-1">三级分类:{inputs.thirdCategory}</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
{inputs.fourthCategory ? (
|
||||||
|
<span className="rounded-full bg-background px-2 py-1">四级分类:{inputs.fourthCategory}</span>
|
||||||
|
) : null}
|
||||||
|
{inputs.fifthCategory ? (
|
||||||
|
<span className="rounded-full bg-background px-2 py-1">五级分类:{inputs.fifthCategory}</span>
|
||||||
|
) : null}
|
||||||
{inputs.priceBand ? (
|
{inputs.priceBand ? (
|
||||||
<span className="rounded-full bg-background px-2 py-1">价格带:{inputs.priceBand}</span>
|
<span className="rounded-full bg-background px-2 py-1">价格带:{inputs.priceBand}</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import { cn } from "@/lib/utils";
|
|||||||
import {
|
import {
|
||||||
defaultAgentWorkflowUiConfig,
|
defaultAgentWorkflowUiConfig,
|
||||||
getAgentWorkflowBrandStoreEntries,
|
getAgentWorkflowBrandStoreEntries,
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth,
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory,
|
||||||
getAgentWorkflowPriceBandOptions,
|
getAgentWorkflowPriceBandOptions,
|
||||||
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
||||||
getAgentWorkflowStoreOptionsByBrand,
|
getAgentWorkflowStoreOptionsByBrand,
|
||||||
@ -34,6 +36,8 @@ export interface AgentWorkflowInputs {
|
|||||||
brand: string;
|
brand: string;
|
||||||
store: string;
|
store: string;
|
||||||
thirdCategory: string;
|
thirdCategory: string;
|
||||||
|
fourthCategory: string;
|
||||||
|
fifthCategory: string;
|
||||||
priceBand: string;
|
priceBand: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +195,27 @@ export function AgentWorkflowPanelDark({
|
|||||||
}),
|
}),
|
||||||
[config, inputs.brand, inputs.store]
|
[config, inputs.brand, inputs.store]
|
||||||
);
|
);
|
||||||
|
const fourthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory]
|
||||||
|
);
|
||||||
|
const fifthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
fourthCategory: inputs.fourthCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory, inputs.fourthCategory]
|
||||||
|
);
|
||||||
const priceBandOptions = useMemo(
|
const priceBandOptions = useMemo(
|
||||||
() => getAgentWorkflowPriceBandOptions(config),
|
() => getAgentWorkflowPriceBandOptions(config),
|
||||||
[config]
|
[config]
|
||||||
@ -284,7 +309,7 @@ export function AgentWorkflowPanelDark({
|
|||||||
onReset();
|
onReset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onInputsChange({ primarySku: "", secondarySku: "", brand: "", store: "", thirdCategory: "", priceBand: "" });
|
onInputsChange({ primarySku: "", secondarySku: "", brand: "", store: "", thirdCategory: "", fourthCategory: "", fifthCategory: "", priceBand: "" });
|
||||||
onMaterialsChange({
|
onMaterialsChange({
|
||||||
primary: createEmptyAgentWorkflowMaterialSelection(),
|
primary: createEmptyAgentWorkflowMaterialSelection(),
|
||||||
secondary: createEmptyAgentWorkflowMaterialSelection(),
|
secondary: createEmptyAgentWorkflowMaterialSelection(),
|
||||||
@ -318,7 +343,7 @@ export function AgentWorkflowPanelDark({
|
|||||||
基础配置
|
基础配置
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4"
|
className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.1 }}
|
transition={{ delay: 0.1 }}
|
||||||
@ -341,6 +366,8 @@ export function AgentWorkflowPanelDark({
|
|||||||
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -359,6 +386,8 @@ export function AgentWorkflowPanelDark({
|
|||||||
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -369,7 +398,44 @@ export function AgentWorkflowPanelDark({
|
|||||||
searchPlaceholder="输入三级分类筛选"
|
searchPlaceholder="输入三级分类筛选"
|
||||||
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
||||||
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
||||||
onValueChange={(value) => onInputsChange({ ...inputs, thirdCategory: value })}
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
thirdCategory: value,
|
||||||
|
fourthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.thirdCategory.trim().toLowerCase()
|
||||||
|
? inputs.fourthCategory
|
||||||
|
: "",
|
||||||
|
fifthCategory: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fourthCategory}
|
||||||
|
options={fourthCategoryOptions}
|
||||||
|
placeholder="选择四级分类"
|
||||||
|
searchPlaceholder="输入四级分类筛选"
|
||||||
|
emptyText={inputs.thirdCategory ? "当前三级分类下暂无四级分类" : "请先选择三级分类"}
|
||||||
|
disabled={!inputs.thirdCategory || fourthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
fourthCategory: value,
|
||||||
|
fifthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.fourthCategory.trim().toLowerCase()
|
||||||
|
? inputs.fifthCategory
|
||||||
|
: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fifthCategory}
|
||||||
|
options={fifthCategoryOptions}
|
||||||
|
placeholder="选择五级分类"
|
||||||
|
searchPlaceholder="输入五级分类筛选"
|
||||||
|
emptyText={inputs.fourthCategory ? "当前四级分类下暂无五级分类" : "请先选择四级分类"}
|
||||||
|
disabled={!inputs.fourthCategory || fifthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) => onInputsChange({ ...inputs, fifthCategory: value })}
|
||||||
/>
|
/>
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
value={inputs.priceBand}
|
value={inputs.priceBand}
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import {
|
|||||||
flattenVisibleAgentWorkflowSteps,
|
flattenVisibleAgentWorkflowSteps,
|
||||||
getAgentWorkflowBlockedOptionKeysForSelections,
|
getAgentWorkflowBlockedOptionKeysForSelections,
|
||||||
getAgentWorkflowBrandStoreEntries,
|
getAgentWorkflowBrandStoreEntries,
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth,
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory,
|
||||||
getAgentWorkflowPriceBandOptions,
|
getAgentWorkflowPriceBandOptions,
|
||||||
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
||||||
getAgentWorkflowSelectedOptionPath,
|
getAgentWorkflowSelectedOptionPath,
|
||||||
@ -43,6 +45,8 @@ export interface AgentWorkflowInputs {
|
|||||||
brand: string;
|
brand: string;
|
||||||
store: string;
|
store: string;
|
||||||
thirdCategory: string;
|
thirdCategory: string;
|
||||||
|
fourthCategory: string;
|
||||||
|
fifthCategory: string;
|
||||||
priceBand: string;
|
priceBand: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +278,7 @@ export function AgentWorkflowBrandStorePanel({
|
|||||||
onInputsChange,
|
onInputsChange,
|
||||||
theme = "dark",
|
theme = "dark",
|
||||||
title = "品牌层级选择",
|
title = "品牌层级选择",
|
||||||
description = "先选择品牌、店铺、三级分类和价格带,后续品牌模特与风格步骤会按这里的选择联动。",
|
description = "先选择品牌、店铺到五级分类和价格带,后续品牌模特与风格步骤会按这里的选择联动。",
|
||||||
}: AgentWorkflowBrandStorePanelProps) {
|
}: AgentWorkflowBrandStorePanelProps) {
|
||||||
const brandOptions = useMemo(
|
const brandOptions = useMemo(
|
||||||
() => config?.inputOptions?.brands || defaultAgentWorkflowUiConfig.inputOptions?.brands || [],
|
() => config?.inputOptions?.brands || defaultAgentWorkflowUiConfig.inputOptions?.brands || [],
|
||||||
@ -297,6 +301,27 @@ export function AgentWorkflowBrandStorePanel({
|
|||||||
}),
|
}),
|
||||||
[config, inputs.brand, inputs.store]
|
[config, inputs.brand, inputs.store]
|
||||||
);
|
);
|
||||||
|
const fourthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory]
|
||||||
|
);
|
||||||
|
const fifthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
fourthCategory: inputs.fourthCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory, inputs.fourthCategory]
|
||||||
|
);
|
||||||
const priceBandOptions = useMemo(
|
const priceBandOptions = useMemo(
|
||||||
() => getAgentWorkflowPriceBandOptions(config),
|
() => getAgentWorkflowPriceBandOptions(config),
|
||||||
[config]
|
[config]
|
||||||
@ -318,7 +343,7 @@ export function AgentWorkflowBrandStorePanel({
|
|||||||
{description}
|
{description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid max-w-6xl grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
|
<div className="grid max-w-7xl grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6">
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
value={inputs.brand}
|
value={inputs.brand}
|
||||||
options={brandOptions}
|
options={brandOptions}
|
||||||
@ -338,6 +363,8 @@ export function AgentWorkflowBrandStorePanel({
|
|||||||
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -357,6 +384,8 @@ export function AgentWorkflowBrandStorePanel({
|
|||||||
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -368,7 +397,46 @@ export function AgentWorkflowBrandStorePanel({
|
|||||||
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
||||||
onValueChange={(value) => onInputsChange({ ...inputs, thirdCategory: value })}
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
thirdCategory: value,
|
||||||
|
fourthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.thirdCategory.trim().toLowerCase()
|
||||||
|
? inputs.fourthCategory
|
||||||
|
: "",
|
||||||
|
fifthCategory: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fourthCategory}
|
||||||
|
options={fourthCategoryOptions}
|
||||||
|
placeholder="选择四级分类"
|
||||||
|
searchPlaceholder="输入四级分类筛选"
|
||||||
|
emptyText={inputs.thirdCategory ? "当前三级分类下暂无四级分类" : "请先选择三级分类"}
|
||||||
|
theme={theme}
|
||||||
|
disabled={!inputs.thirdCategory || fourthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
fourthCategory: value,
|
||||||
|
fifthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.fourthCategory.trim().toLowerCase()
|
||||||
|
? inputs.fifthCategory
|
||||||
|
: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fifthCategory}
|
||||||
|
options={fifthCategoryOptions}
|
||||||
|
placeholder="选择五级分类"
|
||||||
|
searchPlaceholder="输入五级分类筛选"
|
||||||
|
emptyText={inputs.fourthCategory ? "当前四级分类下暂无五级分类" : "请先选择四级分类"}
|
||||||
|
theme={theme}
|
||||||
|
disabled={!inputs.fourthCategory || fifthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) => onInputsChange({ ...inputs, fifthCategory: value })}
|
||||||
/>
|
/>
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
value={inputs.priceBand}
|
value={inputs.priceBand}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import { cn } from "@/lib/utils";
|
|||||||
import {
|
import {
|
||||||
defaultAgentWorkflowUiConfig,
|
defaultAgentWorkflowUiConfig,
|
||||||
getAgentWorkflowBrandStoreEntries,
|
getAgentWorkflowBrandStoreEntries,
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth,
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory,
|
||||||
getAgentWorkflowPriceBandOptions,
|
getAgentWorkflowPriceBandOptions,
|
||||||
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
getAgentWorkflowThirdCategoryOptionsByBrandStore,
|
||||||
getAgentWorkflowStoreOptionsByBrand,
|
getAgentWorkflowStoreOptionsByBrand,
|
||||||
@ -34,6 +36,8 @@ export interface AgentWorkflowInputs {
|
|||||||
brand: string;
|
brand: string;
|
||||||
store: string;
|
store: string;
|
||||||
thirdCategory: string;
|
thirdCategory: string;
|
||||||
|
fourthCategory: string;
|
||||||
|
fifthCategory: string;
|
||||||
priceBand: string;
|
priceBand: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +207,27 @@ export function AgentWorkflowPanelNew({
|
|||||||
}),
|
}),
|
||||||
[config, inputs.brand, inputs.store]
|
[config, inputs.brand, inputs.store]
|
||||||
);
|
);
|
||||||
|
const fourthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory]
|
||||||
|
);
|
||||||
|
const fifthCategoryOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth({
|
||||||
|
config,
|
||||||
|
brand: inputs.brand,
|
||||||
|
store: inputs.store,
|
||||||
|
thirdCategory: inputs.thirdCategory,
|
||||||
|
fourthCategory: inputs.fourthCategory,
|
||||||
|
}),
|
||||||
|
[config, inputs.brand, inputs.store, inputs.thirdCategory, inputs.fourthCategory]
|
||||||
|
);
|
||||||
const priceBandOptions = useMemo(
|
const priceBandOptions = useMemo(
|
||||||
() => getAgentWorkflowPriceBandOptions(config),
|
() => getAgentWorkflowPriceBandOptions(config),
|
||||||
[config]
|
[config]
|
||||||
@ -305,7 +330,7 @@ export function AgentWorkflowPanelNew({
|
|||||||
onReset();
|
onReset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onInputsChange({ primarySku: "", secondarySku: "", brand: "", store: "", thirdCategory: "", priceBand: "" });
|
onInputsChange({ primarySku: "", secondarySku: "", brand: "", store: "", thirdCategory: "", fourthCategory: "", fifthCategory: "", priceBand: "" });
|
||||||
onMaterialsChange({
|
onMaterialsChange({
|
||||||
primary: createEmptyAgentWorkflowMaterialSelection(),
|
primary: createEmptyAgentWorkflowMaterialSelection(),
|
||||||
secondary: createEmptyAgentWorkflowMaterialSelection(),
|
secondary: createEmptyAgentWorkflowMaterialSelection(),
|
||||||
@ -357,7 +382,7 @@ export function AgentWorkflowPanelNew({
|
|||||||
</span>
|
</span>
|
||||||
基础配置
|
基础配置
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4">
|
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6">
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
value={inputs.brand}
|
value={inputs.brand}
|
||||||
options={brandOptions}
|
options={brandOptions}
|
||||||
@ -376,6 +401,8 @@ export function AgentWorkflowPanelNew({
|
|||||||
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -394,6 +421,8 @@ export function AgentWorkflowPanelNew({
|
|||||||
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
value.trim().toLowerCase() === inputs.store.trim().toLowerCase()
|
||||||
? inputs.thirdCategory
|
? inputs.thirdCategory
|
||||||
: "",
|
: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -404,7 +433,44 @@ export function AgentWorkflowPanelNew({
|
|||||||
searchPlaceholder="输入三级分类筛选"
|
searchPlaceholder="输入三级分类筛选"
|
||||||
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
emptyText={inputs.store ? "当前店铺下暂无三级分类" : "请先选择店铺"}
|
||||||
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
disabled={!inputs.store || thirdCategoryOptions.length === 0}
|
||||||
onValueChange={(value) => onInputsChange({ ...inputs, thirdCategory: value })}
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
thirdCategory: value,
|
||||||
|
fourthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.thirdCategory.trim().toLowerCase()
|
||||||
|
? inputs.fourthCategory
|
||||||
|
: "",
|
||||||
|
fifthCategory: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fourthCategory}
|
||||||
|
options={fourthCategoryOptions}
|
||||||
|
placeholder="选择四级分类"
|
||||||
|
searchPlaceholder="输入四级分类筛选"
|
||||||
|
emptyText={inputs.thirdCategory ? "当前三级分类下暂无四级分类" : "请先选择三级分类"}
|
||||||
|
disabled={!inputs.thirdCategory || fourthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
onInputsChange({
|
||||||
|
...inputs,
|
||||||
|
fourthCategory: value,
|
||||||
|
fifthCategory:
|
||||||
|
value.trim().toLowerCase() === inputs.fourthCategory.trim().toLowerCase()
|
||||||
|
? inputs.fifthCategory
|
||||||
|
: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SearchableSelect
|
||||||
|
value={inputs.fifthCategory}
|
||||||
|
options={fifthCategoryOptions}
|
||||||
|
placeholder="选择五级分类"
|
||||||
|
searchPlaceholder="输入五级分类筛选"
|
||||||
|
emptyText={inputs.fourthCategory ? "当前四级分类下暂无五级分类" : "请先选择四级分类"}
|
||||||
|
disabled={!inputs.fourthCategory || fifthCategoryOptions.length === 0}
|
||||||
|
onValueChange={(value) => onInputsChange({ ...inputs, fifthCategory: value })}
|
||||||
/>
|
/>
|
||||||
<SearchableSelect
|
<SearchableSelect
|
||||||
value={inputs.priceBand}
|
value={inputs.priceBand}
|
||||||
|
|||||||
@ -35,6 +35,8 @@ export function createEmptyAgentWorkflowPersistedState(): AgentWorkflowPersisted
|
|||||||
brand: "",
|
brand: "",
|
||||||
store: "",
|
store: "",
|
||||||
thirdCategory: "",
|
thirdCategory: "",
|
||||||
|
fourthCategory: "",
|
||||||
|
fifthCategory: "",
|
||||||
priceBand: "",
|
priceBand: "",
|
||||||
},
|
},
|
||||||
materials: createEmptyAgentWorkflowMaterialSelections(),
|
materials: createEmptyAgentWorkflowMaterialSelections(),
|
||||||
@ -231,6 +233,8 @@ function normalizeWorkflowState(value: unknown): AgentWorkflowPersistedState {
|
|||||||
brand: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).brand || "" : "").trim(),
|
brand: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).brand || "" : "").trim(),
|
||||||
store: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).store || "" : "").trim(),
|
store: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).store || "" : "").trim(),
|
||||||
thirdCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).thirdCategory || "" : "").trim(),
|
thirdCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).thirdCategory || "" : "").trim(),
|
||||||
|
fourthCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).fourthCategory || "" : "").trim(),
|
||||||
|
fifthCategory: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).fifthCategory || "" : "").trim(),
|
||||||
priceBand: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).priceBand || "" : "").trim(),
|
priceBand: String(raw.inputs && typeof raw.inputs === "object" ? (raw.inputs as Record<string, unknown>).priceBand || "" : "").trim(),
|
||||||
},
|
},
|
||||||
materials: normalizeAgentWorkflowMaterialSelections(raw.materials),
|
materials: normalizeAgentWorkflowMaterialSelections(raw.materials),
|
||||||
|
|||||||
@ -21,6 +21,8 @@ export interface AgentWorkflowInputsValue {
|
|||||||
brand: string;
|
brand: string;
|
||||||
store: string;
|
store: string;
|
||||||
thirdCategory: string;
|
thirdCategory: string;
|
||||||
|
fourthCategory: string;
|
||||||
|
fifthCategory: string;
|
||||||
priceBand: string;
|
priceBand: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +218,12 @@ export function buildAgentWorkflowComposerContext(params: {
|
|||||||
if (params.inputs.thirdCategory.trim()) {
|
if (params.inputs.thirdCategory.trim()) {
|
||||||
summaryParts.push(`三级分类:${params.inputs.thirdCategory.trim()}`);
|
summaryParts.push(`三级分类:${params.inputs.thirdCategory.trim()}`);
|
||||||
}
|
}
|
||||||
|
if (params.inputs.fourthCategory.trim()) {
|
||||||
|
summaryParts.push(`四级分类:${params.inputs.fourthCategory.trim()}`);
|
||||||
|
}
|
||||||
|
if (params.inputs.fifthCategory.trim()) {
|
||||||
|
summaryParts.push(`五级分类:${params.inputs.fifthCategory.trim()}`);
|
||||||
|
}
|
||||||
if (params.inputs.priceBand.trim()) {
|
if (params.inputs.priceBand.trim()) {
|
||||||
summaryParts.push(`价格带:${params.inputs.priceBand.trim()}`);
|
summaryParts.push(`价格带:${params.inputs.priceBand.trim()}`);
|
||||||
}
|
}
|
||||||
@ -276,6 +284,12 @@ export function buildAgentWorkflowComposerContext(params: {
|
|||||||
if (params.inputs.thirdCategory.trim()) {
|
if (params.inputs.thirdCategory.trim()) {
|
||||||
promptLines.push(`三级分类:${params.inputs.thirdCategory.trim()}`);
|
promptLines.push(`三级分类:${params.inputs.thirdCategory.trim()}`);
|
||||||
}
|
}
|
||||||
|
if (params.inputs.fourthCategory.trim()) {
|
||||||
|
promptLines.push(`四级分类:${params.inputs.fourthCategory.trim()}`);
|
||||||
|
}
|
||||||
|
if (params.inputs.fifthCategory.trim()) {
|
||||||
|
promptLines.push(`五级分类:${params.inputs.fifthCategory.trim()}`);
|
||||||
|
}
|
||||||
if (params.inputs.priceBand.trim()) {
|
if (params.inputs.priceBand.trim()) {
|
||||||
promptLines.push(`价格带:${params.inputs.priceBand.trim()}`);
|
promptLines.push(`价格带:${params.inputs.priceBand.trim()}`);
|
||||||
}
|
}
|
||||||
@ -362,6 +376,8 @@ export function buildAgentWorkflowComposerContext(params: {
|
|||||||
brand: params.inputs.brand.trim() || undefined,
|
brand: params.inputs.brand.trim() || undefined,
|
||||||
store: params.inputs.store.trim() || undefined,
|
store: params.inputs.store.trim() || undefined,
|
||||||
thirdCategory: params.inputs.thirdCategory.trim() || undefined,
|
thirdCategory: params.inputs.thirdCategory.trim() || undefined,
|
||||||
|
fourthCategory: params.inputs.fourthCategory.trim() || undefined,
|
||||||
|
fifthCategory: params.inputs.fifthCategory.trim() || undefined,
|
||||||
priceBand: params.inputs.priceBand.trim() || undefined,
|
priceBand: params.inputs.priceBand.trim() || undefined,
|
||||||
selections,
|
selections,
|
||||||
referenceImages,
|
referenceImages,
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import type {
|
import type {
|
||||||
AgentWorkflowBrandStoreEntry,
|
AgentWorkflowBrandStoreEntry,
|
||||||
|
AgentWorkflowFourthCategoryEntry,
|
||||||
AgentWorkflowOptionUiConfig,
|
AgentWorkflowOptionUiConfig,
|
||||||
AgentWorkflowStoreEntry,
|
AgentWorkflowStoreEntry,
|
||||||
|
AgentWorkflowThirdCategoryEntry,
|
||||||
AgentWorkflowStepKey,
|
AgentWorkflowStepKey,
|
||||||
AgentWorkflowStepUiConfig,
|
AgentWorkflowStepUiConfig,
|
||||||
AgentWorkflowUiConfig,
|
AgentWorkflowUiConfig,
|
||||||
@ -141,6 +143,8 @@ export const defaultAgentWorkflowUiConfig: AgentWorkflowUiConfig = {
|
|||||||
brands: [],
|
brands: [],
|
||||||
stores: [],
|
stores: [],
|
||||||
thirdCategories: [],
|
thirdCategories: [],
|
||||||
|
fourthCategories: [],
|
||||||
|
fifthCategories: [],
|
||||||
priceBands: [],
|
priceBands: [],
|
||||||
brandStoreEntries: [],
|
brandStoreEntries: [],
|
||||||
},
|
},
|
||||||
@ -434,6 +438,143 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
|||||||
const normalizeBrandStoreEntries = (input: unknown) => {
|
const normalizeBrandStoreEntries = (input: unknown) => {
|
||||||
if (!Array.isArray(input)) return [] as AgentWorkflowBrandStoreEntry[];
|
if (!Array.isArray(input)) return [] as AgentWorkflowBrandStoreEntry[];
|
||||||
|
|
||||||
|
const normalizeFourthCategoryEntries = (
|
||||||
|
rawFourthCategoryEntries: unknown,
|
||||||
|
legacyFourthCategories: string[]
|
||||||
|
): AgentWorkflowFourthCategoryEntry[] => {
|
||||||
|
const mappedEntries: Array<AgentWorkflowFourthCategoryEntry | null> = Array.isArray(
|
||||||
|
rawFourthCategoryEntries
|
||||||
|
)
|
||||||
|
? rawFourthCategoryEntries.map((fourthItem, fourthIndex) => {
|
||||||
|
if (typeof fourthItem === "string") {
|
||||||
|
const categoryName = normalizeText(fourthItem, 64);
|
||||||
|
if (!categoryName) return null;
|
||||||
|
return {
|
||||||
|
categoryName,
|
||||||
|
fifthCategories: [],
|
||||||
|
sortOrder: fourthIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const fourthRecord =
|
||||||
|
fourthItem && typeof fourthItem === "object" && !Array.isArray(fourthItem)
|
||||||
|
? (fourthItem as Record<string, unknown>)
|
||||||
|
: {};
|
||||||
|
const categoryName = normalizeText(
|
||||||
|
fourthRecord.categoryName ?? fourthRecord.fourthCategoryName,
|
||||||
|
64
|
||||||
|
);
|
||||||
|
if (!categoryName) return null;
|
||||||
|
return {
|
||||||
|
categoryName,
|
||||||
|
fifthCategories: normalizeStringList(fourthRecord.fifthCategories).slice(0, 300),
|
||||||
|
sortOrder: normalizeNumber(fourthRecord.sortOrder, fourthIndex),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const normalizedFromEntries = mappedEntries
|
||||||
|
.filter((item): item is AgentWorkflowFourthCategoryEntry => item !== null)
|
||||||
|
.sort((left, right) => (left.sortOrder || 0) - (right.sortOrder || 0))
|
||||||
|
.map((entry, index) => ({
|
||||||
|
...entry,
|
||||||
|
fifthCategories: Array.from(
|
||||||
|
new Set((entry.fifthCategories || []).map((item) => item.trim()).filter(Boolean))
|
||||||
|
).slice(0, 300),
|
||||||
|
sortOrder: index,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (normalizedFromEntries.length > 0) {
|
||||||
|
return normalizedFromEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
return legacyFourthCategories.map((categoryName, index) => ({
|
||||||
|
categoryName,
|
||||||
|
fifthCategories: [],
|
||||||
|
sortOrder: index,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeThirdCategoryEntries = (
|
||||||
|
rawThirdCategoryEntries: unknown,
|
||||||
|
legacyThirdCategories: string[]
|
||||||
|
): AgentWorkflowThirdCategoryEntry[] => {
|
||||||
|
const mappedEntries: Array<AgentWorkflowThirdCategoryEntry | null> = Array.isArray(
|
||||||
|
rawThirdCategoryEntries
|
||||||
|
)
|
||||||
|
? rawThirdCategoryEntries.map((thirdItem, thirdIndex) => {
|
||||||
|
if (typeof thirdItem === "string") {
|
||||||
|
const categoryName = normalizeText(thirdItem, 64);
|
||||||
|
if (!categoryName) return null;
|
||||||
|
return {
|
||||||
|
categoryName,
|
||||||
|
fourthCategories: [],
|
||||||
|
fourthCategoryEntries: [],
|
||||||
|
sortOrder: thirdIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const thirdRecord =
|
||||||
|
thirdItem && typeof thirdItem === "object" && !Array.isArray(thirdItem)
|
||||||
|
? (thirdItem as Record<string, unknown>)
|
||||||
|
: {};
|
||||||
|
const categoryName = normalizeText(
|
||||||
|
thirdRecord.categoryName ?? thirdRecord.thirdCategoryName,
|
||||||
|
64
|
||||||
|
);
|
||||||
|
if (!categoryName) return null;
|
||||||
|
|
||||||
|
const legacyFourthCategories = normalizeStringList(thirdRecord.fourthCategories).slice(
|
||||||
|
0,
|
||||||
|
300
|
||||||
|
);
|
||||||
|
const fourthCategoryEntries = normalizeFourthCategoryEntries(
|
||||||
|
thirdRecord.fourthCategoryEntries,
|
||||||
|
legacyFourthCategories
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
categoryName,
|
||||||
|
fourthCategories:
|
||||||
|
fourthCategoryEntries.length > 0
|
||||||
|
? fourthCategoryEntries.map((entry) => entry.categoryName)
|
||||||
|
: legacyFourthCategories,
|
||||||
|
fourthCategoryEntries,
|
||||||
|
sortOrder: normalizeNumber(thirdRecord.sortOrder, thirdIndex),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const normalizedFromEntries = mappedEntries
|
||||||
|
.filter((item): item is AgentWorkflowThirdCategoryEntry => item !== null)
|
||||||
|
.sort((left, right) => (left.sortOrder || 0) - (right.sortOrder || 0))
|
||||||
|
.map((entry, index) => ({
|
||||||
|
...entry,
|
||||||
|
fourthCategories: Array.from(
|
||||||
|
new Set((entry.fourthCategories || []).map((item) => item.trim()).filter(Boolean))
|
||||||
|
).slice(0, 300),
|
||||||
|
fourthCategoryEntries: (entry.fourthCategoryEntries || []).map((fourthEntry, fourthIndex) => ({
|
||||||
|
...fourthEntry,
|
||||||
|
fifthCategories: Array.from(
|
||||||
|
new Set((fourthEntry.fifthCategories || []).map((item) => item.trim()).filter(Boolean))
|
||||||
|
).slice(0, 300),
|
||||||
|
sortOrder: fourthIndex,
|
||||||
|
})),
|
||||||
|
sortOrder: index,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (normalizedFromEntries.length > 0) {
|
||||||
|
return normalizedFromEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
return legacyThirdCategories.map((categoryName, index) => ({
|
||||||
|
categoryName,
|
||||||
|
fourthCategories: [],
|
||||||
|
fourthCategoryEntries: [],
|
||||||
|
sortOrder: index,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeStoreEntries = (
|
const normalizeStoreEntries = (
|
||||||
rawStoreEntries: unknown,
|
rawStoreEntries: unknown,
|
||||||
legacyStores: string[]
|
legacyStores: string[]
|
||||||
@ -456,9 +597,21 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
|||||||
: {};
|
: {};
|
||||||
const storeName = normalizeText(storeRecord.storeName, 64);
|
const storeName = normalizeText(storeRecord.storeName, 64);
|
||||||
if (!storeName) return null;
|
if (!storeName) return null;
|
||||||
|
const legacyThirdCategories = normalizeStringList(storeRecord.thirdCategories).slice(
|
||||||
|
0,
|
||||||
|
300
|
||||||
|
);
|
||||||
|
const thirdCategoryEntries = normalizeThirdCategoryEntries(
|
||||||
|
storeRecord.thirdCategoryEntries,
|
||||||
|
legacyThirdCategories
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
storeName,
|
storeName,
|
||||||
thirdCategories: normalizeStringList(storeRecord.thirdCategories).slice(0, 300),
|
thirdCategories:
|
||||||
|
thirdCategoryEntries.length > 0
|
||||||
|
? thirdCategoryEntries.map((entry) => entry.categoryName)
|
||||||
|
: legacyThirdCategories,
|
||||||
|
thirdCategoryEntries,
|
||||||
sortOrder: normalizeNumber(storeRecord.sortOrder, storeIndex),
|
sortOrder: normalizeNumber(storeRecord.sortOrder, storeIndex),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -472,6 +625,22 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
|||||||
thirdCategories: Array.from(
|
thirdCategories: Array.from(
|
||||||
new Set((entry.thirdCategories || []).map((item) => item.trim()).filter(Boolean))
|
new Set((entry.thirdCategories || []).map((item) => item.trim()).filter(Boolean))
|
||||||
).slice(0, 300),
|
).slice(0, 300),
|
||||||
|
thirdCategoryEntries: (entry.thirdCategoryEntries || []).map((thirdEntry, thirdIndex) => ({
|
||||||
|
...thirdEntry,
|
||||||
|
fourthCategories: Array.from(
|
||||||
|
new Set((thirdEntry.fourthCategories || []).map((item) => item.trim()).filter(Boolean))
|
||||||
|
).slice(0, 300),
|
||||||
|
fourthCategoryEntries: (thirdEntry.fourthCategoryEntries || []).map(
|
||||||
|
(fourthEntry, fourthIndex) => ({
|
||||||
|
...fourthEntry,
|
||||||
|
fifthCategories: Array.from(
|
||||||
|
new Set((fourthEntry.fifthCategories || []).map((item) => item.trim()).filter(Boolean))
|
||||||
|
).slice(0, 300),
|
||||||
|
sortOrder: fourthIndex,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
sortOrder: thirdIndex,
|
||||||
|
})),
|
||||||
sortOrder: index,
|
sortOrder: index,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -482,6 +651,7 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
|||||||
return legacyStores.map((storeName, index) => ({
|
return legacyStores.map((storeName, index) => ({
|
||||||
storeName,
|
storeName,
|
||||||
thirdCategories: [],
|
thirdCategories: [],
|
||||||
|
thirdCategoryEntries: [],
|
||||||
sortOrder: index,
|
sortOrder: index,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@ -519,6 +689,8 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
|||||||
const normalizedBrands = normalizeStringList(rawInputOptions.brands);
|
const normalizedBrands = normalizeStringList(rawInputOptions.brands);
|
||||||
const normalizedStores = normalizeStringList(rawInputOptions.stores);
|
const normalizedStores = normalizeStringList(rawInputOptions.stores);
|
||||||
const normalizedThirdCategories = normalizeStringList(rawInputOptions.thirdCategories);
|
const normalizedThirdCategories = normalizeStringList(rawInputOptions.thirdCategories);
|
||||||
|
const normalizedFourthCategories = normalizeStringList(rawInputOptions.fourthCategories);
|
||||||
|
const normalizedFifthCategories = normalizeStringList(rawInputOptions.fifthCategories);
|
||||||
const normalizedPriceBands = normalizeStringList(rawInputOptions.priceBands);
|
const normalizedPriceBands = normalizeStringList(rawInputOptions.priceBands);
|
||||||
const fallbackBrandStoreEntries: AgentWorkflowBrandStoreEntry[] =
|
const fallbackBrandStoreEntries: AgentWorkflowBrandStoreEntry[] =
|
||||||
normalizedBrandStoreEntries.length > 0
|
normalizedBrandStoreEntries.length > 0
|
||||||
@ -531,6 +703,7 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
|||||||
? normalizedStores.map((storeName, storeIndex) => ({
|
? normalizedStores.map((storeName, storeIndex) => ({
|
||||||
storeName,
|
storeName,
|
||||||
thirdCategories: [],
|
thirdCategories: [],
|
||||||
|
thirdCategoryEntries: [],
|
||||||
sortOrder: storeIndex,
|
sortOrder: storeIndex,
|
||||||
}))
|
}))
|
||||||
: [],
|
: [],
|
||||||
@ -718,6 +891,36 @@ export function normalizeAgentWorkflowUiConfig(value: unknown): AgentWorkflowUiC
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
: normalizedThirdCategories,
|
: normalizedThirdCategories,
|
||||||
|
fourthCategories:
|
||||||
|
fallbackBrandStoreEntries.length > 0
|
||||||
|
? Array.from(
|
||||||
|
new Set(
|
||||||
|
fallbackBrandStoreEntries.flatMap((entry) =>
|
||||||
|
(entry.storeEntries || []).flatMap((storeEntry) =>
|
||||||
|
(storeEntry.thirdCategoryEntries || []).flatMap(
|
||||||
|
(thirdEntry) => thirdEntry.fourthCategories || []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: normalizedFourthCategories,
|
||||||
|
fifthCategories:
|
||||||
|
fallbackBrandStoreEntries.length > 0
|
||||||
|
? Array.from(
|
||||||
|
new Set(
|
||||||
|
fallbackBrandStoreEntries.flatMap((entry) =>
|
||||||
|
(entry.storeEntries || []).flatMap((storeEntry) =>
|
||||||
|
(storeEntry.thirdCategoryEntries || []).flatMap((thirdEntry) =>
|
||||||
|
(thirdEntry.fourthCategoryEntries || []).flatMap(
|
||||||
|
(fourthEntry) => fourthEntry.fifthCategories || []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: normalizedFifthCategories,
|
||||||
priceBands: normalizedPriceBands,
|
priceBands: normalizedPriceBands,
|
||||||
brandStoreEntries: fallbackBrandStoreEntries,
|
brandStoreEntries: fallbackBrandStoreEntries,
|
||||||
},
|
},
|
||||||
@ -773,6 +976,13 @@ export function getAgentWorkflowThirdCategoryOptionsByBrandStore(params: {
|
|||||||
const matchedStoreEntry = matchedEntry?.storeEntries?.find(
|
const matchedStoreEntry = matchedEntry?.storeEntries?.find(
|
||||||
(storeEntry) => storeEntry.storeName.trim().toLowerCase() === normalizedStore
|
(storeEntry) => storeEntry.storeName.trim().toLowerCase() === normalizedStore
|
||||||
);
|
);
|
||||||
|
const thirdCategoryOptions =
|
||||||
|
matchedStoreEntry?.thirdCategoryEntries && matchedStoreEntry.thirdCategoryEntries.length > 0
|
||||||
|
? matchedStoreEntry.thirdCategoryEntries.map((entry) => entry.categoryName).filter(Boolean)
|
||||||
|
: matchedStoreEntry?.thirdCategories || [];
|
||||||
|
if (thirdCategoryOptions.length > 0) {
|
||||||
|
return thirdCategoryOptions;
|
||||||
|
}
|
||||||
if (matchedStoreEntry && matchedStoreEntry.thirdCategories.length > 0) {
|
if (matchedStoreEntry && matchedStoreEntry.thirdCategories.length > 0) {
|
||||||
return matchedStoreEntry.thirdCategories;
|
return matchedStoreEntry.thirdCategories;
|
||||||
}
|
}
|
||||||
@ -780,6 +990,71 @@ export function getAgentWorkflowThirdCategoryOptionsByBrandStore(params: {
|
|||||||
return params.config?.inputOptions?.thirdCategories || defaultAgentWorkflowUiConfig.inputOptions?.thirdCategories || [];
|
return params.config?.inputOptions?.thirdCategories || defaultAgentWorkflowUiConfig.inputOptions?.thirdCategories || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAgentWorkflowFourthCategoryOptionsByBrandStoreThirdCategory(params: {
|
||||||
|
config?: AgentWorkflowUiConfig | null;
|
||||||
|
brand?: string | null;
|
||||||
|
store?: string | null;
|
||||||
|
thirdCategory?: string | null;
|
||||||
|
}) {
|
||||||
|
const entries = getAgentWorkflowBrandStoreEntries(params.config);
|
||||||
|
const normalizedBrand = String(params.brand || "").trim().toLowerCase();
|
||||||
|
const normalizedStore = String(params.store || "").trim().toLowerCase();
|
||||||
|
const normalizedThirdCategory = String(params.thirdCategory || "").trim().toLowerCase();
|
||||||
|
if (!normalizedBrand || !normalizedStore || !normalizedThirdCategory) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchedEntry = entries.find(
|
||||||
|
(entry) => entry.brandName.trim().toLowerCase() === normalizedBrand
|
||||||
|
);
|
||||||
|
const matchedStoreEntry = matchedEntry?.storeEntries?.find(
|
||||||
|
(storeEntry) => storeEntry.storeName.trim().toLowerCase() === normalizedStore
|
||||||
|
);
|
||||||
|
const matchedThirdEntry = matchedStoreEntry?.thirdCategoryEntries?.find(
|
||||||
|
(thirdEntry) => thirdEntry.categoryName.trim().toLowerCase() === normalizedThirdCategory
|
||||||
|
);
|
||||||
|
if (matchedThirdEntry && matchedThirdEntry.fourthCategories.length > 0) {
|
||||||
|
return matchedThirdEntry.fourthCategories;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params.config?.inputOptions?.fourthCategories || defaultAgentWorkflowUiConfig.inputOptions?.fourthCategories || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentWorkflowFifthCategoryOptionsByBrandStoreThirdFourth(params: {
|
||||||
|
config?: AgentWorkflowUiConfig | null;
|
||||||
|
brand?: string | null;
|
||||||
|
store?: string | null;
|
||||||
|
thirdCategory?: string | null;
|
||||||
|
fourthCategory?: string | null;
|
||||||
|
}) {
|
||||||
|
const entries = getAgentWorkflowBrandStoreEntries(params.config);
|
||||||
|
const normalizedBrand = String(params.brand || "").trim().toLowerCase();
|
||||||
|
const normalizedStore = String(params.store || "").trim().toLowerCase();
|
||||||
|
const normalizedThirdCategory = String(params.thirdCategory || "").trim().toLowerCase();
|
||||||
|
const normalizedFourthCategory = String(params.fourthCategory || "").trim().toLowerCase();
|
||||||
|
if (!normalizedBrand || !normalizedStore || !normalizedThirdCategory || !normalizedFourthCategory) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchedEntry = entries.find(
|
||||||
|
(entry) => entry.brandName.trim().toLowerCase() === normalizedBrand
|
||||||
|
);
|
||||||
|
const matchedStoreEntry = matchedEntry?.storeEntries?.find(
|
||||||
|
(storeEntry) => storeEntry.storeName.trim().toLowerCase() === normalizedStore
|
||||||
|
);
|
||||||
|
const matchedThirdEntry = matchedStoreEntry?.thirdCategoryEntries?.find(
|
||||||
|
(thirdEntry) => thirdEntry.categoryName.trim().toLowerCase() === normalizedThirdCategory
|
||||||
|
);
|
||||||
|
const matchedFourthEntry = matchedThirdEntry?.fourthCategoryEntries?.find(
|
||||||
|
(fourthEntry) => fourthEntry.categoryName.trim().toLowerCase() === normalizedFourthCategory
|
||||||
|
);
|
||||||
|
if (matchedFourthEntry && matchedFourthEntry.fifthCategories.length > 0) {
|
||||||
|
return matchedFourthEntry.fifthCategories;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params.config?.inputOptions?.fifthCategories || defaultAgentWorkflowUiConfig.inputOptions?.fifthCategories || [];
|
||||||
|
}
|
||||||
|
|
||||||
export function getAgentWorkflowPriceBandOptions(config?: AgentWorkflowUiConfig | null) {
|
export function getAgentWorkflowPriceBandOptions(config?: AgentWorkflowUiConfig | null) {
|
||||||
return config?.inputOptions?.priceBands || defaultAgentWorkflowUiConfig.inputOptions?.priceBands || [];
|
return config?.inputOptions?.priceBands || defaultAgentWorkflowUiConfig.inputOptions?.priceBands || [];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -618,6 +618,8 @@ export const canvasProject = pgTable("CanvasProject", {
|
|||||||
brand?: string;
|
brand?: string;
|
||||||
store?: string;
|
store?: string;
|
||||||
thirdCategory?: string;
|
thirdCategory?: string;
|
||||||
|
fourthCategory?: string;
|
||||||
|
fifthCategory?: string;
|
||||||
priceBand?: string;
|
priceBand?: string;
|
||||||
};
|
};
|
||||||
materials?: {
|
materials?: {
|
||||||
|
|||||||
@ -106,15 +106,31 @@ export interface AgentWorkflowUiConfig {
|
|||||||
brands: string[];
|
brands: string[];
|
||||||
stores: string[];
|
stores: string[];
|
||||||
thirdCategories?: string[];
|
thirdCategories?: string[];
|
||||||
|
fourthCategories?: string[];
|
||||||
|
fifthCategories?: string[];
|
||||||
priceBands?: string[];
|
priceBands?: string[];
|
||||||
brandStoreEntries?: AgentWorkflowBrandStoreEntry[];
|
brandStoreEntries?: AgentWorkflowBrandStoreEntry[];
|
||||||
};
|
};
|
||||||
steps: AgentWorkflowStepUiConfig[];
|
steps: AgentWorkflowStepUiConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AgentWorkflowFourthCategoryEntry {
|
||||||
|
categoryName: string;
|
||||||
|
fifthCategories: string[];
|
||||||
|
sortOrder?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentWorkflowThirdCategoryEntry {
|
||||||
|
categoryName: string;
|
||||||
|
fourthCategories: string[];
|
||||||
|
fourthCategoryEntries?: AgentWorkflowFourthCategoryEntry[];
|
||||||
|
sortOrder?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AgentWorkflowStoreEntry {
|
export interface AgentWorkflowStoreEntry {
|
||||||
storeName: string;
|
storeName: string;
|
||||||
thirdCategories: string[];
|
thirdCategories: string[];
|
||||||
|
thirdCategoryEntries?: AgentWorkflowThirdCategoryEntry[];
|
||||||
sortOrder?: number;
|
sortOrder?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +274,8 @@ export interface AgentWorkflowComposerContext {
|
|||||||
brand?: string;
|
brand?: string;
|
||||||
store?: string;
|
store?: string;
|
||||||
thirdCategory?: string;
|
thirdCategory?: string;
|
||||||
|
fourthCategory?: string;
|
||||||
|
fifthCategory?: string;
|
||||||
priceBand?: string;
|
priceBand?: string;
|
||||||
selections: AgentWorkflowComposerSelection[];
|
selections: AgentWorkflowComposerSelection[];
|
||||||
referenceImages: AgentWorkflowReferenceImage[];
|
referenceImages: AgentWorkflowReferenceImage[];
|
||||||
@ -392,6 +410,8 @@ export interface AgentWorkflowPersistedState {
|
|||||||
brand: string;
|
brand: string;
|
||||||
store: string;
|
store: string;
|
||||||
thirdCategory: string;
|
thirdCategory: string;
|
||||||
|
fourthCategory: string;
|
||||||
|
fifthCategory: string;
|
||||||
priceBand: string;
|
priceBand: string;
|
||||||
};
|
};
|
||||||
materials?: AgentWorkflowMaterialSelections;
|
materials?: AgentWorkflowMaterialSelections;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user