import { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import api from "../api/client"; const TIER_OPTIONS = ["basic", "small", "mini", "premium", "vip", "custom"]; const LOCALE_OPTIONS = ["orthodox", "catholic", "all"]; const RING_ALERT_OPTIONS = ["disabled", "single", "multi"]; const defaultAttributes = { hasAssistant: false, hasClock: false, hasBells: false, totalBells: 0, bellOutputs: [], hammerTimings: [], bellGuardOn: false, bellGuardSafetyOn: false, warningsOn: false, towerClockTime: "", clockSettings: { clockOutputs: [], clockTimings: [], ringAlertsMasterOn: false, ringAlerts: "disabled", ringIntervals: 0, hourAlertsBell: 0, halfhourAlertsBell: 0, quarterAlertsBell: 0, isDaySilenceOn: false, isNightSilenceOn: false, daySilenceFrom: "", daySilenceTo: "", nightSilenceFrom: "", nightSilenceTo: "", backlightTurnOnTime: "", backlightTurnOffTime: "", isBacklightAutomationOn: false, backlightOutput: 0, }, deviceLocale: "all", networkSettings: { hostname: "", useStaticIP: false, ipAddress: [], gateway: [], subnet: [], dns1: [], dns2: [], }, serialLogLevel: 0, sdLogLevel: 0, }; const defaultSubscription = { subscrTier: "basic", subscrStart: "", subscrDuration: 0, maxUsers: 0, maxOutputs: 0, }; const defaultStats = { totalPlaybacks: 0, totalHammerStrikes: 0, perBellStrikes: [], totalWarningsGiven: 0, warrantyActive: false, warrantyStart: "", warrantyPeriod: 0, maintainedOn: "", maintainancePeriod: 0, }; const parseIntList = (str) => { if (!str.trim()) return []; return str .split(",") .map((s) => parseInt(s.trim(), 10)) .filter((n) => !isNaN(n)); }; export default function DeviceForm() { const { id } = useParams(); const isEdit = Boolean(id); const navigate = useNavigate(); const [deviceName, setDeviceName] = useState(""); const [devicePhoto, setDevicePhoto] = useState(""); const [deviceLocation, setDeviceLocation] = useState(""); const [isOnline, setIsOnline] = useState(false); const [eventsOn, setEventsOn] = useState(false); const [locationCoordinates, setLocationCoordinates] = useState(""); const [websocketUrl, setWebsocketUrl] = useState(""); const [churchAssistantURL, setChurchAssistantURL] = useState(""); const [attributes, setAttributes] = useState({ ...defaultAttributes, clockSettings: { ...defaultAttributes.clockSettings }, networkSettings: { ...defaultAttributes.networkSettings } }); const [subscription, setSubscription] = useState({ ...defaultSubscription }); const [stats, setStats] = useState({ ...defaultStats }); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); useEffect(() => { if (isEdit) loadDevice(); }, [id]); const loadDevice = async () => { setLoading(true); try { const device = await api.get(`/devices/${id}`); setDeviceName(device.device_name || ""); setDevicePhoto(device.device_photo || ""); setDeviceLocation(device.device_location || ""); setIsOnline(device.is_Online || false); setEventsOn(device.events_on || false); setLocationCoordinates(device.device_location_coordinates || ""); setWebsocketUrl(device.websocket_url || ""); setChurchAssistantURL(device.churchAssistantURL || ""); setAttributes({ ...defaultAttributes, ...device.device_attributes, clockSettings: { ...defaultAttributes.clockSettings, ...(device.device_attributes?.clockSettings || {}), }, networkSettings: { ...defaultAttributes.networkSettings, ...(device.device_attributes?.networkSettings || {}), }, }); setSubscription({ ...defaultSubscription, ...(device.device_subscription || {}) }); setStats({ ...defaultStats, ...(device.device_stats || {}) }); } catch (err) { setError(err.message); } finally { setLoading(false); } }; const updateAttr = (field, value) => setAttributes((prev) => ({ ...prev, [field]: value })); const updateClock = (field, value) => setAttributes((prev) => ({ ...prev, clockSettings: { ...prev.clockSettings, [field]: value }, })); const updateNetwork = (field, value) => setAttributes((prev) => ({ ...prev, networkSettings: { ...prev.networkSettings, [field]: value }, })); const updateSub = (field, value) => setSubscription((prev) => ({ ...prev, [field]: value })); const updateStats = (field, value) => setStats((prev) => ({ ...prev, [field]: value })); const handleSubmit = async (e) => { e.preventDefault(); setSaving(true); setError(""); try { const body = { device_name: deviceName, device_photo: devicePhoto, device_location: deviceLocation, is_Online: isOnline, device_attributes: attributes, device_subscription: subscription, device_stats: stats, events_on: eventsOn, device_location_coordinates: locationCoordinates, device_melodies_all: [], device_melodies_favorites: [], user_list: [], websocket_url: websocketUrl, churchAssistantURL, }; let deviceId = id; if (isEdit) { await api.put(`/devices/${id}`, body); } else { const created = await api.post("/devices", body); deviceId = created.id; } navigate(`/devices/${deviceId}`); } catch (err) { setError(err.message); } finally { setSaving(false); } }; if (loading) { return
Loading...
; } const inputClass = "w-full px-3 py-2 rounded-md text-sm border"; return (

{isEdit ? "Edit Device" : "Add Device"}

{error && (
{error}
)}
{/* ===== Left Column ===== */}
{/* --- Basic Info --- */}

Basic Information

setDeviceName(e.target.value)} className={inputClass} />
setDeviceLocation(e.target.value)} placeholder="e.g. St. Mary's Church, Vienna" className={inputClass} />
setLocationCoordinates(e.target.value)} placeholder="e.g. 48.2082,16.3738" className={inputClass} />
setDevicePhoto(e.target.value)} className={inputClass} />
{/* --- Device Attributes --- */}

