uchill/front_material/components/dashboard/MentorDashboard.tsx

106 lines
3.3 KiB
TypeScript

/**
* Dashboard для ментора (iOS 26).
* Без календаря. Переиспользуемые секции в dashboard/mentor.
*/
'use client';
import React, { useState, useEffect, useCallback } from 'react';
import { getMentorDashboard, getMentorIncome } from '@/api/dashboard';
import type { MentorDashboardResponse, MentorIncomeResponse } from '@/api/dashboard';
import type { IncomePeriod } from './mentor';
import { DashboardLayout } from './ui';
import {
StatsSection,
IncomeSection,
ExtraStatsSection,
UpcomingLessonsSection,
RecentSubmissionsSection,
} from './mentor';
import { ErrorDisplay } from '@/components/common/ErrorDisplay';
const isAbortError = (e: unknown): boolean =>
(e as any)?.name === 'AbortError' ||
(e as any)?.name === 'CanceledError' ||
(e as any)?.code === 'ERR_CANCELED';
export const MentorDashboard: React.FC = () => {
const [stats, setStats] = useState<MentorDashboardResponse | null>(null);
const [incomeStats, setIncomeStats] = useState<MentorIncomeResponse | null>(null);
const [loading, setLoading] = useState(true);
const [incomeLoading, setIncomeLoading] = useState(false);
const [incomePeriod, setIncomePeriod] = useState<IncomePeriod>('week');
const [error, setError] = useState<string | null>(null);
const loadDashboard = useCallback(async (signal?: AbortSignal) => {
try {
setLoading(true);
setError(null);
const data = await getMentorDashboard(signal ? { signal } : undefined);
if (signal?.aborted) return;
setStats(data);
} catch (err: unknown) {
if (isAbortError(err)) return;
setError(err instanceof Error ? err.message : 'Ошибка загрузки данных');
} finally {
if (!signal?.aborted) setLoading(false);
}
}, []);
const loadIncome = useCallback(
async (signal?: AbortSignal) => {
try {
setIncomeLoading(true);
const data = await getMentorIncome(
incomePeriod,
undefined,
undefined,
signal ? { signal } : undefined
);
if (signal?.aborted) return;
setIncomeStats(data);
} catch (err: unknown) {
if (isAbortError(err)) return;
// игнор прочих
} finally {
if (!signal?.aborted) setIncomeLoading(false);
}
},
[incomePeriod]
);
useEffect(() => {
const c = new AbortController();
loadDashboard(c.signal);
return () => c.abort();
}, [loadDashboard]);
useEffect(() => {
const c = new AbortController();
loadIncome(c.signal);
return () => c.abort();
}, [loadIncome]);
// if (error && !stats) {
// return (
// <DashboardLayout>
// <ErrorDisplay error={error} onRetry={loadDashboard} />
// </DashboardLayout>
// );
// }
return (
<DashboardLayout className="ios26-dashboard-grid">
<IncomeSection
data={incomeStats}
period={incomePeriod}
onPeriodChange={setIncomePeriod}
loading={incomeLoading}
/>
<ExtraStatsSection stats={stats} loading={loading} />
<UpcomingLessonsSection data={stats} loading={loading} />
<RecentSubmissionsSection data={stats} loading={loading} />
</DashboardLayout>
);
};