feat: unified map click selection & switch to decision cards with paywall overlay for non-Pro users

This commit is contained in:
2569718930@qq.com
2026-05-20 09:41:44 +08:00
parent 23f7e29acc
commit 4de009b401
@@ -16,6 +16,7 @@ import type { ScanOpportunityRow } from "@/lib/dashboard-types";
import { AiPinnedForecastView } from "@/components/dashboard/scan-terminal/AiPinnedForecastView"; import { AiPinnedForecastView } from "@/components/dashboard/scan-terminal/AiPinnedForecastView";
import { MobileCityPicker } from "@/components/dashboard/scan-terminal/MobileCityPicker"; import { MobileCityPicker } from "@/components/dashboard/scan-terminal/MobileCityPicker";
import { WelcomeOverlay } from "@/components/dashboard/scan-terminal/WelcomeOverlay"; import { WelcomeOverlay } from "@/components/dashboard/scan-terminal/WelcomeOverlay";
import { ProFeaturePaywall } from "@/components/dashboard/ProFeaturePaywall";
import { import {
ScanPaywallModal, ScanPaywallModal,
ScanTerminalLoadingScreen, ScanTerminalLoadingScreen,
@@ -331,12 +332,11 @@ function ScanTerminalScreen() {
void store.ensureCityDetail(cityName, false, "panel").catch(() => {}); void store.ensureCityDetail(cityName, false, "panel").catch(() => {});
setSelectedRowId(null); setSelectedRowId(null);
} }
if (isPro) { void store.selectCity(cityName);
addAiPinnedCity(cityName); addAiPinnedCity(cityName);
setActiveView("analysis"); setActiveView("analysis");
}
}, },
[store, timeSortedRows, isPro, addAiPinnedCity], [store, timeSortedRows, addAiPinnedCity],
); );
useEffect(() => { useEffect(() => {
@@ -400,7 +400,7 @@ function ScanTerminalScreen() {
<MobileCityPicker <MobileCityPicker
isEn={isEn} isEn={isEn}
rows={cityListRows} rows={cityListRows}
onSelectCity={isPro ? handleOpenDecisionRow : handleSelectRow} onSelectCity={handleOpenDecisionRow}
/> />
); );
} }
@@ -420,15 +420,21 @@ function ScanTerminalScreen() {
/> />
</div> </div>
</div> </div>
{resolvedView === "analysis" && isPro ? ( {resolvedView === "analysis" ? (
<AiPinnedForecastView isPro ? (
items={aiPinnedCities} <AiPinnedForecastView
rows={timeSortedRows} items={aiPinnedCities}
detailsByName={store.cityDetailsByName} rows={timeSortedRows}
locale={locale} detailsByName={store.cityDetailsByName}
onRefreshCityDetail={refreshAiPinnedCityDetail} locale={locale}
onRemoveCity={removeAiPinnedCity} onRefreshCityDetail={refreshAiPinnedCityDetail}
/> onRemoveCity={removeAiPinnedCity}
/>
) : (
<div className="scan-ai-workspace empty">
<ProFeaturePaywall feature="scan" />
</div>
)
) : null} ) : null}
</> </>
); );
@@ -545,19 +551,22 @@ function ScanTerminalScreen() {
{isEn ? "Distribution View" : "分布视图"} {isEn ? "Distribution View" : "分布视图"}
</button> </button>
)} )}
{isPro ? ( <button
<button type="button"
type="button" role="tab"
role="tab" aria-selected={resolvedView === "analysis"}
aria-selected={resolvedView === "analysis"} className={resolvedView === "analysis" ? "active" : ""}
className={resolvedView === "analysis" ? "active" : ""} onClick={() => {
onClick={() => { setActiveView("analysis");
setActiveView("analysis"); }}
}} >
> {isEn ? "Decision Cards" : "城市决策卡"}
{isEn ? "Decision Cards" : "城市决策卡"} {!isPro && (
</button> <span className="scan-lock-icon" style={{ marginLeft: "4.5px", fontSize: "13.5px" }}>
) : null} 🔒
</span>
)}
</button>
</div> </div>
<div className="scan-list-status"> <div className="scan-list-status">
{terminalData?.generated_at ? ( {terminalData?.generated_at ? (