remove sign up prompts

This commit is contained in:
Cayden Schwechten 2026-10-31 21:50:28 -05:00
parent 77d33c185b
commit 70182d8bb7
10 changed files with 31 additions and 138 deletions

View file

@ -1,114 +1,34 @@
"use client";
import { useState } from "react";
import { pixelLabel } from "@/lib/styles";
import type { ProviderId, ProviderInfo } from "@/lib/weather/types";
import { Slot } from "./Slot";
import { ChoiceChips } from "./ui/ChoiceChips";
const PROVIDER_KEY_HINTS: Record<ProviderId, { envVar: string | null; summary: string }> = {
openweather: {
envVar: "OPENWEATHER_API_KEY",
summary: "5-day forecast, reverse location naming, and 5 extra map layers (precip, clouds, temp, wind, pressure).",
},
weatherapi: {
envVar: "WEATHERAPI_API_KEY",
summary: "3-day hourly & daily forecast plus worldwide weather alerts (instant key activation).",
},
tomorrow: {
envVar: "TOMORROW_API_KEY",
summary: "5-day hourly & 6-day daily timelines (free tier: 25 calls/hr, 500/day).",
},
openmeteo: {
envVar: null,
summary: "14-day daily & 7-day hourly forecast — free out of the box, no API key required.",
},
};
/**
* Provider choices. Each shows how many forecast days it delivers on the
* configured plan. Providers without an API key stay visible with an optional
* setup guide so adding free keys to `.env.local` is self-explanatory.
* Switcher between active weather data sources.
* Only configured providers are shown; hidden automatically if only one source is active.
*/
export function ProviderPicker({ providers, selected, onChange }: {
providers: ProviderInfo[];
selected: ProviderId;
onChange: (id: ProviderId) => void;
}) {
const [showKeyGuide, setShowKeyGuide] = useState(false);
const missingProviders = providers.filter((p) => !p.ready);
if (providers.length <= 1) return null;
return (
<div className="flex flex-col gap-2">
<div className="flex flex-wrap items-center justify-between gap-2">
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-x-3 gap-y-1.5">
<span className={pixelLabel}>Source</span>
<ChoiceChips
legend="Weather data source"
size="sm"
selected={selected}
onChange={onChange}
choices={providers.map((p) => ({
value: p.id,
label: p.label,
disabled: !p.ready,
note: p.ready ? `${p.forecastDays}d` : "Locked",
noteTone: p.ready ? "muted" : "subtle",
}))}
/>
</div>
{missingProviders.length > 0 && (
<button
type="button"
aria-expanded={showKeyGuide}
onClick={() => setShowKeyGuide((open) => !open)}
className="cursor-pointer font-pixel text-[0.7rem] text-gold hover:text-gold-bright hover:underline"
>
{showKeyGuide ? "Hide key guide" : `+ Unlock ${missingProviders.length} ${missingProviders.length === 1 ? "source" : "sources"}`}
</button>
)}
</div>
{showKeyGuide && missingProviders.length > 0 && (
<Slot className="[--cut:10px]" innerClassName="flex flex-col gap-3 p-4 text-sm">
<div className="flex flex-wrap items-baseline justify-between gap-2">
<p className="font-display text-sm font-medium tracking-[0.06em] text-gold-bright">
Optional free API keys
</p>
<p className="text-xs text-stone">
Add keys to <code className="border border-gold-dim/60 bg-black px-1.5 py-0.5 font-pixel text-[0.68rem] text-ink">.env.local</code> and reload.
</p>
</div>
<ul className="grid gap-2.5 sm:grid-cols-3">
{missingProviders.map((p) => {
const info = PROVIDER_KEY_HINTS[p.id];
return (
<li key={p.id} className="flex flex-col justify-between gap-2 border border-gold-dim/60 bg-black/70 p-3">
<div className="flex flex-col gap-1">
<div className="flex items-center justify-between gap-2">
<span className="font-medium text-ink">{p.label}</span>
{info.envVar && (
<code className="font-pixel text-[0.62rem] text-teal">{info.envVar}</code>
)}
</div>
<p className="text-xs leading-relaxed text-stone">{info.summary}</p>
</div>
<a
href={p.signupUrl}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1 self-start font-pixel text-[0.7rem] text-gold hover:text-gold-bright hover:underline"
>
Get free key <span aria-hidden="true">↗</span>
</a>
</li>
);
})}
</ul>
</Slot>
)}
<div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5">
<span className={pixelLabel}>Source</span>
<ChoiceChips
legend="Weather data source"
size="sm"
selected={selected}
onChange={onChange}
choices={providers.map((p) => ({
value: p.id,
label: p.label,
note: `${p.forecastDays}d`,
}))}
/>
</div>
);
}

View file

@ -106,11 +106,9 @@ export default function WeatherApp() {
fetchProviders()
.then((list) => {
setProviders(list);
// Prefer OpenWeather if its key was just added and user hasn't explicitly saved a choice;
// otherwise keep saved provider if ready, or fall back to the first ready source.
const savedIsReady = list.some((p) => p.id === saved.provider && p.ready);
const fallback = list.find((p) => p.ready)?.id ?? saved.provider;
setSettings({ ...saved, provider: savedIsReady ? saved.provider : fallback });
const savedIsAvailable = list.some((p) => p.id === saved.provider);
const fallback = list[0]?.id ?? saved.provider;
setSettings({ ...saved, provider: savedIsAvailable ? saved.provider : fallback });
})
.catch(() => setSettings(saved))
.finally(() => setLoaded(true));
@ -146,7 +144,7 @@ export default function WeatherApp() {
const activeProvider = providers.find((p) => p.id === settings.provider);
const reportProvider = providers.find((p) => p.id === report?.provider);
const hasOpenWeather = providers.some((p) => p.id === "openweather" && p.ready);
const hasOpenWeather = providers.some((p) => p.id === "openweather");
return (
<main

View file

@ -81,7 +81,7 @@ export function DailyView({ daily, units, timeZone, providerLabel, maxDaysOnPlan
{daily.length < 14 && (
<p className={pixelLabel}>
{providerLabel} gives {maxDaysOnPlan} days on this plan. Choose Open-Meteo for 14.
{providerLabel} provides a {maxDaysOnPlan}-day outlook. Switch to Open-Meteo above for 14 days.
</p>
)}
</div>

View file

@ -3,8 +3,7 @@ import { useId } from "react";
export interface Choice<T extends string> {
value: T;
label: string;
note?: string; // small extra text, e.g. "14d" or "No key"
noteTone?: "muted" | "subtle" | "warning";
note?: string; // small extra text, e.g. "14d"
disabled?: boolean;
}
@ -54,19 +53,7 @@ export function ChoiceChips<T extends string>({ legend, choices, selected, onCha
onChange={() => onChange(choice.value)}
/>
{choice.label}
{choice.note && (
<span
className={
choice.noteTone === "warning"
? "text-heart"
: choice.noteTone === "subtle"
? "text-[0.65rem] text-stone/80"
: "text-teal/90"
}
>
{choice.note}
</span>
)}
{choice.note && <span className="text-teal/90">{choice.note}</span>}
</label>
))}
</fieldset>