Major overhaul to the Notes/Issues. Minor tweaks to the UI. Added Profile photos

This commit is contained in:
2026-02-19 06:30:57 +02:00
parent a9a1531d57
commit f09979c653
21 changed files with 988 additions and 308 deletions

View File

@@ -485,37 +485,13 @@ export default function DeviceDetail() {
<section className="rounded-lg border p-6" style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}>
<h2 className="text-lg font-semibold mb-4" style={{ color: "var(--text-heading)" }}>Device Information</h2>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: "1rem", alignItems: "start" }}>
{/* Col 1: Status on top, device image below */}
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem", height: "100%" }}>
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
<div
className="w-10 h-10 rounded-full flex items-center justify-center shrink-0"
style={{ backgroundColor: isOnline ? "var(--success-bg)" : "var(--bg-card-hover)" }}
>
<span
className="w-3 h-3 rounded-full inline-block"
style={{ backgroundColor: isOnline ? "var(--success-text)" : "var(--text-muted)" }}
/>
</div>
<div>
<div className="text-xs font-medium uppercase tracking-wide" style={{ color: "var(--text-muted)" }}>Status</div>
<div className="text-sm font-semibold" style={{ color: isOnline ? "var(--success-text)" : "var(--text-muted)" }}>
{isOnline ? "Online" : "Offline"}
{mqttStatus && (
<span className="ml-2 text-xs font-normal" style={{ color: "var(--text-muted)" }}>
{mqttStatus.seconds_since_heartbeat}s ago
</span>
)}
</div>
</div>
</div>
<div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }}>
<img
src={hwImage}
alt={hwVariant}
style={{ maxHeight: 80, maxWidth: "100%", objectFit: "contain", opacity: 0.85 }}
/>
</div>
{/* Col 1: Device image */}
<div style={{ display: "flex", alignItems: "center", height: "100%" }}>
<img
src={hwImage}
alt={hwVariant}
style={{ maxHeight: 120, maxWidth: "100%", objectFit: "contain", opacity: 0.85 }}
/>
</div>
{/* Col 2: Serial Number, Hardware Variant, Document ID */}
@@ -785,23 +761,35 @@ export default function DeviceDetail() {
{attr.bellOutputs.map((output, i) => (
<div
key={i}
className="relative rounded-md border px-4 py-3 text-center overflow-hidden"
style={{ borderColor: "var(--border-primary)", backgroundColor: "var(--bg-primary)", minWidth: 90 }}
className="rounded-md border overflow-hidden"
style={{ borderColor: "var(--border-primary)", backgroundColor: "var(--bg-primary)", minWidth: 120 }}
>
<span
className="absolute inset-0 flex items-center justify-center font-bold pointer-events-none select-none"
style={{ fontSize: "3rem", color: "var(--text-heading)", opacity: 0.06 }}
>
{i + 1}
</span>
<div className="relative">
<div className="text-xs" style={{ color: "var(--text-muted)" }}>
Output <span style={{ color: "var(--text-primary)" }}>{output}</span>
<div style={{ display: "flex", alignItems: "stretch" }}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
width: 44,
fontSize: "1.5rem",
fontWeight: 700,
color: "var(--text-heading)",
opacity: 0.15,
borderRight: "1px solid var(--border-primary)",
flexShrink: 0,
}}
>
{i + 1}
</div>
<div className="text-xs mt-1" style={{ color: "var(--text-muted)" }}>
{attr.hammerTimings?.[i] != null ? (
<><span style={{ color: "var(--text-primary)" }}>{attr.hammerTimings[i]}</span> ms</>
) : "-"}
<div style={{ padding: "0.5rem 0.75rem" }}>
<div className="text-xs" style={{ color: "var(--text-muted)" }}>
Output <span style={{ color: "var(--text-primary)" }}>{output}</span>
</div>
<div className="text-xs mt-1" style={{ color: "var(--text-muted)" }}>
{attr.hammerTimings?.[i] != null ? (
<>Timing <span style={{ color: "var(--text-primary)" }}>{attr.hammerTimings[i]}</span> ms</>
) : "Timing -"}
</div>
</div>
</div>
</div>
@@ -920,17 +908,28 @@ export default function DeviceDetail() {
style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-primary)" }}
onClick={() => user.user_id && navigate(`/users/${user.user_id}`)}
>
<div className="flex items-center justify-between">
<div className="min-w-0">
<p className="text-sm font-medium truncate" style={{ color: "var(--text-heading)" }}>
{user.display_name || user.email || "Unknown User"}
</p>
{user.email && user.display_name && (
<p className="text-xs truncate" style={{ color: "var(--text-muted)" }}>{user.email}</p>
)}
{user.user_id && (
<p className="text-xs font-mono" style={{ color: "var(--text-muted)" }}>{user.user_id}</p>
)}
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-3 min-w-0">
<div
className="w-8 h-8 rounded-full overflow-hidden shrink-0"
style={{ backgroundColor: "var(--bg-card-hover)" }}
>
{user.photo_url ? (
<img src={user.photo_url} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />
) : (
<div className="w-full h-full flex items-center justify-center text-xs font-bold" style={{ color: "var(--text-muted)" }}>
{(user.display_name || user.email || "?").charAt(0).toUpperCase()}
</div>
)}
</div>
<div className="min-w-0">
<p className="text-sm font-medium truncate" style={{ color: "var(--text-heading)" }}>
{user.display_name || user.email || "Unknown User"}
</p>
{user.email && user.display_name && (
<p className="text-xs truncate" style={{ color: "var(--text-muted)", opacity: 0.7 }}>{user.email}</p>
)}
</div>
</div>
{user.role && (
<span className="px-2 py-0.5 text-xs rounded-full capitalize shrink-0 ml-2" style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}>
@@ -1026,11 +1025,20 @@ export default function DeviceDetail() {
<h1 className="text-2xl font-bold" style={{ color: "var(--text-heading)" }}>
{device.device_name || "Unnamed Device"}
</h1>
<span
className={`inline-block w-3 h-3 rounded-full ${isOnline ? "bg-green-500" : ""}`}
style={!isOnline ? { backgroundColor: "var(--border-primary)" } : undefined}
title={isOnline ? "Online" : "Offline"}
/>
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
<span
className="w-3 h-3 rounded-full inline-block"
style={{ backgroundColor: isOnline ? "var(--success-text)" : "var(--text-muted)" }}
/>
<span className="text-sm font-semibold" style={{ color: isOnline ? "var(--success-text)" : "var(--text-muted)" }}>
{isOnline ? "Online" : "Offline"}
{mqttStatus && (
<span className="ml-2 text-xs font-normal" style={{ color: "var(--text-muted)" }}>
{mqttStatus.seconds_since_heartbeat}s ago
</span>
)}
</span>
</div>
</div>
</div>
{canEdit && (