diff --git a/frontend/components/account/AccountCenter.tsx b/frontend/components/account/AccountCenter.tsx
index 8eecb7cd..705f6ef3 100644
--- a/frontend/components/account/AccountCenter.tsx
+++ b/frontend/components/account/AccountCenter.tsx
@@ -52,6 +52,7 @@ import {
WALLETCONNECT_PROJECT_ID,
} from "./constants";
import { InfoRow, PlusIcon } from "./AccountInfoRow";
+import { AccountFeedbackPanel } from "./AccountFeedbackPanel";
import {
chainIdToDisplayName,
clearStoredPaymentRecovery,
@@ -943,6 +944,13 @@ export function AccountCenter() {
)}
+
+
{/* Telegram Bot Section & Payment Details */}
{showSecondarySections ? (
([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState("");
+ const [available, setAvailable] = useState(true);
+
+ const load = useCallback(async (signal?: AbortSignal) => {
+ if (typeof fetch !== "function") return;
+ setLoading(true);
+ setError("");
+ try {
+ const res = await fetch("/api/feedback?limit=10", {
+ cache: "no-store",
+ headers: { Accept: "application/json" },
+ signal,
+ });
+ if (res.status === 401 || res.status === 403) {
+ setAvailable(false);
+ setEntries([]);
+ return;
+ }
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ const payload = (await res.json()) as UserFeedbackPayload;
+ setEntries(Array.isArray(payload.feedback) ? payload.feedback : []);
+ setAvailable(true);
+ } catch (err) {
+ if (signal?.aborted) return;
+ setError(String(err).slice(0, 140));
+ } finally {
+ if (!signal?.aborted) setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ const controller = new AbortController();
+ void load(controller.signal);
+ return () => controller.abort();
+ }, [load]);
+
+ const emptyText = useMemo(() => {
+ if (loading) return isEn ? "Loading feedback..." : "正在加载反馈...";
+ return isEn ? "No submitted feedback yet." : "暂无已提交反馈。";
+ }, [isEn, loading]);
+
+ if (!available) return null;
+
+ return (
+
+
+
+
+
+ {title}
+
+
{description}
+
+
+
+
+ {error && (
+
+ {isEn ? "Failed to load feedback: " : "反馈加载失败:"}{error}
+
+ )}
+
+ {entries.length === 0 ? (
+
+ {emptyText}
+
+ ) : (
+
+ {entries.map((entry) => (
+
+
+
+
+ {feedbackStatusLabel(entry.status, isEn)}
+
+
+ {categoryLabel(entry.category, isEn)}
+
+
+ {compactDate(entry.created_at)}
+
+
+
+ {entry.message || (isEn ? "Feedback" : "反馈")}
+
+
+
+
{compactDate(entry.updated_at)}
+
+ {isEn ? "Updated" : "最近更新"}
+
+
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/frontend/components/account/__tests__/accountFeedbackPanel.test.ts b/frontend/components/account/__tests__/accountFeedbackPanel.test.ts
new file mode 100644
index 00000000..8dcd53ee
--- /dev/null
+++ b/frontend/components/account/__tests__/accountFeedbackPanel.test.ts
@@ -0,0 +1,34 @@
+import fs from "node:fs";
+import path from "node:path";
+
+function assert(condition: unknown, message: string): asserts condition {
+ if (!condition) throw new Error(message);
+}
+
+export function runTests() {
+ const projectRoot = process.cwd();
+ const accountDir = path.join(projectRoot, "components", "account");
+ const accountCenterSource = fs.readFileSync(
+ path.join(accountDir, "AccountCenter.tsx"),
+ "utf8",
+ );
+ const feedbackPanelPath = path.join(accountDir, "AccountFeedbackPanel.tsx");
+
+ assert(fs.existsSync(feedbackPanelPath), "account center must ship a My Feedback panel component");
+
+ const feedbackPanelSource = fs.readFileSync(feedbackPanelPath, "utf8");
+ assert(
+ accountCenterSource.includes('import { AccountFeedbackPanel } from "./AccountFeedbackPanel";') &&
+ accountCenterSource.includes("
{
weeklyRewards: isEn ? "Referral Rewards" : "邀请奖励",
membershipDetails: isEn ? "Membership Details" : "会员权限详情",
identityStatus: isEn ? "Identity Status" : "身份状态",
+ accountFeedbackTitle: isEn ? "My Feedback" : "我的反馈",
+ accountFeedbackDescription: isEn
+ ? "Recent reports you submitted and their current handling status."
+ : "你最近提交过的反馈及当前处理状态。",
authMode: isEn ? "Auth Mode" : "鉴权模式",
weatherEngine: isEn ? "Weather Engine" : "气象引擎",
intradayAnalysis: isEn ? "Intraday Analysis" : "今日内分析",
diff --git a/frontend/components/dashboard/scan-terminal/UserFeedbackStatusButton.tsx b/frontend/components/dashboard/scan-terminal/UserFeedbackStatusButton.tsx
index 8bb7ba76..a248bb59 100644
--- a/frontend/components/dashboard/scan-terminal/UserFeedbackStatusButton.tsx
+++ b/frontend/components/dashboard/scan-terminal/UserFeedbackStatusButton.tsx
@@ -6,12 +6,12 @@ import type { UserFeedbackEntry, UserFeedbackPayload } from "@/types/ops";
import {
buildFeedbackNotificationKey,
countUnseenFeedbackUpdates,
+ FEEDBACK_STATUS_POLL_MS,
feedbackStatusLabel,
feedbackStatusTone,
} from "./feedback-status";
const FEEDBACK_STATUS_SEEN_KEY = "polyweather_feedback_status_seen_v1";
-const FEEDBACK_STATUS_POLL_MS = 60_000;
function loadSeenKeys() {
if (typeof window === "undefined") return new Set();
diff --git a/frontend/components/dashboard/scan-terminal/__tests__/feedbackStatusNotification.test.ts b/frontend/components/dashboard/scan-terminal/__tests__/feedbackStatusNotification.test.ts
index fd2ebefb..59266107 100644
--- a/frontend/components/dashboard/scan-terminal/__tests__/feedbackStatusNotification.test.ts
+++ b/frontend/components/dashboard/scan-terminal/__tests__/feedbackStatusNotification.test.ts
@@ -1,6 +1,7 @@
import {
buildFeedbackNotificationKey,
countUnseenFeedbackUpdates,
+ FEEDBACK_STATUS_POLL_MS,
feedbackStatusLabel,
} from "@/components/dashboard/scan-terminal/feedback-status";
@@ -9,6 +10,7 @@ function assert(condition: unknown, message: string): asserts condition {
}
export function runTests() {
+ assert(FEEDBACK_STATUS_POLL_MS === 600_000, "feedback bell background polling should run every 10 minutes");
assert(feedbackStatusLabel("open", false) === "已收到", "open feedback should read as received to users");
assert(feedbackStatusLabel("triaged", false) === "已确认", "triaged feedback should read as confirmed to users");
assert(feedbackStatusLabel("investigating", false) === "处理中", "investigating feedback should read as in progress");
diff --git a/frontend/components/dashboard/scan-terminal/feedback-status.ts b/frontend/components/dashboard/scan-terminal/feedback-status.ts
index 7e5dcd0e..c8e68832 100644
--- a/frontend/components/dashboard/scan-terminal/feedback-status.ts
+++ b/frontend/components/dashboard/scan-terminal/feedback-status.ts
@@ -1,5 +1,7 @@
import type { UserFeedbackEntry } from "@/types/ops";
+export const FEEDBACK_STATUS_POLL_MS = 10 * 60 * 1000;
+
export function feedbackStatusLabel(status: string | undefined, isEn: boolean) {
const key = String(status || "open").toLowerCase();
if (key === "triaged") return isEn ? "Confirmed" : "已确认";