Redesign Brīdinājumi cards as icon chips with a detail popup

This commit is contained in:
b0txec
2026-08-22 21:26:13 +03:00
parent da146b01ed
commit cafc75abeb
2 changed files with 83 additions and 13 deletions
+53 -12
View File
@@ -1,4 +1,6 @@
import { createEffect, createMemo, createResource, createSignal, For, Show } from 'solid-js';
import { createEffect, createMemo, createResource, createSignal, For, onCleanup, Show } from 'solid-js';
import { Portal } from 'solid-js/web';
import { AlertTriangle, CloudFog, CloudLightning, CloudRain, CloudSnow, Flame, ThermometerSnowflake, Waves, Wind, X } from 'lucide-solid';
import { apiHost } from '../../consts';
import { download } from '../../helpers/download';
import { LoadingSpinner } from '../../components/spinner/LoadingSpinner';
@@ -6,6 +8,24 @@ import { drawWarningMap, type WeatherWarning, warningSeverity } from './warningD
import { warningTemplates, type WarningOutput } from './warningTemplates';
import './warnings.css';
function phenomenonIcon(phenomenon: string) {
const normalized = (phenomenon || '').toLocaleLowerCase('lv-LV');
if (normalized.includes('vēj')) return Wind;
if (normalized.includes('pērkon') || normalized.includes('negais')) return CloudLightning;
if (normalized.includes('lietus') || normalized.includes('lietav')) return CloudRain;
if (normalized.includes('snieg')) return CloudSnow;
if (normalized.includes('migl')) return CloudFog;
if (normalized.includes('saln') || normalized.includes('sals')) return ThermometerSnowflake;
if (normalized.includes('karstum')) return Flame;
if (normalized.includes('plūd') || normalized.includes('ūdens')) return Waves;
return AlertTriangle;
}
function PhenomenonIcon(props: { phenomenon: string; size?: number }) {
const Icon = phenomenonIcon(props.phenomenon);
return <Icon size={props.size ?? 18} strokeWidth={1.8}/>;
}
type WarningResponse = { source: string; fetchedAt: string; warnings: WeatherWarning[] };
const fetchWarnings = async (): Promise<WarningResponse> => {
@@ -34,6 +54,16 @@ export function Warnings() {
}));
const [selectedId, setSelectedId] = createSignal<string>();
const selectedWarning = createMemo(() => filteredWarnings().find(item => item.id === selectedId()));
const [detailOpen, setDetailOpen] = createSignal(false);
const openWarning = (id: string) => { setSelectedId(id); setDetailOpen(true); };
const closeDetail = () => setDetailOpen(false);
createEffect(() => {
if (!detailOpen()) return;
const onKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') closeDetail(); };
document.addEventListener('keydown', onKeyDown);
onCleanup(() => document.removeEventListener('keydown', onKeyDown));
});
const loadArtwork = () => {
const current = template();
@@ -97,20 +127,31 @@ export function Warnings() {
<Show when={data.error}><div class="inlineNotice error"><strong>Brīdinājumu datus neizdevās ielādēt.</strong><button class="secondary" onClick={() => refetch()}>Mēģināt vēlreiz</button></div></Show>
<Show when={!data.loading && !data.error && filteredWarnings().length === 0}><div class="inlineNotice">Izvēlētajam periodam nav aktīvu brīdinājumu. Karti joprojām var priekšskatīt, bet tajā nebūs iekrāsotu teritoriju.</div></Show>
<Show when={filteredWarnings().length > 0}><div class="warningSummary"><For each={filteredWarnings()}>{warning => {
const active = () => warning.id === selectedId();
return <button type="button" class={`warningCard ${warningSeverity(warning.intensity)}`} classList={{ active: active() }} aria-pressed={active()} onClick={() => setSelectedId(warning.id)}>
<Show when={filteredWarnings().length > 0}><div class="warningSummary"><For each={filteredWarnings()}>{warning =>
<button type="button" class={`warningCard ${warningSeverity(warning.intensity)}`} classList={{ active: warning.id === selectedId() }} aria-pressed={warning.id === selectedId()} onClick={() => openWarning(warning.id)}>
<PhenomenonIcon phenomenon={warning.phenomenon}/>
<strong>{warning.phenomenon || 'Brīdinājums'}</strong>
<Show when={active()}><div class="warningCardDetails">
<span>{warning.intensity} · {warning.regions}</span>
<small>{formatPeriod(warning.validFrom, warning.validUntil)}</small>
<Show when={warning.description}><p>{warning.description}</p></Show>
<Show when={warning.risks}><p class="warningRisks">{warning.risks}</p></Show>
</div></Show>
</button>;
}}</For></div></Show>
</button>
}</For></div></Show>
<section class="warningsPreview"><div class="mapToolbar"><div><strong>3. Kartes priekšskatījums</strong><span>{template().width} × {template().height} px eksports</span></div><button class="primary" disabled={data.loading || Boolean(data.error)} onClick={downloadPng}>Lejupielādēt PNG</button></div><div class="canvasFrame"><canvas ref={canvas} width={template().width} height={template().height}/></div></section>
<Show when={detailOpen() && selectedWarning()}>{warning =>
<Portal>
<div class="warningBackdrop" onClick={closeDetail}>
<div class="warningModal" onClick={event => event.stopPropagation()}>
<button type="button" class="warningModalClose" aria-label="Aizvērt" onClick={closeDetail}><X size={20}/></button>
<div class={`warningModalHeader ${warningSeverity(warning().intensity)}`}>
<PhenomenonIcon phenomenon={warning().phenomenon} size={30}/>
<div><strong>{warning().phenomenon || 'Brīdinājums'}</strong><span>{warning().intensity} · {warning().regions}</span></div>
</div>
<p class="warningModalPeriod">{formatPeriod(warning().validFrom, warning().validUntil)}</p>
<Show when={warning().description}><p>{warning().description}</p></Show>
<Show when={warning().risks}><p class="warningRisks">{warning().risks}</p></Show>
</div>
</div>
</Portal>
}</Show>
</section>;
}