Files
Wiring-Designer/frontend/js/api.js
T
Kyle 82d8570767 Add formboard harness layout designer
Separate read-only canvas for physically laying out wire harnesses.
Syncs connectors from the main diagram (one-way), no changes flow back.

Backend:
- FormboardLayout, FormboardNode, FormboardSegment models
- /api/formboard/ router: get/create layout, sync connectors from diagram,
  full CRUD for nodes and segments

Frontend (formboard.js):
- Konva canvas: drag nodes, click-to-connect segments, zoom/pan
- Node types: Connector (blue rect, synced from diagram), Branch (green
  circle, manual), Splice/joint (orange diamond, manual)
- Segments scale thickness by wire count; fitting overlays for open,
  split loom, conduit, heat shrink, tape wrap
- Right-click context menu for delete on nodes/segments

App integration:
- "⊞ Formboard" tab in view-tab-bar (distinct color, independent of views)
- Sub-toolbar: Select / Branch / Splice / Connect / Sync / Fit / Delete
- Right panel swaps to formboard props when active:
  - Node: label, notes, linked device info
  - Segment: fitting type, fitting color, label, length, wire checklist
    (checkboxes for every wire in the main diagram)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 07:18:43 -04:00

82 lines
5.8 KiB
JavaScript

const API_BASE = "/api";
async function apiFetch(path, options = {}) {
const res = await fetch(`${API_BASE}${path}`, options);
if (!res.ok) {
const text = await res.text();
throw new Error(`API ${res.status}: ${text}`);
}
return res.json();
}
const api = {
diagrams: {
list: () => apiFetch("/diagrams/"),
create: (data) => apiFetch("/diagrams/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
get: (id) => apiFetch(`/diagrams/${id}`),
update: (id, data) => apiFetch(`/diagrams/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/diagrams/${id}`, { method: "DELETE" }),
duplicate: (id) => apiFetch(`/diagrams/${id}/duplicate`, { method: "POST" }),
import: (data) => apiFetch("/diagrams/import", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
},
views: {
listForDiagram: (diagramId) => apiFetch(`/views/diagram/${diagramId}`),
create: (data) => apiFetch("/views/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
update: (id, data) => apiFetch(`/views/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/views/${id}`, { method: "DELETE" }),
getLayout: (id) => apiFetch(`/views/${id}/layout`),
updateDevicePos: (viewId, deviceId, data) => apiFetch(`/views/${viewId}/devices/${deviceId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
updateWireWaypoints: (viewId, wireId, data) => apiFetch(`/views/${viewId}/wires/${wireId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
},
devices: {
create: (data) => apiFetch("/devices/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
update: (id, data) => apiFetch(`/devices/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/devices/${id}`, { method: "DELETE" }),
},
wires: {
create: (data) => apiFetch("/wires/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
update: (id, data) => apiFetch(`/wires/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/wires/${id}`, { method: "DELETE" }),
},
bundles: {
listForDiagram: (diagramId) => apiFetch(`/bundles/diagram/${diagramId}`),
create: (data) => apiFetch("/bundles/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
update: (id, data) => apiFetch(`/bundles/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/bundles/${id}`, { method: "DELETE" }),
},
connectors: {
list: () => apiFetch("/connectors/"),
create: (data) => apiFetch("/connectors/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
update: (id, data) => apiFetch(`/connectors/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
delete: (id) => apiFetch(`/connectors/${id}`, { method: "DELETE" }),
},
octopart: {
status: () => apiFetch("/octopart/status"),
search: (q, n) => apiFetch(`/octopart/search?q=${encodeURIComponent(q)}&limit=${n || 8}`),
},
export: {
bomUrl: (id) => `${API_BASE}/export/${id}/bom/csv`,
assemblyUrl: (id) => `${API_BASE}/export/${id}/assembly/txt`,
jsonUrl: (id) => `${API_BASE}/export/${id}/json`,
formboardUrl: (id) => `${API_BASE}/export/${id}/formboard`,
},
formboard: {
get: (diagramId) => apiFetch(`/formboard/${diagramId}`),
sync: (diagramId) => apiFetch(`/formboard/${diagramId}/sync`, { method: "POST" }),
createNode: (diagramId, data) => apiFetch(`/formboard/${diagramId}/nodes`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
updateNode: (nodeId, data) => apiFetch(`/formboard/nodes/${nodeId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
deleteNode: (nodeId) => apiFetch(`/formboard/nodes/${nodeId}`, { method: "DELETE" }),
createSegment: (diagramId, data) => apiFetch(`/formboard/${diagramId}/segments`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
updateSegment: (segId, data) => apiFetch(`/formboard/segments/${segId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }),
deleteSegment: (segId) => apiFetch(`/formboard/segments/${segId}`, { method: "DELETE" }),
},
git: {
status: () => apiFetch("/git/status"),
log: (diagram_id) => apiFetch(diagram_id != null ? `/git/log?diagram_id=${diagram_id}` : "/git/log"),
commit: (message, diagram_id) => apiFetch("/git/commit", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message, diagram_id: diagram_id ?? null }) }),
push: () => apiFetch("/git/push", { method: "POST" }),
history: (hash) => apiFetch(`/git/history/${hash}`),
restore: (commit_hash, filepath, name) => apiFetch("/git/restore", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ commit_hash, filepath, name }) }),
},
};