update: added assets manager and extra nvs settings on cloudflash
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import QRCode from "qrcode";
|
||||
import { MapContainer, TileLayer, Marker } from "react-leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import L from "leaflet";
|
||||
@@ -198,6 +199,32 @@ function LogLevelSelect({ value, onChange }) {
|
||||
);
|
||||
}
|
||||
|
||||
function QrModal({ value, onClose }) {
|
||||
const canvasRef = useRef(null);
|
||||
useEffect(() => {
|
||||
if (!value || !canvasRef.current) return;
|
||||
QRCode.toCanvas(canvasRef.current, value, { width: 220, margin: 2, color: { dark: "#e3e5ea", light: "#1f2937" } });
|
||||
}, [value]);
|
||||
useEffect(() => {
|
||||
const onKey = (e) => { if (e.key === "Escape") onClose(); };
|
||||
document.addEventListener("keydown", onKey);
|
||||
return () => document.removeEventListener("keydown", onKey);
|
||||
}, [onClose]);
|
||||
if (!value) return null;
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center" style={{ backgroundColor: "rgba(0,0,0,0.7)" }}
|
||||
onClick={onClose}>
|
||||
<div className="rounded-xl border p-5 shadow-2xl flex flex-col items-center gap-3"
|
||||
style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
<p className="text-xs font-mono" style={{ color: "var(--text-muted)" }}>{value}</p>
|
||||
<canvas ref={canvasRef} style={{ borderRadius: 8 }} />
|
||||
<p className="text-xs" style={{ color: "var(--text-muted)" }}>Click outside or press ESC to close</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionModal({ open, title, onCancel, onSave, saving, disabled, size = "max-w-lg", children }) {
|
||||
if (!open) return null;
|
||||
return (
|
||||
@@ -1151,6 +1178,12 @@ const LOG_LEVEL_STYLES = {
|
||||
|
||||
function parseFirestoreDate(str) {
|
||||
if (!str) return null;
|
||||
// Handle Firestore Timestamp objects serialised as {_seconds, _nanoseconds} or {seconds, nanoseconds}
|
||||
if (typeof str === "object") {
|
||||
const secs = str._seconds ?? str.seconds;
|
||||
if (typeof secs === "number") return new Date(secs * 1000);
|
||||
return null;
|
||||
}
|
||||
const cleaned = str.replace(" at ", " ").replace("UTC+0000", "UTC").replace(/UTC\+(\d{4})/, "UTC");
|
||||
const d = new Date(cleaned);
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
@@ -1711,6 +1744,7 @@ export default function DeviceDetail() {
|
||||
const [editingBacklight, setEditingBacklight] = useState(false);
|
||||
const [editingSubscription, setEditingSubscription] = useState(false);
|
||||
const [editingWarranty, setEditingWarranty] = useState(false);
|
||||
const [qrTarget, setQrTarget] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
@@ -2201,7 +2235,23 @@ export default function DeviceDetail() {
|
||||
<div className="db-row">
|
||||
<div className="db-info-field">
|
||||
<span className="db-info-label">SERIAL NUMBER</span>
|
||||
<span className="db-info-value">{device.serial_number || device.device_id || "-"}</span>
|
||||
<span className="db-info-value" style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
|
||||
{device.serial_number || device.device_id || "-"}
|
||||
{(device.serial_number || device.device_id) && (
|
||||
<button
|
||||
type="button"
|
||||
title="Show QR Code"
|
||||
onClick={() => setQrTarget(device.serial_number || device.device_id)}
|
||||
className="cursor-pointer hover:opacity-70 transition-opacity"
|
||||
style={{ color: "var(--text-muted)", background: "none", border: "none", padding: 0, lineHeight: 1 }}
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
|
||||
<path d="M14 14h.01M14 18h.01M18 14h.01M18 18h.01M21 14h.01M21 21h.01M14 21h.01"/>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="db-row">
|
||||
@@ -3893,6 +3943,7 @@ export default function DeviceDetail() {
|
||||
stats={stats}
|
||||
id={id}
|
||||
/>
|
||||
<QrModal value={qrTarget} onClose={() => setQrTarget(null)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user