feat: implement AI city forecast and market scan integration for terminal dashboard
This commit is contained in:
@@ -32,6 +32,10 @@ import type {
|
||||
ScanOpportunityRow,
|
||||
ScanTerminalResponse,
|
||||
} from "@/lib/dashboard-types";
|
||||
import {
|
||||
buildBrowserBackendHeaders,
|
||||
resolveBackendApiUrl,
|
||||
} from "@/lib/backend-api";
|
||||
import { getLocalizedCityName } from "@/lib/dashboard-home-copy";
|
||||
import { AiPinnedForecastView } from "@/components/dashboard/scan-terminal/AiPinnedForecastView";
|
||||
import { CalendarView } from "@/components/dashboard/scan-terminal/CalendarView";
|
||||
@@ -240,11 +244,19 @@ function ScanTerminalScreen() {
|
||||
time_range: "today",
|
||||
limit: "36",
|
||||
});
|
||||
fetch(`/api/scan/terminal?${params.toString()}`, {
|
||||
cache: "no-store",
|
||||
signal: controller.signal,
|
||||
void buildBrowserBackendHeaders({
|
||||
Accept: "application/json",
|
||||
})
|
||||
.then((headers) => {
|
||||
if (cancelled) return null;
|
||||
return fetch(resolveBackendApiUrl(`/api/scan/terminal?${params.toString()}`), {
|
||||
cache: "no-store",
|
||||
headers,
|
||||
signal: controller.signal,
|
||||
});
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response) return null;
|
||||
if (!response.ok) {
|
||||
let message = `HTTP ${response.status}`;
|
||||
try {
|
||||
@@ -258,6 +270,7 @@ function ScanTerminalScreen() {
|
||||
return response.json() as Promise<ScanTerminalResponse>;
|
||||
})
|
||||
.then((payload) => {
|
||||
if (!payload) return;
|
||||
if (cancelled) return;
|
||||
setTerminalData(payload);
|
||||
setScanError(null);
|
||||
|
||||
@@ -6,6 +6,10 @@ import type {
|
||||
AiCityForecastState,
|
||||
} from "@/components/dashboard/scan-terminal/types";
|
||||
import { useDashboardStore } from "@/hooks/useDashboardStore";
|
||||
import {
|
||||
buildBrowserBackendHeaders,
|
||||
resolveBackendApiUrl,
|
||||
} from "@/lib/backend-api";
|
||||
import type { CityDetail, MarketScan } from "@/lib/dashboard-types";
|
||||
import { normalizeCityKey } from "./decision-utils";
|
||||
|
||||
@@ -182,21 +186,26 @@ export function useAiCityForecast({
|
||||
? "DeepSeek V4-Pro is reading the latest airport bulletin..."
|
||||
: "DeepSeek V4-Pro 正在解读最新机场报文...",
|
||||
});
|
||||
fetch("/api/scan/terminal/ai-city", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
void buildBrowserBackendHeaders({
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
cache: "no-store",
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify({
|
||||
city: detailCityName,
|
||||
force_refresh: aiRefreshToken > 0,
|
||||
locale,
|
||||
}),
|
||||
})
|
||||
})
|
||||
.then((headers) => {
|
||||
if (cancelled) return null;
|
||||
return fetch(resolveBackendApiUrl("/api/scan/terminal/ai-city"), {
|
||||
method: "POST",
|
||||
headers,
|
||||
cache: "no-store",
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify({
|
||||
city: detailCityName,
|
||||
force_refresh: aiRefreshToken > 0,
|
||||
locale,
|
||||
}),
|
||||
});
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response) return null;
|
||||
if (!response.ok) {
|
||||
let detailMessage = "";
|
||||
try {
|
||||
@@ -226,6 +235,7 @@ export function useAiCityForecast({
|
||||
return response.json() as Promise<AiCityForecastPayload>;
|
||||
})
|
||||
.then((payload) => {
|
||||
if (!payload) return;
|
||||
if (!cancelled) {
|
||||
const usablePayload =
|
||||
payload?.city_forecast
|
||||
|
||||
Reference in New Issue
Block a user