1import { j as e, C as o, a$ as S, b0 as H, by as q, a4 as h, L as i, I as g, S as ne, f as re, g as ie, h as le, l as V, bk as ce, B as u, r as c, d as oe, e as Z, J as x, y as p, K as f, a5 as J, T as U } from "./index-B9miRpwC.js"; 2import { u as ue, t as me } from "./zod-C9ZqaWF4.js"; 3import { a as he } from "./schema-B1XK9SWN.js"; 4import { S as je } from "./save-fTnHtNzX.js"; 5import { A as F, a as D, b as O, c as P, d as L, e as $, f as k, g as I } from "./alert-dialog-Cv39x3F5.js"; 6import { P as _ } from "./plus-BCaMPs2R.js"; 7import { P as G } from "./pencil-CWIGhVLH.js"; 8import "./types-DkmJhT54.js"; 9function W({ onSubmit: A, isSubmitting: j = !1, initialData: l, isEditing: v = !1 }) { 10 const a = ue({ resolver: me(he), defaultValues: l || { name: "", profileType: "A", referenceHeadTubeLength: 90, description: "" } }); 11 return e.jsxs("form", { onSubmit: a.handleSubmit(A), className: "space-y-6", children: [e.jsxs(o, { children: [e.jsxs(S, { children: [e.jsx(H, { children: "Headset Specifications" }), e.jsx(q, { children: "Define headset profile and reference dimensions" })] }), e.jsxs(h, { className: "space-y-4", children: [e.jsxs("div", { className: "space-y-2", children: [e.jsx(i, { htmlFor: "name", children: "Headset Name*" }), e.jsx(g, { id: "name", ...a.register("name"), placeholder: "e.g., ZS44/28.6 | ZS56/40", "data-testid": "input-headset-name" }), a.formState.errors.name && e.jsx("p", { className: "text-sm text-destructive", children: a.formState.errors.name.message })] }), e.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [e.jsxs("div", { className: "space-y-2", children: [e.jsx(i, { htmlFor: "profileType", children: "Profile Type*" }), e.jsxs(ne, { value: a.watch("profileType"), onValueChange: (y) => a.setValue("profileType", y), children: [e.jsx(re, { id: "profileType", "data-testid": "select-profile-type", children: e.jsx(ie, { placeholder: "Select profile type" }) }), e.jsxs(le, { children: [e.jsx(V, { value: "A", children: "Profile A (90mm reference)" }), e.jsx(V, { value: "B", children: "Profile B (160mm reference)" })] })] }), a.formState.errors.profileType && e.jsx("p", { className: "text-sm text-destructive", children: a.formState.errors.profileType.message })] }), e.jsxs("div", { className: "space-y-2", children: [e.jsx(i, { htmlFor: "referenceHeadTubeLength", children: "Reference Head Tube Length (mm)*" }), e.jsx(g, { id: "referenceHeadTubeLength", type: "number", step: "0.1", ...a.register("referenceHeadTubeLength", { valueAsNumber: !0 }), "data-testid": "input-reference-head-tube-length" }), a.formState.errors.referenceHeadTubeLength && e.jsx("p", { className: "text-sm text-destructive", children: a.formState.errors.referenceHeadTubeLength.message }), e.jsx("p", { className: "text-sm text-muted-foreground", children: "Typically 90.0 for Profile A, 160.0 for Profile B" })] })] }), e.jsxs("div", { className: "space-y-2", children: [e.jsx(i, { htmlFor: "description", children: "Description" }), e.jsx(ce, { id: "description", ...a.register("description"), placeholder: "e.g., Standard ZS44 upper and ZS56 lower headset", className: "resize-none", rows: 3, "data-testid": "input-description" })] })] })] }), e.jsx("div", { className: "flex justify-end", children: e.jsxs(u, { type: "submit", disabled: j, "data-testid": "button-save-headset", children: [e.jsx(je, { className: "w-4 h-4 mr-2" }), j ? "Saving..." : v ? "Update Headset" : "Create Headset"] }) })] }); 12} 13function Se() { 14 var R; 15 const [A, j] = c.useState(!1), [l, v] = c.useState(null), [a, y] = c.useState(null), [t, C] = c.useState(null), [n, N] = c.useState(null), [w, T] = c.useState(null), [b, E] = c.useState({ adjustment: 0, sortOrder: 0 }), { toast: d } = oe(), { data: r = [], isLoading: X } = Z({ queryKey: ["/api/admin/headsets"] }); 16 c.useEffect(() => { 17 r.length === 0 ? C(null) : (!t || !r.find((s) => s.id === t)) && C(r[0].id); 18 }, [r, t]); 19 const M = x({ mutationFn: async (s) => f("POST", "/api/admin/headsets", s), onSuccess: () => { 20 p.invalidateQueries({ queryKey: ["/api/admin/headsets"] }), j(!1), d({ title: "Success", description: "Headset created successfully" }); 21 }, onError: (s) => { 22 d({ title: "Error", description: s.message, variant: "destructive" }); 23 } }), K = x({ mutationFn: async ({ id: s, data: m }) => f("PATCH", `/api/admin/headsets/${s}`, m), onSuccess: () => { 24 p.invalidateQueries({ queryKey: ["/api/admin/headsets"] }), v(null), d({ title: "Success", description: "Headset updated successfully" }); 25 }, onError: (s) => { 26 d({ title: "Error", description: s.message, variant: "destructive" }); 27 } }), Y = x({ mutationFn: async (s) => f("DELETE", `/api/admin/headsets/${s}`), onSuccess: (s, m) => { 28 p.invalidateQueries({ queryKey: ["/api/admin/headsets"] }), t === m && C(null), d({ title: "Success", description: "Headset deleted successfully" }), y(null); 29 }, onError: (s) => { 30 d({ title: "Error", description: s.message, variant: "destructive" }); 31 } }), { data: Q = [], isLoading: ee } = Z({ queryKey: ["/api/admin/headsets", t, "adjustments"], enabled: !!t }), B = x({ mutationFn: async (s) => { 32 if (!t) throw new Error("No headset selected"); 33 return f("POST", `/api/admin/headsets/${t}/adjustments`, s); 34 }, onSuccess: () => { 35 p.invalidateQueries({ queryKey: ["/api/admin/headsets", t, "adjustments"] }), E({ adjustment: 0, sortOrder: 0 }), d({ title: "Success", description: "Headset adjustment created successfully" }); 36 }, onError: (s) => { 37 d({ title: "Error", description: s.message, variant: "destructive" }); 38 } }), se = x({ mutationFn: async ({ id: s, data: m }) => { 39 if (!t) throw new Error("No headset selected"); 40 return f("PATCH", `/api/admin/headsets/${t}/adjustments/${s}`, m); 41 }, onSuccess: () => { 42 p.invalidateQueries({ queryKey: ["/api/admin/headsets", t, "adjustments"] }), N(null), d({ title: "Success", description: "Headset adjustment updated successfully" }); 43 }, onError: (s) => { 44 d({ title: "Error", description: s.message, variant: "destructive" }); 45 } }), te = x({ mutationFn: async (s) => { 46 if (!t) throw new Error("No headset selected"); 47 return f("DELETE", `/api/admin/headsets/${t}/adjustments/${s}`); 48 }, onSuccess: () => { 49 p.invalidateQueries({ queryKey: ["/api/admin/headsets", t, "adjustments"] }), d({ title: "Success", description: "Headset adjustment deleted successfully" }), T(null); 50 }, onError: (s) => { 51 d({ title: "Error", description: s.message, variant: "destructive" }); 52 } }), z = (s) => { 53 l ? K.mutate({ id: l.id, data: s }) : M.mutate(s); 54 }, ae = (s) => { 55 v(s), j(!1); 56 }, de = () => { 57 v(null); 58 }; 59 return e.jsxs("div", { className: "space-y-6", children: [e.jsxs("div", { className: "flex items-center justify-between", children: [e.jsxs("div", { children: [e.jsx("h1", { className: "text-3xl font-bold", children: "Headset Profiles" }), e.jsx("p", { className: "text-muted-foreground", children: "Manage headset specifications and reference dimensions" })] }), e.jsxs(u, { onClick: () => j(!A), "data-testid": "button-add-headset", children: [e.jsx(_, { className: "w-4 h-4 mr-2" }), "Add Headset"] })] }), A && !l && e.jsx(W, { onSubmit: z, isSubmitting: M.isPending }), l && e.jsxs(o, { children: [e.jsx(S, { children: e.jsxs("div", { className: "flex items-center justify-between", children: [e.jsxs(H, { children: ["Edit Headset: ", l.name] }), e.jsx(u, { variant: "ghost", onClick: de, "data-testid": "button-cancel-edit", children: "Cancel" })] }) }), e.jsx(h, { children: e.jsx(W, { onSubmit: z, isSubmitting: K.isPending, initialData: l, isEditing: !0 }) })] }), X ? e.jsx("p", { children: "Loading headsets..." }) : e.jsx("div", { className: "grid gap-4", children: r.length === 0 ? e.jsx(o, { children: e.jsx(h, { className: "pt-6", children: e.jsx("p", { className: "text-center text-muted-foreground", children: "N
59o headsets found. Create one to get started." }) }) }) : r.map((s) => e.jsxs(o, { children: [e.jsx(S, { children: e.jsxs("div", { className: "flex items-start justify-between", children: [e.jsxs("div", { className: "space-y-2", children: [e.jsxs("div", { className: "flex items-center gap-2", children: [e.jsx(H, { children: s.name }), e.jsxs(J, { variant: "secondary", "data-testid": `badge-profile-${s.id}`, children: ["Profile ", s.profileType] })] }), s.description && e.jsx(q, { children: s.description })] }), e.jsxs("div", { className: "flex gap-2", children: [e.jsx(u, { variant: "ghost", size: "icon", onClick: () => ae(s), "data-testid": `button-edit-headset-${s.id}`, children: e.jsx(G, { className: "w-4 h-4" }) }), e.jsx(u, { variant: "ghost", size: "icon", onClick: () => y(s.id), "data-testid": `button-delete-headset-${s.id}`, children: e.jsx(U, { className: "w-4 h-4" }) })] })] }) }), e.jsx(h, { children: e.jsx("div", { className: "flex items-center gap-4 text-sm", children: e.jsxs("div", { children: [e.jsx("p", { className: "text-muted-foreground", children: "Reference Head Tube Length" }), e.jsxs("p", { className: "font-medium", children: [s.referenceHeadTubeLength, " mm"] })] }) }) })] }, s.id)) }), e.jsxs("div", { className: "mt-12 space-y-4", children: [e.jsxs("div", { className: "flex items-start justify-between", children: [e.jsxs("div", { children: [e.jsx("h2", { className: "text-2xl font-bold", children: "Headset Angle Adjustments" }), e.jsx("p", { className: "text-muted-foreground", children: "Manage available headset angle adjustments (\xB0) for each headset profile" })] }), r.length > 0 && e.jsxs("div", { className: "space-y-2", children: [e.jsx(i, { htmlFor: "headset-selector", children: "Select Headset Profile" }), e.jsx("select", { id: "headset-selector", value: t || "", onChange: (s) => C(s.target.value), className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "data-testid": "select-headset", children: r.map((s) => e.jsx("option", { value: s.id, children: s.name }, s.id)) })] })] }), e.jsxs(o, { children: [e.jsxs(S, { children: [e.jsx(H, { children: "Add New Adjustment" }), e.jsxs(q, { children: ["Create a new angle adjustment for ", ((R = r.find((s) => s.id === t)) == null ? void 0 : R.name) || "selected headset"] })] }), e.jsx(h, { children: e.jsxs("div", { className: "flex items-end gap-3", children: [e.jsxs("div", { className: "flex-1 space-y-2", children: [e.jsx(i, { htmlFor: "new-adjustment", children: "Adjustment (\xB0)" }), e.jsx(g, { id: "new-adjustment", type: "number", step: "0.1", value: b.adjustment, onChange: (s) => E({ ...b, adjustment: parseFloat(s.target.value) || 0 }), placeholder: "e.g., -2, 0, 1.5", "data-testid": "input-new-adjustment" })] }), e.jsxs("div", { className: "flex-1 space-y-2", children: [e.jsx(i, { htmlFor: "new-sort-order", children: "Sort Order" }), e.jsx(g, { id: "new-sort-order", type: "number", value: b.sortOrder, onChange: (s) => E({ ...b, sortOrder: parseInt(s.target.value) || 0 }), placeholder: "e.g., 0, 1, 2", "data-testid": "input-new-sort-order" })] }), e.jsxs(u, { onClick: () => B.mutate(b), disabled: B.isPending, "data-testid": "button-add-adjustment", children: [e.jsx(_, { className: "w-4 h-4 mr-2" }), "Add Adjustment"] })] }) })] }), ee ? e.jsx(o, { children: e.jsx(h, { className: "py-8", children: e.jsx("p", { className: "text-center text-muted-foreground", children: "Loading adjustments..." }) }) }) : Q.length === 0 ? e.jsx(o, { children: e.jsx(h, { className: "py-8", children: e.jsx("p", { className: "text-center text-muted-foreground", children: "No headset adjustments yet. Add your first adjustment above." }) }) }) : e.jsx("div", { className: "space-y-2", children: Q.sort((s, m) => s.sortOrder - m.sortOrder).map((s) => e.jsx(o, { children: e.jsx(S, { children: e.jsxs("div", { className: "flex items-center justify-between", children: [e.jsxs("div", { className: "flex items-center gap-3", children: [e.jsxs(J, { variant: s.adjustment === 0 ? "default" : "secondary", children: [s.adjustment >
59 0 ? "+" : "", s.adjustment, "\xB0"] }), e.jsx("div", { children: e.jsxs("p", { className: "text-sm text-muted-foreground", children: ["Sort Order: ", s.sortOrder] }) })] }), e.jsxs("div", { className: "flex items-center gap-2", children: [e.jsx(u, { variant: "ghost", size: "icon", onClick: () => N(s), "data-testid": `button-edit-adjustment-${s.id}`, children: e.jsx(G, { className: "w-4 h-4" }) }), e.jsx(u, { variant: "ghost", size: "icon", onClick: () => T(s.id), "data-testid": `button-delete-adjustment-${s.id}`, children: e.jsx(U, { className: "w-4 h-4" }) })] })] }) }) }, s.id)) })] }), e.jsx(F, { open: n !== null, onOpenChange: () => N(null), children: e.jsxs(D, { children: [e.jsxs(O, { children: [e.jsx(P, { children: "Edit Headset Adjustment" }), e.jsx(L, { children: "Modify the adjustment value and sort order" })] }), n && e.jsxs("div", { className: "space-y-4", children: [e.jsxs("div", { className: "space-y-2", children: [e.jsx(i, { htmlFor: "edit-adjustment", children: "Adjustment (\xB0)" }), e.jsx(g, { id: "edit-adjustment", type: "number", step: "0.1", value: n.adjustment, onChange: (s) => N({ ...n, adjustment: parseFloat(s.target.value) || 0 }), "data-testid": "input-edit-adjustment" })] }), e.jsxs("div", { className: "space-y-2", children: [e.jsx(i, { htmlFor: "edit-sort-order", children: "Sort Order" }), e.jsx(g, { id: "edit-sort-order", type: "number", value: n.sortOrder, onChange: (s) => N({ ...n, sortOrder: parseInt(s.target.value) || 0 }), "data-testid": "input-edit-sort-order" })] })] }), e.jsxs($, { children: [e.jsx(k, { "data-testid": "button-cancel-edit-adjustment", children: "Cancel" }), e.jsx(I, { onClick: () => n && se.mutate({ id: n.id, data: { adjustment: n.adjustment, sortOrder: n.sortOrder } }), "data-testid": "button-confirm-edit-adjustment", children: "Save Changes" })] })] }) }), e.jsx(F, { open: w !== null, onOpenChange: () => T(null), children: e.jsxs(D, { children: [e.jsxs(O, { children: [e.jsx(P, { children: "Delete Headset Adjustment" }), e.jsx(L, { children: "Are you sure you want to delete this headset adjustment? This action cannot be undone." })] }), e.jsxs($, { children: [e.jsx(k, { "data-testid": "button-cancel-delete-adjustment", children: "Cancel" }), e.jsx(I, { onClick: () => w && te.mutate(w), "data-testid": "button-confirm-delete-adjustment", children: "Delete" })] })] }) }), e.jsx(F, { open: a !== null, onOpenChange: () => y(null), children: e.jsxs(D, { children: [e.jsxs(O, { children: [e.jsx(P, { children: "Delete Headset" }), e.jsx(L, { children: "Are you sure you want to delete this headset? This action cannot be undone." })] }), e.jsxs($, { children: [e.jsx(k, { "data-testid": "button-cancel-delete", children: "Cancel" }), e.jsx(I, { onClick: () => a && Y.mutate(a), "data-testid": "button-confirm-delete", children: "Delete" })] })] }) })] }); 60} 61export { Se as default };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.