wayfarer-weather/components/SiteFooter.tsx

212 lines
8.3 KiB
TypeScript
Raw Permalink Normal View History

2026-10-31 21:20:16 -05:00
import { formatClock } from "@/lib/display";
import { pixelLabel } from "@/lib/styles";
import type { ProviderId, WeatherReport } from "@/lib/weather/types";
const LINK = "text-gold hover:text-gold-bright underline decoration-gold-dim underline-offset-2";
function ProviderCredit({ provider }: { provider: ProviderId }) {
switch (provider) {
case "openweather":
return (
<>
Weather data provided by{" "}
<a href="https://openweathermap.org/" target="_blank" rel="noopener" className={LINK}>
OpenWeather
</a>{" "}
(
<a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener" className={LINK}>
CC BY-SA 4.0
</a>{" "}
/{" "}
<a href="https://opendatacommons.org/licenses/odbl/" target="_blank" rel="noopener" className={LINK}>
ODbL
</a>
)
</>
);
case "openmeteo":
return (
<>
Weather data by{" "}
<a href="https://open-meteo.com/" target="_blank" rel="noopener" className={LINK}>
Open-Meteo.com
</a>{" "}
(
<a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener" className={LINK}>
CC BY 4.0
</a>
)
</>
);
case "weatherapi":
return (
<>
Powered by{" "}
<a href="https://www.weatherapi.com/" title="Free Weather API" target="_blank" rel="noopener" className={LINK}>
WeatherAPI.com
</a>
</>
);
case "tomorrow":
return (
<>
Powered by{" "}
<a href="https://www.tomorrow.io/" target="_blank" rel="noopener" className={LINK}>
Tomorrow.io
</a>
</>
);
}
}
/**
* Visible attribution, safety disclaimer, and privacy disclosure required
* across the weather, geocoding, air-quality, alert, and basemap providers.
*/
export function SiteFooter({
report,
activeProvider,
hasOpenWeather,
}: {
report: WeatherReport | null;
activeProvider: ProviderId;
hasOpenWeather: boolean;
}) {
const creditedProvider = report?.provider ?? activeProvider;
const camsYear = report ? new Date(report.fetchedAt).getUTCFullYear() : null;
return (
<footer className="mt-4 flex flex-col gap-2.5 border-t border-gold-dim/50 pt-4 text-xs leading-relaxed text-stone">
{/* Live visible attribution line for active data on screen */}
<div className="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1">
<p>
<ProviderCredit provider={creditedProvider} />
{report && <>, updated {formatClock(report.fetchedAt, null)}</>}.
{report?.airQuality && (
<>
{" "}Air quality by{" "}
<a href="https://open-meteo.com/" target="_blank" rel="noopener" className={LINK}>
Open-Meteo.com
</a>{" "}
— contains modified{" "}
<a href="https://atmosphere.copernicus.eu/" target="_blank" rel="noopener" className={LINK}>
Copernicus Atmosphere Monitoring Service (CAMS)
</a>{" "}
information ({camsYear}).
</>
)}
</p>
<p className="text-[11px]">
{hasOpenWeather ? (
<>
Places via{" "}
<a href="https://openweathermap.org/" target="_blank" rel="noopener" className={LINK}>
OpenWeather
</a>
</>
) : (
<>
Location data based on{" "}
<a href="https://www.geonames.org/" target="_blank" rel="noopener" className={LINK}>
GeoNames
</a>{" "}
(
<a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener" className={LINK}>
CC BY 4.0
</a>
)
</>
)}
</p>
</div>
{/* Expandable legal, safety & privacy notice + author signature */}
<div className="flex flex-col gap-2.5">
<div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-2">
<details className="group w-full">
<summary className={`inline-flex cursor-pointer list-none items-center gap-1.5 ${pixelLabel} hover:text-gold-bright`}>
<span className="inline-block transition-transform group-open:rotate-90" aria-hidden="true">
›
</span>
Data licenses, safety disclaimer &amp; privacy
</summary>
<div className="mt-2.5 grid gap-3 border border-gold-dim/50 bg-panel/70 p-4 text-xs leading-relaxed sm:grid-cols-3">
<div className="flex flex-col gap-1">
<p className="font-pixel text-[0.68rem] text-gold-bright">Safety disclaimer</p>
<p>
Weather forecasts, radar imagery, and alert summaries are provided &ldquo;as is&rdquo; without warranty for general
informational use. Do not rely solely on this site for life-safety or emergency decisions; monitor{" "}
<a href="https://www.weather.gov/" target="_blank" rel="noopener" className={LINK}>
weather.gov
</a>{" "}
or your national meteorological service. Not affiliated with or endorsed by NOAA/NWS.
</p>
</div>
<div className="flex flex-col gap-1">
<p className="font-pixel text-[0.68rem] text-gold-bright">Map &amp; radar credits</p>
<p>
Basemap &copy;{" "}
<a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener" className={LINK}>
OpenStreetMap
</a>{" "}
contributors, &copy;{" "}
<a href="https://carto.com/attributions" target="_blank" rel="noopener" className={LINK}>
CARTO
</a>
. Global radar by{" "}
<a href="https://www.rainviewer.com/" target="_blank" rel="noopener" className={LINK}>
RainViewer
</a>
; US NEXRAD composite via{" "}
<a href="https://mesonet.agron.iastate.edu/" target="_blank" rel="noopener" className={LINK}>
Iowa Environmental Mesonet
</a>{" "}
(Iowa State University). Raw weather metrics are unit-converted and normalized for display.
</p>
</div>
<div className="flex flex-col gap-1">
<p className="font-pixel text-[0.68rem] text-gold-bright">Privacy</p>
<p>
Wayfarer uses no accounts, ad trackers, or analytics cookies. Unit preferences and recent waypoints stay in your
browser&rsquo;s <code className="font-pixel text-[0.65rem] text-ink">localStorage</code>. Coordinates shared via
&ldquo;Use my location&rdquo; are used only in real time to fetch forecast and map data and are never stored on
our server.
</p>
</div>
</div>
</details>
</div>
<div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-1 border-t border-gold-dim/35 pt-2.5 text-xs">
<p className="flex items-baseline gap-2">
<span className="font-display text-xs font-bold tracking-[0.22em] text-gold uppercase">Wayfarer</span>
<a
href="https://caydens.dev"
target="_blank"
rel="noopener"
className="group/author font-pixel text-[0.68rem] tracking-[0.05em] text-stone transition-colors hover:text-gold-bright"
>
by{" "}
<span className="text-gold underline decoration-gold-dim underline-offset-2 transition-colors group-hover/author:text-gold-bright">
Cayden Codes
</span>
</a>
</p>
<a
href="https://caydens.dev"
target="_blank"
rel="noopener"
className="font-pixel text-[0.68rem] text-stone transition-colors hover:text-gold-bright"
>
caydens.dev ↗
</a>
</div>
</div>
</footer>
);
}