feat: add datapoint delete, show emoji icons in preview
- DELETE /api/datapoints/:id endpoint + frontend button - Preview shows emoji symbols instead of [icon] text Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4964a2e8ab
commit
8a5031649a
@ -49,6 +49,21 @@ interface EpaperPreviewProps {
|
|||||||
|
|
||||||
const SCALE = 0.6;
|
const SCALE = 0.6;
|
||||||
|
|
||||||
|
const ICON_SYMBOLS: Record<string, string> = {
|
||||||
|
thermometer: '🌡',
|
||||||
|
droplet: '💧',
|
||||||
|
wind: '🌬',
|
||||||
|
pressure: '⏱',
|
||||||
|
sun: '☀',
|
||||||
|
bolt: '⚡',
|
||||||
|
gauge: '📊',
|
||||||
|
wifi: '📶',
|
||||||
|
battery: '🔋',
|
||||||
|
check: '✅',
|
||||||
|
warning: '⚠',
|
||||||
|
clock: '🕐',
|
||||||
|
};
|
||||||
|
|
||||||
export default function EpaperPreview({ layoutId, elements, layoutWidth = 800, layoutHeight = 480 }: EpaperPreviewProps) {
|
export default function EpaperPreview({ layoutId, elements, layoutWidth = 800, layoutHeight = 480 }: EpaperPreviewProps) {
|
||||||
const [liveElements, setLiveElements] = useState<DisplayApiElement[] | null>(null);
|
const [liveElements, setLiveElements] = useState<DisplayApiElement[] | null>(null);
|
||||||
const [displayId, setDisplayId] = useState<number | null>(null);
|
const [displayId, setDisplayId] = useState<number | null>(null);
|
||||||
@ -170,7 +185,7 @@ export default function EpaperPreview({ layoutId, elements, layoutWidth = 800, l
|
|||||||
display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
|
display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
|
||||||
}}>
|
}}>
|
||||||
<div style={{ fontSize: fontSize * 0.45, color: '#555', textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
<div style={{ fontSize: fontSize * 0.45, color: '#555', textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
||||||
{el.icon ? `[${el.icon}] ` : ''}{el.label || ''}
|
{el.icon ? (ICON_SYMBOLS[el.icon] || el.icon) + ' ' : ''}{el.label || ''}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize, fontWeight: 'bold', textAlign: 'right' }}>
|
<div style={{ fontSize, fontWeight: 'bold', textAlign: 'right' }}>
|
||||||
{el.value ?? '—'} <span style={{ fontSize: fontSize * 0.5, color: '#555' }}>{el.unit || ''}</span>
|
{el.value ?? '—'} <span style={{ fontSize: fontSize * 0.5, color: '#555' }}>{el.unit || ''}</span>
|
||||||
|
|||||||
@ -40,6 +40,16 @@ export default function Datapoints() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleDelete(id: number) {
|
||||||
|
if (!confirm('Datenpunkt wirklich loeschen?')) return;
|
||||||
|
try {
|
||||||
|
await api.del(`/api/datapoints/${id}`);
|
||||||
|
load();
|
||||||
|
} catch (e) {
|
||||||
|
setError((e as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-semibold text-gray-800 mb-6">Datenpunkte</h2>
|
<h2 className="text-xl font-semibold text-gray-800 mb-6">Datenpunkte</h2>
|
||||||
@ -55,11 +65,12 @@ export default function Datapoints() {
|
|||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Einheit</th>
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Einheit</th>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Quelle</th>
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Quelle</th>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Status</th>
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Status</th>
|
||||||
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Aktion</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-100">
|
<tbody className="divide-y divide-gray-100">
|
||||||
{datapoints.length === 0 && (
|
{datapoints.length === 0 && (
|
||||||
<tr><td colSpan={6} className="px-4 py-6 text-center text-gray-400">Keine Datenpunkte vorhanden</td></tr>
|
<tr><td colSpan={7} className="px-4 py-6 text-center text-gray-400">Keine Datenpunkte vorhanden</td></tr>
|
||||||
)}
|
)}
|
||||||
{datapoints.map((dp) => (
|
{datapoints.map((dp) => (
|
||||||
<tr key={dp.id} className="hover:bg-gray-50">
|
<tr key={dp.id} className="hover:bg-gray-50">
|
||||||
@ -83,6 +94,9 @@ export default function Datapoints() {
|
|||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2 text-gray-500 text-xs">{dp.source_type}</td>
|
<td className="px-4 py-2 text-gray-500 text-xs">{dp.source_type}</td>
|
||||||
<td className="px-4 py-2"><StatusBadge status={dp.status} /></td>
|
<td className="px-4 py-2"><StatusBadge status={dp.status} /></td>
|
||||||
|
<td className="px-4 py-2">
|
||||||
|
<button onClick={() => handleDelete(dp.id)} className="text-red-400 hover:text-red-600 text-xs">Loeschen</button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {
|
|||||||
getAllDatapoints,
|
getAllDatapoints,
|
||||||
getDatapointById,
|
getDatapointById,
|
||||||
updateDatapoint,
|
updateDatapoint,
|
||||||
|
deleteDatapoint,
|
||||||
} from '../../modules/datapoints/datapoint-manager';
|
} from '../../modules/datapoints/datapoint-manager';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
@ -38,4 +39,15 @@ router.put('/:id', (req, res): void => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// DELETE /api/datapoints/:id
|
||||||
|
router.delete('/:id', (req, res): void => {
|
||||||
|
try {
|
||||||
|
const deleted = deleteDatapoint(parseInt(req.params.id));
|
||||||
|
if (!deleted) { res.status(404).json({ error: 'Datapoint not found' }); return; }
|
||||||
|
res.status(204).send();
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: (err as Error).message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@ -118,6 +118,12 @@ export function createDatapointFromPg(
|
|||||||
return db.prepare('SELECT * FROM datapoints WHERE id = ?').get(id) as Datapoint;
|
return db.prepare('SELECT * FROM datapoints WHERE id = ?').get(id) as Datapoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function deleteDatapoint(id: number): boolean {
|
||||||
|
const db = getDb();
|
||||||
|
const result = db.prepare('DELETE FROM datapoints WHERE id = ?').run(id);
|
||||||
|
return (result.changes as number) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
export function getDatapointByTopicId(topicId: number): Datapoint | undefined {
|
export function getDatapointByTopicId(topicId: number): Datapoint | undefined {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
return db
|
return db
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user