396 lines
19 KiB
HTML
396 lines
19 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Умный планировщик учёбы</title>
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📚</text></svg>">
|
||
<style>
|
||
:root {
|
||
--primary: #4F46E5;
|
||
--primary-light: #818CF8;
|
||
--success: #10B981;
|
||
--warning: #F59E0B;
|
||
--danger: #EF4444;
|
||
--background: #F9FAFB;
|
||
--surface: #FFFFFF;
|
||
--text: #111827;
|
||
--text-secondary: #6B7280;
|
||
--border: #E5E7EB;
|
||
--shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||
--radius: 12px;
|
||
}
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif; background: var(--background); color: var(--text); line-height: 1.5; }
|
||
|
||
/* Layout */
|
||
.app { display: flex; min-height: 100vh; }
|
||
.sidebar { width: 260px; background: var(--surface); border-right: 1px solid var(--border); padding: 24px 16px; position: fixed; height: 100vh; overflow-y: auto; }
|
||
.main { margin-left: 260px; flex: 1; padding: 32px; }
|
||
|
||
/* Logo */
|
||
.logo { display: flex; align-items: center; gap: 12px; margin-bottom: 32px; padding: 0 8px; }
|
||
.logo-icon { width: 42px; height: 42px; background: linear-gradient(135deg, var(--primary), var(--primary-light)); border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 22px; }
|
||
.logo-text { font-size: 18px; font-weight: 700; color: var(--text); }
|
||
|
||
/* Navigation */
|
||
.nav { list-style: none; }
|
||
.nav-item { margin-bottom: 4px; }
|
||
.nav-link { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: var(--radius); color: var(--text-secondary); text-decoration: none; font-weight: 500; transition: all 0.2s; cursor: pointer; }
|
||
.nav-link:hover { background: var(--background); color: var(--primary); }
|
||
.nav-link.active { background: var(--primary); color: white; }
|
||
.nav-icon { font-size: 20px; width: 24px; text-align: center; }
|
||
|
||
/* Header */
|
||
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 32px; }
|
||
.header h1 { font-size: 28px; font-weight: 700; color: var(--text); }
|
||
.header-date { color: var(--text-secondary); font-size: 14px; margin-top: 4px; }
|
||
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 20px; border-radius: var(--radius); border: none; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
|
||
.btn-primary { background: var(--primary); color: white; }
|
||
.btn-primary:hover { background: #4338CA; }
|
||
.btn-secondary { background: var(--background); color: var(--text); border: 1px solid var(--border); }
|
||
.btn-secondary:hover { background: var(--border); }
|
||
|
||
/* Stats cards */
|
||
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 32px; }
|
||
.stat-card { background: var(--surface); border-radius: var(--radius); padding: 24px; box-shadow: var(--shadow); }
|
||
.stat-value { font-size: 32px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
|
||
.stat-label { font-size: 14px; color: var(--text-secondary); }
|
||
.stat-card.highlight { background: linear-gradient(135deg, var(--primary), var(--primary-light)); }
|
||
.stat-card.highlight .stat-value, .stat-card.highlight .stat-label { color: white; }
|
||
|
||
/* Sections */
|
||
.section { background: var(--surface); border-radius: var(--radius); padding: 24px; box-shadow: var(--shadow); margin-bottom: 24px; }
|
||
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||
.section-title { font-size: 18px; font-weight: 600; color: var(--text); }
|
||
.section-link { color: var(--primary); text-decoration: none; font-size: 14px; font-weight: 500; cursor: pointer; }
|
||
.section-link:hover { text-decoration: underline; }
|
||
|
||
/* Task list */
|
||
.task-list { list-style: none; }
|
||
.task-item { display: flex; align-items: flex-start; gap: 12px; padding: 16px; border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 12px; transition: all 0.2s; }
|
||
.task-item:hover { border-color: var(--primary-light); }
|
||
.task-item.completed { opacity: 0.6; }
|
||
.task-item.completed .task-title { text-decoration: line-through; }
|
||
.task-checkbox { width: 20px; height: 20px; border: 2px solid var(--border); border-radius: 6px; cursor: pointer; flex-shrink: 0; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
|
||
.task-checkbox.checked { background: var(--success); border-color: var(--success); }
|
||
.task-checkbox.checked::after { content: '✓'; color: white; font-size: 14px; }
|
||
.task-content { flex: 1; min-width: 0; }
|
||
.task-title { font-weight: 500; color: var(--text); margin-bottom: 4px; }
|
||
.task-meta { font-size: 13px; color: var(--text-secondary); display: flex; gap: 12px; }
|
||
.task-subject { background: var(--primary-light); color: white; padding: 2px 8px; border-radius: 4px; font-size: 12px; }
|
||
.task-priority { padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; }
|
||
.priority-high { background: #FEE2E2; color: var(--danger); }
|
||
.priority-medium { background: #FEF3C7; color: var(--warning); }
|
||
.priority-low { background: #D1FAE5; color: var(--success); }
|
||
.task-due { display: flex; align-items: center; gap: 4px; }
|
||
.task-due.overdue { color: var(--danger); font-weight: 500; }
|
||
.task-actions { display: flex; gap: 8px; opacity: 0; transition: opacity 0.2s; }
|
||
.task-item:hover .task-actions { opacity: 1; }
|
||
.task-action-btn { background: none; border: none; cursor: pointer; font-size: 16px; padding: 4px; border-radius: 4px; }
|
||
.task-action-btn:hover { background: var(--background); }
|
||
|
||
/* Schedule */
|
||
.schedule-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; }
|
||
.schedule-day { background: var(--background); border-radius: var(--radius); padding: 16px; }
|
||
.schedule-day-title { font-weight: 600; color: var(--text); margin-bottom: 12px; padding-bottom: 8px; border-bottom: 2px solid var(--border); }
|
||
.schedule-item { background: var(--surface); border-radius: 8px; padding: 12px; margin-bottom: 8px; border-left: 3px solid var(--primary); }
|
||
.schedule-subject { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
|
||
.schedule-time { font-size: 12px; color: var(--text-secondary); }
|
||
.schedule-location { font-size: 12px; color: var(--text-secondary); margin-top: 4px; }
|
||
|
||
/* Modal */
|
||
.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: none; align-items: center; justify-content: center; z-index: 1000; }
|
||
.modal-overlay.open { display: flex; }
|
||
.modal { background: var(--surface); border-radius: var(--radius); padding: 32px; width: 100%; max-width: 480px; max-height: 90vh; overflow-y: auto; }
|
||
.modal-title { font-size: 20px; font-weight: 600; margin-bottom: 24px; }
|
||
.form-group { margin-bottom: 16px; }
|
||
.form-label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 8px; color: var(--text); }
|
||
.form-input { width: 100%; padding: 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 14px; font-family: inherit; }
|
||
.form-input:focus { outline: none; border-color: var(--primary); }
|
||
.form-select { width: 100%; padding: 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 14px; font-family: inherit; background: white; }
|
||
.form-actions { display: flex; gap: 12px; justify-content: flex-end; margin-top: 24px; }
|
||
|
||
/* Empty state */
|
||
.empty-state { text-align: center; padding: 48px 24px; color: var(--text-secondary); }
|
||
.empty-state-icon { font-size: 48px; margin-bottom: 16px; }
|
||
.empty-state-text { font-size: 16px; margin-bottom: 16px; }
|
||
|
||
/* Progress chart */
|
||
.progress-container { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
|
||
.progress-circle { width: 120px; height: 120px; position: relative; }
|
||
.progress-circle svg { transform: rotate(-90deg); }
|
||
.progress-circle-bg { fill: none; stroke: var(--border); stroke-width: 8; }
|
||
.progress-circle-fg { fill: none; stroke: var(--success); stroke-width: 8; stroke-dasharray: 283; stroke-dashoffset: 283; transition: stroke-dashoffset 0.5s; }
|
||
.progress-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24px; font-weight: 700; color: var(--text); }
|
||
.progress-details { flex: 1; }
|
||
.progress-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--border); }
|
||
.progress-item:last-child { border-bottom: none; }
|
||
|
||
/* Responsive */
|
||
@media (max-width: 768px) {
|
||
.sidebar { width: 100%; height: auto; position: relative; border-right: none; border-bottom: 1px solid var(--border); }
|
||
.main { margin-left: 0; padding: 20px; }
|
||
.nav { display: flex; overflow-x: auto; gap: 8px; padding-bottom: 8px; }
|
||
.nav-item { margin-bottom: 0; }
|
||
.nav-link { white-space: nowrap; padding: 10px 14px; }
|
||
.stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||
.header { flex-direction: column; gap: 16px; align-items: flex-start; }
|
||
.schedule-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<!-- Sidebar -->
|
||
<aside class="sidebar">
|
||
<div class="logo">
|
||
<div class="logo-icon">📚</div>
|
||
<div class="logo-text">StudyPlanner</div>
|
||
</div>
|
||
<ul class="nav">
|
||
<li class="nav-item">
|
||
<a class="nav-link active" data-page="dashboard">
|
||
<span class="nav-icon">🏠</span>
|
||
Главная
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" data-page="tasks">
|
||
<span class="nav-icon">✓</span>
|
||
Задания
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" data-page="schedule">
|
||
<span class="nav-icon">📅</span>
|
||
Расписание
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" data-page="calendar">
|
||
<span class="nav-icon">📆</span>
|
||
Календарь
|
||
</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" data-page="progress">
|
||
<span class="nav-icon">📊</span>
|
||
Прогресс
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</aside>
|
||
|
||
<!-- Main content -->
|
||
<main class="main">
|
||
<div class="header">
|
||
<div>
|
||
<h1 id="page-title">Главная</h1>
|
||
<div class="header-date" id="current-date"></div>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="openTaskModal()">
|
||
<span>+</span> Добавить задание
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Dashboard -->
|
||
<div id="page-dashboard" class="page">
|
||
<div class="stats-grid">
|
||
<div class="stat-card highlight">
|
||
<div class="stat-value" id="stat-total">0</div>
|
||
<div class="stat-label">Всего заданий</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="stat-completed">0</div>
|
||
<div class="stat-label">Выполнено</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="stat-pending">0</div>
|
||
<div class="stat-label">В процессе</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="stat-overdue" style="color: var(--danger)">0</div>
|
||
<div class="stat-label">Просрочено</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">📋 Ближайшие дедлайны</h2>
|
||
<span class="section-link" onclick="navigateTo('tasks')">Все задания →</span>
|
||
</div>
|
||
<ul class="task-list" id="dashboard-tasks"></ul>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">📚 Расписание на сегодня</h2>
|
||
<span class="section-link" onclick="navigateTo('schedule')">Всё расписание →</span>
|
||
</div>
|
||
<div class="schedule-grid" id="dashboard-schedule"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tasks page -->
|
||
<div id="page-tasks" class="page" style="display: none;">
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">Все задания</h2>
|
||
<div style="display: flex; gap: 8px;">
|
||
<select class="form-select" style="width: auto;" id="task-filter" onchange="renderTasks()">
|
||
<option value="all">Все</option>
|
||
<option value="pending">В процессе</option>
|
||
<option value="completed">Выполнено</option>
|
||
<option value="overdue">Просрочено</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<ul class="task-list" id="tasks-list"></ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Schedule page -->
|
||
<div id="page-schedule" class="page" style="display: none;">
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">Расписание на неделю</h2>
|
||
<button class="btn btn-primary" onclick="openScheduleModal()">+ Добавить</button>
|
||
</div>
|
||
<div class="schedule-grid" id="schedule-full"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Calendar page -->
|
||
<div id="page-calendar" class="page" style="display: none;">
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">Календарь заданий</h2>
|
||
</div>
|
||
<div id="calendar-view"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Progress page -->
|
||
<div id="page-progress" class="page" style="display: none;">
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">Ваш прогресс</h2>
|
||
</div>
|
||
<div class="progress-container">
|
||
<div class="progress-circle">
|
||
<svg width="120" height="120">
|
||
<circle class="progress-circle-bg" cx="60" cy="60" r="45"></circle>
|
||
<circle class="progress-circle-fg" id="progress-ring" cx="60" cy="60" r="45"></circle>
|
||
</svg>
|
||
<div class="progress-text" id="progress-percent">0%</div>
|
||
</div>
|
||
<div class="progress-details">
|
||
<div class="progress-item">
|
||
<span>Всего заданий</span>
|
||
<strong id="progress-total">0</strong>
|
||
</div>
|
||
<div class="progress-item">
|
||
<span>Выполнено</span>
|
||
<strong id="progress-completed" style="color: var(--success)">0</strong>
|
||
</div>
|
||
<div class="progress-item">
|
||
<span>В процессе</span>
|
||
<strong id="progress-pending">0</strong>
|
||
</div>
|
||
<div class="progress-item">
|
||
<span>Просрочено</span>
|
||
<strong id="progress-overdue" style="color: var(--danger)">0</strong>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">По предметам</h2>
|
||
</div>
|
||
<div id="progress-by-subject"></div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- Task Modal -->
|
||
<div class="modal-overlay" id="task-modal">
|
||
<div class="modal">
|
||
<h3 class="modal-title">Новое задание</h3>
|
||
<form id="task-form" onsubmit="saveTask(event)">
|
||
<div class="form-group">
|
||
<label class="form-label">Название</label>
|
||
<input type="text" class="form-input" name="title" required placeholder="Например: Решить задачу №5">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Предмет</label>
|
||
<input type="text" class="form-input" name="subject" required placeholder="Например: Математика">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Срок выполнения</label>
|
||
<input type="date" class="form-input" name="dueDate" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Приоритет</label>
|
||
<select class="form-select" name="priority">
|
||
<option value="low">Низкий</option>
|
||
<option value="medium" selected>Средний</option>
|
||
<option value="high">Высокий</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Описание (необязательно)</label>
|
||
<textarea class="form-input" name="description" rows="3" placeholder="Детали задания"></textarea>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button type="button" class="btn btn-secondary" onclick="closeTaskModal()">Отмена</button>
|
||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Schedule Modal -->
|
||
<div class="modal-overlay" id="schedule-modal">
|
||
<div class="modal">
|
||
<h3 class="modal-title">Добавить занятие</h3>
|
||
<form id="schedule-form" onsubmit="saveSchedule(event)">
|
||
<div class="form-group">
|
||
<label class="form-label">Предмет</label>
|
||
<input type="text" class="form-input" name="subject" required placeholder="Например: Физика">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">День недели</label>
|
||
<select class="form-select" name="day">
|
||
<option value="mon">Понедельник</option>
|
||
<option value="tue">Вторник</option>
|
||
<option value="wed">Среда</option>
|
||
<option value="thu">Четверг</option>
|
||
<option value="fri">Пятница</option>
|
||
<option value="sat">Суббота</option>
|
||
<option value="sun">Воскресенье</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Время</label>
|
||
<input type="time" class="form-input" name="time" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Место (кабинет или ссылка)</label>
|
||
<input type="text" class="form-input" name="location" placeholder="Например: каб. 305 или Zoom">
|
||
</div>
|
||
<div class="form-actions">
|
||
<button type="button" class="btn btn-secondary" onclick="closeScheduleModal()">Отмена</button>
|
||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="app.js"></script>
|
||
</body>
|
||
</html>
|