feat(review): View prompt + Call 2 declared attrs, Create-desc gate
Shared assemble_call2 for GET .../call2/prompt and POST call2 (seed + DECLARED ATTRIBUTES + dimension fact + brand/geo; photo slots without base64). Create desc disabled until content LLM + title/short + species + wood cats/primary + edge + ≥1 figure + grade + moisture + price; show missing. figure_term_ids required for ready. Moisture defaults to Kiln dried (kd). Pin snl / liveedge in wood-cat UI. Ready-on-change via MultiSelect onCommit.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Create-desc gate (Call 2): disable Create desc unless these are present.
|
||||
* Does NOT require description (that is what Create desc writes).
|
||||
* Price included for parity with publish-ready.
|
||||
*/
|
||||
|
||||
export type CreateDescInput = {
|
||||
contentLlmEnabled: boolean;
|
||||
title: string;
|
||||
shortDescription: string;
|
||||
speciesId: number | null | undefined;
|
||||
woodCategoryIds: number[];
|
||||
primaryCatId: number | '' | null | undefined;
|
||||
edgeTermId: number | string | '' | null | undefined;
|
||||
figureTermIds: number[];
|
||||
gradeTermId: number | string | '' | null | undefined;
|
||||
moistureTermId: number | string | '' | null | undefined;
|
||||
price: string | number | null | undefined;
|
||||
};
|
||||
|
||||
/** Human labels shown under Create desc when the gate fails. */
|
||||
export const CREATE_DESC_LABELS: Record<string, string> = {
|
||||
content_llm: 'content LLM on',
|
||||
title: 'title',
|
||||
short_description: 'short description',
|
||||
species: 'species',
|
||||
wood_categories: '≥1 wood category',
|
||||
primary: 'primary wood category',
|
||||
edge: 'edge type',
|
||||
figure: '≥1 figure',
|
||||
grade: 'grade',
|
||||
moisture: 'moisture',
|
||||
price: 'price',
|
||||
};
|
||||
|
||||
function presentId(v: number | string | '' | null | undefined): boolean {
|
||||
if (v === null || v === undefined || v === '') return false;
|
||||
if (typeof v === 'number') return Number.isFinite(v);
|
||||
return String(v).trim() !== '';
|
||||
}
|
||||
|
||||
function presentPrice(v: string | number | null | undefined): boolean {
|
||||
if (v === null || v === undefined || v === '') return false;
|
||||
const n = typeof v === 'number' ? v : Number(v);
|
||||
return Number.isFinite(n);
|
||||
}
|
||||
|
||||
/** Return missing field keys (stable order). Empty ⇒ Create desc enabled. */
|
||||
export function missingCreateDesc(input: CreateDescInput): string[] {
|
||||
const missing: string[] = [];
|
||||
if (!input.contentLlmEnabled) missing.push('content_llm');
|
||||
if (!String(input.title ?? '').trim()) missing.push('title');
|
||||
if (!String(input.shortDescription ?? '').trim()) missing.push('short_description');
|
||||
if (input.speciesId == null || !Number.isFinite(Number(input.speciesId))) missing.push('species');
|
||||
if (!input.woodCategoryIds?.length) missing.push('wood_categories');
|
||||
if (!presentId(input.primaryCatId)) missing.push('primary');
|
||||
if (!presentId(input.edgeTermId)) missing.push('edge');
|
||||
if (!input.figureTermIds?.length) missing.push('figure');
|
||||
if (!presentId(input.gradeTermId)) missing.push('grade');
|
||||
if (!presentId(input.moistureTermId)) missing.push('moisture');
|
||||
if (!presentPrice(input.price)) missing.push('price');
|
||||
return missing;
|
||||
}
|
||||
|
||||
export function canCreateDesc(input: CreateDescInput): boolean {
|
||||
return missingCreateDesc(input).length === 0;
|
||||
}
|
||||
|
||||
export function missingCreateDescLabels(input: CreateDescInput): string[] {
|
||||
return missingCreateDesc(input).map((k) => CREATE_DESC_LABELS[k] ?? k);
|
||||
}
|
||||
Reference in New Issue
Block a user