568 lines
23 KiB
HTML
568 lines
23 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;--green:#22C55E;--red:#EF4444;--orange:#F97316}
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{font:16px -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--white)}
|
||
.container{max-width:1200px;margin:0 auto;padding:60px 24px}
|
||
header{background:var(--ink);color:var(--white);padding:20px 0;position:sticky;top:0;z-index:100}
|
||
.header-wrap{max-width:1200px;margin:0 auto;padding:0 24px;display:flex;justify-content:space-between;align-items:center}
|
||
.logo{font-size:20px;font-weight:700}
|
||
nav{display:flex;gap:20px}
|
||
nav a{color:var(--white);text-decoration:none;font-size:14px;opacity:0.9}
|
||
nav a:hover{opacity:1}
|
||
.hero{background:linear-gradient(135deg,var(--ink) 0%,#1a1f2e 100%);color:var(--white);padding:60px 24px}
|
||
.hero h1{font-size:42px;font-weight:800;line-height:1.1;margin-bottom:16px}
|
||
.hero p{font-size:18px;color:#9aa3b2;max-width:600px}
|
||
.section{padding:60px 24px}
|
||
.section h2{font-size:32px;font-weight:700;margin-bottom:32px;display:flex;align-items:center;gap:12px}
|
||
.card{background:var(--gray-100);border-radius:12px;padding:24px;margin-bottom:16px}
|
||
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}
|
||
.task-card{background:var(--white);border:1px solid var(--gray-100);border-radius:12px;padding:20px;box-shadow:0 2px 8px rgba(0,0,0,0.04)}
|
||
.task-title{font-size:16px;font-weight:600;margin-bottom:8px}
|
||
.task-meta{display:flex;gap:12px;font-size:13px;color:var(--gray-500);margin-bottom:12px}
|
||
.task-deadline{background:var(--cyan-50);color:var(--ink);padding:4px 8px;border-radius:4px;font-weight:600}
|
||
.task-status{padding:4px 8px;border-radius:4px;font-size:12px;font-weight:600}
|
||
.status-new{background:#DBEAFE;color:#1E40AF}
|
||
.status-progress{background:#FEF3C7;color:#92400E}
|
||
.status-done{background:#D1FAE5;color:#065F46}
|
||
.task-actions{display:flex;gap:8px;margin-top:12px}
|
||
.btn{padding:8px 16px;border-radius:6px;font-weight:600;font-size:13px;cursor:pointer;border:none;transition:0.2s}
|
||
.btn-primary{background:var(--cyan);color:var(--ink)}
|
||
.btn-primary:hover{opacity:0.9}
|
||
.btn-sm{padding:6px 12px;font-size:12px}
|
||
.btn-success{background:var(--green);color:var(--white)}
|
||
.btn-outline{border:1px solid var(--gray-500);background:transparent;color:var(--ink)}
|
||
.search-bar{display:flex;gap:12px;margin-bottom:24px}
|
||
.search-bar input{flex:1;padding:12px 16px;border:1px solid var(--gray-100);border-radius:8px;font-size:15px}
|
||
.filter-btn{padding:10px 16px;border:1px solid var(--gray-100);background:var(--white);border-radius:8px;cursor:pointer;font-weight:600}
|
||
.filter-btn.active{background:var(--ink);color:var(--white);border-color:var(--ink)}
|
||
.doc-item{display:flex;justify-content:space-between;align-items:center;padding:16px;background:var(--white);border:1px solid var(--gray-100);border-radius:8px;margin-bottom:8px}
|
||
.doc-icon{font-size:24px;margin-right:12px}
|
||
.doc-info{flex:1}
|
||
.doc-name{font-weight:600;margin-bottom:4px}
|
||
.doc-meta{font-size:13px;color:var(--gray-500)}
|
||
.template-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:16px}
|
||
.template-card{background:var(--white);border:1px solid var(--gray-100);border-radius:12px;padding:20px;cursor:pointer;transition:0.2s}
|
||
.template-card:hover{border-color:var(--cyan);box-shadow:0 4px 12px rgba(0,229,255,0.15)}
|
||
.template-icon{font-size:32px;margin-bottom:12px}
|
||
.template-name{font-weight:600;margin-bottom:8px}
|
||
.template-desc{font-size:13px;color:var(--gray-500)}
|
||
.checklist-item{display:flex;align-items:center;gap:12px;padding:12px;background:var(--white);border:1px solid var(--gray-100);border-radius:8px;margin-bottom:8px}
|
||
.checklist-item input[type="checkbox"]{width:18px;height:18px;cursor:pointer}
|
||
.checklist-item.checked{text-decoration:line-through;opacity:0.6}
|
||
.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:1000;align-items:center;justify-content:center}
|
||
.modal.open{display:flex}
|
||
.modal-content{background:var(--white);border-radius:12px;padding:32px;max-width:500px;width:90%;max-height:90vh;overflow-y:auto}
|
||
.modal-title{font-size:22px;font-weight:700;margin-bottom:20px}
|
||
.form-group{margin-bottom:16px}
|
||
.form-group label{display:block;font-weight:600;margin-bottom:8px;font-size:14px}
|
||
.form-group input,.form-group textarea,.form-group select{width:100%;padding:10px 12px;border:1px solid var(--gray-100);border-radius:6px;font-size:15px}
|
||
.form-group textarea{min-height:100px;resize:vertical}
|
||
.modal-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:24px}
|
||
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:16px;margin-bottom:32px}
|
||
.stat-card{background:var(--white);border:1px solid var(--gray-100);border-radius:12px;padding:20px;text-align:center}
|
||
.stat-num{font-size:32px;font-weight:800;color:var(--cyan);margin-bottom:8px}
|
||
.stat-label{font-size:14px;color:var(--gray-500)}
|
||
.quick-actions{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:24px}
|
||
.badge{display:inline-block;padding:4px 10px;border-radius:12px;font-size:12px;font-weight:600}
|
||
.badge-urgent{background:var(--red);color:var(--white)}
|
||
.badge-normal{background:var(--cyan-50);color:var(--ink)}
|
||
@media(max-width:768px){
|
||
.hero h1{font-size:28px}
|
||
.grid{grid-template-columns:1fr}
|
||
nav{display:none}
|
||
.section{padding:40px 20px}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<div class="header-wrap">
|
||
<div class="logo">💼 Рабочий помощник</div>
|
||
<nav>
|
||
<a href="#tasks">Задачи</a>
|
||
<a href="#documents">Документы</a>
|
||
<a href="#templates">Шаблоны</a>
|
||
<a href="#checklists">Чек-листы</a>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
|
||
<section class="hero">
|
||
<div class="container">
|
||
<h1>Рабочий помощник сотрудника</h1>
|
||
<p>Управляйте задачами, документами и шаблонами в одном месте. Быстрый доступ ко всему необходимому.</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="tasks" class="section">
|
||
<div class="container">
|
||
<h2>📋 Задачи и дедлайны</h2>
|
||
|
||
<div class="stats">
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="stat-total">0</div>
|
||
<div class="stat-label">Всего задач</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="stat-new">0</div>
|
||
<div class="stat-label">Новые</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="stat-progress">0</div>
|
||
<div class="stat-label">В работе</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-num" id="stat-done">0</div>
|
||
<div class="stat-label">Выполнено</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="search-bar">
|
||
<input type="text" id="task-search" placeholder="Поиск задач...">
|
||
<button class="filter-btn active" data-filter="all">Все</button>
|
||
<button class="filter-btn" data-filter="new">Новые</button>
|
||
<button class="filter-btn" data-filter="progress">В работе</button>
|
||
<button class="filter-btn" data-filter="done">Готово</button>
|
||
</div>
|
||
|
||
<div class="quick-actions">
|
||
<button class="btn btn-primary" onclick="openTaskModal()">+ Добавить задачу</button>
|
||
</div>
|
||
|
||
<div class="grid" id="task-list"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="documents" class="section" style="background:var(--gray-100)">
|
||
<div class="container">
|
||
<h2>📁 Документы</h2>
|
||
<div class="quick-actions">
|
||
<button class="btn btn-primary" onclick="openDocModal()">+ Добавить документ</button>
|
||
</div>
|
||
<div id="doc-list"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="templates" class="section">
|
||
<div class="container">
|
||
<h2>📄 Быстрые шаблоны</h2>
|
||
<div class="template-grid" id="template-list"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="checklists" class="section" style="background:var(--gray-100)">
|
||
<div class="container">
|
||
<h2>✅ Чек-листы</h2>
|
||
<div class="quick-actions">
|
||
<button class="btn btn-primary" onclick="openChecklistModal()">+ Создать чек-лист</button>
|
||
</div>
|
||
<div id="checklist-list"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Модалка задачи -->
|
||
<div class="modal" id="task-modal">
|
||
<div class="modal-content">
|
||
<h3 class="modal-title" id="task-modal-title">Новая задача</h3>
|
||
<form id="task-form">
|
||
<input type="hidden" id="task-id">
|
||
<div class="form-group">
|
||
<label>Название</label>
|
||
<input type="text" id="task-title" required placeholder="Что нужно сделать?">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Описание</label>
|
||
<textarea id="task-desc" placeholder="Детали задачи..."></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Дедлайн</label>
|
||
<input type="date" id="task-deadline" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Статус</label>
|
||
<select id="task-status">
|
||
<option value="new">Новая</option>
|
||
<option value="progress">В работе</option>
|
||
<option value="done">Выполнено</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button type="button" class="btn btn-outline" onclick="closeModal('task-modal')">Отмена</button>
|
||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Модалка документа -->
|
||
<div class="modal" id="doc-modal">
|
||
<div class="modal-content">
|
||
<h3 class="modal-title">Документ</h3>
|
||
<form id="doc-form">
|
||
<input type="hidden" id="doc-id">
|
||
<div class="form-group">
|
||
<label>Название</label>
|
||
<input type="text" id="doc-name" required placeholder="Название документа">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Тип</label>
|
||
<select id="doc-type">
|
||
<option value="doc">📄 Word</option>
|
||
<option value="xls">📊 Excel</option>
|
||
<option value="pdf">📕 PDF</option>
|
||
<option value="link">🔗 Ссылка</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Ссылка / Путь</label>
|
||
<input type="text" id="doc-link" placeholder="https:// или путь к файлу">
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button type="button" class="btn btn-outline" onclick="closeModal('doc-modal')">Отмена</button>
|
||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Модалка чек-листа -->
|
||
<div class="modal" id="checklist-modal">
|
||
<div class="modal-content">
|
||
<h3 class="modal-title">Чек-лист</h3>
|
||
<form id="checklist-form">
|
||
<input type="hidden" id="checklist-id">
|
||
<div class="form-group">
|
||
<label>Название</label>
|
||
<input type="text" id="checklist-name" required placeholder="Например: Подготовка отчёта">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Пункты (каждый с новой строки)</label>
|
||
<textarea id="checklist-items" placeholder="Проверить данные Сохранить файл Отправить руководителю" style="min-height:120px"></textarea>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button type="button" class="btn btn-outline" onclick="closeModal('checklist-modal')">Отмена</button>
|
||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Данные
|
||
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
|
||
let documents = JSON.parse(localStorage.getItem('documents')) || [];
|
||
let checklists = JSON.parse(localStorage.getItem('checklists')) || [];
|
||
|
||
const templates = [
|
||
{ icon: '📧', name: 'Письмо клиенту', desc: 'Шаблон делового письма' },
|
||
{ icon: '📊', name: 'Отчёт за месяц', desc: 'Ежемесячный отчёт' },
|
||
{ icon: '📝', name: 'Служебная записка', desc: 'Шаблон записки' },
|
||
{ icon: '📋', name: 'План встречи', desc: 'Структура совещания' },
|
||
{ icon: '📄', name: 'Договор', desc: 'Типовой договор' },
|
||
{ icon: '📑', name: 'Презентация', desc: 'Структура презентации' },
|
||
{ icon: '📈', name: 'План работ', desc: 'План на квартал' },
|
||
{ icon: '✉️', name: 'Напоминание', desc: 'Письмо-напоминание' }
|
||
];
|
||
|
||
let currentFilter = 'all';
|
||
|
||
// Инициализация
|
||
function init() {
|
||
renderTasks();
|
||
renderDocs();
|
||
renderTemplates();
|
||
renderChecklists();
|
||
updateStats();
|
||
|
||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
currentFilter = btn.dataset.filter;
|
||
renderTasks();
|
||
});
|
||
});
|
||
|
||
document.getElementById('task-search').addEventListener('input', renderTasks);
|
||
}
|
||
|
||
// Задачи
|
||
function renderTasks() {
|
||
const search = document.getElementById('task-search').value.toLowerCase();
|
||
let filtered = tasks.filter(t => t.title.toLowerCase().includes(search));
|
||
|
||
if (currentFilter !== 'all') {
|
||
filtered = filtered.filter(t => t.status === currentFilter);
|
||
}
|
||
|
||
document.getElementById('task-list').innerHTML = filtered.map(t => `
|
||
<div class="task-card">
|
||
<div class="task-title">${escapeHtml(t.title)}</div>
|
||
${t.desc ? `<div style="font-size:14px;color:var(--gray-500);margin-bottom:8px">${escapeHtml(t.desc)}</div>` : ''}
|
||
<div class="task-meta">
|
||
<span class="task-deadline">📅 ${formatDate(t.deadline)}</span>
|
||
<span class="task-status status-${t.status}">${getStatusName(t.status)}</span>
|
||
</div>
|
||
<div class="task-actions">
|
||
<button class="btn btn-sm btn-outline" onclick="editTask(${t.id})">Изменить</button>
|
||
${t.status !== 'done' ? `<button class="btn btn-sm btn-success" onclick="completeTask(${t.id})">✓ Готово</button>` : ''}
|
||
<button class="btn btn-sm" style="background:#FEE2E2;color:#DC2626" onclick="deleteTask(${t.id})">Удалить</button>
|
||
</div>
|
||
</div>
|
||
`).join('') || '<div class="card">Нет задач</div>';
|
||
|
||
updateStats();
|
||
}
|
||
|
||
function updateStats() {
|
||
document.getElementById('stat-total').textContent = tasks.length;
|
||
document.getElementById('stat-new').textContent = tasks.filter(t => t.status === 'new').length;
|
||
document.getElementById('stat-progress').textContent = tasks.filter(t => t.status === 'progress').length;
|
||
document.getElementById('stat-done').textContent = tasks.filter(t => t.status === 'done').length;
|
||
}
|
||
|
||
function getStatusName(status) {
|
||
return { new: 'Новая', progress: 'В работе', done: 'Выполнено' }[status];
|
||
}
|
||
|
||
function openTaskModal(id = null) {
|
||
document.getElementById('task-modal-title').textContent = id ? 'Редактировать задачу' : 'Новая задача';
|
||
document.getElementById('task-id').value = id || '';
|
||
|
||
if (id) {
|
||
const t = tasks.find(x => x.id === id);
|
||
document.getElementById('task-title').value = t.title;
|
||
document.getElementById('task-desc').value = t.desc || '';
|
||
document.getElementById('task-deadline').value = t.deadline;
|
||
document.getElementById('task-status').value = t.status;
|
||
} else {
|
||
document.getElementById('task-form').reset();
|
||
document.getElementById('task-deadline').value = new Date().toISOString().split('T')[0];
|
||
}
|
||
|
||
document.getElementById('task-modal').classList.add('open');
|
||
}
|
||
|
||
function editTask(id) { openTaskModal(id); }
|
||
|
||
function completeTask(id) {
|
||
const t = tasks.find(x => x.id === id);
|
||
if (t) { t.status = 'done'; saveTasks(); renderTasks(); }
|
||
}
|
||
|
||
function deleteTask(id) {
|
||
if (confirm('Удалить задачу?')) {
|
||
tasks = tasks.filter(t => t.id !== id);
|
||
saveTasks();
|
||
renderTasks();
|
||
}
|
||
}
|
||
|
||
document.getElementById('task-form').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
const id = document.getElementById('task-id').value;
|
||
const data = {
|
||
id: id ? parseInt(id) : Date.now(),
|
||
title: document.getElementById('task-title').value,
|
||
desc: document.getElementById('task-desc').value,
|
||
deadline: document.getElementById('task-deadline').value,
|
||
status: document.getElementById('task-status').value
|
||
};
|
||
|
||
if (id) {
|
||
const idx = tasks.findIndex(t => t.id == id);
|
||
tasks[idx] = data;
|
||
} else {
|
||
tasks.push(data);
|
||
}
|
||
|
||
saveTasks();
|
||
renderTasks();
|
||
closeModal('task-modal');
|
||
});
|
||
|
||
function saveTasks() { localStorage.setItem('tasks', JSON.stringify(tasks)); }
|
||
|
||
// Документы
|
||
function renderDocs() {
|
||
document.getElementById('doc-list').innerHTML = documents.map(d => `
|
||
<div class="doc-item">
|
||
<div style="display:flex;align-items:center;gap:12px">
|
||
<span class="doc-icon">${getDocIcon(d.type)}</span>
|
||
<div class="doc-info">
|
||
<div class="doc-name">${escapeHtml(d.name)}</div>
|
||
<div class="doc-meta">${d.link || 'Локальный файл'}</div>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;gap:8px">
|
||
${d.link ? `<a href="${d.link}" target="_blank" class="btn btn-sm btn-primary">Открыть</a>` : ''}
|
||
<button class="btn btn-sm btn-outline" onclick="editDoc(${d.id})">Изменить</button>
|
||
<button class="btn btn-sm" style="background:#FEE2E2;color:#DC2626" onclick="deleteDoc(${d.id})">Удалить</button>
|
||
</div>
|
||
</div>
|
||
`).join('') || '<div class="card">Нет документов</div>';
|
||
}
|
||
|
||
function getDocIcon(type) {
|
||
return { doc: '📄', xls: '📊', pdf: '📕', link: '🔗' }[type] || '📁';
|
||
}
|
||
|
||
function openDocModal(id = null) {
|
||
document.getElementById('doc-id').value = id || '';
|
||
if (id) {
|
||
const d = documents.find(x => x.id === id);
|
||
document.getElementById('doc-name').value = d.name;
|
||
document.getElementById('doc-type').value = d.type;
|
||
document.getElementById('doc-link').value = d.link || '';
|
||
} else {
|
||
document.getElementById('doc-form').reset();
|
||
}
|
||
document.getElementById('doc-modal').classList.add('open');
|
||
}
|
||
|
||
function editDoc(id) { openDocModal(id); }
|
||
|
||
function deleteDoc(id) {
|
||
if (confirm('Удалить документ?')) {
|
||
documents = documents.filter(d => d.id !== id);
|
||
saveDocs();
|
||
renderDocs();
|
||
}
|
||
}
|
||
|
||
document.getElementById('doc-form').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
const id = document.getElementById('doc-id').value;
|
||
const data = {
|
||
id: id ? parseInt(id) : Date.now(),
|
||
name: document.getElementById('doc-name').value,
|
||
type: document.getElementById('doc-type').value,
|
||
link: document.getElementById('doc-link').value
|
||
};
|
||
|
||
if (id) {
|
||
const idx = documents.findIndex(d => d.id == id);
|
||
documents[idx] = data;
|
||
} else {
|
||
documents.push(data);
|
||
}
|
||
|
||
saveDocs();
|
||
renderDocs();
|
||
closeModal('doc-modal');
|
||
});
|
||
|
||
function saveDocs() { localStorage.setItem('documents', JSON.stringify(documents)); }
|
||
|
||
// Шаблоны
|
||
function renderTemplates() {
|
||
document.getElementById('template-list').innerHTML = templates.map(t => `
|
||
<div class="template-card" onclick="alert('Шаблон: ${t.name}\\n\\n${t.desc}')">
|
||
<div class="template-icon">${t.icon}</div>
|
||
<div class="template-name">${t.name}</div>
|
||
<div class="template-desc">${t.desc}</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// Чек-листы
|
||
function renderChecklists() {
|
||
document.getElementById('checklist-list').innerHTML = checklists.map(c => `
|
||
<div class="card">
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
|
||
<h3 style="font-size:18px">${escapeHtml(c.name)}</h3>
|
||
<div style="display:flex;gap:8px">
|
||
<button class="btn btn-sm btn-outline" onclick="editChecklist(${c.id})">Изменить</button>
|
||
<button class="btn btn-sm" style="background:#FEE2E2;color:#DC2626" onclick="deleteChecklist(${c.id})">Удалить</button>
|
||
</div>
|
||
</div>
|
||
${c.items.map((item, i) => `
|
||
<div class="checklist-item ${c.checked[i] ? 'checked' : ''}" onclick="toggleChecklistItem(${c.id}, ${i})">
|
||
<input type="checkbox" ${c.checked[i] ? 'checked' : ''} onclick="event.stopPropagation();toggleChecklistItem(${c.id}, ${i})">
|
||
<span>${escapeHtml(item)}</span>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
`).join('') || '<div class="card">Нет чек-листов</div>';
|
||
}
|
||
|
||
function openChecklistModal(id = null) {
|
||
document.getElementById('checklist-id').value = id || '';
|
||
if (id) {
|
||
const c = checklists.find(x => x.id === id);
|
||
document.getElementById('checklist-name').value = c.name;
|
||
document.getElementById('checklist-items').value = c.items.join('\n');
|
||
} else {
|
||
document.getElementById('checklist-form').reset();
|
||
}
|
||
document.getElementById('checklist-modal').classList.add('open');
|
||
}
|
||
|
||
function editChecklist(id) { openChecklistModal(id); }
|
||
|
||
function toggleChecklistItem(id, idx) {
|
||
const c = checklists.find(x => x.id === id);
|
||
if (c) {
|
||
c.checked[idx] = !c.checked[idx];
|
||
saveChecklists();
|
||
renderChecklists();
|
||
}
|
||
}
|
||
|
||
function deleteChecklist(id) {
|
||
if (confirm('Удалить чек-лист?')) {
|
||
checklists = checklists.filter(c => c.id !== id);
|
||
saveChecklists();
|
||
renderChecklists();
|
||
}
|
||
}
|
||
|
||
document.getElementById('checklist-form').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
const id = document.getElementById('checklist-id').value;
|
||
const items = document.getElementById('checklist-items').value.split('\n').filter(i => i.trim());
|
||
const data = {
|
||
id: id ? parseInt(id) : Date.now(),
|
||
name: document.getElementById('checklist-name').value,
|
||
items: items,
|
||
checked: new Array(items.length).fill(false)
|
||
};
|
||
|
||
if (id) {
|
||
const idx = checklists.findIndex(c => c.id == id);
|
||
checklists[idx] = data;
|
||
} else {
|
||
checklists.push(data);
|
||
}
|
||
|
||
saveChecklists();
|
||
renderChecklists();
|
||
closeModal('checklist-modal');
|
||
});
|
||
|
||
function saveChecklists() { localStorage.setItem('checklists', JSON.stringify(checklists)); }
|
||
|
||
// Утилиты
|
||
function closeModal(id) { document.getElementById(id).classList.remove('open'); }
|
||
function escapeHtml(text) { return (text || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); }
|
||
function formatDate(dateStr) {
|
||
const d = new Date(dateStr);
|
||
return d.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: '2-digit' });
|
||
}
|
||
|
||
window.onclick = (e) => {
|
||
if (e.target.classList.contains('modal')) e.target.classList.remove('open');
|
||
};
|
||
|
||
init();
|
||
</script>
|
||
</body>
|
||
</html>
|