tablitsa-grafik-syna/index.html

224 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>График занятий сына</title>
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--purple:#7C3AED;--orange:#F97316;--green:#22C55E}
*{box-sizing:border-box;margin:0;padding:0}
body{font:16px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--gray-50);padding-bottom:40px}
.container{max-width:1400px;margin:0 auto;padding:40px 24px}
h1{font-size:36px;font-weight:800;margin-bottom:12px;color:var(--ink)}
.subtitle{color:var(--gray-500);margin-bottom:32px;font-size:17px}
.section{background:var(--white);border-radius:16px;padding:24px;margin-bottom:24px;box-shadow:0 1px 3px rgba(0,0,0,0.08)}
.section-title{font-size:22px;font-weight:700;margin-bottom:20px;display:flex;align-items:center;gap:10px}
.table-wrapper{overflow-x:auto}
table{width:100%;border-collapse:collapse;min-width:900px}
th,td{padding:14px 12px;text-align:left;border-bottom:1px solid var(--gray-100);vertical-align:top}
th{background:var(--gray-100);font-weight:700;font-size:14px;color:var(--gray-500);text-transform:uppercase;letter-spacing:0.5px;white-space:nowrap}
tr:hover{background:var(--cyan-50)}
tr:last-child td{border-bottom:none}
input,select{width:100%;border:1px solid var(--gray-100);border-radius:6px;padding:10px 12px;font-size:15px;background:var(--white);transition:border-color 0.2s}
input:focus,select:focus{outline:none;border-color:var(--cyan);box-shadow:0 0 0 3px rgba(0,229,255,0.1)}
input[type="time"]{font-family:inherit}
input[type="number"]{width:100px}
.day-header{background:var(--ink);color:var(--white);font-weight:700;text-align:center;padding:12px;border-radius:8px;margin-bottom:16px}
.day-header.purple{background:var(--purple)}
.day-header.orange{background:var(--orange)}
.day-header.green{background:var(--green)}
.badge{display:inline-block;padding:4px 10px;border-radius:20px;font-size:12px;font-weight:700;text-transform:uppercase}
.badge-cyan{background:var(--cyan-50);color:#008B9E}
.badge-purple{background:#F3E8FF;color:#7C3AED}
.badge-orange{background:#FFEDD5;color:#C2410C}
.add-row-btn{background:var(--ink);color:var(--white);border:none;padding:12px 24px;border-radius:8px;font-weight:700;cursor:pointer;font-size:15px;transition:opacity 0.2s}
.add-row-btn:hover{opacity:0.85}
.controls{display:flex;gap:12px;margin-bottom:20px;flex-wrap:wrap}
.export-btn{background:var(--cyan);color:var(--ink);border:none;padding:12px 20px;border-radius:8px;font-weight:700;cursor:pointer;font-size:15px}
.search-box{flex:1;min-width:250px}
.total-row{background:var(--cyan-50);font-weight:700}
.school-day{background:var(--purple);color:var(--white)}
.school-day th{background:rgba(124,58,237,0.2);color:var(--white)}
.pickup-row{background:var(--orange);color:var(--white)}
.pickup-row th{background:rgba(249,115,22,0.2);color:var(--white)}
.pickup-row td{border-color:rgba(249,115,22,0.3)}
.money-row{background:var(--green);color:var(--white)}
.money-row th{background:rgba(34,197,94,0.2);color:var(--white)}
.money-row td{border-color:rgba(34,197,94,0.3)}
input::placeholder{color:var(--gray-500)}
@media (max-width:768px){.container{padding:24px 16px}h1{font-size:28px}.section{padding:16px;border-radius:12px}th,td{padding:10px 8px;font-size:14px}input,select{padding:8px 10px;font-size:14px}}
</style>
</head>
<body>
<div class="container">
<h1>📅 График занятий сына</h1>
<p class="subtitle">Заполняй поля прямо в таблице — данные сохраняются автоматически</p>
<div class="controls">
<input type="text" class="search-box" id="search" placeholder="🔍 Поиск по названию, учителю...">
<button class="export-btn" onclick="exportCSV()">📥 Выгрузить в Excel</button>
</div>
<div class="section">
<div class="section-title">🏫 Школьное расписание (2-я смена)</div>
<div class="table-wrapper">
<table id="schoolTable">
<thead>
<tr>
<th style="width:60px">№</th>
<th>Предмет</th>
<th style="width:140px">Начало</th>
<th style="width:140px">Окончание</th>
<th style="width:120px">Кто забирает</th>
</tr>
</thead>
<tbody id="schoolBody"></tbody>
</table>
</div>
<button class="add-row-btn" onclick="addSchoolRow()" style="margin-top:16px">+ Добавить урок</button>
</div>
<div class="section">
<div class="section-title">🎨 Кружки и дополнительные занятия</div>
<div class="table-wrapper">
<table id="classesTable">
<thead>
<tr>
<th style="width:120px">День недели</th>
<th>Название кружка/занятия</th>
<th style="width:140px">Время</th>
<th style="width:180px">Имя учителя</th>
<th style="width:120px">Сумма (₸)</th>
<th style="width:150px">Кто отвозит/забирает</th>
<th style="width:80px"></th>
</tr>
</thead>
<tbody id="classesBody"></tbody>
</table>
</div>
<button class="add-row-btn" onclick="addClassRow()" style="margin-top:16px">+ Добавить занятие</button>
</div>
<div class="section">
<div class="section-title">💰 Итого к оплате</div>
<div style="font-size:28px;font-weight:800;color:var(--green)" id="totalAmount">0 ₸</div>
<p style="color:var(--gray-500);margin-top:8px">Автоматический подсчёт по всем занятиям</p>
</div>
</div>
<script>
const DAYS = ['Понедельник','Вторник','Среда','Четверг','Пятница','Суббота','Воскресенье'];
const COLORS = ['','purple','orange','green','purple','orange',''];
let schoolData = [
{num:1,subject:'',startTime:'08:00',endTime:'08:45',pickup:''},
{num:2,subject:'',startTime:'09:00',endTime:'09:45',pickup:''},
{num:3,subject:'',startTime:'10:00',endTime:'10:45',pickup:''},
{num:4,subject:'',startTime:'11:00',endTime:'11:45',pickup:''},
{num:5,subject:'',startTime:'12:00',endTime:'12:45',pickup:''},
{num:6,subject:'',startTime:'13:00',endTime:'13:45',pickup:''},
{num:7,subject:'',startTime:'14:00',endTime:'14:45',pickup:''}
];
let classesData = [];
function load() {
const s = localStorage.getItem('schoolData');
const c = localStorage.getItem('classesData');
if (s) schoolData = JSON.parse(s);
if (c) classesData = JSON.parse(c);
}
function save() {
localStorage.setItem('schoolData', JSON.stringify(schoolData));
localStorage.setItem('classesData', JSON.stringify(classesData));
render();
}
function render() {
const schoolBody = document.getElementById('schoolBody');
schoolBody.innerHTML = schoolData.map((row, i) => `
<tr class="${i % 2 === 0 ? 'school-day' : ''}">
<td><input type="number" value="${row.num}" min="1" max="10" onchange="updateSchool(${i},'num',this.value)"></td>
<td><input type="text" value="${row.subject}" placeholder="Предмет" onchange="updateSchool(${i},'subject',this.value)"></td>
<td><input type="time" value="${row.startTime}" onchange="updateSchool(${i},'startTime',this.value)"></td>
<td><input type="time" value="${row.endTime}" onchange="updateSchool(${i},'endTime',this.value)"></td>
<td><input type="text" value="${row.pickup}" placeholder="Кто забирает" onchange="updateSchool(${i},'pickup',this.value)"></td>
</tr>
`).join('');
const classesBody = document.getElementById('classesBody');
classesBody.innerHTML = classesData.map((row, i) => `
<tr>
<td>
<select onchange="updateClass(${i},'day',this.value)">
<option value="">Выбрать день</option>
${DAYS.map((d, idx) => `<option value="${d}" ${row.day === d ? 'selected' : ''}>${d}</option>`).join('')}
</select>
</td>
<td><input type="text" value="${row.name}" placeholder="Название кружка" onchange="updateClass(${i},'name',this.value)"></td>
<td><input type="time" value="${row.time}" onchange="updateClass(${i},'time',this.value)"></td>
<td><input type="text" value="${row.teacher}" placeholder="Имя учителя" onchange="updateClass(${i},'teacher',this.value)"></td>
<td><input type="number" value="${row.amount}" placeholder="0" min="0" onchange="updateClass(${i},'amount',this.value)"></td>
<td><input type="text" value="${row.transport}" placeholder="Кто возит" onchange="updateClass(${i},'transport',this.value)"></td>
<td><button onclick="removeClass(${i})" style="background:none;border:none;cursor:pointer;font-size:18px;color:var(--gray-500)" title="Удалить">✕</button></td>
</tr>
`).join('');
const total = classesData.reduce((sum, row) => sum + (parseFloat(row.amount) || 0), 0);
document.getElementById('totalAmount').textContent = total.toLocaleString('ru-RU') + ' ₸';
}
function updateSchool(index, field, value) {
schoolData[index][field] = field === 'num' ? parseInt(value) || 0 : value;
save();
}
function updateClass(index, field, value) {
classesData[index][field] = field === 'amount' ? parseFloat(value) || 0 : value;
save();
}
function addSchoolRow() {
schoolData.push({num: schoolData.length + 1, subject:'', startTime:'14:00', endTime:'14:45', pickup:''});
save();
}
function addClassRow() {
classesData.push({day:'', name:'', time:'', teacher:'', amount:0, transport:''});
save();
}
function removeClass(index) {
if (confirm('Удалить это занятие?')) {
classesData.splice(index, 1);
save();
}
}
function exportCSV() {
const head = ['День','Название','Время','Учитель','Сумма (₸)','Кто возит'];
const rows = classesData.map(r => [r.day, r.name, r.time, r.teacher, r.amount, r.transport]
.map(v => '"' + String(v ?? '').replace(/"/g, '""') + '"').join(';'));
const csv = '\uFEFF' + [head.join(';'), ...rows].join('\n');
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([csv], {type: 'text/csv'}));
a.download = 'grafik_zanyatiy.csv';
a.click();
}
document.getElementById('search').addEventListener('input', function(e) {
const q = e.target.value.toLowerCase();
document.querySelectorAll('#classesBody tr').forEach((tr, i) => {
const row = classesData[i];
const text = (row.name + ' ' + row.teacher + ' ' + row.day).toLowerCase();
tr.style.display = text.includes(q) ? '' : 'none';
});
});
load();
render();
</script>
</body>
</html>