Device Attributes

updateAttr("totalBells", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateAttr("bellOutputs", parseIntList(e.target.value))} placeholder="e.g. 1, 2, 3" className={inputClass} />
updateAttr("hammerTimings", parseIntList(e.target.value))} placeholder="e.g. 100, 150, 200" className={inputClass} />
updateAttr("serialLogLevel", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateAttr("sdLogLevel", parseInt(e.target.value, 10) || 0)} className={inputClass} />
{/* --- Network Settings --- */}

Network Settings

updateNetwork("hostname", e.target.value)} className={inputClass} />
updateNetwork("useStaticIP", e.target.checked)} className="h-4 w-4 text-blue-600 rounded border-gray-300" />
setWebsocketUrl(e.target.value)} className={inputClass} />
setChurchAssistantURL(e.target.value)} className={inputClass} />
{/* ===== Right Column ===== */}
{/* --- Subscription --- */}

Subscription

updateSub("subscrStart", e.target.value)} className={inputClass} />
updateSub("subscrDuration", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateSub("maxUsers", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateSub("maxOutputs", parseInt(e.target.value, 10) || 0)} className={inputClass} />
{/* --- Clock Settings --- */}

Clock Settings

updateClock("ringIntervals", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateClock("ringAlertsMasterOn", e.target.checked)} className="h-4 w-4 text-blue-600 rounded border-gray-300" /> Ring Alerts Master On
updateClock("clockOutputs", parseIntList(e.target.value))} className={inputClass} />
updateClock("clockTimings", parseIntList(e.target.value))} className={inputClass} />
updateClock("hourAlertsBell", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateClock("halfhourAlertsBell", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateClock("quarterAlertsBell", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateClock("backlightOutput", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateClock("isBacklightAutomationOn", e.target.checked)} className="h-4 w-4 text-blue-600 rounded border-gray-300" /> Backlight Automation
{/* Silence settings */}

Silence Periods

updateClock("isDaySilenceOn", e.target.checked)} className="h-4 w-4 text-blue-600 rounded border-gray-300" /> Day Silence
updateClock("isNightSilenceOn", e.target.checked)} className="h-4 w-4 text-blue-600 rounded border-gray-300" /> Night Silence
updateClock("daySilenceFrom", e.target.value)} className={inputClass} />
updateClock("daySilenceTo", e.target.value)} className={inputClass} />
updateClock("nightSilenceFrom", e.target.value)} className={inputClass} />
updateClock("nightSilenceTo", e.target.value)} className={inputClass} />
{/* --- Statistics --- */}

Statistics & Warranty

updateStats("totalPlaybacks", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateStats("totalHammerStrikes", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateStats("totalWarningsGiven", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateStats("perBellStrikes", parseIntList(e.target.value))} className={inputClass} />
updateStats("warrantyActive", e.target.checked)} className="h-4 w-4 text-blue-600 rounded border-gray-300" /> Warranty Active
updateStats("warrantyStart", e.target.value)} className={inputClass} />
updateStats("warrantyPeriod", parseInt(e.target.value, 10) || 0)} className={inputClass} />
updateStats("maintainedOn", e.target.value)} className={inputClass} />
updateStats("maintainancePeriod", parseInt(e.target.value, 10) || 0)} className={inputClass} />
); }