Two product MVPs based on iterations of PRD.md / TRD.md:
1. Atlas AI Wealth (initial TRD: portfolio/cashflow/forecasting platform)
- backend/ FastAPI + SQLAlchemy + SQLite: XLSX/CSV parser with
fuzzy header matching, rule-based transaction classifier,
analytics engine (TWR via Modified Dietz, Newton IRR,
moving-avg cost, allocation, cashflow), AI Copilot stub.
- frontend/ Next.js 14 + TS + Tailwind: Dashboard, Portfolio,
Transactions, Cash Flow, Upload screens; SWR data fetching.
- index.html / app.js / data.js / styles.css vanilla zero-build
version of the same MVP using mock data.
2. ExecutiveOS AI (revised TRD: AI Chief of Staff for Lotus Notes)
- executive-os/ vanilla HTML+CSS+JS PWA: Daily Briefing,
Priority Inbox, Approvals, Escalations, AI Search, AI Copilot.
- Mock dataset with 18 emails (P1-P3, 8 categories, AI scoring,
SLA tracking, recommendations).
- Russian UI. Service Worker + manifest for offline install.
Docs:
- README.md run instructions + TRD coverage table for both MVPs
- README.korabayev.md preserved original onboarding doc
194 lines
7.6 KiB
HTML
194 lines
7.6 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>ExecutiveOS AI — Помощник для руководителя (Lotus)</title>
|
||
<link rel="manifest" href="manifest.webmanifest" />
|
||
<link rel="icon" type="image/svg+xml" href="icon.svg" />
|
||
<link rel="apple-touch-icon" href="icon.svg" />
|
||
<meta name="theme-color" content="#7bb7ff" />
|
||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||
<meta name="apple-mobile-web-app-title" content="ExecutiveOS" />
|
||
<link rel="stylesheet" href="styles.css" />
|
||
</head>
|
||
<body>
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<span class="logo-dot"></span>
|
||
<span><strong>ExecutiveOS</strong> <span class="muted">AI</span></span>
|
||
</div>
|
||
<nav class="nav" id="nav">
|
||
<button data-route="briefing" class="active">Брифинг</button>
|
||
<button data-route="inbox">Входящие</button>
|
||
<button data-route="approvals">Согласования</button>
|
||
<button data-route="escalations">Эскалации</button>
|
||
<button data-route="search">AI-поиск</button>
|
||
</nav>
|
||
<div class="user">
|
||
<span id="online-dot" class="status-dot"></span>
|
||
<span class="mono">ceo@acme.corp</span>
|
||
<span class="muted small">· HCL Notes</span>
|
||
<button id="btn-install" class="btn-ghost install-btn" hidden>↓ Установить</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main id="view"></main>
|
||
|
||
<!-- Брифинг -->
|
||
<template id="tpl-briefing">
|
||
<section>
|
||
<div class="row spread wrap">
|
||
<div>
|
||
<h1>Ежедневный брифинг</h1>
|
||
<div class="muted small" id="brief-date"></div>
|
||
</div>
|
||
<div class="row gap">
|
||
<button id="btn-refresh" class="btn-ghost">↻ Синхронизация с Lotus</button>
|
||
<button id="btn-mark-read" class="btn-ghost">Отметить все просмотренными</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="kpi-grid">
|
||
<div class="card kpi"><div class="kpi-label">Критических непрочитанных</div><div class="kpi-value danger" data-k="critical"></div></div>
|
||
<div class="card kpi"><div class="kpi-label">Ожидают решения</div><div class="kpi-value warn" data-k="pending"></div></div>
|
||
<div class="card kpi"><div class="kpi-label">К Совету директоров</div><div class="kpi-value" data-k="board"></div></div>
|
||
<div class="card kpi"><div class="kpi-label">Нарушений SLA</div><div class="kpi-value danger" data-k="sla"></div></div>
|
||
</div>
|
||
|
||
<div class="grid-2-1">
|
||
<div class="card">
|
||
<div class="kpi-label">Сводка дня</div>
|
||
<div id="brief-summary" class="briefing"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="kpi-label">Топ-3 приоритета</div>
|
||
<ol id="brief-top3" class="priorities"></ol>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="kpi-label">AI-копилот</div>
|
||
<div class="copilot">
|
||
<div class="row gap">
|
||
<input id="cp-input" placeholder="«Что требует моей подписи?», «Покажи просроченные»…" />
|
||
<button id="cp-ask" class="btn-primary">Спросить</button>
|
||
</div>
|
||
<div id="cp-suggest" class="chips"></div>
|
||
<div id="cp-answer" class="answer"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<!-- Входящие -->
|
||
<template id="tpl-inbox">
|
||
<section>
|
||
<h1>Приоритетные входящие</h1>
|
||
<div class="card row gap wrap">
|
||
<input id="inbox-search" placeholder="Поиск по теме, отправителю, тексту…" />
|
||
<select id="inbox-cat">
|
||
<option value="">Все категории</option>
|
||
</select>
|
||
<select id="inbox-pri">
|
||
<option value="">Все приоритеты</option>
|
||
<option value="P1">P1 — Критический</option>
|
||
<option value="P2">P2 — Высокий</option>
|
||
<option value="P3">P3 — Обычный</option>
|
||
</select>
|
||
<label class="row gap small"><input type="checkbox" id="inbox-unread" /> <span>Только непрочитанные</span></label>
|
||
</div>
|
||
<div id="inbox-list" class="email-list"></div>
|
||
</section>
|
||
</template>
|
||
|
||
<!-- Согласования -->
|
||
<template id="tpl-approvals">
|
||
<section>
|
||
<div class="row spread wrap">
|
||
<h1>Ожидают согласования</h1>
|
||
<div class="muted small">AI оценил риск и предложил действие. Решение всегда за вами.</div>
|
||
</div>
|
||
<div id="appr-list" class="approval-list"></div>
|
||
</section>
|
||
</template>
|
||
|
||
<!-- Эскалации -->
|
||
<template id="tpl-escalations">
|
||
<section>
|
||
<h1>Монитор эскалаций</h1>
|
||
<div class="card">
|
||
<table class="data">
|
||
<thead><tr>
|
||
<th>Возраст</th><th>Тема</th><th>От кого</th><th>Должность</th>
|
||
<th class="r">SLA</th><th>Статус</th>
|
||
</tr></thead>
|
||
<tbody id="esc-rows"></tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<!-- Поиск -->
|
||
<template id="tpl-search">
|
||
<section>
|
||
<h1>AI-поиск по корпоративной почте</h1>
|
||
<div class="card stack">
|
||
<div class="row gap">
|
||
<input id="srch-input" placeholder="Семантический поиск по письмам, согласованиям, материалам Совета…" />
|
||
<button id="srch-go" class="btn-primary">Найти</button>
|
||
</div>
|
||
<div class="chips" id="srch-suggest"></div>
|
||
</div>
|
||
<div id="srch-results" class="email-list"></div>
|
||
</section>
|
||
</template>
|
||
|
||
<script src="data.js"></script>
|
||
<script src="app.js"></script>
|
||
<script>
|
||
// ---- PWA: регистрация Service Worker ----
|
||
if ("serviceWorker" in navigator) {
|
||
window.addEventListener("load", () => {
|
||
navigator.serviceWorker.register("./sw.js")
|
||
.then(reg => console.log("[PWA] SW registered:", reg.scope))
|
||
.catch(err => console.error("[PWA] SW error:", err));
|
||
});
|
||
}
|
||
|
||
// ---- Install prompt ----
|
||
let deferredPrompt = null;
|
||
const installBtn = document.getElementById("btn-install");
|
||
window.addEventListener("beforeinstallprompt", (e) => {
|
||
e.preventDefault();
|
||
deferredPrompt = e;
|
||
installBtn.hidden = false;
|
||
});
|
||
installBtn.addEventListener("click", async () => {
|
||
if (!deferredPrompt) return;
|
||
deferredPrompt.prompt();
|
||
const { outcome } = await deferredPrompt.userChoice;
|
||
console.log("[PWA] install outcome:", outcome);
|
||
deferredPrompt = null;
|
||
installBtn.hidden = true;
|
||
});
|
||
window.addEventListener("appinstalled", () => {
|
||
installBtn.hidden = true;
|
||
console.log("[PWA] installed");
|
||
});
|
||
|
||
// ---- Online/offline индикатор ----
|
||
const dot = document.getElementById("online-dot");
|
||
function updateOnline() {
|
||
const online = navigator.onLine;
|
||
dot.classList.toggle("offline", !online);
|
||
dot.title = online ? "Online — Lotus синхронизирован" : "Offline — данные из кэша";
|
||
}
|
||
window.addEventListener("online", updateOnline);
|
||
window.addEventListener("offline", updateOnline);
|
||
updateOnline();
|
||
</script>
|
||
</body>
|
||
</html>
|