import { useState, useEffect } from "react"; import { useParams, useNavigate } from "react-router-dom"; import api from "../api/client"; import { useAuth } from "../auth/AuthContext"; // ─── Default permission sets ─────────────────────────────────────────────── const EDITOR_PERMS = { melodies: { view: true, add: true, delete: true, safe_edit: true, full_edit: true, archetype_access: true, settings_access: true, compose_access: true }, devices: { view: true, add: true, delete: true, safe_edit: true, edit_bells: true, edit_clock: true, edit_warranty: true, full_edit: true, control: true }, app_users: { view: true, add: true, delete: true, safe_edit: true, full_edit: true }, issues_notes: { view: true, add: true, delete: true, edit: true }, mail: { view: true, compose: true, reply: true }, crm: { activity_log: true }, crm_customers: { full_access: true, overview: true, orders_view: true, orders_edit: true, quotations_view: true, quotations_edit: true, comms_view: true, comms_log: true, comms_edit: true, comms_compose: true, add: true, delete: true, files_view: true, files_edit: true, devices_view: true, devices_edit: true }, crm_products: { view: true, add: true, edit: true }, mfg: { view_inventory: true, edit: true, provision: true, firmware_view: true, firmware_edit: true }, api_reference: { access: true }, mqtt: { access: true }, }; const USER_PERMS = { melodies: { view: true, add: false, delete: false, safe_edit: false, full_edit: false, archetype_access: false, settings_access: false, compose_access: false }, devices: { view: true, add: false, delete: false, safe_edit: false, edit_bells: false, edit_clock: false, edit_warranty: false, full_edit: false, control: false }, app_users: { view: true, add: false, delete: false, safe_edit: false, full_edit: false }, issues_notes: { view: true, add: false, delete: false, edit: false }, mail: { view: true, compose: false, reply: false }, crm: { activity_log: false }, crm_customers: { full_access: false, overview: true, orders_view: true, orders_edit: false, quotations_view: true, quotations_edit: false, comms_view: true, comms_log: false, comms_edit: false, comms_compose: false, add: false, delete: false, files_view: true, files_edit: false, devices_view: true, devices_edit: false }, crm_products: { view: true, add: false, edit: false }, mfg: { view_inventory: true, edit: false, provision: false, firmware_view: true, firmware_edit: false }, api_reference: { access: false }, mqtt: { access: false }, }; function deepClone(obj) { return JSON.parse(JSON.stringify(obj)); } // ─── Dependency rules ────────────────────────────────────────────────────── function applyDependencies(section, key, value, prev) { const s = { ...prev[section] }; s[key] = value; if (value) { const VIEW_FORCING = ["add", "delete", "safe_edit", "full_edit", "edit_bells", "edit_clock", "edit_warranty", "control", "edit"]; if (VIEW_FORCING.includes(key) && "view" in s) s.view = true; if (section === "melodies" && key === "full_edit") s.safe_edit = true; if (section === "devices" && key === "full_edit") { s.safe_edit = true; s.edit_bells = true; s.edit_clock = true; s.edit_warranty = true; s.view = true; } if (section === "app_users" && key === "full_edit") s.safe_edit = true; if (section === "crm_customers") { if (key === "full_access") Object.keys(s).forEach((k) => { s[k] = true; }); if (key === "orders_edit") s.orders_view = true; if (key === "quotations_edit") s.quotations_view = true; if (key === "files_edit") s.files_view = true; if (key === "devices_edit") s.devices_view = true; if (["comms_log", "comms_edit", "comms_compose"].includes(key)) s.comms_view = true; } if (section === "mfg" && key === "firmware_edit") s.firmware_view = true; if (section === "mfg" && key === "edit") s.view_inventory = true; if (section === "mfg" && key === "provision") s.view_inventory = true; } return { ...prev, [section]: s }; } // ─── Pill button colors ──────────────────────────────────────────────────── // Disabled / No Access → danger red // View (middle) → badge blue // Enabled / Edit → accent green const PILL_STYLES = { off: { active: { bg: "var(--danger-bg)", border: "var(--danger)", color: "var(--danger-text)", fontWeight: 600 }, inactive: { bg: "var(--bg-input)", border: "var(--border-primary)", color: "var(--text-muted)", fontWeight: 400 }, }, view: { active: { bg: "var(--badge-blue-bg)", border: "var(--badge-blue-text)", color: "var(--badge-blue-text)", fontWeight: 600 }, inactive: { bg: "var(--bg-input)", border: "var(--border-primary)", color: "var(--text-muted)", fontWeight: 400 }, }, on: { active: { bg: "var(--success-bg)", border: "var(--accent)", color: "var(--success-text)", fontWeight: 600 }, inactive: { bg: "var(--bg-input)", border: "var(--border-primary)", color: "var(--text-muted)", fontWeight: 400 }, }, }; function pillStyle(tone, isActive) { const s = PILL_STYLES[tone][isActive ? "active" : "inactive"]; return { backgroundColor: s.bg, borderColor: s.border, color: s.color, fontWeight: s.fontWeight }; } // ─── Sub-components ──────────────────────────────────────────────────────── /** * A permission row: label/description on the left, dual pill buttons on the right. * Disabled / Enabled (red / green) */ function PermRow({ label, description, value, onChange, disabled }) { return (
{description}
)}{description}
)}