Add target comparison modal, integration goal progress, and session planning + full catalog expansion
Features added this session: - Target comparison: side-by-side overlay (CompareModal) from Targets page via ⊕ button on each row; shows altitude curves, key times, filter recommendations and per-filter integration progress for two targets simultaneously - Integration goal progress dashboard card: per-target keeper minutes vs goal hours (from CLAUDE.md §16.3) broken down by filter, with color-coded progress bars; powered by new stats.integration_goals backend query - Session planning timeline: Gantt-style "Plan Tonight" section on Dashboard (PlanningTimeline component) — search targets, set durations, sequential scheduling from dusk, overrun warnings, clipboard export - Slew-optimized run order toggle (nearest-neighbor sort by RA/Dec angular distance) - Best Nights 14-day card + Monthly Highlights card on Dashboard Catalog expansions: - Sharpless (Sh2), VdB, LDN, Barnard dark nebulae, LBN, Melotte, Collinder, Gum, RCW, Abell PN, Abell GC, PGC bright subset - Caldwell/Arp/Melotte/Collinder number columns + cross-reference maps - Weather score multiplier applied to composite sort - galaxy_cluster type (ACO badge) throughout TypeBadge, CSS, filter chips Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
interface Props {
|
||||
level?: 'warning' | 'critical';
|
||||
temp?: number;
|
||||
@@ -5,6 +7,31 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function DewAlert({ level, temp, dewPoint }: Props) {
|
||||
const notifiedRef = useRef<string | null>(null);
|
||||
|
||||
// Trigger browser notification when dew alert level appears or escalates
|
||||
useEffect(() => {
|
||||
if (!level) return;
|
||||
const key = level;
|
||||
if (notifiedRef.current === key) return;
|
||||
notifiedRef.current = key;
|
||||
|
||||
if (!('Notification' in window)) return;
|
||||
const send = () => {
|
||||
const margin = temp != null && dewPoint != null ? `Margin: ${(temp - dewPoint).toFixed(1)}°C. ` : '';
|
||||
const body = level === 'critical'
|
||||
? `${margin}Condensation imminent — protect optics immediately.`
|
||||
: `${margin}Enable dew heaters now.`;
|
||||
new Notification('Astronome — Dew Alert', { body, tag: 'dew-alert' });
|
||||
};
|
||||
|
||||
if (Notification.permission === 'granted') {
|
||||
send();
|
||||
} else if (Notification.permission !== 'denied') {
|
||||
void Notification.requestPermission().then(p => { if (p === 'granted') send(); });
|
||||
}
|
||||
}, [level, temp, dewPoint]);
|
||||
|
||||
if (!level) return null;
|
||||
|
||||
const margin = temp != null && dewPoint != null ? (temp - dewPoint).toFixed(1) : null;
|
||||
|
||||
Reference in New Issue
Block a user