69 lines
3.1 KiB
JavaScript
69 lines
3.1 KiB
JavaScript
// What saving the machine form should do about its controlling-PC link.
|
|
//
|
|
// Pulled out of MachineForm.vue because the component decided this inline and
|
|
// got it wrong in a way nothing could catch: it treated "I know the id of the
|
|
// existing link" as "there IS an existing link". The id was read from
|
|
// `rel.assetrelationshipid`, a key the API has never emitted - the column is
|
|
// `relationshipid` and to_dict serialises column names - so it was undefined on
|
|
// every load, and the two questions gave opposite answers on a machine that
|
|
// plainly had a controlling PC.
|
|
//
|
|
// Both failures followed from that single conflation:
|
|
// - Saving ANY change, a map position included, re-POSTed the link that was
|
|
// already there. The server answered 409 "this relationship already
|
|
// exists", correctly, and the form surfaced it as a failed save.
|
|
// - Clearing the controlling PC did nothing at all, because "no selection and
|
|
// no known id" reads as "nothing to do".
|
|
//
|
|
// So THE LOADED PC IS THE AUTHORITY on whether a link exists, and the
|
|
// relationship id is only ever used to delete a row. If the id goes missing
|
|
// again the worst case is a link that cannot be removed, never a duplicate
|
|
// create.
|
|
|
|
/**
|
|
* Decide what to do with the controlling-PC link on save.
|
|
*
|
|
* @param {object} state
|
|
* @param {number|string|null} state.selected - PC id chosen in the form now.
|
|
* @param {number|string|null} state.loaded - PC id the link named on load.
|
|
* @param {number|null} state.existingRelationshipId - row id to delete, if known.
|
|
* @returns {{action: 'none'|'unlink'|'link'|'relink', deleteId: number|null}}
|
|
*/
|
|
export function planControllingPcChange({ selected, loaded, existingRelationshipId } = {}) {
|
|
const now = normalize(selected)
|
|
const before = normalize(loaded)
|
|
const deleteId = existingRelationshipId ?? null
|
|
|
|
// Unchanged is not a change. Somebody moving a marker on the map is not
|
|
// asking to be re-linked to the same PC, and delete-then-create on an
|
|
// untouched link opens a window where the machine has no controlling PC -
|
|
// which resolve_asset_position walks across, so a failure between the two
|
|
// calls would lose the machine's inherited position along with the link.
|
|
if (now === before) {
|
|
return { action: 'none', deleteId: null }
|
|
}
|
|
|
|
if (now === null) {
|
|
// Cleared. Nothing to delete means nothing to do, rather than an error:
|
|
// the link is already absent, which is the state being asked for.
|
|
return deleteId === null
|
|
? { action: 'none', deleteId: null }
|
|
: { action: 'unlink', deleteId }
|
|
}
|
|
|
|
if (before === null) {
|
|
return { action: 'link', deleteId: null }
|
|
}
|
|
|
|
// Swapped to a different PC. The old row goes first: 'controls' is singular
|
|
// per source, not per target, so leaving it would leave the machine with two.
|
|
return { action: 'relink', deleteId }
|
|
}
|
|
|
|
// Ids arrive as numbers from the API and strings from a <select>, and '' means
|
|
// "nothing chosen". Comparing those raw is how an unchanged PC looks changed.
|
|
function normalize(value) {
|
|
if (value === null || value === undefined || value === '') return null
|
|
return String(value)
|
|
}
|