Promote Pro trial and rename settlement source to station
This commit is contained in:
@@ -71,6 +71,9 @@ export function LoginClient({ nextPath }: LoginClientProps) {
|
||||
signupCheckEmail: isEn
|
||||
? "Sign-up successful. Please verify your email before signing in."
|
||||
: "注册成功,请检查邮箱并完成验证后登录。",
|
||||
trialPromo: isEn
|
||||
? "New users unlock a free 3-day Pro trial after sign-up."
|
||||
: "新用户注册后可免费体验 3 天 Pro。",
|
||||
} as const;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -190,6 +193,9 @@ export function LoginClient({ nextPath }: LoginClientProps) {
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-white">PolyWeather</h1>
|
||||
<p className="mt-2 text-sm text-slate-400">{copy.subtitle}</p>
|
||||
<div className="mt-4 inline-flex items-center rounded-full border border-cyan-400/30 bg-cyan-400/10 px-4 py-1.5 text-xs font-semibold text-cyan-200 shadow-[0_0_20px_rgba(34,211,238,0.08)]">
|
||||
{copy.trialPromo}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
@@ -2683,6 +2683,29 @@
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
.root :global(.trial-promo-badge) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(34, 211, 238, 0.28);
|
||||
background: rgba(34, 211, 238, 0.1);
|
||||
color: #a5f3fc;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.01em;
|
||||
text-decoration: none;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.root :global(.trial-promo-badge:hover) {
|
||||
background: rgba(34, 211, 238, 0.16);
|
||||
border-color: rgba(34, 211, 238, 0.45);
|
||||
color: #ecfeff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.root :global(.info-btn) {
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
border: 1px solid rgba(99, 102, 241, 0.3);
|
||||
|
||||
@@ -1011,27 +1011,19 @@ export function FutureForecastModal() {
|
||||
settlementSourceCode === "wunderground";
|
||||
const settlementProfileLabel = isOfficialSettlementSource
|
||||
? locale === "en-US"
|
||||
? "Settlement source"
|
||||
: "结算源"
|
||||
? "Settlement station"
|
||||
: "结算站点"
|
||||
: t("section.airport");
|
||||
const settlementProfileValue =
|
||||
settlementSourceCode === "hko"
|
||||
settlementSourceCode === "noaa"
|
||||
? locale === "en-US"
|
||||
? "Hong Kong Observatory (HKO)"
|
||||
: "香港天文台 (HKO)"
|
||||
: settlementSourceCode === "noaa"
|
||||
? locale === "en-US"
|
||||
? `NOAA ${noaaStationCode} (${noaaStationName})`
|
||||
: `NOAA ${noaaStationCode}(${noaaStationName})`
|
||||
: settlementSourceCode === "cwa"
|
||||
? locale === "en-US"
|
||||
? "Central Weather Administration (CWA)"
|
||||
: "交通部中央气象署 (CWA)"
|
||||
: settlementSourceCode === "wunderground"
|
||||
? locale === "en-US"
|
||||
? `${detail.current?.settlement_source_label || risk.airport || "Wunderground"}`
|
||||
: `${detail.current?.settlement_source_label || risk.airport || "Wunderground"}`
|
||||
: risk.airport
|
||||
? `${noaaStationName}${noaaStationCode ? ` (${noaaStationCode})` : ""}`
|
||||
: `${noaaStationName}${noaaStationCode ? `(${noaaStationCode})` : ""}`
|
||||
: detail.current?.station_name
|
||||
? `${detail.current.station_name}${
|
||||
detail.current?.station_code ? ` (${detail.current.station_code})` : ""
|
||||
}`
|
||||
: risk.airport
|
||||
? `${risk.airport}${risk.icao ? ` (${risk.icao})` : ""}`
|
||||
: "--";
|
||||
const airportCurrentText =
|
||||
@@ -1398,26 +1390,6 @@ export function FutureForecastModal() {
|
||||
</span>
|
||||
<span>{settlementProfileValue}</span>
|
||||
</div>
|
||||
{settlementSourceCode === "wunderground" ? (
|
||||
<div className="risk-row">
|
||||
<span className="risk-label">
|
||||
{locale === "en-US"
|
||||
? "Airport METAR"
|
||||
: "机场 METAR"}
|
||||
</span>
|
||||
<span>{airportCurrentText}</span>
|
||||
</div>
|
||||
) : null}
|
||||
{settlementSourceCode === "wunderground" ? (
|
||||
<div className="risk-row">
|
||||
<span className="risk-label">
|
||||
{locale === "en-US"
|
||||
? "Airport high"
|
||||
: "机场目前最高温"}
|
||||
</span>
|
||||
<span>{airportMaxText}</span>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="risk-row">
|
||||
<span className="risk-label">
|
||||
{t("section.distance")}
|
||||
|
||||
@@ -34,6 +34,8 @@ export function HeaderBar() {
|
||||
const supabaseReady = hasSupabasePublicEnv();
|
||||
const docsHref = "/docs/intro";
|
||||
const docsActive = pathname?.startsWith("/docs");
|
||||
const trialPromoLabel =
|
||||
locale === "en-US" ? "New users get 3-day Pro trial" : "新用户可免费体验 3 天 Pro";
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
@@ -135,6 +137,15 @@ export function HeaderBar() {
|
||||
{t("header.docs")}
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/account"
|
||||
className="trial-promo-badge"
|
||||
title={trialPromoLabel}
|
||||
aria-label={trialPromoLabel}
|
||||
>
|
||||
<span>{trialPromoLabel}</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href={accountHref}
|
||||
className="account-btn"
|
||||
|
||||
@@ -156,6 +156,21 @@
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.trialPromo {
|
||||
margin-top: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(34, 211, 238, 0.24);
|
||||
background: rgba(34, 211, 238, 0.08);
|
||||
color: #a5f3fc;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* ── Card grid ── */
|
||||
.grid {
|
||||
position: relative;
|
||||
|
||||
@@ -141,6 +141,11 @@ export function UnlockProOverlay({
|
||||
? "High-precision weather intelligence, delivered everywhere."
|
||||
: "全球最精准的高精度气象推送,全平台覆盖"}
|
||||
</p>
|
||||
<div className={s.trialPromo}>
|
||||
{isEn
|
||||
? "New users get a free 3-day Pro trial before billing."
|
||||
: "新用户可先免费体验 3 天 Pro,再决定是否付费。"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Cards ── */}
|
||||
|
||||
@@ -188,7 +188,7 @@ export const DOCS_PAGES: DocsPage[] = [
|
||||
title: "什么叫机场端压温风险偏高",
|
||||
blocks: [
|
||||
{ type: "paragraph", text: "它的意思不是整座城市一定更冷,而是作为结算依据的机场站点,在峰值窗口里更可能因为云、阵雨或雷暴扰动,冲不到本来可能达到的更高温度。" },
|
||||
{ type: "callout", tone: "warning", title: "重点区别", text: "TAF 负责告诉你机场侧未来几个小时会不会出现压温扰动,不直接等于结算温度本身。结算仍然看 METAR、HKO、MGM、NOAA 指定站点、Wunderground 指定站点等实际结算源。" },
|
||||
{ type: "callout", tone: "warning", title: "重点区别", text: "TAF 负责告诉你机场侧未来几个小时会不会出现压温扰动,不直接等于结算温度本身。结算仍然看 METAR、HKO、MGM、NOAA 指定站点、Wunderground 指定站点等实际结算站点读数。" },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -216,7 +216,7 @@ export const DOCS_PAGES: DocsPage[] = [
|
||||
title: "What airport-side suppression risk means",
|
||||
blocks: [
|
||||
{ type: "paragraph", text: "It does not mean the entire city must run cooler. It means the airport station used for settlement is more likely to get capped by clouds, showers, or thunderstorm disruption during the peak window and fail to reach the next warmer bucket." },
|
||||
{ type: "callout", tone: "warning", title: "Important distinction", text: "TAF explains whether the airport side may face suppressive weather over the next few hours. Settlement still comes from the actual settlement source such as METAR, HKO, MGM, a designated NOAA station, or a designated Wunderground station." },
|
||||
{ type: "callout", tone: "warning", title: "Important distinction", text: "TAF explains whether the airport side may face suppressive weather over the next few hours. Settlement still comes from the actual settlement station reading, such as METAR, HKO, MGM, a designated NOAA station, or a designated Wunderground station." },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -228,12 +228,12 @@ export const DOCS_PAGES: DocsPage[] = [
|
||||
group: "settlement",
|
||||
content: {
|
||||
"zh-CN": {
|
||||
title: "结算来源说明",
|
||||
description: "不同城市的结算口径不同。理解结算源,比单纯看模型曲线更重要。",
|
||||
title: "结算站点说明",
|
||||
description: "不同城市的结算口径不同。理解结算站点,比单纯看模型曲线更重要。",
|
||||
sections: [
|
||||
{
|
||||
id: "why-settlement-matters",
|
||||
title: "为什么先看结算源",
|
||||
title: "为什么先看结算站点",
|
||||
blocks: [
|
||||
{ type: "paragraph", text: "同样是“城市最高温”,市场真正结算看的往往不是城区平均温度,而是规则指定的机场或官方站点。交易上最常见的错觉,是把城市体感温度当成结算温度。" },
|
||||
],
|
||||
@@ -249,18 +249,18 @@ export const DOCS_PAGES: DocsPage[] = [
|
||||
id: "common-mistakes",
|
||||
title: "最常见的误解",
|
||||
blocks: [
|
||||
{ type: "bullets", items: ["TAF 不是结算源,它只告诉你机场未来有没有压温扰动。", "市场按机场结算时,城区更热不代表市场就该结到更高温桶。", "NOAA 市场要优先看 weather.gov 指定站点的最终 Temp 列,不要拿其他站或第三方页面替代。", "香港和台北不能简单套用机场 TAF / METAR 主链逻辑。"] },
|
||||
{ type: "bullets", items: ["TAF 不是结算站点,它只告诉你机场未来有没有压温扰动。", "市场按机场结算时,城区更热不代表市场就该结到更高温桶。", "NOAA 市场要优先看 weather.gov 指定站点的最终 Temp 列,不要拿其他站或第三方页面替代。", "香港和台北不能简单套用机场 TAF / METAR 主链逻辑。"] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
"en-US": {
|
||||
title: "Settlement Sources",
|
||||
description: "Settlement rules differ by city. Understanding the settlement source matters more than staring only at model curves.",
|
||||
title: "Settlement Stations",
|
||||
description: "Settlement rules differ by city. Understanding the settlement station matters more than staring only at model curves.",
|
||||
sections: [
|
||||
{
|
||||
id: "why-settlement-matters",
|
||||
title: "Why settlement source comes first",
|
||||
title: "Why the settlement station comes first",
|
||||
blocks: [
|
||||
{ type: "paragraph", text: "A market may say “city high”, but the true settlement often comes from a designated airport or official site rather than the broader urban feel. One of the most common mistakes is to trade the city feel instead of the actual settlement station." },
|
||||
],
|
||||
@@ -276,7 +276,7 @@ export const DOCS_PAGES: DocsPage[] = [
|
||||
id: "common-mistakes",
|
||||
title: "Common mistakes",
|
||||
blocks: [
|
||||
{ type: "bullets", items: ["TAF is not a settlement source. It only tells you whether airport-side suppressive weather may appear.", "If the market settles on an airport site, a hotter downtown feel does not automatically justify a warmer settlement bucket.", "NOAA markets should anchor to the designated weather.gov Temp column once the date is finalized, not to a nearby third-party station page.", "Hong Kong and Taipei should not be forced into the generic airport TAF / METAR chain."] },
|
||||
{ type: "bullets", items: ["TAF is not the settlement station. It only tells you whether airport-side suppressive weather may appear.", "If the market settles on an airport site, a hotter downtown feel does not automatically justify a warmer settlement bucket.", "NOAA markets should anchor to the designated weather.gov Temp column once the date is finalized, not to a nearby third-party station page.", "Hong Kong and Taipei should not be forced into the generic airport TAF / METAR chain."] },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -2392,18 +2392,17 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN"
|
||||
const noaaCode = getNoaaStationCode(detail);
|
||||
const noaaName = getNoaaStationName(detail);
|
||||
return isEnglish(locale)
|
||||
? `NOAA ${noaaCode} (${noaaName})`
|
||||
: `NOAA ${noaaCode}(${noaaName})`;
|
||||
? `${noaaName}${noaaCode ? ` (${noaaCode})` : ""}`
|
||||
: `${noaaName}${noaaCode ? `(${noaaCode})` : ""}`;
|
||||
}
|
||||
if (sourceCode === "wunderground") {
|
||||
const stationName = String(
|
||||
detail.current?.settlement_source_label ||
|
||||
detail.risk?.airport ||
|
||||
"Wunderground",
|
||||
).trim();
|
||||
return isEnglish(locale)
|
||||
? `${stationName} (Wunderground)`
|
||||
: `${stationName}(Wunderground)`;
|
||||
const stationName = String(
|
||||
detail.current?.station_name || detail.risk?.airport || "",
|
||||
).trim();
|
||||
const stationCode = String(
|
||||
detail.current?.station_code || detail.risk?.icao || "",
|
||||
).trim();
|
||||
if (stationName) {
|
||||
return `${stationName}${stationCode ? ` (${stationCode})` : ""}`;
|
||||
}
|
||||
const tag = getObservationSourceTag(detail);
|
||||
if (sourceCode === "mgm") {
|
||||
@@ -2418,8 +2417,8 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN"
|
||||
{
|
||||
label: isOfficialSource
|
||||
? isEnglish(locale)
|
||||
? "Settlement source"
|
||||
: "结算源"
|
||||
? "Settlement station"
|
||||
: "结算站点"
|
||||
: isEnglish(locale)
|
||||
? "Settlement airport"
|
||||
: "结算机场",
|
||||
|
||||
@@ -31,7 +31,7 @@ const MESSAGES: Record<Locale, Record<string, string>> = {
|
||||
"sidebar.group.low": "近期偏弱",
|
||||
"sidebar.group.other": "样本不足",
|
||||
|
||||
"dashboard.loading": "正在同步站点观测与结算源,请稍候...",
|
||||
"dashboard.loading": "正在同步站点观测与结算站点信息,请稍候...",
|
||||
|
||||
"detail.closeAria": "关闭城市详情面板",
|
||||
"detail.waitSelect": "等待选择城市",
|
||||
@@ -195,7 +195,7 @@ const MESSAGES: Record<Locale, Record<string, string>> = {
|
||||
"sidebar.group.other": "Low Sample",
|
||||
|
||||
"dashboard.loading":
|
||||
"Synchronizing station observations and settlement feeds...",
|
||||
"Synchronizing station observations and settlement station data...",
|
||||
|
||||
"detail.closeAria": "Close city detail panel",
|
||||
"detail.waitSelect": "Waiting for city selection",
|
||||
|
||||
Reference in New Issue
Block a user