moy-rabochiy-pomoschnik-sayt/index.html

568 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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.

<!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="Проверить данные&#10;Сохранить файл&#10;Отправить руководителю" 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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
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>