fix: put annotations and live indicator on same row in chart footer

This commit is contained in:
Usman Baig
2026-03-09 23:03:58 +01:00
parent 5625703168
commit cc4f924fb8

View File

@@ -518,66 +518,70 @@ export default function Chart({
)} )}
</CardContent> </CardContent>
{/* Annotation tags */} {/* Footer: Annotations + Live indicator on same row */}
{annotationMarkers.length > 0 && ( {(annotationMarkers.length > 0 || lastUpdatedAt != null) && (
<div className="px-4 sm:px-6 flex items-center gap-1 flex-wrap py-1.5 border-t border-neutral-100 dark:border-neutral-800"> <div className="px-4 sm:px-6 flex items-center justify-between gap-2 flex-wrap py-1.5 border-t border-neutral-100 dark:border-neutral-800">
<span className="text-[10px] font-medium text-neutral-400 dark:text-neutral-500 mr-1">Annotations:</span> {/* Annotations left */}
{annotationMarkers.map((marker) => { <div className="flex items-center gap-1 flex-wrap">
const primary = marker.annotations[0] {annotationMarkers.length > 0 && (
const color = ANNOTATION_COLORS[primary.category] || ANNOTATION_COLORS.other <>
const count = marker.annotations.length <span className="text-[10px] font-medium text-neutral-400 dark:text-neutral-500 mr-1">Annotations:</span>
return ( {annotationMarkers.map((marker) => {
<button const primary = marker.annotations[0]
key={`ann-btn-${marker.x}`} const color = ANNOTATION_COLORS[primary.category] || ANNOTATION_COLORS.other
type="button" const count = marker.annotations.length
className="relative group inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium text-neutral-600 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors cursor-pointer" return (
onClick={() => { <button
if (canManageAnnotations) { key={`ann-btn-${marker.x}`}
setAnnotationForm({ type="button"
visible: true, className="relative group inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium text-neutral-600 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors cursor-pointer"
editingId: primary.id, onClick={() => {
date: primary.date, if (canManageAnnotations) {
time: primary.time || '', setAnnotationForm({
text: primary.text, visible: true,
category: primary.category, editingId: primary.id,
}) date: primary.date,
} time: primary.time || '',
}} text: primary.text,
> category: primary.category,
<span className="w-2 h-2 rounded-full shrink-0" style={{ backgroundColor: color }} /> })
<span className="max-w-[120px] truncate">{primary.text}</span> }
{count > 1 && <span className="text-neutral-400">+{count - 1}</span>} }}
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 hidden group-hover:block z-50 pointer-events-none"> >
<div className="bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-lg p-2 min-w-[180px] max-w-[240px]"> <span className="w-2 h-2 rounded-full shrink-0" style={{ backgroundColor: color }} />
{marker.annotations.map((a) => ( <span className="max-w-[120px] truncate">{primary.text}</span>
<div key={a.id} className="flex items-start gap-1.5 text-[11px] mb-1 last:mb-0"> {count > 1 && <span className="text-neutral-400">+{count - 1}</span>}
<span className="w-1.5 h-1.5 rounded-full mt-1 shrink-0" style={{ backgroundColor: ANNOTATION_COLORS[a.category] || ANNOTATION_COLORS.other }} /> <div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 hidden group-hover:block z-50 pointer-events-none">
<div> <div className="bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-lg p-2 min-w-[180px] max-w-[240px]">
<span className="font-medium text-neutral-400 dark:text-neutral-500"> {marker.annotations.map((a) => (
{ANNOTATION_LABELS[a.category] || 'Note'} &middot; {formatEU(a.date)}{a.time ? ` at ${a.time}` : ''} <div key={a.id} className="flex items-start gap-1.5 text-[11px] mb-1 last:mb-0">
</span> <span className="w-1.5 h-1.5 rounded-full mt-1 shrink-0" style={{ backgroundColor: ANNOTATION_COLORS[a.category] || ANNOTATION_COLORS.other }} />
<p className="text-neutral-900 dark:text-white">{a.text}</p> <div>
<span className="font-medium text-neutral-400 dark:text-neutral-500">
{ANNOTATION_LABELS[a.category] || 'Note'} &middot; {formatEU(a.date)}{a.time ? ` at ${a.time}` : ''}
</span>
<p className="text-neutral-900 dark:text-white">{a.text}</p>
</div>
</div>
))}
</div> </div>
</div> </div>
))} </button>
</div> )
</div> })}
</button> </>
) )}
})}
</div>
)}
{/* Live indicator */}
{lastUpdatedAt != null && (
<div className="px-4 sm:px-6 pb-3 pt-2 flex justify-end">
<div className="flex items-center gap-1.5 text-[11px] text-neutral-400 dark:text-neutral-500">
<span className="relative flex h-1.5 w-1.5">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-500 opacity-75" />
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-green-500" />
</span>
Live · {formatUpdatedAgo(lastUpdatedAt)}
</div> </div>
{/* Live indicator right */}
{lastUpdatedAt != null && (
<div className="flex items-center gap-1.5 text-[11px] text-neutral-400 dark:text-neutral-500">
<span className="relative flex h-1.5 w-1.5">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-500 opacity-75" />
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-green-500" />
</span>
Live · {formatUpdatedAgo(lastUpdatedAt)}
</div>
)}
</div> </div>
)} )}
</Card> </Card>