mirror of
https://github.com/hansjone/netx.git
synced 2026-10-12 13:00:57 +08:00
Improve biz_state monitor-item table UX with select-all and visible scroll.
Keep the horizontal scrollbar in the viewport, sticky enable/title columns, and show selected/total (and unbound) counts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a1acd4c2af
commit
86fcb98218
4 changed files with 193 additions and 38 deletions
|
|
@ -455,6 +455,28 @@ export function BizStatePage() {
|
|||
[profiles],
|
||||
);
|
||||
|
||||
const profileSelectionStats = useMemo(() => {
|
||||
const items = (detail?.items || []) as Array<{
|
||||
source_profile_id?: string;
|
||||
enabled?: boolean;
|
||||
bindings?: unknown[];
|
||||
}>;
|
||||
const byId = new Map(items.map((it) => [String(it.source_profile_id || ""), it]));
|
||||
let enabled = 0;
|
||||
let unbound = 0;
|
||||
for (const prof of collectProfiles) {
|
||||
const it = byId.get(prof.profile_id);
|
||||
if (!it?.enabled) continue;
|
||||
enabled += 1;
|
||||
if ((prof.placeholders || []).length > 0) {
|
||||
const binds = (it.bindings || []) as Array<{ value?: string }>;
|
||||
const hasVal = binds.some((b) => String(b.value || "").trim());
|
||||
if (!hasVal) unbound += 1;
|
||||
}
|
||||
}
|
||||
return { total: collectProfiles.length, enabled, unbound };
|
||||
}, [collectProfiles, detail?.items]);
|
||||
|
||||
const sheetTabs = useMemo(
|
||||
() => (batchDetail ? buildSheetTabs(batchDetail, t) : []),
|
||||
[batchDetail, t],
|
||||
|
|
@ -951,6 +973,40 @@ export function BizStatePage() {
|
|||
}
|
||||
};
|
||||
|
||||
const setAllProfilesEnabled = async (enable: boolean) => {
|
||||
if (!taskId || !detail || !collectProfiles.length) return;
|
||||
const items = [...(detail.items || [])] as any[];
|
||||
const byId = new Map(items.map((it, i) => [String(it.source_profile_id || ""), i]));
|
||||
for (const prof of collectProfiles) {
|
||||
const idx = byId.get(prof.profile_id);
|
||||
if (idx != null) {
|
||||
items[idx] = { ...items[idx], enabled: enable };
|
||||
} else if (enable) {
|
||||
items.push({
|
||||
source_profile_id: prof.profile_id,
|
||||
kind: "catalog",
|
||||
enabled: true,
|
||||
title: prof.title,
|
||||
bindings: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
setBusy(true);
|
||||
try {
|
||||
await bizStatePatchTask(taskId, { items });
|
||||
await loadTask(taskId);
|
||||
showOk(
|
||||
enable
|
||||
? t("bizState.profilesSelectAllOk", { n: collectProfiles.length })
|
||||
: t("bizState.profilesDeselectAllOk"),
|
||||
);
|
||||
} catch (e) {
|
||||
showError(formatErr(e));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const closeBindModal = () => {
|
||||
setBindItemId("");
|
||||
setCandidates([]);
|
||||
|
|
@ -1650,7 +1706,34 @@ export function BizStatePage() {
|
|||
</div>
|
||||
|
||||
{taskTab === "profiles" ? (
|
||||
<>
|
||||
<div className="bs-profiles-panel">
|
||||
<div className="btn-row bs-profiles-toolbar">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
isDisabled={busy || !collectProfiles.length}
|
||||
onPress={() => void setAllProfilesEnabled(true)}
|
||||
>
|
||||
{t("bizState.profilesSelectAll")}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
isDisabled={busy || !profileSelectionStats.enabled}
|
||||
onPress={() => void setAllProfilesEnabled(false)}
|
||||
>
|
||||
{t("bizState.profilesDeselectAll")}
|
||||
</Button>
|
||||
<span className="muted bs-profiles-stats">
|
||||
{t("bizState.profilesSelectionStats", {
|
||||
enabled: profileSelectionStats.enabled,
|
||||
total: profileSelectionStats.total,
|
||||
})}
|
||||
{profileSelectionStats.unbound
|
||||
? ` · ${t("bizState.profilesUnboundStats", { n: profileSelectionStats.unbound })}`
|
||||
: ""}
|
||||
</span>
|
||||
</div>
|
||||
<div className="pt-list-table-wrap bs-profiles-table-wrap">
|
||||
<table className="data-table pt-list-table bs-profiles-table">
|
||||
<thead>
|
||||
|
|
@ -1658,8 +1741,8 @@ export function BizStatePage() {
|
|||
<th>{t("bizState.enable")}</th>
|
||||
<th>{t("bizState.profiles")}</th>
|
||||
<th>{t("bizState.colLane")}</th>
|
||||
<th>{t("bizState.params")}</th>
|
||||
<th>{t("bizState.colActions")}</th>
|
||||
<th>{t("bizState.params")}</th>
|
||||
<th>{t("bizState.command")}</th>
|
||||
<th>{t("bizState.colAux")}</th>
|
||||
</tr>
|
||||
|
|
@ -1710,7 +1793,7 @@ export function BizStatePage() {
|
|||
: "—";
|
||||
return (
|
||||
<tr key={prof.profile_id}>
|
||||
<td>
|
||||
<td className="bs-profiles-sticky-col bs-profiles-sticky-col--1">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={enabled}
|
||||
|
|
@ -1718,7 +1801,7 @@ export function BizStatePage() {
|
|||
onChange={(e) => void toggleProfileItem(prof, e.target.checked)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<td className="bs-profiles-sticky-col bs-profiles-sticky-col--2">
|
||||
<div className="pt-list-task-name">{prof.title}</div>
|
||||
{prof.description ? <div className="muted">{prof.description}</div> : null}
|
||||
{needsBind ? (
|
||||
|
|
@ -1736,6 +1819,22 @@ export function BizStatePage() {
|
|||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="bs-actions-cell">
|
||||
{needsBind && enabled && it ? (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
isDisabled={busy || discoverLoading}
|
||||
onPress={() => void startDiscover(it)}
|
||||
>
|
||||
{t("bizState.discoverBind")}
|
||||
</Button>
|
||||
) : needsBind ? (
|
||||
<span className="muted">—</span>
|
||||
) : (
|
||||
"—"
|
||||
)}
|
||||
</td>
|
||||
<td className="bs-params-cell">
|
||||
{needsBind ? (
|
||||
<div
|
||||
|
|
@ -1761,22 +1860,6 @@ export function BizStatePage() {
|
|||
"—"
|
||||
)}
|
||||
</td>
|
||||
<td className="bs-actions-cell">
|
||||
{needsBind && enabled && it ? (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
isDisabled={busy || discoverLoading}
|
||||
onPress={() => void startDiscover(it)}
|
||||
>
|
||||
{t("bizState.discoverBind")}
|
||||
</Button>
|
||||
) : needsBind ? (
|
||||
<span className="muted">—</span>
|
||||
) : (
|
||||
"—"
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<code className="bs-cmd-cell" title={prof.command_template}>
|
||||
{prof.command_template}
|
||||
|
|
@ -1809,7 +1892,7 @@ export function BizStatePage() {
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pt-list-table-wrap">
|
||||
<div className="btn-row" style={{ marginBottom: 8, gap: 8, flexWrap: "wrap" }}>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue