feat: implement AI city forecast and market scan integration for terminal dashboard
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getSupabaseBrowserClient,
|
||||
hasSupabasePublicEnv,
|
||||
} from "@/lib/supabase/client";
|
||||
|
||||
const PUBLIC_BACKEND_API_BASE_URL =
|
||||
process.env.NEXT_PUBLIC_POLYWEATHER_API_BASE_URL?.trim() || "";
|
||||
|
||||
export function hasDirectBackendApiBaseUrl() {
|
||||
return Boolean(PUBLIC_BACKEND_API_BASE_URL);
|
||||
}
|
||||
|
||||
export function resolveBackendApiUrl(path: string) {
|
||||
if (!PUBLIC_BACKEND_API_BASE_URL || /^https?:\/\//i.test(path)) {
|
||||
return path;
|
||||
}
|
||||
const base = PUBLIC_BACKEND_API_BASE_URL.replace(/\/+$/, "");
|
||||
const suffix = path.startsWith("/") ? path : `/${path}`;
|
||||
return `${base}${suffix}`;
|
||||
}
|
||||
|
||||
export async function buildBrowserBackendHeaders(init?: HeadersInit) {
|
||||
const headers = new Headers(init);
|
||||
if (!headers.has("Accept")) {
|
||||
headers.set("Accept", "application/json");
|
||||
}
|
||||
|
||||
if (!headers.has("Authorization") && hasSupabasePublicEnv()) {
|
||||
try {
|
||||
const {
|
||||
data: { session },
|
||||
} = await getSupabaseBrowserClient().auth.getSession();
|
||||
const accessToken = session?.access_token || "";
|
||||
if (accessToken) {
|
||||
headers.set("Authorization", `Bearer ${accessToken}`);
|
||||
}
|
||||
} catch {
|
||||
// Direct backend mode must also work for public/optional-auth dashboards.
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
ScanTerminalFilters,
|
||||
ScanTerminalResponse,
|
||||
} from "@/lib/dashboard-types";
|
||||
import {
|
||||
buildBrowserBackendHeaders,
|
||||
resolveBackendApiUrl,
|
||||
} from "@/lib/backend-api";
|
||||
|
||||
export type AssistantOpportunityContext = {
|
||||
city_name: string;
|
||||
@@ -108,11 +112,15 @@ async function fetchJson<T>(url: string, options?: { timeoutMs?: number }): Prom
|
||||
const timeoutId = controller
|
||||
? window.setTimeout(() => controller.abort(), timeoutMs)
|
||||
: null;
|
||||
const requestUrl = resolveBackendApiUrl(url);
|
||||
const headers = await buildBrowserBackendHeaders({
|
||||
Accept: "application/json",
|
||||
});
|
||||
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
headers: { Accept: "application/json" },
|
||||
response = await fetch(requestUrl, {
|
||||
headers,
|
||||
cache: "no-store",
|
||||
signal: controller?.signal,
|
||||
});
|
||||
@@ -457,18 +465,18 @@ export const dashboardClient = {
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
const request = fetch("/api/scan/terminal/ai", {
|
||||
const request = buildBrowserBackendHeaders({
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}).then((headers) => fetch(resolveBackendApiUrl("/api/scan/terminal/ai"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
headers,
|
||||
cache: "no-store",
|
||||
body: JSON.stringify({
|
||||
filters: payload.filters,
|
||||
snapshot_id: snapshotId || null,
|
||||
}),
|
||||
})
|
||||
}))
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
|
||||
Reference in New Issue
Block a user