Changes to the Device Menu

This commit is contained in:
2026-02-18 15:04:17 +02:00
parent dbd15c00f8
commit cc59622378

View File

@@ -59,7 +59,7 @@ function Subsection({ title, children, isFirst = false }) {
return (
<div className={isFirst ? "" : "mt-4 pt-4 border-t"} style={isFirst ? {} : { borderColor: "var(--border-secondary)" }}>
<h3 className="text-sm font-semibold mb-3" style={{ color: "var(--text-primary)" }}>{title}</h3>
<dl className="grid grid-cols-2 md:grid-cols-3 gap-4">{children}</dl>
<dl className="grid gap-4" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))" }}>{children}</dl>
</div>
);
}
@@ -298,10 +298,10 @@ export default function DeviceDetail() {
)}
</div>
<div className="grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 gap-6">
<div className="grid gap-6 items-start" style={{ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 480px), 1fr))" }}>
{/* Basic Information */}
<SectionCard title="Basic Information">
<dl className="grid grid-cols-2 md:grid-cols-3 gap-4">
<dl className="grid gap-4" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))" }}>
<Field label="Serial Number">
<span className="font-mono">{device.device_id}</span>
</Field>
@@ -321,12 +321,12 @@ export default function DeviceDetail() {
{/* Misc */}
<SectionCard title="Misc">
<dl className="grid grid-cols-2 md:grid-cols-3 gap-4">
<dl className="grid gap-4" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))" }}>
<Field label="Automated Events"><BoolBadge value={device.events_on} yesLabel="ON" noLabel="OFF" /></Field>
<Field label="Device Locale"><span className="capitalize">{attr.deviceLocale || "-"}</span></Field>
<Field label="WebSocket URL">{device.websocket_url}</Field>
<Field label="Has Assistant"><BoolBadge value={attr.hasAssistant} /></Field>
<div className="col-span-2 md:col-span-3">
<div style={{ gridColumn: "1 / -1" }}>
<Field label="Church Assistant URL">{device.churchAssistantURL}</Field>
</div>
</dl>
@@ -334,7 +334,7 @@ export default function DeviceDetail() {
{/* Subscription */}
<SectionCard title="Subscription">
<dl className="grid grid-cols-2 md:grid-cols-3 gap-4">
<dl className="grid gap-4" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))" }}>
<Field label="Tier">
<span className="px-2 py-0.5 text-xs rounded-full capitalize" style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}>
{sub.subscrTier}
@@ -452,7 +452,7 @@ export default function DeviceDetail() {
<Field label="Total Melodies">{device.device_melodies_all?.length ?? 0}</Field>
<Field label="Favorite Melodies">{device.device_melodies_favorites?.length ?? 0}</Field>
{stats.perBellStrikes?.length > 0 && (
<div className="col-span-2 md:col-span-3">
<div style={{ gridColumn: "1 / -1" }}>
<Field label="Per Bell Strikes">
<div className="flex flex-wrap gap-2 mt-1">
{stats.perBellStrikes.slice(0, attr.totalBells || stats.perBellStrikes.length).map((count, i) => (
@@ -506,73 +506,108 @@ export default function DeviceDetail() {
)}
</SectionCard>
{/* Device Settings — spans 2 columns on ultrawide */}
<div className="2xl:col-span-2">
<SectionCard title="Device Settings">
{/* Subsection 1: Basic Attributes */}
<Subsection title="Basic Attributes" isFirst>
<Field label="Bell Guard"><BoolBadge value={attr.bellGuardOn} /></Field>
<Field label="Warnings On"><BoolBadge value={attr.warningsOn} /></Field>
<Field label="Bell Guard Safety"><BoolBadge value={attr.bellGuardSafetyOn} /></Field>
</Subsection>
{/* Device Settings */}
<SectionCard title="Device Settings">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Left Column */}
<div>
{/* Basic Attributes */}
<Subsection title="Basic Attributes" isFirst>
<Field label="Bell Guard"><BoolBadge value={attr.bellGuardOn} /></Field>
<Field label="Warnings On"><BoolBadge value={attr.warningsOn} /></Field>
<Field label="Bell Guard Safety"><BoolBadge value={attr.bellGuardSafetyOn} /></Field>
</Subsection>
{/* Subsection 2: Bell Settings */}
<Subsection title="Bell Settings">
<Field label="Has Bells"><BoolBadge value={attr.hasBells} /></Field>
<Field label="Total Bells">{attr.totalBells}</Field>
<Field label="Bell Outputs">{attr.bellOutputs?.length > 0 ? attr.bellOutputs.join(", ") : "-"}</Field>
<Field label="Hammer Timings">{attr.hammerTimings?.length > 0 ? attr.hammerTimings.join(", ") : "-"}</Field>
</Subsection>
{/* Subsection 3.1: Clock Settings */}
<Subsection title="Clock Settings">
<Field label="Has Clock"><BoolBadge value={attr.hasClock} /></Field>
<Field label="Odd Output">{clock.clockOutputs?.[0] ?? "-"}</Field>
<Field label="Even Output">{clock.clockOutputs?.[1] ?? "-"}</Field>
<Field label="Run Pulse">{clock.clockTimings?.[0] != null ? msToSeconds(clock.clockTimings[0]) : "-"}</Field>
<Field label="Pause Pulse">{clock.clockTimings?.[1] != null ? msToSeconds(clock.clockTimings[1]) : "-"}</Field>
<Field label="Alerts Status"><BoolBadge value={clock.ringAlertsMasterOn} yesLabel="ON" noLabel="OFF" /></Field>
<Field label="Alerts Type"><span className="capitalize">{clock.ringAlerts || "-"}</span></Field>
<Field label="Ring Intervals">{clock.ringIntervals}</Field>
<Field label="Hour Bell">{clock.hourAlertsBell}</Field>
<Field label="Half-Hour Bell">{clock.halfhourAlertsBell}</Field>
<Field label="Quarter Bell">{clock.quarterAlertsBell}</Field>
<Field label="Daytime Silence"><BoolBadge value={clock.isDaySilenceOn} yesLabel="ON" noLabel="OFF" /></Field>
{clock.isDaySilenceOn && (
<Field label="Daytime Period">
{/* Alert Settings */}
<Subsection title="Alert Settings">
<Field label="Alerts Status"><BoolBadge value={clock.ringAlertsMasterOn} yesLabel="ON" noLabel="OFF" /></Field>
<Field label="Alerts Type"><span className="capitalize">{clock.ringAlerts || "-"}</span></Field>
<Field label="Ring Intervals">{clock.ringIntervals}</Field>
<Field label="Hour Bell">{clock.hourAlertsBell}</Field>
<Field label="Half-Hour Bell">{clock.halfhourAlertsBell}</Field>
<Field label="Quarter Bell">{clock.quarterAlertsBell}</Field>
<Field label="Daytime Silence"><BoolBadge value={clock.isDaySilenceOn} yesLabel="ON" noLabel="OFF" /></Field>
<Field label="Day-Time Period">
{formatTimestamp(clock.daySilenceFrom)} - {formatTimestamp(clock.daySilenceTo)}
</Field>
)}
<Field label="Nighttime Silence"><BoolBadge value={clock.isNightSilenceOn} yesLabel="ON" noLabel="OFF" /></Field>
{clock.isNightSilenceOn && (
<Field label="Nighttime Silence"><BoolBadge value={clock.isNightSilenceOn} yesLabel="ON" noLabel="OFF" /></Field>
<Field label="Nighttime Period">
{formatTimestamp(clock.nightSilenceFrom)} - {formatTimestamp(clock.nightSilenceTo)}
</Field>
)}
</Subsection>
</Subsection>
{/* Subsection 3.2: Backlight Settings */}
<Subsection title="Backlight Settings">
<Field label="Auto Backlight"><BoolBadge value={clock.isBacklightAutomationOn} yesLabel="ON" noLabel="OFF" /></Field>
<Field label="Backlight Output">{clock.backlightOutput}</Field>
<Field label="On Time">{formatTimestamp(clock.backlightTurnOnTime)}</Field>
<Field label="Off Time">{formatTimestamp(clock.backlightTurnOffTime)}</Field>
</Subsection>
{/* Backlight Settings */}
<Subsection title="Backlight Settings">
<Field label="Auto Backlight"><BoolBadge value={clock.isBacklightAutomationOn} yesLabel="ON" noLabel="OFF" /></Field>
<Field label="Backlight Output">{clock.backlightOutput}</Field>
<Field label="Period">
{formatTimestamp(clock.backlightTurnOnTime)} - {formatTimestamp(clock.backlightTurnOffTime)}
</Field>
</Subsection>
{/* Subsection 4: Network */}
<Subsection title="Network">
<Field label="Hostname">{net.hostname}</Field>
<Field label="Has Static IP"><BoolBadge value={net.useStaticIP} /></Field>
</Subsection>
{/* Logging */}
<Subsection title="Logging">
<Field label="Serial Log Level">{attr.serialLogLevel}</Field>
<Field label="SD Log Level">{attr.sdLogLevel}</Field>
<Field label="MQTT Log Level">{attr.mqttLogLevel ?? 0}</Field>
</Subsection>
</div>
{/* Subsection 5: Logging */}
<Subsection title="Logging">
<Field label="Serial Log Level">{attr.serialLogLevel}</Field>
<Field label="SD Log Level">{attr.sdLogLevel}</Field>
<Field label="MQTT Log Level">{attr.mqttLogLevel ?? 0}</Field>
</Subsection>
</SectionCard>
</div>
{/* Right Column */}
<div>
{/* Network */}
<Subsection title="Network" isFirst>
<Field label="Hostname">{net.hostname}</Field>
<Field label="Has Static IP"><BoolBadge value={net.useStaticIP} /></Field>
</Subsection>
{/* Clock Settings */}
<Subsection title="Clock Settings">
<Field label="Has Clock"><BoolBadge value={attr.hasClock} /></Field>
<Field label="Ring Intervals">{clock.ringIntervals}</Field>
<Field label="Odd Output">{clock.clockOutputs?.[0] ?? "-"}</Field>
<Field label="Even Output">{clock.clockOutputs?.[1] ?? "-"}</Field>
<Field label="Run Pulse">{clock.clockTimings?.[0] != null ? msToSeconds(clock.clockTimings[0]) : "-"}</Field>
<Field label="Pause Pulse">{clock.clockTimings?.[1] != null ? msToSeconds(clock.clockTimings[1]) : "-"}</Field>
</Subsection>
{/* Bell Settings */}
<Subsection title="Bell Settings">
<Field label="Bells Active"><BoolBadge value={attr.hasBells} /></Field>
<Field label="Total">{attr.totalBells ?? "-"}</Field>
{/* Bell Output-to-Timing mapping */}
{attr.bellOutputs?.length > 0 && (
<div style={{ gridColumn: "1 / -1" }}>
<dt className="text-xs font-medium uppercase tracking-wide mb-2" style={{ color: "var(--text-muted)" }}>
Output &amp; Timing Map
</dt>
<div className="flex flex-wrap gap-2">
{attr.bellOutputs.map((output, i) => (
<div
key={i}
className="rounded-md border px-3 py-2 text-center"
style={{ borderColor: "var(--border-primary)", backgroundColor: "var(--bg-primary)", minWidth: 80 }}
>
<div className="text-xs font-medium mb-1" style={{ color: "var(--text-heading)" }}>
Bell {i + 1}
</div>
<div className="text-xs" style={{ color: "var(--text-muted)" }}>
Output <span style={{ color: "var(--text-primary)" }}>{output}</span>
</div>
<div className="text-xs" style={{ color: "var(--text-muted)" }}>
{attr.hammerTimings?.[i] != null ? (
<><span style={{ color: "var(--text-primary)" }}>{attr.hammerTimings[i]}</span> ms</>
) : "-"}
</div>
</div>
))}
</div>
</div>
)}
</Subsection>
</div>
</div>
</SectionCard>
{/* Equipment Notes */}
<NotesPanel deviceId={id} />