uchill/front_material/components/dashboard/ParentDashboard.tsx

257 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Dashboard для родителя
*/
'use client';
import React, { useState, useEffect } from 'react';
import { getParentDashboard, DashboardStats } from '@/api/dashboard';
import { StatCard } from './StatCard';
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
export const ParentDashboard: React.FC = () => {
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
loadDashboard();
}, []);
const loadDashboard = async () => {
try {
setLoading(true);
setError(null);
const data = await getParentDashboard();
setStats(data);
} catch (err: any) {
console.error('Error loading dashboard:', err);
setError(err.message || 'Ошибка загрузки данных');
} finally {
setLoading(false);
}
};
if (error && !stats) {
return (
<div style={{
padding: '24px',
textAlign: 'center',
color: 'var(--md-sys-color-error)'
}}>
<p>{error}</p>
<button
onClick={loadDashboard}
style={{
marginTop: '16px',
padding: '12px 24px',
background: 'var(--md-sys-color-primary)',
color: 'var(--md-sys-color-on-primary)',
border: 'none',
borderRadius: '20px',
cursor: 'pointer',
fontSize: '14px',
fontWeight: '500'
}}
>
Попробовать снова
</button>
</div>
);
}
return (
<div style={{
width: '100%',
maxWidth: '100%',
padding: '16px',
background: 'var(--md-sys-color-background)',
}}>
{/* Приветствие */}
<div style={{
background: 'linear-gradient(135deg, var(--md-sys-color-primary) 0%, var(--md-sys-color-tertiary) 100%)',
borderRadius: '20px',
padding: '32px',
marginBottom: '24px',
color: 'var(--md-sys-color-on-primary)'
}}>
<h1 className="parent-dashboard-title" style={{
fontSize: '28px',
fontWeight: '500',
margin: '0 0 8px 0'
}}>
Добро пожаловать! 👨👩👧👦
</h1>
<p style={{
fontSize: '16px',
margin: 0,
opacity: 0.9
}}>
Отслеживайте прогресс ваших детей и их успехи в обучении
</p>
</div>
{/* Статистика по детям */}
{stats?.children_stats && stats.children_stats.length > 0 && (
<div className="parent-children-grid" style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(300px, 100%), 1fr))',
gap: '16px',
marginBottom: '24px'
}}>
{stats.children_stats.map((child) => (
<div
key={child.id}
style={{
background: 'var(--md-sys-color-surface)',
borderRadius: '20px',
padding: '24px',
border: '1px solid var(--md-sys-color-outline-variant)'
}}
>
<div style={{
display: 'flex',
alignItems: 'center',
gap: '16px',
marginBottom: '20px'
}}>
{child.avatar_url ? (
<img
src={child.avatar_url}
alt={child.name}
style={{
width: '56px',
height: '56px',
borderRadius: '50%',
objectFit: 'cover'
}}
/>
) : (
<div style={{
width: '56px',
height: '56px',
borderRadius: '50%',
background: 'var(--md-sys-color-primary-container)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--md-sys-color-on-primary-container)',
fontSize: '24px',
fontWeight: '500'
}}>
{child.name.charAt(0).toUpperCase()}
</div>
)}
<div>
<h3 style={{
fontSize: '18px',
fontWeight: '500',
color: 'var(--md-sys-color-on-surface)',
margin: '0 0 4px 0'
}}>
{child.name}
</h3>
<p style={{
fontSize: '14px',
color: 'var(--md-sys-color-on-surface-variant)',
margin: 0
}}>
{child.completed_lessons} / {child.total_lessons} занятий
</p>
</div>
</div>
<div style={{
display: 'flex',
flexDirection: 'column',
gap: '12px'
}}>
<div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
marginBottom: '4px',
fontSize: '12px',
color: 'var(--md-sys-color-on-surface-variant)'
}}>
<span>Прогресс занятий</span>
<span>{Math.round((child.completed_lessons / child.total_lessons) * 100)}%</span>
</div>
<div style={{
width: '100%',
height: '6px',
background: 'var(--md-sys-color-surface-variant)',
borderRadius: '3px',
overflow: 'hidden'
}}>
<div style={{
height: '100%',
width: `${(child.completed_lessons / child.total_lessons) * 100}%`,
background: 'var(--md-sys-color-primary)',
transition: 'width 0.3s ease'
}}></div>
</div>
</div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
fontSize: '14px'
}}>
<span style={{ color: 'var(--md-sys-color-on-surface-variant)' }}>
Средняя оценка:
</span>
<span style={{
fontWeight: '500',
color: 'var(--md-sys-color-on-surface)'
}}>
{child.average_grade != null ? String(parseFloat(Number(child.average_grade).toFixed(2))) : '—'}
</span>
</div>
<div style={{
display: 'flex',
justifyContent: 'space-between',
fontSize: '14px'
}}>
<span style={{ color: 'var(--md-sys-color-on-surface-variant)' }}>
Домашних заданий:
</span>
<span style={{
fontWeight: '500',
color: child.homework_pending > 0 ? 'var(--md-sys-color-error)' : 'var(--md-sys-color-tertiary)'
}}>
{child.homework_pending}
</span>
</div>
</div>
</div>
))}
</div>
)}
{/* Общая статистика */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))',
gap: '16px',
marginBottom: '24px'
}}>
<StatCard
title="Всего детей"
value={loading ? '—' : (stats?.children_count || 0)}
loading={loading}
icon={
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="9" cy="7" r="4"></circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</svg>
}
/>
</div>
</div>
);
};