Розробка особистого кабінету студента для LMS
Особистий кабінет студента—головна точка входу на платформу. Звідси студент управляє своїми курсами, бачить прогрес, отримує завдання та сертифікати. Добре спроектований кабінет зменшує кількість запитань у підтримку та утримує користувачів.
Розділи особистого кабінету
Дашборд—перший екран після входу. Показує: активні курси з прогресом, ближайші дедлайни, останні нотифікації, streak.
Мої курси—список всіх записів з фільтрами (активні, завершені, зупинені). Карточка курсу: обкладинка, назва, прогресс-бар, останній урок.
Розклад—календар вебінарів та дедлайнів, інтеграція з Google Calendar через iCal.
Завдання—зведка по всіх незакритих завданнях: що треба здати, що чекає перевірки, що вже перевірено.
Мої сертифікати—список отриманих сертифікатів з можливістю скачати PDF та поділитися посиланням.
Налаштування—профіль, нотифікації, безпека, способи оплати.
Архітектура дашборда
// Агреговані дані для дашборда—один запит
interface StudentDashboard {
activeCourses: {
id: string;
title: string;
coverUrl: string;
progress: number;
lastLesson: { id: string; title: string };
nextDeadline: { title: string; dueAt: Date } | null;
}[];
upcomingDeadlines: {
assignmentId: string;
title: string;
courseName: string;
dueAt: Date;
status: 'not_started' | 'in_progress' | 'submitted';
}[];
upcomingEvents: {
id: string;
title: string;
startsAt: Date;
joinUrl: string;
}[];
recentActivity: {
type: string;
description: string;
createdAt: Date;
}[];
stats: {
totalCourses: number;
completedCourses: number;
totalXp: number;
currentStreak: number;
certificatesCount: number;
};
}
Дашборд завантажується одним API-викликом—ніяких N+1 запитів у компонентах.
Прогресс-бар курсу
function CourseProgressCard({ course }) {
return (
<Link to={`/courses/${course.id}/continue`} className="block rounded-xl border p-4 hover:shadow-md">
<div className="flex gap-3">
<img src={course.coverUrl} className="w-16 h-16 rounded-lg object-cover" />
<div className="flex-1 min-w-0">
<h3 className="font-medium truncate">{course.title}</h3>
<p className="text-sm text-gray-500 mt-1">Останній урок: {course.lastLesson.title}</p>
<div className="mt-2">
<div className="flex justify-between text-xs text-gray-500 mb-1">
<span>Прогрес</span>
<span>{course.progress}%</span>
</div>
<div className="h-1.5 bg-gray-100 rounded-full">
<div
className="h-full bg-blue-500 rounded-full transition-all duration-500"
style={{ width: `${course.progress}%` }}
/>
</div>
</div>
</div>
</div>
</Link>
);
}
Нотифікації в кабінету
Центр нотифікацій (дзвіночок в хедері) + сторінка всіх нотифікацій. Типи нотифікацій:
- Завдання перевірено: «Ваша робота по React отримала оцінку 92/100»
- Дедлайн через 24 години
- Новий урок добавлений у курс
- Відповідь на ваш пост у форумі
- Вебінар через 15 хвилин
CREATE TABLE notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
type VARCHAR(100) NOT NULL,
title VARCHAR(500),
body TEXT,
action_url VARCHAR(2000),
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX ON notifications (user_id, is_read, created_at DESC);
Лічильник непрочитаних кешується в Redis: user:notifications:unread:{user_id}. Інкрементується при створенні нотифікації, скидається при відкритті центру нотифікацій.
Налаштування профілю
- Аватар: завантаження файлу → кроппер (react-image-crop) → зберігання в S3
- Зміна email з підтвердженням на новій адресі
- Зміна пароля з перевіркою поточного
- Налаштування нотифікацій: які типи та по яких каналах (email, push)
- Часовий пояс: впливає на відображення всіх дат в інтерфейсі
Терміни
Дашборд з активними курсами, дедлайнами та статистикою—4–5 днів. Сторінки завдань, сертифікатів, розкладу—3–4 дня. Центр нотифікацій з real-time оновленнями—2–3 дня. Налаштування профілю—2–3 дня.







