fix(ui): P0-4 — SaaS settings page crash from paginated API response
Some checks failed
CI / Build Frontend (push) Has been cancelled
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

listRelayTasks() expected RelayTaskInfo[] but API returns
{items:[], total:0, page:1, page_size:20}. When setTasks() received
the paginated object, tasks.map() crashed during render, triggering
the ErrorBoundary fallback "SaaS 平台加载失败".

Fix: extract .items from paginated response with Array.isArray fallback.
Also adds onError logging to ErrorBoundary wrappers for easier debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-10 21:42:52 +08:00
parent 34ef41c96f
commit b2d5b4075c
2 changed files with 9 additions and 2 deletions

View File

@@ -113,6 +113,9 @@ export function SettingsLayout({ onBack }: SettingsLayoutProps) {
case 'saas': return ( case 'saas': return (
<ErrorBoundary <ErrorBoundary
fallback={<div className="p-6 text-center text-gray-500">SaaS </div>} fallback={<div className="p-6 text-center text-gray-500">SaaS </div>}
onError={(err, info) => {
console.error('[Settings] SaaS page render error:', err, info.componentStack);
}}
> >
<SaaSSettings /> <SaaSSettings />
</ErrorBoundary> </ErrorBoundary>
@@ -120,6 +123,9 @@ export function SettingsLayout({ onBack }: SettingsLayoutProps) {
case 'billing': return ( case 'billing': return (
<ErrorBoundary <ErrorBoundary
fallback={<div className="p-6 text-center text-gray-500"></div>} fallback={<div className="p-6 text-center text-gray-500"></div>}
onError={(err, info) => {
console.error('[Settings] Billing page render error:', err, info.componentStack);
}}
> >
<PricingPage /> <PricingPage />
</ErrorBoundary> </ErrorBoundary>

View File

@@ -16,14 +16,15 @@ export function installRelayMethods(ClientClass: { prototype: any }): void {
// --- Relay Task Management --- // --- Relay Task Management ---
/** List relay tasks for the current user */ /** List relay tasks for the current user (extracts items from paginated response) */
proto.listRelayTasks = async function (this: { request<T>(method: string, path: string, body?: unknown): Promise<T> }, query?: { status?: string; page?: number; page_size?: number }): Promise<RelayTaskInfo[]> { proto.listRelayTasks = async function (this: { request<T>(method: string, path: string, body?: unknown): Promise<T> }, query?: { status?: string; page?: number; page_size?: number }): Promise<RelayTaskInfo[]> {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (query?.status) params.set('status', query.status); if (query?.status) params.set('status', query.status);
if (query?.page) params.set('page', String(query.page)); if (query?.page) params.set('page', String(query.page));
if (query?.page_size) params.set('page_size', String(query.page_size)); if (query?.page_size) params.set('page_size', String(query.page_size));
const qs = params.toString(); const qs = params.toString();
return this.request<RelayTaskInfo[]>('GET', `/api/v1/relay/tasks${qs ? '?' + qs : ''}`); const result = await this.request<{ items: RelayTaskInfo[]; total: number }>('GET', `/api/v1/relay/tasks${qs ? '?' + qs : ''}`);
return Array.isArray(result) ? result : (result?.items ?? []);
}; };
/** Retry a failed relay task (admin only) */ /** Retry a failed relay task (admin only) */