fix(ui): refine config save guardrails (#1707)
* fix: refine config save guardrails * docs: add changelog for config save guardrails (#1707) (thanks @Glucksberg)
This commit is contained in:
committed by
GitHub
parent
bac80f0886
commit
495616d13e
@@ -26,6 +26,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.
|
- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.
|
||||||
- Web UI: hide internal `message_id` hints in chat bubbles.
|
- Web UI: hide internal `message_id` hints in chat bubbles.
|
||||||
- Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.
|
- Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.
|
||||||
|
- Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg.
|
||||||
- Heartbeat: normalize target identifiers for consistent routing.
|
- Heartbeat: normalize target identifiers for consistent routing.
|
||||||
- TUI: reload history after gateway reconnect to restore session state. (#1663)
|
- TUI: reload history after gateway reconnect to restore session state. (#1663)
|
||||||
- Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639)
|
- Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639)
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export function connectGateway(host: GatewayHost) {
|
|||||||
mode: "webchat",
|
mode: "webchat",
|
||||||
onHello: (hello) => {
|
onHello: (hello) => {
|
||||||
host.connected = true;
|
host.connected = true;
|
||||||
|
host.lastError = null;
|
||||||
host.hello = hello;
|
host.hello = hello;
|
||||||
applySnapshot(host, hello);
|
applySnapshot(host, hello);
|
||||||
void loadAssistantIdentity(host as unknown as ClawdbotApp);
|
void loadAssistantIdentity(host as unknown as ClawdbotApp);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ describe("config view", () => {
|
|||||||
onSubsectionChange: vi.fn(),
|
onSubsectionChange: vi.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
it("disables save when form is unsafe", () => {
|
it("allows save when form is unsafe", () => {
|
||||||
const container = document.createElement("div");
|
const container = document.createElement("div");
|
||||||
render(
|
render(
|
||||||
renderConfig({
|
renderConfig({
|
||||||
@@ -59,6 +59,28 @@ describe("config view", () => {
|
|||||||
container,
|
container,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const saveButton = Array.from(
|
||||||
|
container.querySelectorAll("button"),
|
||||||
|
).find((btn) => btn.textContent?.trim() === "Save") as
|
||||||
|
| HTMLButtonElement
|
||||||
|
| undefined;
|
||||||
|
expect(saveButton).not.toBeUndefined();
|
||||||
|
expect(saveButton?.disabled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disables save when schema is missing", () => {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
render(
|
||||||
|
renderConfig({
|
||||||
|
...baseProps(),
|
||||||
|
schema: null,
|
||||||
|
formMode: "form",
|
||||||
|
formValue: { gateway: { mode: "local" } },
|
||||||
|
originalValue: {},
|
||||||
|
}),
|
||||||
|
container,
|
||||||
|
);
|
||||||
|
|
||||||
const saveButton = Array.from(
|
const saveButton = Array.from(
|
||||||
container.querySelectorAll("button"),
|
container.querySelectorAll("button"),
|
||||||
).find((btn) => btn.textContent?.trim() === "Save") as
|
).find((btn) => btn.textContent?.trim() === "Save") as
|
||||||
|
|||||||
@@ -233,10 +233,10 @@ export function renderConfig(props: ConfigProps) {
|
|||||||
const hasRawChanges = props.formMode === "raw" && props.raw !== props.originalRaw;
|
const hasRawChanges = props.formMode === "raw" && props.raw !== props.originalRaw;
|
||||||
const hasChanges = props.formMode === "form" ? diff.length > 0 : hasRawChanges;
|
const hasChanges = props.formMode === "form" ? diff.length > 0 : hasRawChanges;
|
||||||
|
|
||||||
// Save/apply buttons require actual changes to be enabled
|
// Save/apply buttons require actual changes to be enabled.
|
||||||
// Note: formUnsafe warns about unsupported schema paths but shouldn't block saving
|
// Note: formUnsafe warns about unsupported schema paths but shouldn't block saving.
|
||||||
const canSaveForm =
|
const canSaveForm =
|
||||||
Boolean(props.formValue) && !props.loading;
|
Boolean(props.formValue) && !props.loading && Boolean(analysis.schema);
|
||||||
const canSave =
|
const canSave =
|
||||||
props.connected &&
|
props.connected &&
|
||||||
!props.saving &&
|
!props.saving &&
|
||||||
|
|||||||
Reference in New Issue
Block a user