feat: implement user account management and dashboard components.

This commit is contained in:
2569718930@qq.com
2026-03-14 13:33:06 +08:00
parent 2397a7569b
commit 6d1f77cac1
2 changed files with 76 additions and 22 deletions
+32 -15
View File
@@ -2,6 +2,7 @@
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import dynamic from "next/dynamic";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import type { User } from "@supabase/supabase-js"; import type { User } from "@supabase/supabase-js";
import { import {
@@ -40,7 +41,17 @@ import {
getSupabaseBrowserClient, getSupabaseBrowserClient,
hasSupabasePublicEnv, hasSupabasePublicEnv,
} from "@/lib/supabase/client"; } from "@/lib/supabase/client";
import { UnlockProOverlay } from "@/components/subscription/UnlockProOverlay";
const UnlockProOverlay = dynamic(
() =>
import("@/components/subscription/UnlockProOverlay").then(
(module) => module.UnlockProOverlay,
),
{
ssr: false,
loading: () => null,
},
);
// --- Types --- // --- Types ---
@@ -612,16 +623,20 @@ export function AccountCenter() {
return; return;
} }
try { try {
const authHeaders = await buildAuthedHeaders(false); const authHeadersPromise = buildAuthedHeaders(false);
const [configRes, walletsRes] = await Promise.all([ const [configRes, walletsRes] = await Promise.all([
fetch("/api/payments/config", { authHeadersPromise.then((headers) =>
cache: "no-store", fetch("/api/payments/config", {
headers: authHeaders, cache: "no-store",
}), headers,
fetch("/api/payments/wallets", { }),
cache: "no-store", ),
headers: authHeaders, authHeadersPromise.then((headers) =>
}), fetch("/api/payments/wallets", {
cache: "no-store",
headers,
}),
),
]); ]);
if (configRes.ok) { if (configRes.ok) {
const configJson = (await configRes.json()) as PaymentConfig; const configJson = (await configRes.json()) as PaymentConfig;
@@ -674,11 +689,13 @@ export function AccountCenter() {
const userPromise = supabaseReady const userPromise = supabaseReady
? getSupabaseBrowserClient().auth.getUser() ? getSupabaseBrowserClient().auth.getUser()
: Promise.resolve({ data: { user: null as User | null } }); : Promise.resolve({ data: { user: null as User | null } });
const authHeaders = await buildAuthedHeaders(false); const authHeadersPromise = buildAuthedHeaders(false);
const backendPromise = fetch("/api/auth/me", { const backendPromise = authHeadersPromise.then((headers) =>
cache: "no-store", fetch("/api/auth/me", {
headers: authHeaders, cache: "no-store",
}); headers,
}),
);
const [userResult, backendResult] = await Promise.all([ const [userResult, backendResult] = await Promise.all([
userPromise, userPromise,
backendPromise, backendPromise,
@@ -1,6 +1,7 @@
"use client"; "use client";
import { useEffect } from "react"; import { useEffect } from "react";
import dynamic from "next/dynamic";
import styles from "./Dashboard.module.css"; import styles from "./Dashboard.module.css";
import { import {
DashboardStoreProvider, DashboardStoreProvider,
@@ -9,11 +10,47 @@ import {
import { I18nProvider, useI18n } from "@/hooks/useI18n"; import { I18nProvider, useI18n } from "@/hooks/useI18n";
import { CitySidebar } from "@/components/dashboard/CitySidebar"; import { CitySidebar } from "@/components/dashboard/CitySidebar";
import { DetailPanel } from "@/components/dashboard/DetailPanel"; import { DetailPanel } from "@/components/dashboard/DetailPanel";
import { FutureForecastModal } from "@/components/dashboard/FutureForecastModal";
import { GuideModal } from "@/components/dashboard/GuideModal";
import { HeaderBar } from "@/components/dashboard/HeaderBar"; import { HeaderBar } from "@/components/dashboard/HeaderBar";
import { HistoryModal } from "@/components/dashboard/HistoryModal";
import { MapCanvas } from "@/components/dashboard/MapCanvas"; const MapCanvas = dynamic(
() =>
import("@/components/dashboard/MapCanvas").then((module) => module.MapCanvas),
{
ssr: false,
loading: () => <div className="map" aria-hidden="true" />,
},
);
const GuideModal = dynamic(
() =>
import("@/components/dashboard/GuideModal").then((module) => module.GuideModal),
{
ssr: false,
loading: () => null,
},
);
const HistoryModal = dynamic(
() =>
import("@/components/dashboard/HistoryModal").then(
(module) => module.HistoryModal,
),
{
ssr: false,
loading: () => null,
},
);
const FutureForecastModal = dynamic(
() =>
import("@/components/dashboard/FutureForecastModal").then(
(module) => module.FutureForecastModal,
),
{
ssr: false,
loading: () => null,
},
);
function DashboardScreen() { function DashboardScreen() {
const store = useDashboardStore(); const store = useDashboardStore();
@@ -57,9 +94,9 @@ function DashboardScreen() {
<HeaderBar /> <HeaderBar />
<CitySidebar /> <CitySidebar />
<DetailPanel /> <DetailPanel />
<GuideModal /> {store.isGuideOpen && <GuideModal />}
<HistoryModal /> {store.historyState.isOpen && <HistoryModal />}
<FutureForecastModal /> {store.futureModalDate && <FutureForecastModal />}
{showLoading && ( {showLoading && (
<div className="loading-overlay"> <div className="loading-overlay">
<div className="loading-spinner" /> <div className="loading-spinner" />