import { KNOWN_CONFIG_TABLE_KEYS, OPERATOR_LABELS, TABLE_SERVER_CONFIG } from "../../shared/constants.js";
import { boolLabel, fmtDate, listPreview, normalizeReferenceMeta, roleLabel, statusKindLabel, statusLabel } from "../../shared/utils.js";
function fmtBalance(value) {
const number = Number(value);
if (!Number.isFinite(number)) return "-";
return number.toLocaleString("ru-RU", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + " ₽";
}
function smsBalanceSummary(health) {
if (!health || typeof health !== "object") return "Баланс SMS Aero: загрузка...";
const provider = String(health.provider || "").toLowerCase();
if (provider !== "smsaero") {
return "SMS провайдер: " + String(health.provider || "-") + " (баланс недоступен)";
}
if (health.balance_available) {
return "Баланс SMS Aero: " + fmtBalance(health.balance_amount);
}
const issues = Array.isArray(health.issues) ? health.issues.filter(Boolean) : [];
return "Баланс SMS Aero недоступен" + (issues.length ? " • " + String(issues[0]) : "");
}
export function ConfigSection(props) {
const {
token,
tables,
dictionaries,
configActiveKey,
activeConfigTableState,
activeConfigMeta,
genericConfigHeaders,
canCreateInConfig,
canUpdateInConfig,
canDeleteInConfig,
statusDesignerTopicCode,
statusDesignerCards,
getTableLabel,
getFieldDef,
getFilterValuePreview,
resolveReferenceLabel,
resolveTableConfig,
getStatus,
loadCurrentConfigTable,
onRefreshSmsProviderHealth,
smsProviderHealth,
openCreateRecordModal,
openFilterModal,
removeFilterChip,
openFilterEditModal,
toggleTableSort,
openEditRecordModal,
deleteRecord,
loadStatusDesignerTopic,
openCreateStatusTransitionForTopic,
loadPrevPage,
loadNextPage,
loadAllRows,
FilterToolbarComponent,
DataTableComponent,
TablePagerComponent,
StatusLineComponent,
IconButtonComponent,
UserAvatarComponent,
} = props;
const FilterToolbar = FilterToolbarComponent;
const DataTable = DataTableComponent;
const TablePager = TablePagerComponent;
const StatusLine = StatusLineComponent;
const IconButton = IconButtonComponent;
const UserAvatar = UserAvatarComponent;
return (
<>
Справочники
{configActiveKey ? getTableLabel(configActiveKey) : "Справочник не выбран"}
{configActiveKey === "otp_sessions" ? (
{smsBalanceSummary(smsProviderHealth)}
{smsProviderHealth?.loaded_at ? " • обновлено " + fmtDate(smsProviderHealth.loaded_at) : ""}
) : null}
{configActiveKey === "otp_sessions" ? (
) : null}
{configActiveKey ? getTableLabel(configActiveKey) : "Справочник не выбран"}
{canCreateInConfig && configActiveKey ? (
) : null}
openFilterModal(configActiveKey)}
onRemove={(index) => removeFilterChip(configActiveKey, index)}
onEdit={(index) => openFilterEditModal(configActiveKey, index)}
getChipLabel={(clause) => {
const fieldDef = getFieldDef(configActiveKey, clause.field);
return (
(fieldDef ? fieldDef.label : clause.field) +
" " +
OPERATOR_LABELS[clause.op] +
" " +
getFilterValuePreview(configActiveKey, clause)
);
}}
/>
{configActiveKey === "topics" ? (
toggleTableSort("topics", field)}
sortClause={(tables.topics.sort && tables.topics.sort[0]) || TABLE_SERVER_CONFIG.topics.sort[0]}
renderRow={(row) => (
{row.code || "-"}
|
{row.name || "-"} |
{boolLabel(row.enabled)} |
{String(row.sort_order ?? 0)} |
openEditRecordModal("topics", row)} />
deleteRecord("topics", row.id)} tone="danger" />
|
)}
/>
) : null}
{configActiveKey === "quotes" ? (
toggleTableSort("quotes", field)}
sortClause={(tables.quotes.sort && tables.quotes.sort[0]) || TABLE_SERVER_CONFIG.quotes.sort[0]}
renderRow={(row) => (
| {row.author || "-"} |
{row.text || "-"} |
{row.source || "-"} |
{boolLabel(row.is_active)} |
{String(row.sort_order ?? 0)} |
{fmtDate(row.created_at)} |
openEditRecordModal("quotes", row)} />
deleteRecord("quotes", row.id)} tone="danger" />
|
)}
/>
) : null}
{configActiveKey === "statuses" ? (
toggleTableSort("statuses", field)}
sortClause={(tables.statuses.sort && tables.statuses.sort[0]) || TABLE_SERVER_CONFIG.statuses.sort[0]}
renderRow={(row) => (
{row.code || "-"}
|
{row.name || "-"} |
{resolveReferenceLabel({ table: "status_groups", value_field: "id", label_field: "name" }, row.status_group_id)} |
{statusKindLabel(row.kind)} |
{boolLabel(row.enabled)} |
{String(row.sort_order ?? 0)} |
{boolLabel(row.is_terminal)} |
{row.invoice_template || "-"} |
openEditRecordModal("statuses", row)} />
deleteRecord("statuses", row.id)} tone="danger" />
|
)}
/>
) : null}
{configActiveKey === "formFields" ? (
toggleTableSort("formFields", field)}
sortClause={(tables.formFields.sort && tables.formFields.sort[0]) || TABLE_SERVER_CONFIG.formFields.sort[0]}
renderRow={(row) => (
{row.key || "-"}
|
{row.label || "-"} |
{row.type || "-"} |
{boolLabel(row.required)} |
{boolLabel(row.enabled)} |
{String(row.sort_order ?? 0)} |
openEditRecordModal("formFields", row)} />
deleteRecord("formFields", row.id)} tone="danger" />
|
)}
/>
) : null}
{configActiveKey === "topicRequiredFields" ? (
toggleTableSort("topicRequiredFields", field)}
sortClause={
(tables.topicRequiredFields.sort && tables.topicRequiredFields.sort[0]) ||
TABLE_SERVER_CONFIG.topicRequiredFields.sort[0]
}
renderRow={(row) => (
| {row.topic_code || "-"} |
{row.field_key || "-"}
|
{boolLabel(row.required)} |
{boolLabel(row.enabled)} |
{String(row.sort_order ?? 0)} |
{fmtDate(row.created_at)} |
openEditRecordModal("topicRequiredFields", row)}
/>
deleteRecord("topicRequiredFields", row.id)}
tone="danger"
/>
|
)}
/>
) : null}
{configActiveKey === "topicDataTemplates" ? (
toggleTableSort("topicDataTemplates", field)}
sortClause={
(tables.topicDataTemplates.sort && tables.topicDataTemplates.sort[0]) ||
TABLE_SERVER_CONFIG.topicDataTemplates.sort[0]
}
renderRow={(row) => (
| {row.topic_code || "-"} |
{row.key || "-"}
|
{row.label || "-"} |
{row.description || "-"} |
{boolLabel(row.required)} |
{boolLabel(row.enabled)} |
{String(row.sort_order ?? 0)} |
{fmtDate(row.created_at)} |
openEditRecordModal("topicDataTemplates", row)} />
deleteRecord("topicDataTemplates", row.id)} tone="danger" />
|
)}
/>
) : null}
{configActiveKey === "statusTransitions" ? (
<>
Конструктор маршрута статусов
Ветвления, возвраты, SLA и требования к данным/файлам на каждом переходе.
{statusDesignerCards.length ? (
{statusDesignerCards.map((card) => (
{card.name}
{card.code}
{card.isTerminal ?
Терминальный : null}
{card.outgoing.length ? (
{card.outgoing.map((link) => (
-
))}
) : (
Нет исходящих переходов
)}
))}
) : (
Для выбранной темы переходы пока не настроены.
)}
toggleTableSort("statusTransitions", field)}
sortClause={
(tables.statusTransitions.sort && tables.statusTransitions.sort[0]) || TABLE_SERVER_CONFIG.statusTransitions.sort[0]
}
renderRow={(row) => (
| {row.topic_code || "-"} |
{statusLabel(row.from_status)} |
{statusLabel(row.to_status)} |
{row.sla_hours == null ? "-" : String(row.sla_hours)} |
{listPreview(row.required_data_keys, "-")} |
{listPreview(row.required_mime_types, "-")} |
{boolLabel(row.enabled)} |
{String(row.sort_order ?? 0)} |
openEditRecordModal("statusTransitions", row)}
/>
deleteRecord("statusTransitions", row.id)}
tone="danger"
/>
|
)}
/>
>
) : null}
{configActiveKey === "users" ? (
toggleTableSort("users", field)}
sortClause={(tables.users.sort && tables.users.sort[0]) || TABLE_SERVER_CONFIG.users.sort[0]}
renderRow={(row) => (
|
|
{row.email || "-"} |
{roleLabel(row.role)} |
{resolveReferenceLabel({ table: "topics", value_field: "code", label_field: "name" }, row.primary_topic_code)} |
{row.default_rate == null ? "-" : String(row.default_rate)} |
{row.salary_percent == null ? "-" : String(row.salary_percent)} |
{boolLabel(row.is_active)} |
{row.responsible || "-"} |
{fmtDate(row.created_at)} |
openEditRecordModal("users", row)} />
deleteRecord("users", row.id)} tone="danger" />
|
)}
/>
) : null}
{configActiveKey === "userTopics" ? (
toggleTableSort("userTopics", field)}
sortClause={(tables.userTopics.sort && tables.userTopics.sort[0]) || TABLE_SERVER_CONFIG.userTopics.sort[0]}
renderRow={(row) => {
const lawyer = (dictionaries.users || []).find((item) => String(item.id) === String(row.admin_user_id));
const lawyerLabel = lawyer ? (lawyer.name || lawyer.email || row.admin_user_id) : row.admin_user_id || "-";
return (
| {lawyerLabel} |
{row.topic_code || "-"} |
{row.responsible || "-"} |
{fmtDate(row.created_at)} |
openEditRecordModal("userTopics", row)} />
deleteRecord("userTopics", row.id)} tone="danger" />
|
);
}}
/>
) : null}
{configActiveKey && !KNOWN_CONFIG_TABLE_KEYS.has(configActiveKey) ? (
toggleTableSort(configActiveKey, field)}
sortClause={
(activeConfigTableState.sort && activeConfigTableState.sort[0]) ||
((resolveTableConfig(configActiveKey)?.sort || [])[0])
}
renderRow={(row) => (
{(activeConfigMeta?.columns || []).map((column) => {
const key = String(column.name || "");
const value = row[key];
if (column.kind === "boolean") return | {boolLabel(Boolean(value))} | ;
if (column.kind === "date" || column.kind === "datetime") return {fmtDate(value)} | ;
if (column.kind === "json") return {value == null ? "-" : JSON.stringify(value)} | ;
const reference = normalizeReferenceMeta(column.reference);
if (reference) return {resolveReferenceLabel(reference, value)} | ;
return {value == null || value === "" ? "-" : String(value)} | ;
})}
{canUpdateInConfig || canDeleteInConfig ? (
{canUpdateInConfig ? (
openEditRecordModal(configActiveKey, row)} />
) : null}
{canDeleteInConfig ? (
deleteRecord(configActiveKey, row.id)} tone="danger" />
) : null}
|
) : null}
)}
/>
) : null}
loadPrevPage(configActiveKey)}
onNext={() => loadNextPage(configActiveKey)}
onLoadAll={() => loadAllRows(configActiveKey)}
/>
>
);
}
export default ConfigSection;