129 lines
28 KiB
TypeScript
129 lines
28 KiB
TypeScript
export const portalWorkspaceHtml = `<!doctype html>
|
||
<html lang="ru" data-omni-theme="noble-green" data-omni-density="workspace">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#315F46">
|
||
<title>Krona Staff</title>
|
||
<link rel="manifest" href="/staff/manifest.webmanifest">
|
||
<link rel="icon" type="image/svg+xml" href="/staff/icon-192.svg">
|
||
<link rel="apple-touch-icon" href="/staff/icon-192.svg">
|
||
<link rel="stylesheet" href="/staff/styles.css">
|
||
</head>
|
||
<body>
|
||
<section id="auth-screen" class="auth-screen" aria-labelledby="login-title">
|
||
<div class="auth-card">
|
||
<div class="auth-brand"><span class="mark">К</span><span>Krona <b>Staff</b></span></div>
|
||
<p class="eyebrow">Защищённый кабинет</p>
|
||
<h1 id="login-title">Вход в Krona Staff</h1>
|
||
<p class="auth-copy">Введите ключ руководителя или личный ключ сотрудника.</p>
|
||
<form id="login-form"><label for="login-token">Ключ доступа</label><input id="login-token" type="password" autocomplete="current-password" required minlength="32" autofocus><button id="login-submit" type="submit">Войти</button></form>
|
||
<div id="login-error" class="login-error" role="alert" hidden></div>
|
||
<small>Сессия действует 8 часов и хранится только в защищённой cookie.</small>
|
||
</div>
|
||
</section>
|
||
|
||
<div id="app-shell" hidden>
|
||
<header class="platform-bar">
|
||
<div class="platform-context"><button class="app-grid" type="button" aria-label="Модули платформы">⠿</button><span>Бизнес-платформа</span><b>/</b><strong>KRONA Staff</strong></div>
|
||
<label class="global-search"><span aria-hidden="true">⌕</span><input id="global-search" type="search" placeholder="Поиск по сотрудникам, операциям, документам…" aria-label="Глобальный поиск"></label>
|
||
<div class="platform-actions"><span id="connection" class="status">Подключение…</span><button id="refresh" class="quiet compact" type="button">Обновить</button><button id="logout" class="quiet compact" type="button">Выйти</button></div>
|
||
</header>
|
||
<header class="module-bar">
|
||
<a class="brand" href="/staff/dashboard" aria-label="Krona Staff"><span class="mark">К</span><span><b>KRONA Staff</b><small>Люди. Время. Развитие.</small></span></a>
|
||
<nav class="section-nav" aria-label="Разделы Krona Staff">
|
||
<button class="nav-item selected" type="button" data-target="overview-view" data-route="/staff/dashboard"><span>▦</span>Дашборд</button>
|
||
<button class="nav-item manager-only" type="button" data-target="employees-view" data-route="/staff/employees"><span>♟</span>Сотрудники</button>
|
||
<button class="nav-item" type="button" data-target="attendance-view" data-route="/staff/attendance"><span>◷</span>Посещаемость</button>
|
||
<button class="nav-item" type="button" data-target="shifts-view" data-route="/staff/shifts"><span>◫</span>Смены</button>
|
||
<button class="nav-item" type="button" data-target="schedule-view" data-route="/staff/schedules"><span>▦</span>Графики</button>
|
||
<button class="nav-item manager-only" type="button" data-target="finance-view" data-route="/staff/finance"><span>◎</span>Финансы</button>
|
||
<button class="nav-item" type="button" data-target="payroll-view" data-route="/staff/payroll"><span>▤</span>Зарплата</button>
|
||
<button class="nav-item manager-only" type="button" data-target="rules-view" data-route="/staff/rules"><span>⌘</span>Правила</button>
|
||
<button class="nav-item manager-only" type="button" data-target="terminal-view" data-route="/staff/terminal"><span>▣</span>Терминал</button>
|
||
<button class="nav-item" type="button" data-target="notifications-view" data-route="/staff/notifications"><span>♢</span>Уведомления</button>
|
||
<button class="nav-item" type="button" data-target="reports-view" data-route="/staff/reports"><span>▥</span>Отчёты</button>
|
||
<button class="nav-item" type="button" data-target="settings-view" data-route="/staff/settings"><span>⚙</span>Настройки</button>
|
||
</nav>
|
||
</header>
|
||
|
||
<main>
|
||
<div id="message" class="message" hidden></div>
|
||
<section id="overview-view" class="app-view">
|
||
<div class="page-title-row"><div><h1>Дашборд</h1><p id="viewer">Кабинет руководителя</p></div><div class="page-context"><span id="today">—</span><small id="last-refresh">Данные загружаются</small></div></div>
|
||
<h2 id="greeting" class="sr-only">Команда и рабочее время</h2>
|
||
<section class="metric-strip" aria-label="Оперативная сводка">
|
||
<article><span class="metric-icon">♟</span><div><small>Всего сотрудников</small><strong id="employees-count">—</strong><span>В штате компании</span></div></article>
|
||
<article><span class="metric-icon">◷</span><div><small>Отметок сегодня</small><strong id="today-count">—</strong><span>Приходы и уходы</span></div></article>
|
||
<article><span class="metric-icon">◫</span><div><small>Часов в табеле</small><strong id="hours-count">—</strong><span>За доступный период</span></div></article>
|
||
<article class="manager-only"><span class="metric-icon">◎</span><div><small>Финансовый результат</small><strong id="dashboard-profit">—</strong><span>За выбранный месяц</span></div></article>
|
||
<article><span class="metric-icon">!</span><div><small>Требуют внимания</small><strong id="dashboard-attention">—</strong><span>Смены и проверки</span></div></article>
|
||
</section>
|
||
<div class="dashboard-grid">
|
||
<section class="panel dashboard-people"><div class="panel-head"><h2>Сотрудники</h2><button class="text-action" type="button" data-open-view="employees-view">Все сотрудники →</button></div><div id="dashboard-employees" class="dense-list"><div class="empty">Загрузка…</div></div></section>
|
||
<section class="panel dashboard-attendance"><div class="panel-head"><h2>Посещаемость сегодня</h2><button class="text-action" type="button" data-open-view="attendance-view">Подробнее →</button></div><div id="dashboard-attendance" class="dense-list"><div class="empty">Загрузка…</div></div></section>
|
||
<aside class="panel dashboard-terminal"><div class="panel-head"><h2>Терминал учёта</h2><span class="status ok">Онлайн</span></div><div class="terminal-device"><span class="terminal-glyph">▣</span><div><strong>SLK20R</strong><small>Локальный терминал отпечатков</small></div></div><dl><div><dt>Последнее событие</dt><dd id="terminal-last-event">—</dd></div><div><dt>Синхронизация</dt><dd id="terminal-last-sync">Ожидание данных</dd></div></dl><button class="quiet" type="button" data-open-view="terminal-view">Открыть терминал</button></aside>
|
||
<section class="panel dashboard-payroll manager-only"><div class="panel-head"><h2>Расчёт зарплаты</h2><button class="text-action" type="button" data-open-view="payroll-view">Подробнее →</button></div><div class="dashboard-totals"><div><small>Начислено</small><strong id="dashboard-accrued">—</strong></div><div><small>Выплачено</small><strong id="dashboard-paid">—</strong></div><div><small>Остаток</small><strong id="dashboard-balance">—</strong></div></div><div class="rule-chips" id="dashboard-rules"></div></section>
|
||
<section class="panel dashboard-analytics manager-only"><div class="panel-head"><h2>Финансы месяца</h2><button class="text-action" type="button" data-open-view="finance-view">Подробнее →</button></div><div id="dashboard-finance-chart" class="mini-chart"><div class="empty">Загрузка…</div></div></section>
|
||
<section class="panel dashboard-alerts"><div class="panel-head"><h2>Уведомления</h2><button class="text-action" type="button" data-open-view="notifications-view">Все →</button></div><div id="dashboard-alert-list" class="alert-list"><div class="empty">Нет новых данных</div></div></section>
|
||
</div>
|
||
<div class="quick-bar manager-only"><strong>Быстрые действия</strong><button type="button" id="dashboard-add-employee">+ Добавить сотрудника</button><button type="button" class="quiet" data-open-view="shifts-view">Открыть смены</button><button type="button" class="quiet" data-open-view="payroll-view">Запустить расчёт</button><button type="button" class="quiet" data-open-view="rules-view">Настроить правила</button></div>
|
||
</section>
|
||
|
||
<section id="employees-view" class="app-view manager-only" hidden>
|
||
<div class="page-title-row"><div><h1>Сотрудники</h1><p>Персонал, профессии, статусы и зарплатные профили</p></div><div class="actions"><button id="manage-professions-directory" class="quiet" type="button">Профессии</button><button id="add-employee-directory" type="button">+ Добавить сотрудника</button></div></div>
|
||
<section class="metric-strip compact-metrics"><article><span class="metric-icon">♟</span><div><small>Всего сотрудников</small><strong id="directory-total">—</strong></div></article><article><span class="metric-icon">✓</span><div><small>Показано</small><strong id="directory-active">—</strong></div></article><article><span class="metric-icon">!</span><div><small>Требуют внимания</small><strong id="directory-attention">—</strong></div></article></section>
|
||
<button id="add-employee" type="button" hidden>Добавить сотрудника</button><div class="employee-workspace"><section class="panel employee-list-panel"><div class="directory-toolbar"><input id="employee-search" type="search" placeholder="Поиск по имени, должности или табельному номеру" aria-label="Поиск сотрудников"><select id="employee-status-filter" aria-label="Статус"><option value="">Все статусы</option><option value="active">Работает</option><option value="blocked">Заблокирован</option><option value="dismissed">Уволен</option></select><span id="employee-search-result" class="status">Все сотрудники</span></div><div id="employees-directory" class="people directory-list"></div><div id="employees" hidden></div></section><aside id="employee-inspector" class="panel inspector"><div class="empty">Выберите сотрудника в списке</div></aside></div>
|
||
</section>
|
||
|
||
<section id="attendance-view" class="app-view" hidden>
|
||
<div class="page-title-row"><div><h1>Посещаемость</h1><p>Фактические приходы и уходы со сканера</p></div><span class="live">● в реальном времени</span></div>
|
||
<div class="attendance-workspace"><section class="panel"><div class="panel-head"><h2>Журнал отметок</h2><div class="segmented"><button class="selected" type="button" data-event-filter="">Все</button><button type="button" data-event-filter="check_in">Приходы</button><button type="button" data-event-filter="check_out">Уходы</button></div></div><div id="attendance" class="feed"></div></section><aside class="panel"><div class="panel-head"><h2>Сводка дня</h2></div><div id="attendance-summary" class="summary-stack"><div class="empty">Загрузка…</div></div></aside></div>
|
||
</section>
|
||
|
||
<section id="shifts-view" class="app-view" hidden>
|
||
<div class="page-title-row"><div><h1>Смены</h1><p>Сверка отметок, аномалии и подтверждение рабочего времени</p></div><button id="reconcile-sessions" type="button">Сверить 7 дней</button></div>
|
||
<section class="panel"><div class="panel-head"><h2>Рабочие смены</h2><span>План и фактический интервал</span></div><div class="table-wrap"><table><thead><tr><th>Дата</th><th>Сотрудник</th><th>Подразделение</th><th>Интервал</th><th>Часы</th><th>Состояние</th><th>Причина правки</th><th></th></tr></thead><tbody id="work-sessions"></tbody></table></div></section>
|
||
</section>
|
||
|
||
<section id="schedule-view" class="app-view" hidden>
|
||
<div class="page-title-row"><div><h1>Графики</h1><p>Планирование смен и контроль фактических выходов</p></div><div class="actions"><label class="period-control">Месяц <input id="period-month" type="month" aria-label="Месяц данных"></label><button id="schedule-refresh" class="quiet" type="button">Обновить</button></div></div>
|
||
<div class="schedule-tabs segmented"><button type="button">Год</button><button type="button">Квартал</button><button class="selected" type="button">Месяц</button><button type="button">Неделя</button></div>
|
||
<section class="panel manager-only schedule-editor"><form id="schedule-form" class="inline-form"><label>Сотрудник<select id="schedule-employee" required></select></label><label>Точка<select id="schedule-unit" required></select></label><label>Дата<input id="schedule-date" type="date" required></label><label>Часы<input id="schedule-hours" type="number" inputmode="decimal" min="0" max="24" step="0.5" value="12" required></label><label>Начало<input id="schedule-start" type="time" value="10:00"></label><label>Конец<input id="schedule-end" type="time" value="22:00"></label><button type="submit">+ Добавить</button></form></section>
|
||
<section class="panel calendar-panel"><div class="calendar-legend"><span><i class="planned"></i>Запланировано</span><span><i class="actual"></i>Есть факты</span><span><i class="warning"></i>Требует внимания</span></div><div class="calendar-weekdays" aria-hidden="true"><span>Пн</span><span>Вт</span><span>Ср</span><span>Чт</span><span>Пт</span><span>Сб</span><span>Вс</span></div><div id="calendar-grid" class="calendar-grid"></div></section>
|
||
</section>
|
||
|
||
<section id="finance-view" class="app-view manager-only" hidden>
|
||
<div class="page-title-row"><div><h1>Финансы</h1><p>Выручка, расходы и финансовый результат</p></div><div class="actions"><button id="finance-refresh" class="quiet" type="button">Обновить</button></div></div>
|
||
<section class="metric-strip compact-metrics"><article><span class="metric-icon">↗</span><div><small>Доход</small><strong id="finance-income">—</strong></div></article><article><span class="metric-icon danger">↘</span><div><small>Расход</small><strong id="finance-expense">—</strong></div></article><article><span class="metric-icon">◕</span><div><small>Прибыль</small><strong id="finance-profit">—</strong></div></article></section>
|
||
<div class="finance-workspace"><section class="panel finance-journal"><div class="panel-head"><h2>Журнал операций</h2><span id="finance-count">0 записей</span></div><div class="table-wrap"><table><thead><tr><th>Дата</th><th>Точка</th><th>Категория</th><th>Тип</th><th>Сумма</th></tr></thead><tbody id="finance-entries"></tbody></table></div></section><aside class="finance-side"><section class="panel"><div class="panel-head"><h2>Быстрое внесение</h2></div><form id="finance-form" class="stack-form"><label>Дата<input id="finance-date" type="date" required></label><label>Торговая точка<select id="finance-unit" required></select></label><label>Категория<select id="finance-category" required></select></label><label>Сумма, ₽<input id="finance-amount" type="number" inputmode="decimal" min="0" step="0.01" required></label><button type="submit">Сохранить операцию</button></form></section><section class="panel"><div class="panel-head"><h2>Доходы и расходы</h2><span>₽</span></div><div id="finance-chart" class="finance-chart" role="img" aria-label="График доходов и расходов"></div></section></aside></div>
|
||
</section>
|
||
|
||
<section id="payroll-view" class="app-view" hidden>
|
||
<div class="page-title-row"><div><h1>Зарплата</h1><p>Расчёт начислений, выплат и остатка по сотрудникам</p></div><div class="actions"><button id="payroll-calc-btn" class="manager-only" type="button">▶ Запустить расчёт</button><button id="payroll-refresh" class="quiet" type="button">Пересчитать</button></div></div>
|
||
<div id="payroll-calc-status" class="message" hidden></div><div id="payroll-warnings" class="payroll-warnings message" hidden></div>
|
||
<section class="metric-strip compact-metrics"><article><span class="metric-icon">◎</span><div><small>Начислено</small><strong id="payroll-accrued">—</strong></div></article><article><span class="metric-icon">▣</span><div><small>Выплачено</small><strong id="payroll-paid">—</strong></div></article><article><span class="metric-icon">◷</span><div><small>Остаток</small><strong id="payroll-balance">—</strong></div></article></section>
|
||
<section id="payroll-preview-card" class="panel manager-only" hidden><div class="panel-head"><h2 id="payroll-preview-title">Предварительный результат</h2><button id="payroll-approve-btn" type="button">Утвердить расчёт</button></div><div class="table-wrap"><table><thead><tr><th>Сотрудник</th><th>Должность</th><th>Дней</th><th>Факт, часов</th><th>Начислено</th></tr></thead><tbody id="payroll-preview-rows"></tbody></table></div></section>
|
||
<div class="payroll-workspace"><section class="panel"><div class="panel-head"><h2>Расчёт по сотрудникам</h2><span>По подтверждённым сменам</span></div><div class="table-wrap"><table><thead><tr><th>Сотрудник</th><th>Должность</th><th>Факт, часов</th><th>Начислено</th><th>Выплачено</th><th>Остаток</th></tr></thead><tbody id="payroll-rows"></tbody></table></div></section><aside class="panel payroll-detail"><div class="panel-head"><h2>История операций</h2></div><div class="table-wrap"><table><thead><tr><th>Дата</th><th>Сотрудник</th><th>Операция</th><th>Сумма</th><th>Примечание</th></tr></thead><tbody id="payroll-entries"></tbody></table></div></aside></div>
|
||
</section>
|
||
|
||
<section id="rules-view" class="app-view manager-only" hidden><div class="page-title-row"><div><h1>Правила начислений</h1><p>Схемы оплаты и профессии сотрудников</p></div><button id="manage-professions" class="quiet" type="button">+ Профессия</button></div><div class="rules-workspace"><section class="panel"><div class="panel-head"><h2>Активные схемы оплаты</h2></div><div id="rules-list" class="rule-grid"><div class="empty">Загрузка…</div></div></section><section class="panel"><div class="panel-head"><h2>Профессии</h2></div><div id="rules-professions" class="dense-list"><div class="empty">Загрузка…</div></div></section></div></section>
|
||
|
||
<section id="terminal-view" class="app-view manager-only" hidden><div class="page-title-row"><div><h1>Терминал</h1><p>Состояние локального сканера и последние события</p></div><span class="status ok">Сервер онлайн</span></div><div class="terminal-workspace"><section class="panel terminal-card"><span class="terminal-glyph large">▣</span><div><h2>BioLink SLK20R</h2><p>Локальный агент фиксирует отпечатки и безопасно передаёт события на сервер.</p><dl><div><dt>Последняя синхронизация</dt><dd id="terminal-sync-detail">—</dd></div><div><dt>Последняя отметка</dt><dd id="terminal-event-detail">—</dd></div></dl></div></section><section class="panel"><div class="panel-head"><h2>Последние события терминала</h2></div><div id="terminal-events" class="dense-list"><div class="empty">Загрузка…</div></div></section></div></section>
|
||
|
||
<section id="notifications-view" class="app-view" hidden><div class="page-title-row"><div><h1>Уведомления</h1><p>События, требующие внимания руководителя</p></div><button id="notify" class="quiet" type="button">Включить push</button></div><section class="panel"><div id="notifications-list" class="alert-list"><div class="empty">Нет новых уведомлений</div></div></section></section>
|
||
|
||
<section id="reports-view" class="app-view" hidden><div class="page-title-row"><div><h1>Отчёты</h1><p>Табель рабочего времени и подтверждённые данные</p></div><span id="timesheet-help">Изменения сохраняет руководитель</span></div><section class="panel"><form id="timesheet-create" class="timesheet-create"><label>Сотрудник<select id="new-time-employee" required></select></label><label>Дата<input id="new-time-date" type="date" required></label><label>Часы<input id="new-time-hours" type="number" min="0" max="24" step="0.25" value="8" required></label><label>Статус<select id="new-time-status"><option value="draft">Черновик</option><option value="submitted">На проверке</option><option value="approved">Утверждён</option><option value="rejected">Возвращён</option></select></label><label class="grow">Примечание<input id="new-time-note" maxlength="1000" placeholder="Необязательно"></label><button type="submit">Добавить строку</button></form><div class="table-wrap"><table><thead><tr><th>Дата</th><th>Сотрудник</th><th>Часы</th><th>Статус</th><th>Примечание</th><th></th></tr></thead><tbody id="timesheet"></tbody></table></div></section></section>
|
||
|
||
<section id="settings-view" class="app-view" hidden><div class="page-title-row"><div><h1>Настройки</h1><p>Внешний вид, уведомления и состояние подключения</p></div></div><div class="settings-grid"><section class="panel"><div class="panel-head"><h2>Интерфейс</h2></div><label class="settings-field">Тема<select id="theme-select" aria-label="Тема интерфейса"><option value="noble-green">Благородный зелёный</option><option value="parchment-light">Пергамент</option><option value="graphite-forest-dark">Тёмная</option></select></label></section><section class="panel"><div class="panel-head"><h2>О системе</h2></div><dl class="system-list"><div><dt>Модуль</dt><dd>Krona Staff</dd></div><div><dt>Режим</dt><dd>Автономный с интеграцией платформы</dd></div><div><dt>Данные</dt><dd>PostgreSQL с tenant isolation</dd></div></dl></section></div></section>
|
||
</main>
|
||
|
||
<input id="employee-position" type="hidden">
|
||
<dialog id="employee-dialog" class="employee-dialog"><form id="employee-form" method="dialog"><div class="dialog-head"><div><p class="eyebrow">Карточка сотрудника</p><h2 id="employee-dialog-title">Новый сотрудник</h2></div><button id="employee-close" class="icon-button" type="button" aria-label="Закрыть">×</button></div><input id="employee-id" type="hidden"><label>Табельный номер<input id="employee-number" maxlength="80" required></label><label>Фамилия, имя и отчество<input id="employee-name" maxlength="240" required></label><label>Профессия<select id="employee-profession"><option value="">Не указана</option></select></label><p class="role-note">Роли и права платформы управляются в Krona Core отдельно от кадровой профессии.</p><label>Пол<select id="employee-gender"><option value="">Не указан</option><option value="female">Женский</option><option value="male">Мужской</option></select></label><label id="employee-status-wrap" hidden>Статус<select id="employee-status"><option value="active">Работает</option><option value="blocked">Заблокирован</option><option value="dismissed">Уволен</option></select></label><div id="employee-pay-section" class="pay-profile-section manager-only"><p class="eyebrow">Зарплатный профиль</p><label>Торговая точка<select id="employee-pay-unit"></select></label><label>Схема расчёта<select id="employee-pay-rule"></select></label><div class="form-row-2"><label>Ставка в час, ₽<input id="employee-pay-rate" type="number" min="0" step="1" value="100"></label><label>Процент от выручки, %<input id="employee-pay-percent" type="number" min="0" max="100" step="0.1" value="0"></label></div><div class="form-row-2"><label>Гарантия за смену, ₽<input id="employee-pay-guarantee" type="number" min="0" step="100" value="0"></label><label>Доля в фонде, %<input id="employee-pay-pool-weight" type="number" min="0" max="200" step="10" value="100"></label></div><label>Действует с даты<input id="employee-pay-effective" type="date"></label><small>Новая версия действует с выбранной даты.</small></div><div id="employee-error" class="login-error" role="alert" hidden></div><div class="dialog-actions"><button id="employee-cancel" class="quiet" type="button">Отмена</button><button id="employee-save" type="submit">Сохранить</button></div></form></dialog>
|
||
<dialog id="profession-dialog" class="employee-dialog"><form id="profession-form" method="dialog"><div class="dialog-head"><div><p class="eyebrow">Кадровый справочник</p><h2>Профессии</h2></div><button id="profession-close" class="icon-button" type="button" aria-label="Закрыть">×</button></div><label>Новая профессия<input id="profession-name" maxlength="160" required placeholder="Например, Повар"></label><button type="submit">Добавить профессию</button><div id="profession-list" class="people"></div><div id="profession-error" class="login-error" role="alert" hidden></div></form></dialog>
|
||
<dialog id="access-dialog" class="employee-dialog"><form method="dialog"><div class="dialog-head"><div><p class="eyebrow">Личный кабинет</p><h2>Ключ сотрудника</h2></div><button id="access-close" class="icon-button" type="submit" aria-label="Закрыть">×</button></div><p>Скопируйте ключ сейчас. Повторно показать его нельзя.</p><label>Ключ доступа<input id="access-token" readonly></label><div class="dialog-actions"><button id="access-copy" type="button">Скопировать</button><button class="quiet" type="submit">Закрыть</button></div></form></dialog>
|
||
<footer>Krona Business Platform · Staff</footer>
|
||
</div>
|
||
<script src="/staff/app.js" defer></script>
|
||
</body>
|
||
</html>`;
|