// Portal & chat-style mock primitives for digstaff v9.
// Visual idiom: "office in your messenger" — topic-grouped chat threads.
// Generic chat UI (not a pixel-perfect clone of any specific messenger).
function PortalChrome({ children, dense=false, dark=false, className='', style={} }) {
const C = window.DT.color;
const bg = dark ? C.panel : '#FFFFFF';
const border = dark ? '1px solid rgba(247,244,236,0.10)' : '1px solid rgba(19,17,14,0.08)';
const shadow = dark
? '0 30px 80px -24px rgba(0,0,0,0.55), 0 6px 20px rgba(0,0,0,0.20)'
: '0 30px 80px -24px rgba(19,17,14,0.18), 0 6px 20px rgba(19,17,14,0.06)';
return (
{children}
);
}
function PortalTopbar({ dark=false, title='AI-офис', subtitle='ООО «Ромашка»' }) {
const C = window.DT.color;
const fg = dark ? 'rgba(247,244,236,0.92)' : C.ink;
const fg2 = dark ? 'rgba(247,244,236,0.50)' : C.inkMute;
const line= dark ? 'rgba(247,244,236,0.10)' : 'rgba(19,17,14,0.08)';
return (
🏢
{title}
● live
{subtitle} · 12 топиков · вся команда онлайн
);
}
function PortalKPIs({ dark=false }) {
const C = window.DT.color;
const fg = dark ? '#F7F4EC' : C.ink;
const fg2 = dark ? 'rgba(247,244,236,0.55)' : C.inkMute;
const line= dark ? 'rgba(247,244,236,0.10)' : 'rgba(19,17,14,0.08)';
const items = [
{ v:'47', l:'выполнено', c:fg },
{ v:'12', l:'в работе', c:fg },
{ v:'2', l:'ждут подтверждения', c:C.accent },
{ v:'10/10',l:'сотрудников онлайн', c:C.grass },
];
return (
);
}
function FeedRow({ ev, dark=false, fresh=false, compact=false }) {
const C = window.DT.color;
const role = window.EMPLOYEES.find(e=>e.id===ev.role) || { short:ev.role, dot:C.inkMute, emoji:'•' };
const fg = dark ? '#F7F4EC' : C.ink;
const fg2 = dark ? 'rgba(247,244,236,0.55)' : C.inkMute;
const line= dark ? 'rgba(247,244,236,0.08)' : 'rgba(19,17,14,0.06)';
const stateColor =
ev.kind==='approval' ? C.accent :
ev.kind==='attention' ? C.warn :
ev.kind==='in_progress' ? '#3970C2' :
C.grass;
return (
{ev.t}
{role.emoji}
{role.short}
{ev.text}
{ev.kind==='approval' && }
);
}
// Compact portal — used in hero right column.
// Layout: chat-app header → 3 curated message rows → approval strip. (No KPI bar — was visual noise.)
function PortalCompact() {
const C = window.DT.color;
// Curated 3 messages — most informative snapshot of current office state.
const curated = [
{ t:'10:14', role:'sales', text:'Сделка ООО «Восход» · 480 000 ₽ — ждёт вашего «ок»', kind:'approval' },
{ t:'10:14', role:'lawyer', text:'Договор № 2026-118 — 3 правки готовы', kind:'done' },
{ t:'09:41', role:'director', text:'Сводка дня для CEO к 17:00 — собираю', kind:'in_progress' },
];
return (
{curated.map((ev,i)=>())}
Нужно подтверждение
/tasks
);
}
// Chat-bubble primitive — generic messenger style. Used in DemoScenario + Hero teaser.
function ChatMessage({ emoji, name, color, time, children, system=false, ownDir=false, mono=false }) {
const C = window.DT.color;
if (system) {
return (
{emoji && {emoji}}
{children}
{time && · {time}}
);
}
return (
);
}
// Office topics list — used in ActivityFeed and Hero.
function TopicsList({ dark=false, compact=false }) {
const C = window.DT.color;
const fg = dark ? 'rgba(247,244,236,0.92)' : C.ink;
const fg2 = dark ? 'rgba(247,244,236,0.55)' : C.inkMute;
const line= dark ? 'rgba(247,244,236,0.06)' : 'rgba(19,17,14,0.05)';
const baseRow = compact ? 'py-1.5' : 'py-2.5';
const topics = [
{ emoji:'🎯', name:'Общий чат', badge:'2', badgeKind:'unread' },
{ emoji:'📋', name:'Задачник', badge:'1', badgeKind:'approval' },
...window.EMPLOYEES.map((e,i)=>({
emoji:e.emoji, name:e.short,
badge: i===0 ? '3' : i===2 ? '2' : i===3 ? '1' : null,
badgeKind: i===2 ? 'approval' : 'unread',
})),
];
return (
{topics.map((t,i)=>(
{t.emoji}
{t.name}
{t.badge && (
{t.badge}
)}
))}
);
}
// Hero chat teaser — replaces grass-feet video. Shows a real moment of work.
// Header "AI-офис · ООО «Ромашка»" + a single conversation excerpt.
function HeroChatTeaser() {
const C = window.DT.color;
return (
{/* header bar */}
🎯
Общий чат
AI-офис · ООО «Ромашка»
10 онлайн
{/* thread */}
Иван прислал договор от ООО «Восход». Проверь и посчитай НДС с 1 800 000.
Договор № 2026-118. Нашёл 3 момента: п.5.2 — пеня 0.5%/день, п.7 — нет акта приёма, п.11 — чужая подсудность.
{`НДС 20% из 1 800 000 ₽:
НДС: 300 000 ₽
Без НДС: 1 500 000 ₽`}
{/* status strip */}
📋 1 задача ждёт вашего «ок» в /tasks
Открыть
);
}
Object.assign(window, { PortalChrome, PortalTopbar, PortalKPIs, FeedRow, PortalCompact, ChatMessage, TopicsList, HeroChatTeaser });