Replace IDE form mode placeholder
This commit is contained in:
@@ -3036,22 +3036,59 @@ function FormDesignerPanel({
|
||||
const t = messages[language];
|
||||
const objectForms = data.selectedObjectUi?.forms.length ? data.selectedObjectUi.forms : data.forms;
|
||||
const [selectedFormId, setSelectedFormId] = useState<string | null>(objectForms[0]?.form.lineage_id ?? null);
|
||||
const [titleByForm, setTitleByForm] = useState<Record<string, string>>({});
|
||||
const [layoutByForm, setLayoutByForm] = useState<Record<string, "auto" | "columns" | "compact">>({});
|
||||
const [elementDrafts, setElementDrafts] = useState<Record<string, IdeFormElementDraft[]>>({});
|
||||
const [newElementName, setNewElementName] = useState("");
|
||||
const [newElementKind, setNewElementKind] = useState<IdeFormElementDraft["controlKind"]>("input");
|
||||
useEffect(() => {
|
||||
if (objectForms.length > 0 && !objectForms.some((item) => item.form.lineage_id === selectedFormId)) {
|
||||
setSelectedFormId(objectForms[0].form.lineage_id);
|
||||
}
|
||||
}, [objectForms, selectedFormId]);
|
||||
const form = objectForms.find((item) => item.form.lineage_id === selectedFormId) ?? objectForms[0];
|
||||
const elements = form?.elements.slice(0, 8) ?? [];
|
||||
const commands = form?.commands.slice(0, 6) ?? [];
|
||||
const formTitle = form?.form.name ?? "ФормаДокумента";
|
||||
const formObjectCaption = language === "ru" ? `${formTitle} (форма 1С)` : `${formTitle} (1C form)`;
|
||||
const commandLabels = [t.postAndClose, ...commands.slice(0, 2).map((command) => command.name)];
|
||||
const formKey = form?.form.lineage_id ?? "draft";
|
||||
const baseElements = useMemo(() => buildIdeFormElements(data, form), [data, form]);
|
||||
const elements = elementDrafts[formKey] ?? baseElements;
|
||||
const formTitle = titleByForm[formKey] ?? form?.form.name ?? "ФормаДокумента";
|
||||
const formObjectCaption = language === "ru" ? `${formTitle} (форма 1С 8.5)` : `${formTitle} (1C 8.5 form)`;
|
||||
const layout = layoutByForm[formKey] ?? "auto";
|
||||
|
||||
useEffect(() => {
|
||||
if (form && !elementDrafts[form.form.lineage_id]) {
|
||||
setElementDrafts((current) => ({ ...current, [form.form.lineage_id]: buildIdeFormElements(data, form) }));
|
||||
}
|
||||
}, [data, elementDrafts, form]);
|
||||
|
||||
const updateElement = (id: string, patch: Partial<IdeFormElementDraft>) => {
|
||||
setElementDrafts((current) => ({
|
||||
...current,
|
||||
[formKey]: (current[formKey] ?? baseElements).map((element) => (element.id === id ? { ...element, ...patch } : element))
|
||||
}));
|
||||
};
|
||||
|
||||
const addElement = () => {
|
||||
const name = newElementName.trim();
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
const next: IdeFormElementDraft = {
|
||||
id: `draft.${formKey}.${Date.now()}`,
|
||||
name,
|
||||
caption: name,
|
||||
controlKind: newElementKind,
|
||||
binding: name,
|
||||
width: "stretch"
|
||||
};
|
||||
setElementDrafts((current) => ({ ...current, [formKey]: [...(current[formKey] ?? baseElements), next] }));
|
||||
setNewElementName("");
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="min-h-0 rounded-none border-0 p-0 shadow-none" data-active-mode={modeId}>
|
||||
<PanelTitle icon={Layers3} title={t.formDesigner} />
|
||||
<div className="grid h-[calc(100%-45px)] min-h-0 grid-cols-[260px_minmax(0,1fr)]">
|
||||
<div className="grid h-[calc(100%-45px)] min-h-0 grid-cols-[260px_minmax(0,1fr)_340px]">
|
||||
<div className="min-h-0 overflow-auto border-r border-border bg-[#f4f4f4] p-3">
|
||||
<div className="flex items-center justify-between border-b border-border pb-2">
|
||||
<div className="text-xs font-semibold uppercase text-muted-foreground">{t.forms}</div>
|
||||
@@ -3086,11 +3123,11 @@ function FormDesignerPanel({
|
||||
<div className="text-sm text-muted-foreground">{t.none}</div>
|
||||
) : (
|
||||
elements.map((element) => (
|
||||
<div className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-background" key={element.lineage_id}>
|
||||
<OneCTreeIcon kind={kindForTreeLabel(element.kind)} />
|
||||
<div className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-background" key={element.id}>
|
||||
<OneCTreeIcon kind={element.controlKind === "table" ? "tabular" : "attribute"} />
|
||||
<div className="min-w-0">
|
||||
<div className="truncate font-medium text-foreground">{element.name}</div>
|
||||
<div className="truncate text-[11px] text-muted-foreground">{element.kind}</div>
|
||||
<div className="truncate text-[11px] text-muted-foreground">{element.controlKind} · {element.binding}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
@@ -3107,146 +3144,234 @@ function FormDesignerPanel({
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-h-0 overflow-auto bg-[#ececec] p-5">
|
||||
<div className="mx-auto flex min-h-[760px] max-w-7xl flex-col rounded-xl border border-black/5 bg-white px-5 py-5 shadow-[0_18px_55px_rgba(15,23,42,0.18)]">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="mx-auto flex min-h-[760px] max-w-5xl flex-col border border-[#b8c0ca] bg-[#fbfbfc] shadow-[0_18px_55px_rgba(15,23,42,0.18)]" data-ide-form-window data-ide-form-layout={layout}>
|
||||
<div className="flex min-h-9 items-center justify-between border-b border-[#cbd3df] bg-gradient-to-b from-[#f8f9fb] to-[#e9edf3] px-3 text-sm text-slate-950">
|
||||
<div>
|
||||
<div className="text-lg font-medium text-slate-950">{formObjectCaption}</div>
|
||||
<button className="mt-8 inline-flex items-center gap-1 text-sm text-slate-900" type="button">
|
||||
{t.mainSection}
|
||||
<ChevronDown className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<div className="font-semibold">{formObjectCaption}</div>
|
||||
<div className="text-[11px] text-slate-500">{data.selectedObjectSchema?.object.qualified_name ?? form?.form.qualified_name ?? data.projectId}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-slate-500">
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200" type="button" title="menu">
|
||||
<button className="flex h-7 w-7 items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200" type="button" title="menu">
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</button>
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200" type="button" title="expand">
|
||||
<button className="flex h-7 w-7 items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200" type="button" title="expand">
|
||||
<Maximize2 className="h-4 w-4" />
|
||||
</button>
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200" type="button" title="close">
|
||||
<button className="flex h-7 w-7 items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200" type="button" title="close">
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-7 grid max-w-3xl grid-cols-[minmax(0,360px)_minmax(0,360px)] gap-x-3 gap-y-3">
|
||||
<label className="grid gap-1 text-xs text-slate-700">
|
||||
<span><span className="text-red-500">*</span>{t.nameField}</span>
|
||||
<span className="flex h-8 items-center rounded-md border border-slate-300 bg-white px-2 text-sm text-slate-900">{formTitle}</span>
|
||||
</label>
|
||||
<label className="grid gap-1 text-xs text-slate-700">
|
||||
{t.code}
|
||||
<span className="flex h-8 items-center gap-2 rounded-md border border-slate-300 bg-slate-100 px-2 text-sm text-slate-700">
|
||||
X-00044
|
||||
<Lock className="ml-auto h-3.5 w-3.5 text-slate-400" />
|
||||
</span>
|
||||
</label>
|
||||
<div className="flex items-center gap-4 pt-5 text-sm text-slate-900">
|
||||
{[t.client, t.agent, t.supplier].map((label, index) => (
|
||||
<label className="inline-flex items-center gap-2" key={label}>
|
||||
<span className={index === 1 ? "flex h-5 w-5 items-center justify-center rounded border border-blue-500 bg-white text-blue-600" : "h-5 w-5 rounded border border-slate-400 bg-white"}>
|
||||
{index === 1 ? "✓" : ""}
|
||||
</span>
|
||||
{label}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
<label className="grid gap-1 text-xs text-slate-700">
|
||||
{t.status}
|
||||
<span className="relative flex h-8 items-center rounded-md border border-blue-600 bg-white px-2 text-sm">
|
||||
<span className="h-4 w-px bg-slate-950" />
|
||||
<ChevronDown className="ml-auto h-4 w-4 text-blue-600" />
|
||||
<span className="absolute left-0 top-9 z-10 grid w-32 gap-1 rounded-lg bg-[#3f3f3f] p-3 text-xs text-white shadow-xl">
|
||||
<span>{t.agent}</span>
|
||||
<span>{language === "ru" ? "Прямой клиент" : "Direct client"}</span>
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<label className="grid gap-1 text-xs text-slate-700">
|
||||
{t.comment}
|
||||
<span className="h-8 rounded-md border border-slate-300 bg-white" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 border-b border-slate-200">
|
||||
<div className="flex gap-5 text-sm">
|
||||
{[t.goods, t.sites, t.compensationTerms, t.agencyAgreements, t.telegram, t.mail].map((tab, index) => (
|
||||
<button
|
||||
className={index === 0 ? "border-b-2 border-yellow-400 px-2 py-2 text-slate-950" : "px-2 py-2 text-slate-500 hover:text-slate-900"}
|
||||
key={tab}
|
||||
type="button"
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 overflow-hidden rounded-md border border-slate-100">
|
||||
<div className="flex h-10 items-center gap-3 bg-[#f0f0f0] px-3 text-sm text-slate-800">
|
||||
<button className="inline-flex h-8 items-center gap-2 rounded px-2 hover:bg-white" type="button">
|
||||
<Plus className="h-4 w-4" />
|
||||
{t.create}
|
||||
<div className="flex min-h-11 flex-wrap items-center gap-1 border-b border-[#ccd4df] bg-[#f4f6f9] px-3 py-2">
|
||||
{commands.length ? commands.map((command) => (
|
||||
<button className="h-7 border border-[#aeb8c6] bg-gradient-to-b from-white to-[#edf1f6] px-3 text-xs font-medium text-[#1f2937]" key={command.lineage_id} type="button">
|
||||
{command.name}
|
||||
</button>
|
||||
<span className="h-6 w-px bg-slate-300" />
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded hover:bg-white" type="button" title="copy">
|
||||
<Copy className="h-4 w-4 text-slate-500" />
|
||||
</button>
|
||||
<div className="ml-auto flex items-center gap-3">
|
||||
<button className="inline-flex items-center gap-2 hover:text-slate-950" type="button">
|
||||
<Search className="h-4 w-4" />
|
||||
{t.search}
|
||||
</button>
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded hover:bg-white" type="button" title="filter">
|
||||
<Filter className="h-4 w-4" />
|
||||
</button>
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded hover:bg-white" type="button" title="more">
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-[48px_1.1fr_90px_1.6fr_1fr_1fr_1.2fr] border-b border-slate-100 text-xs font-semibold text-slate-950">
|
||||
{["", t.nameField, t.code, t.sentToBankCompanyName, t.mergeProject, t.legalEntity, t.result].map((heading) => (
|
||||
<div className="px-3 py-3" key={heading}>{heading}</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex min-h-[210px] items-center justify-center text-sm text-slate-500">{t.emptyList}</div>
|
||||
)) : <span className="text-xs text-muted-foreground">{language === "ru" ? "Команды формы не описаны" : "No form commands"}</span>}
|
||||
</div>
|
||||
|
||||
<div className="mt-auto grid max-w-3xl grid-cols-[minmax(0,330px)_minmax(0,360px)] gap-x-11 gap-y-3 pt-6">
|
||||
{[
|
||||
[t.author, t.none],
|
||||
[t.creationDate, t.none],
|
||||
[t.editor, t.none],
|
||||
[t.editDate, t.none]
|
||||
].map(([label, value]) => (
|
||||
<label className="grid gap-1 text-xs text-slate-700" key={label}>
|
||||
{label}
|
||||
<span className="flex h-8 items-center rounded-md border border-slate-300 bg-slate-100 px-2 text-sm text-slate-700">{value}</span>
|
||||
</label>
|
||||
<div className={[
|
||||
"grid grid-cols-12 gap-x-3 gap-y-2 p-5",
|
||||
layout === "compact" ? "gap-y-1" : ""
|
||||
].join(" ")}>
|
||||
{elements.map((element) => (
|
||||
<IdeFormControl element={element} forceHalf={layout === "columns"} key={element.id} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-5 flex items-center justify-end gap-3">
|
||||
{commandLabels.map((command) => (
|
||||
<button className="h-8 rounded-md bg-slate-100 px-3 text-xs font-medium text-slate-700 hover:bg-slate-200" key={command} type="button">
|
||||
{command}
|
||||
<div className="mt-auto border-t border-[#ccd4df] bg-white px-4 py-3">
|
||||
<div className="flex flex-wrap justify-end gap-2">
|
||||
{commands.slice(0, 3).map((command) => (
|
||||
<button className="h-8 border border-[#aeb8c6] bg-gradient-to-b from-white to-[#edf1f6] px-3 text-xs font-medium text-slate-700" key={`bottom-${command.lineage_id}`} type="button">
|
||||
{command.name}
|
||||
</button>
|
||||
))}
|
||||
<button className="h-8 bg-yellow-400 px-4 text-sm font-semibold text-slate-950 hover:bg-yellow-300" type="button">
|
||||
{t.saveAndClose}
|
||||
</button>
|
||||
))}
|
||||
<button className="h-9 rounded-md bg-yellow-400 px-4 text-sm font-semibold text-slate-950 hover:bg-yellow-300" type="button">
|
||||
{t.saveAndClose}
|
||||
</button>
|
||||
<button className="h-9 rounded-md bg-slate-200 px-4 text-sm font-semibold text-slate-900 hover:bg-slate-300" type="button">
|
||||
{t.save}
|
||||
</button>
|
||||
<button className="h-8 bg-slate-200 px-4 text-sm font-semibold text-slate-900 hover:bg-slate-300" type="button">
|
||||
{t.save}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<aside className="min-h-0 overflow-auto border-l border-border bg-background">
|
||||
<div className="border-b border-border p-3">
|
||||
<div className="text-sm font-medium">{language === "ru" ? "Свойства формы" : "Form properties"}</div>
|
||||
<label className="mt-3 grid gap-1 text-xs font-medium text-muted-foreground">
|
||||
{language === "ru" ? "Заголовок" : "Title"}
|
||||
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={formTitle} onChange={(event) => setTitleByForm((current) => ({ ...current, [formKey]: event.target.value }))} />
|
||||
</label>
|
||||
<label className="mt-2 grid gap-1 text-xs font-medium text-muted-foreground">
|
||||
{language === "ru" ? "Размещение" : "Layout"}
|
||||
<select className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={layout} onChange={(event) => setLayoutByForm((current) => ({ ...current, [formKey]: event.target.value as "auto" | "columns" | "compact" }))}>
|
||||
<option value="auto">{language === "ru" ? "Авто 1С" : "1C auto"}</option>
|
||||
<option value="columns">{language === "ru" ? "Колонки" : "Columns"}</option>
|
||||
<option value="compact">{language === "ru" ? "Компактно" : "Compact"}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2 border-b border-border p-3">
|
||||
<IdeFormMetric label="elements" value={elements.length} />
|
||||
<IdeFormMetric label="commands" value={commands.length} />
|
||||
</div>
|
||||
<div className="border-b border-border p-3">
|
||||
<div className="text-xs font-medium text-muted-foreground">{language === "ru" ? "Добавить элемент" : "Add element"}</div>
|
||||
<div className="mt-2 grid grid-cols-[minmax(0,1fr)_110px] gap-2">
|
||||
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" placeholder={language === "ru" ? "Новый реквизит" : "New attribute"} value={newElementName} onChange={(event) => setNewElementName(event.target.value)} />
|
||||
<select className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={newElementKind} onChange={(event) => setNewElementKind(event.target.value as IdeFormElementDraft["controlKind"])}>
|
||||
<option value="input">{language === "ru" ? "Поле" : "Input"}</option>
|
||||
<option value="date">{language === "ru" ? "Дата" : "Date"}</option>
|
||||
<option value="checkbox">{language === "ru" ? "Флажок" : "Checkbox"}</option>
|
||||
<option value="table">{language === "ru" ? "Таблица" : "Table"}</option>
|
||||
<option value="group">{language === "ru" ? "Группа" : "Group"}</option>
|
||||
<option value="text">{language === "ru" ? "Текст" : "Text"}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button className="mt-2 h-8 w-full bg-primary px-3 text-sm font-medium text-primary-foreground" onClick={addElement} type="button">
|
||||
{language === "ru" ? "Добавить в макет" : "Add to layout"}
|
||||
</button>
|
||||
</div>
|
||||
<div className="divide-y divide-border">
|
||||
{elements.map((element) => (
|
||||
<div className="grid gap-2 p-3" key={`props-${element.id}`}>
|
||||
<div className="truncate text-xs font-semibold">{element.name}</div>
|
||||
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.caption} onChange={(event) => updateElement(element.id, { caption: event.target.value })} />
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<select className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.controlKind} onChange={(event) => updateElement(element.id, { controlKind: event.target.value as IdeFormElementDraft["controlKind"] })}>
|
||||
<option value="input">{language === "ru" ? "Поле ввода" : "Input"}</option>
|
||||
<option value="date">{language === "ru" ? "Дата" : "Date"}</option>
|
||||
<option value="checkbox">{language === "ru" ? "Флажок" : "Checkbox"}</option>
|
||||
<option value="table">{language === "ru" ? "Таблица" : "Table"}</option>
|
||||
<option value="group">{language === "ru" ? "Группа" : "Group"}</option>
|
||||
<option value="text">{language === "ru" ? "Текст" : "Text"}</option>
|
||||
</select>
|
||||
<select className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.width} onChange={(event) => updateElement(element.id, { width: event.target.value as IdeFormElementDraft["width"] })}>
|
||||
<option value="stretch">{language === "ru" ? "Вся строка" : "Full"}</option>
|
||||
<option value="half">1/2</option>
|
||||
<option value="third">1/3</option>
|
||||
</select>
|
||||
</div>
|
||||
<input className="h-8 border border-border bg-background px-2 text-sm text-foreground" value={element.binding} onChange={(event) => updateElement(element.id, { binding: event.target.value })} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
type IdeFormElementDraft = {
|
||||
id: string;
|
||||
name: string;
|
||||
caption: string;
|
||||
controlKind: "input" | "date" | "checkbox" | "table" | "group" | "text";
|
||||
binding: string;
|
||||
width: "stretch" | "half" | "third";
|
||||
};
|
||||
|
||||
function IdeFormControl({ element, forceHalf }: Readonly<{ element: IdeFormElementDraft; forceHalf: boolean }>) {
|
||||
const span = element.controlKind === "table" || element.controlKind === "group" ? "col-span-12" : forceHalf || element.width === "half" ? "col-span-6" : element.width === "third" ? "col-span-4" : "col-span-12";
|
||||
return (
|
||||
<div className={`${span} grid min-h-8 grid-cols-[150px_minmax(0,1fr)] items-center gap-2 border border-transparent px-1 py-1 hover:border-[#b9c1cd] hover:bg-white`} data-ide-form-element={element.id} data-ide-form-control={element.controlKind}>
|
||||
<label className="truncate text-xs font-semibold text-[#4b5563]">{element.caption}</label>
|
||||
{ideFormControlInput(element)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ideFormControlInput(element: IdeFormElementDraft) {
|
||||
if (element.controlKind === "table") {
|
||||
return (
|
||||
<div className="min-h-28 border border-[#aeb8c6] bg-white text-xs">
|
||||
<div className="grid grid-cols-[2fr_1fr_1fr] bg-[#eef2f7] font-semibold">
|
||||
<span className="border-b border-r border-[#d7dde6] px-2 py-1">{element.binding}</span>
|
||||
<span className="border-b border-r border-[#d7dde6] px-2 py-1">Количество</span>
|
||||
<span className="border-b border-[#d7dde6] px-2 py-1">Сумма</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-[2fr_1fr_1fr]"><span className="min-h-7 border-r border-[#d7dde6]" /><span className="border-r border-[#d7dde6]" /><span /></div>
|
||||
<div className="grid grid-cols-[2fr_1fr_1fr] border-t border-[#d7dde6]"><span className="min-h-7 border-r border-[#d7dde6]" /><span className="border-r border-[#d7dde6]" /><span /></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (element.controlKind === "checkbox") {
|
||||
return <label className="flex items-center gap-2 text-xs text-slate-800"><input checked readOnly type="checkbox" />{element.binding}</label>;
|
||||
}
|
||||
if (element.controlKind === "group") {
|
||||
return <div className="min-h-11 border border-dashed border-[#aeb8c6] bg-[#f6f8fb] px-2 py-2 text-xs font-medium text-muted-foreground">{element.binding}</div>;
|
||||
}
|
||||
if (element.controlKind === "text") {
|
||||
return <textarea className="min-h-16 resize-none border border-[#aeb8c6] bg-white px-2 py-1 text-xs" readOnly value={element.binding} />;
|
||||
}
|
||||
return <input className="h-7 border border-[#aeb8c6] bg-white px-2 text-xs" readOnly value={element.controlKind === "date" ? "21.05.2026 0:00:00" : element.binding} />;
|
||||
}
|
||||
|
||||
function IdeFormMetric({ label, value }: Readonly<{ label: string; value: number }>) {
|
||||
return (
|
||||
<div className="border border-border p-2">
|
||||
<div className="text-base font-semibold">{value}</div>
|
||||
<div className="text-[11px] text-muted-foreground">{label}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function buildIdeFormElements(data: ProjectWorkspaceData, form: ProjectWorkspaceData["forms"][number] | undefined): IdeFormElementDraft[] {
|
||||
const explicitElements = form?.elements ?? [];
|
||||
if (explicitElements.length) {
|
||||
return explicitElements.map((element, index) => ({
|
||||
id: element.lineage_id || `element.${index}`,
|
||||
name: element.name,
|
||||
caption: element.name,
|
||||
controlKind: controlKindForFormNode(element.name, element.kind),
|
||||
binding: element.qualified_name || element.name,
|
||||
width: "stretch"
|
||||
}));
|
||||
}
|
||||
const attributes = data.selectedObjectSchema?.attributes ?? [];
|
||||
const tabularSections = data.selectedObjectSchema?.tabular_sections ?? [];
|
||||
const fromSchema: IdeFormElementDraft[] = [
|
||||
...attributes.map((attribute, index) => ({
|
||||
id: attribute.lineage_id || `attribute.${index}`,
|
||||
name: attribute.name,
|
||||
caption: attribute.name,
|
||||
controlKind: controlKindForFormNode(attribute.name, attribute.kind),
|
||||
binding: attribute.name,
|
||||
width: index < 2 ? "half" : "stretch"
|
||||
} satisfies IdeFormElementDraft)),
|
||||
...tabularSections.map((section, index) => ({
|
||||
id: section.tabular_section.lineage_id || `table.${index}`,
|
||||
name: section.tabular_section.name,
|
||||
caption: section.tabular_section.name,
|
||||
controlKind: "table" as const,
|
||||
binding: section.tabular_section.name,
|
||||
width: "stretch" as const
|
||||
}))
|
||||
];
|
||||
if (fromSchema.length) {
|
||||
return fromSchema;
|
||||
}
|
||||
const formName = form?.form.name ?? "ФормаДокумента";
|
||||
if (formName.includes("ФормаДокумента")) {
|
||||
return [
|
||||
{ id: "default.number", name: "Номер", caption: "Номер", controlKind: "input", binding: "Объект.Номер", width: "half" },
|
||||
{ id: "default.date", name: "Дата", caption: "Дата", controlKind: "date", binding: "Объект.Дата", width: "half" },
|
||||
{ id: "default.table", name: "Товары", caption: "Товары", controlKind: "table", binding: "Объект.Товары", width: "stretch" }
|
||||
];
|
||||
}
|
||||
return [{ id: "default.name", name: "Наименование", caption: "Наименование", controlKind: "input", binding: "Объект.Наименование", width: "stretch" }];
|
||||
}
|
||||
|
||||
function controlKindForFormNode(name: string, kind: string): IdeFormElementDraft["controlKind"] {
|
||||
const normalized = `${name} ${kind}`.toLowerCase();
|
||||
if (normalized.includes("таб") || normalized.includes("table")) return "table";
|
||||
if (normalized.includes("дата") || normalized.includes("date")) return "date";
|
||||
if (normalized.includes("флаг") || normalized.includes("boolean") || normalized.includes("булево")) return "checkbox";
|
||||
return "input";
|
||||
}
|
||||
|
||||
function MetadataDesignerPanel({
|
||||
activeMode,
|
||||
data,
|
||||
|
||||
Reference in New Issue
Block a user