dashboard: /approve, /deny, /answer-question, /cancel-question, /request-spawn return 200; matching forms opt out of refetch
This commit is contained in:
parent
d8d393da6d
commit
f559441a06
2 changed files with 28 additions and 12 deletions
|
|
@ -22,10 +22,14 @@
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
const form = (action, btnClass, btnLabel, confirmMsg, extra = {}) => {
|
const form = (action, btnClass, btnLabel, confirmMsg, extra = {}, opts = {}) => {
|
||||||
const f = el('form', {
|
const f = el('form', {
|
||||||
method: 'POST', action, class: 'inline', 'data-async': '',
|
method: 'POST', action, class: 'inline', 'data-async': '',
|
||||||
...(confirmMsg ? { 'data-confirm': confirmMsg } : {}),
|
...(confirmMsg ? { 'data-confirm': confirmMsg } : {}),
|
||||||
|
// Endpoints whose mutation fires a DashboardEvent (and whose
|
||||||
|
// derived store applies it live) opt out of the post-submit
|
||||||
|
// /api/state refetch. See the async-form handler.
|
||||||
|
...(opts.noRefresh ? { 'data-no-refresh': '' } : {}),
|
||||||
});
|
});
|
||||||
for (const [name, value] of Object.entries(extra)) {
|
for (const [name, value] of Object.entries(extra)) {
|
||||||
f.append(el('input', { type: 'hidden', name, value }));
|
f.append(el('input', { type: 'hidden', name, value }));
|
||||||
|
|
@ -195,7 +199,15 @@
|
||||||
if (btn) { btn.disabled = false; btn.innerHTML = original; }
|
if (btn) { btn.disabled = false; btn.innerHTML = original; }
|
||||||
// Clear text inputs whose value was just submitted.
|
// Clear text inputs whose value was just submitted.
|
||||||
f.querySelectorAll('input[type="text"], input:not([type]), textarea').forEach((i) => { i.value = ''; });
|
f.querySelectorAll('input[type="text"], input:not([type]), textarea').forEach((i) => { i.value = ''; });
|
||||||
refreshState();
|
// Forms whose endpoint already emits a DashboardEvent that
|
||||||
|
// updates the derived store can opt out of the post-submit
|
||||||
|
// /api/state refetch (the event delivers the new row faster
|
||||||
|
// than the snapshot poll anyway). Container-lifecycle forms
|
||||||
|
// still rely on the refresh since `ContainerView` isn't yet
|
||||||
|
// event-derivable.
|
||||||
|
if (!f.hasAttribute('data-no-refresh')) {
|
||||||
|
refreshState();
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('action failed: ' + err);
|
alert('action failed: ' + err);
|
||||||
if (btn) { btn.disabled = false; btn.innerHTML = original; }
|
if (btn) { btn.disabled = false; btn.innerHTML = original; }
|
||||||
|
|
@ -587,7 +599,7 @@
|
||||||
li.append(head, el('div', { class: 'q-body' }, q.question));
|
li.append(head, el('div', { class: 'q-body' }, q.question));
|
||||||
const f = el('form', {
|
const f = el('form', {
|
||||||
method: 'POST', action: '/answer-question/' + q.id,
|
method: 'POST', action: '/answer-question/' + q.id,
|
||||||
class: 'qform', 'data-async': '',
|
class: 'qform', 'data-async': '', 'data-no-refresh': '',
|
||||||
});
|
});
|
||||||
const hasOptions = q.options && q.options.length;
|
const hasOptions = q.options && q.options.length;
|
||||||
const isMulti = !!q.multi && hasOptions;
|
const isMulti = !!q.multi && hasOptions;
|
||||||
|
|
@ -638,7 +650,7 @@
|
||||||
// merge-on-submit handler attached to the main form.
|
// merge-on-submit handler attached to the main form.
|
||||||
const cancelForm = el('form', {
|
const cancelForm = el('form', {
|
||||||
method: 'POST', action: '/cancel-question/' + q.id,
|
method: 'POST', action: '/cancel-question/' + q.id,
|
||||||
class: 'qform-cancel', 'data-async': '',
|
class: 'qform-cancel', 'data-async': '', 'data-no-refresh': '',
|
||||||
'data-confirm': 'cancel this question? manager will see '
|
'data-confirm': 'cancel this question? manager will see '
|
||||||
+ '"[cancelled]" as the answer.',
|
+ '"[cancelled]" as the answer.',
|
||||||
});
|
});
|
||||||
|
|
@ -768,7 +780,7 @@
|
||||||
// the containers list (the agent doesn't exist yet).
|
// the containers list (the agent doesn't exist yet).
|
||||||
const spawn = el('form', {
|
const spawn = el('form', {
|
||||||
method: 'POST', action: '/request-spawn',
|
method: 'POST', action: '/request-spawn',
|
||||||
class: 'spawnform', 'data-async': '',
|
class: 'spawnform', 'data-async': '', 'data-no-refresh': '',
|
||||||
});
|
});
|
||||||
spawn.append(
|
spawn.append(
|
||||||
el('input', {
|
el('input', {
|
||||||
|
|
@ -851,13 +863,13 @@
|
||||||
// HelperEvent::ApprovalResolved { note }.
|
// HelperEvent::ApprovalResolved { note }.
|
||||||
const denyForm = el('form', {
|
const denyForm = el('form', {
|
||||||
method: 'POST', action: '/deny/' + a.id,
|
method: 'POST', action: '/deny/' + a.id,
|
||||||
class: 'inline', 'data-async': '',
|
class: 'inline', 'data-async': '', 'data-no-refresh': '',
|
||||||
'data-prompt': 'reason for denying (optional, sent to manager):',
|
'data-prompt': 'reason for denying (optional, sent to manager):',
|
||||||
});
|
});
|
||||||
denyForm.append(el('button', { type: 'submit', class: 'btn btn-deny' }, 'DENY'));
|
denyForm.append(el('button', { type: 'submit', class: 'btn btn-deny' }, 'DENY'));
|
||||||
row.append(
|
row.append(
|
||||||
' ',
|
' ',
|
||||||
form('/approve/' + a.id, 'btn-approve', '◆ APPR0VE'),
|
form('/approve/' + a.id, 'btn-approve', '◆ APPR0VE', null, {}, { noRefresh: true }),
|
||||||
' ',
|
' ',
|
||||||
denyForm,
|
denyForm,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -783,7 +783,11 @@ async fn dashboard_stream(
|
||||||
|
|
||||||
async fn post_approve(State(state): State<AppState>, AxumPath(id): AxumPath<i64>) -> Response {
|
async fn post_approve(State(state): State<AppState>, AxumPath(id): AxumPath<i64>) -> Response {
|
||||||
match actions::approve(state.coord.clone(), id).await {
|
match actions::approve(state.coord.clone(), id).await {
|
||||||
Ok(()) => Redirect::to("/").into_response(),
|
// 200 instead of 303 — `actions::approve` fires
|
||||||
|
// `ApprovalResolved` (success path) or the eventual failure
|
||||||
|
// event, both of which the dashboard's derived store applies
|
||||||
|
// live. The matching form carries `data-no-refresh`.
|
||||||
|
Ok(()) => (StatusCode::OK, "ok").into_response(),
|
||||||
Err(e) => error_response(&format!("approve {id} failed: {e:#}")),
|
Err(e) => error_response(&format!("approve {id} failed: {e:#}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -805,7 +809,7 @@ async fn post_deny(
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|s| !s.is_empty());
|
.filter(|s| !s.is_empty());
|
||||||
match actions::deny(&state.coord, id, note).await {
|
match actions::deny(&state.coord, id, note).await {
|
||||||
Ok(()) => Redirect::to("/").into_response(),
|
Ok(()) => (StatusCode::OK, "ok").into_response(),
|
||||||
Err(e) => error_response(&format!("deny {id} failed: {e:#}")),
|
Err(e) => error_response(&format!("deny {id} failed: {e:#}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -853,7 +857,7 @@ async fn post_answer_question(
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Redirect::to("/").into_response()
|
(StatusCode::OK, "ok").into_response()
|
||||||
}
|
}
|
||||||
Err(e) => error_response(&format!("answer {id} failed: {e:#}")),
|
Err(e) => error_response(&format!("answer {id} failed: {e:#}")),
|
||||||
}
|
}
|
||||||
|
|
@ -894,7 +898,7 @@ async fn post_cancel_question(
|
||||||
answerer: hive_sh4re::OPERATOR_RECIPIENT.to_owned(),
|
answerer: hive_sh4re::OPERATOR_RECIPIENT.to_owned(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Redirect::to("/").into_response()
|
(StatusCode::OK, "ok").into_response()
|
||||||
}
|
}
|
||||||
Err(e) => error_response(&format!("cancel-question {id} failed: {e:#}")),
|
Err(e) => error_response(&format!("cancel-question {id} failed: {e:#}")),
|
||||||
}
|
}
|
||||||
|
|
@ -1199,7 +1203,7 @@ async fn post_request_spawn(
|
||||||
state
|
state
|
||||||
.coord
|
.coord
|
||||||
.emit_approval_added(id, &name, "spawn", None, None, None);
|
.emit_approval_added(id, &name, "spawn", None, None, None);
|
||||||
Redirect::to("/").into_response()
|
(StatusCode::OK, "ok").into_response()
|
||||||
}
|
}
|
||||||
Err(e) => error_response(&format!("request-spawn {name} failed: {e:#}")),
|
Err(e) => error_response(&format!("request-spawn {name} failed: {e:#}")),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue