work-license-server 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,8 @@
7
7
  1. **Витрина `/`** — вкладки «Платформа», «ИИ-агенты», «Тарифы», «Комплекс», «Докупка лицензий» (только после входа), «Франшиза», «Поддержка»; светлая и тёмная темы, мягкий анимированный фон (графики роста и сеть связей).
8
8
  - Комплекс: **AMD Ryzen™ AI Max+ PRO 495 · Radeon™ 8065S · 192 ГБ LPDDR5x-8533 · SSD 4 ТБ**.
9
9
  - Внутри: встроенная система учёта, документооборот, управление процессами, ИИ-консультант в каждой форме, мультиагентная система (13 ролевых агентов, 26+ навыков) и 4 типа локальных моделей (языковая, голосовая, генерация изображений, векторный поиск).
10
- - Базовый комплект — `5 000 000 ₽`: **вечная лицензия на 5 одновременных сессий** и **1-й год поддержки**.
10
+ - Базовый комплект — `5 000 000 ₽`: **вечная лицензия на 10 одновременных сессий** и **1-й год поддержки уровня Enterprise** (далее продление поддержки и докупка мест — по желанию).
11
+ - Интегрированные на комплексе 4 типа моделей предназначены для базовых ежедневных операций внутри контура; при необходимости более мощных ИИ они подключаются через API из любого источника.
11
12
  2. **Годовые лицензии на дополнительных пользователей** со скидкой за количество (5+ мест −5% … 100+ мест −25%):
12
13
  - **Start** — `10 000 ₽ / год` (до 12 обращений в год на место, реакция до 8 рабочих часов);
13
14
  - **Business** — `15 000 ₽ / год` (до 48 обращений, реакция до 2 часов);
@@ -572,6 +572,47 @@ table.t { width: 100%; border-collapse: collapse; font-size: 13.5px; }
572
572
  .ai-input { display: flex; gap: 8px; padding: 12px; border-top: 1px solid var(--line); }
573
573
  .ai-input input { border-radius: 999px; }
574
574
  .ai-input .btn { border-radius: 999px; width: 44px; padding: 0; }
575
+ body[data-stage="admin"] .ai-fab { display: none; }
576
+
577
+ /* ---------- Админка с левой вкладкой ИИ-агента ---------- */
578
+ .admin-workspace { display: grid; grid-template-columns: 380px minmax(0, 1fr); gap: 20px; align-items: start; width: 100%; }
579
+ .admin-workspace.only-agent { grid-template-columns: minmax(0, 1fr); }
580
+ .admin-workspace.only-agent .admin-main { display: none; }
581
+ .admin-left { position: sticky; top: 88px; display: grid; gap: 14px; min-width: 0; }
582
+ .admin-workspace.only-agent .admin-left { position: static; }
583
+ .admin-nav {
584
+ display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 8px;
585
+ border-radius: 18px; border: 1px solid var(--line); background: var(--panel);
586
+ backdrop-filter: blur(18px);
587
+ }
588
+ .adm-tab {
589
+ display: flex; align-items: center; gap: 8px; padding: 10px 12px; border: 1px solid transparent;
590
+ border-radius: 12px; background: none; cursor: pointer; font: inherit; font-size: 12.5px; font-weight: 650;
591
+ color: var(--text-2); text-align: left; transition: all .2s; min-width: 0;
592
+ }
593
+ .adm-tab span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
594
+ .adm-tab svg { width: 16px; height: 16px; flex: none; color: var(--brand-2); }
595
+ .adm-tab:hover { background: color-mix(in srgb, var(--brand) 10%, transparent); color: var(--text); }
596
+ .adm-tab.on {
597
+ background: linear-gradient(135deg, var(--brand), #2563eb); color: #fff;
598
+ box-shadow: 0 10px 24px -10px var(--glow);
599
+ }
600
+ .adm-tab.on svg { color: #fff; }
601
+ .adm-tab .live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 8px var(--ok); margin-left: auto; flex: none; }
602
+ .adm-agent-card { padding: 16px; display: flex; flex-direction: column; min-width: 0; overflow: hidden; border-color: color-mix(in srgb, var(--brand) 38%, var(--line)); }
603
+ .adm-ai-msgs {
604
+ padding: 12px; border-radius: 14px; border: 1px solid var(--line);
605
+ background: color-mix(in srgb, var(--bg-2) 60%, transparent);
606
+ min-height: 290px; max-height: 410px; overflow-x: hidden;
607
+ }
608
+ .admin-workspace.only-agent .adm-ai-msgs { min-height: 420px; max-height: 560px; }
609
+ .admin-main { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; min-width: 0; width: 100%; }
610
+ .admin-main > .card { min-width: 0; overflow: hidden; }
611
+ .admin-main .g4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
612
+ @media (max-width: 1120px) {
613
+ .admin-workspace { grid-template-columns: minmax(0, 1fr); }
614
+ .admin-left { position: static; }
615
+ }
575
616
 
576
617
  /* ---------- Модальное окно ---------- */
577
618
  .modal { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; padding: 16px; background: rgba(2, 4, 10, .6); backdrop-filter: blur(10px); opacity: 0; pointer-events: none; transition: opacity .25s; }
package/assets/landing.js CHANGED
@@ -56,6 +56,7 @@
56
56
  if (id === 'addons' && !currentUser) { openAuth(); return; }
57
57
  if (!$('#stage-' + id)) id = 'product';
58
58
  currentStage = id;
59
+ document.body.setAttribute('data-stage', id);
59
60
  $$('.tab').forEach(t => t.classList.toggle('active', t.dataset.stage === id));
60
61
  $$('.stage').forEach(s => s.classList.toggle('active', s.id === 'stage-' + id));
61
62
  const admBtn = $('#btnAdminTop');
@@ -203,7 +204,7 @@
203
204
  ['Мест × лет', `${seats} × ${years}`],
204
205
  ['Без скидки', rub(gross)],
205
206
  ['Скидка', `−${disc.discountPercent}%`],
206
- ['Всего мест после докупки', `${(D.product?.baseSeats || 5) + seats}`],
207
+ ['Всего мест после докупки', `${(D.product?.baseSeats || 10) + seats}`],
207
208
  ]);
208
209
  markLadder('ladderAddons', seats);
209
210
  }
@@ -224,7 +225,7 @@
224
225
  const rows = [
225
226
  ['Комплекс ODANT-WORK', rub(D.product.basePriceRub)],
226
227
  ['Вечные рабочие места', String(D.product.baseSeats)],
227
- ['Поддержка в комплекте', '1 год · Business'],
228
+ ['Поддержка в комплекте', '1 год · Enterprise (далее — по желанию)'],
228
229
  ];
229
230
  if (extra > 0) rows.push([`Доп. места · ${esc(tier.name)}${disc.discountPercent ? ' · −' + disc.discountPercent + '%' : ''}`, `${extra} · ${rub(net)}`]);
230
231
  $('#lblPakBreakdown').innerHTML = lines(rows);
@@ -696,7 +697,7 @@
696
697
  const out = [];
697
698
  out.push(`Лицензии (${data.licenses?.length || 0})`);
698
699
  for (const l of data.licenses || [])
699
- out.push(` ${l.licenseId} · ${l.customer?.name || '—'} · мест ${l.totalSeats} (${l.baseSeats ?? 5} вечных + ${l.addonSeats}) · ${l.support?.tierName || ''}`);
700
+ out.push(` ${l.licenseId} · ${l.customer?.name || '—'} · мест ${l.totalSeats} (${l.baseSeats ?? 10} вечных + ${l.addonSeats}) · ${l.support?.tierName || ''}`);
700
701
  out.push('', `Резервы франшиз (${data.reservations?.length || 0})`);
701
702
  for (const r of data.reservations || [])
702
703
  out.push(` ${r.reservationId} · ${r.regionName} · ${r.companyName} · залог ${r.depositPaid ? 'оплачен' : 'ожидает оплаты'}`);
@@ -743,8 +744,8 @@
743
744
  <td><b>${esc(l.licenseId)}</b><div class="small muted">Код бота: <code>${esc(l.bindingCode || l.licenseId)}</code></div></td>
744
745
  <td><b>${esc(l.customer?.name || '—')}</b><div class="small muted">ИНН ${esc(l.customer?.inn || '—')} · ${esc(l.customer?.email || '—')}</div></td>
745
746
  <td><code>${esc(l.boundHwid || 'не привязан')}</code><div class="small muted">${esc((l.boundNodeFingerprint || '').slice(0, 18) || 'ожидает синхронизации')}</div></td>
746
- <td class="num"><b>${l.totalSeats}</b> <span class="muted">(${l.baseSeats ?? 5} вечн. + ${l.addonSeats || 0} доп.)</span></td>
747
- <td><span class="tag ok">${esc(l.support?.tierName || 'Business')}</span><div class="small muted">до ${fmtDate(l.support?.expiresAt)}</div></td>
747
+ <td class="num"><b>${l.totalSeats}</b> <span class="muted">(${l.baseSeats ?? 10} вечн. + ${l.addonSeats || 0} доп.)</span></td>
748
+ <td><span class="tag ok">${esc(l.support?.tierName || 'Enterprise')}</span><div class="small muted">до ${fmtDate(l.support?.expiresAt)}</div></td>
748
749
  <td><button class="btn btn-ghost btn-sm" data-cert="${esc(l.licenseId)}">Сертификат JSON</button></td>
749
750
  </tr>`).join('') || '<tr><td colspan="6" class="muted">Лицензий пока нет — нажмите «+ Демо-лицензия ПАК»</td></tr>';
750
751
  }
@@ -895,6 +896,44 @@
895
896
  $('#certModal').classList.add('open');
896
897
  }
897
898
 
899
+ function switchAdminTab(tab = 'all') {
900
+ $$('[data-adm-tab]').forEach(b => b.classList.toggle('on', b.dataset.admTab === tab));
901
+ const ws = $('#adminWorkspace');
902
+ if (ws) ws.classList.toggle('only-agent', tab === 'agent');
903
+ $$('[data-adm-sec]').forEach(sec => {
904
+ sec.style.display = (tab === 'all' || tab === sec.dataset.admSec) ? '' : 'none';
905
+ });
906
+ if (tab === 'agent') setTimeout(() => $('#admAiInput')?.focus(), 50);
907
+ }
908
+
909
+ async function sendAdminAi(text) {
910
+ const inp = $('#admAiInput');
911
+ text = (text ?? inp?.value ?? '').trim();
912
+ if (!text) return;
913
+ if (inp) inp.value = '';
914
+ const box = $('#admAiMessages');
915
+ if (!box) return;
916
+ box.insertAdjacentHTML('beforeend', `<div class="msg me">${esc(text)}</div><div class="msg bot" id="admAiTyping"><span class="typing"><i></i><i></i><i></i></span></div>`);
917
+ box.scrollTop = box.scrollHeight;
918
+ let data;
919
+ try { data = await api('/api/agent/chat', { message: text, sessionKey: currentUser?.sessionKey || '' }); }
920
+ catch (err) { data = { reply: err.message, actions: [] }; }
921
+ $('#admAiTyping')?.remove();
922
+ box.insertAdjacentHTML('beforeend', `<div class="msg bot">${md(data.reply || '')}</div>`);
923
+ box.scrollTop = box.scrollHeight;
924
+ for (const act of data.actions || []) {
925
+ if (act.type === 'refresh_admin') {
926
+ refreshRegistry();
927
+ }
928
+ else if (act.type === 'admin_tab' && act.tab) {
929
+ switchAdminTab(act.tab);
930
+ }
931
+ else if (act.type === 'select_region' && act.regionCode) {
932
+ selectRegion(act.regionCode, { zoom: true, scrollTable: false });
933
+ }
934
+ }
935
+ }
936
+
898
937
  /* ================= ИИ-агент лицензионного центра ================= */
899
938
  function toggleAi(force) {
900
939
  const d = $('#aiDrawer');
@@ -903,7 +942,10 @@
903
942
  if (open) setTimeout(() => $('#aiInput').focus(), 60);
904
943
  }
905
944
  function md(text) {
906
- return esc(text).replace(/\*\*(.+?)\*\*/g, '<b>$1</b>').replace(/(^|\s)\*(.+?)\*(?=\s|$)/g, '$1<i>$2</i>');
945
+ return esc(text)
946
+ .replace(/\*\*(.+?)\*\*/g, '<b>$1</b>')
947
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
948
+ .replace(/(^|\s)\*(.+?)\*(?=\s|$)/g, '$1<i>$2</i>');
907
949
  }
908
950
  async function sendAi(text) {
909
951
  const inp = $('#aiInput');
@@ -920,7 +962,10 @@
920
962
  box.insertAdjacentHTML('beforeend', `<div class="msg bot">${md(data.reply || '')}</div>`);
921
963
  box.scrollTop = box.scrollHeight;
922
964
  for (const act of data.actions || []) {
923
- if (act.type === 'select_region') {
965
+ if (act.type === 'refresh_admin') {
966
+ refreshRegistry();
967
+ }
968
+ else if (act.type === 'select_region') {
924
969
  switchStage('franchise');
925
970
  setTimeout(() => selectRegion(act.regionCode, { zoom: true, scrollTable: true }), 80);
926
971
  }
@@ -1101,9 +1146,11 @@
1101
1146
  /* ================= Привязка событий ================= */
1102
1147
  function bind() {
1103
1148
  document.addEventListener('click', (e) => {
1104
- const el = e.target.closest('[data-stage],[data-action],[data-reserve],[data-bot],[data-ask],[data-district],[data-cert],[data-pay-order],[data-activate-res],[data-certify-partner],[data-reply-ticket],th[data-sort],#regionsTableBody tr');
1149
+ const el = e.target.closest('[data-stage],[data-action],[data-reserve],[data-bot],[data-ask],[data-adm-ask],[data-adm-tab],[data-district],[data-cert],[data-pay-order],[data-activate-res],[data-certify-partner],[data-reply-ticket],th[data-sort],#regionsTableBody tr');
1105
1150
  if (!el) return;
1106
1151
  if (el.matches('[data-reserve]')) { e.stopPropagation(); reserveRegionFranchise(el.dataset.reserve); return; }
1152
+ if (el.matches('[data-adm-tab]')) { switchAdminTab(el.dataset.admTab); return; }
1153
+ if (el.matches('[data-adm-ask]')) { sendAdminAi(el.dataset.admAsk); return; }
1107
1154
  if (el.matches('[data-cert]')) { openCertModal(el.dataset.cert); return; }
1108
1155
  if (el.matches('[data-pay-order]')) { adminConfirmOrder(el.dataset.payOrder); return; }
1109
1156
  if (el.matches('[data-activate-res]')) { adminActivateReservation(el.dataset.activateRes); return; }
@@ -1137,6 +1184,7 @@
1137
1184
  case 'registry': refreshRegistry(); break;
1138
1185
  case 'demo-pak': createDemoPak(); break;
1139
1186
  case 'adm-issue-lic': adminIssueLicense(); break;
1187
+ case 'adm-ai-send': sendAdminAi(); break;
1140
1188
  case 'cert-close': $('#certModal')?.classList.remove('open'); break;
1141
1189
  case 'cert-copy':
1142
1190
  navigator.clipboard?.writeText($('#certJsonBox')?.textContent || '');
@@ -1152,6 +1200,7 @@
1152
1200
  });
1153
1201
  $('#authModal').addEventListener('keydown', (e) => { if (e.key === 'Enter') performLogin(); });
1154
1202
  $('#aiInput').addEventListener('keydown', (e) => { if (e.key === 'Enter') sendAi(); });
1203
+ $('#admAiInput')?.addEventListener('keydown', (e) => { if (e.key === 'Enter') sendAdminAi(); });
1155
1204
  $('#botText').addEventListener('keydown', (e) => { if (e.key === 'Enter') sendBot(); });
1156
1205
  $('#rngAddonSeats').addEventListener('input', (e) => syncSeats(e.target.value));
1157
1206
  $('#inpSeats').addEventListener('input', (e) => syncSeats(e.target.value));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "work-license-server",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Лицензионный сервер, биллинг ЮKassa, боты поддержки (MAX, Telegram, VK) и лендинг ПАК WORK (work.odant.org)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/agent.js CHANGED
@@ -62,12 +62,89 @@ export class LicenseServerAiAgent {
62
62
  const text = String(message || '').trim();
63
63
  if (!text) {
64
64
  return {
65
- reply: 'Здравствуйте! Я ИИ-агент лицензионного центра ODANT-WORK. Спросите о комплексе, ИИ-агентах платформы, расчёте лицензий со скидкой или вакантных франшизах в вашем регионе.',
65
+ reply: 'Здравствуйте! Я ИИ-агент лицензионного центра ODANT-WORK. Спросите о комплексе, ИИ-агентах платформы, расчёте лицензий со скидкой, вакантных франшизах или управлении реестром сервера.',
66
66
  actions: [],
67
67
  };
68
68
  }
69
69
  const lower = text.toLowerCase();
70
70
 
71
+ // 0.1 Команда администратора: выпуск новой лицензии ПАК
72
+ if (/(выпуст|создай|оформ)\S*\s+(?:базов\S*\s+|демо[-\s]*)?лиценз/i.test(lower)) {
73
+ const orgMatch = text.match(/(?:для\s+)?((?:ООО|АО|ПАО|ЗАО|ИП)\s*«[^»]+»)/i);
74
+ const innMatch = text.match(/ИНН\s*(\d{10,12})/i) || text.match(/\b(\d{10,12})\b/);
75
+ const emailMatch = text.match(/([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})/i);
76
+ const lic = this.store.issueOrUpdateLicense({
77
+ customer: {
78
+ name: orgMatch ? orgMatch[1] : 'ООО «ТехноПром-Интеграция»',
79
+ inn: innMatch ? innMatch[1] : '6234109876',
80
+ email: emailMatch ? emailMatch[1] : 'it@technoprom.ru',
81
+ },
82
+ });
83
+ return {
84
+ reply: [
85
+ `Выпущена и подписана сертификатом **Ed25519** базовая лицензия **${lic.licenseId}**:`,
86
+ `• Организация: **${lic.customer?.name || '—'}** (ИНН ${lic.customer?.inn || '—'})`,
87
+ `• Рабочие места: **${lic.totalSeats}** (${lic.baseSeats} вечных лицензий в комплекте)`,
88
+ `• Поддержка: **1 год ${lic.support?.tierName || 'Enterprise'}** (${lic.support?.reactionLabel || 'до 15 минут'}, далее — по желанию)`,
89
+ `• Код привязки бота MAX/TG/VK: \`${lic.support?.bindCode || lic.licenseId}\``,
90
+ ].join('\n'),
91
+ actions: [{ type: 'refresh_admin' }, { type: 'admin_tab', tab: 'licenses' }],
92
+ data: { license: lic },
93
+ };
94
+ }
95
+
96
+ // 0.2 Команда администратора: проведение оплаты ожидающих заказов ЮKassa
97
+ if (/(провед|подтверд)\S*\s+оплат/i.test(lower)) {
98
+ const pending = Object.values(this.store.state.orders || {}).filter(o => o.status === 'pending');
99
+ if (!pending.length) {
100
+ return {
101
+ reply: 'Все выставленные заказы ЮKassa уже оплачены — ожидающих платежей сейчас нет.',
102
+ actions: [{ type: 'refresh_admin' }],
103
+ };
104
+ }
105
+ const completed = [];
106
+ for (const o of pending) {
107
+ const done = this.store.completePaidOrder(o.orderId, { paymentId: o.paymentId || ('yk_' + o.orderId) });
108
+ if (done) completed.push(done);
109
+ }
110
+ return {
111
+ reply: [
112
+ `Проведена оплата по **${completed.length}** заказам ЮKassa:`,
113
+ ...completed.map(o => `• **${o.orderId}** — ${o.customer?.name || 'Клиент'} · **${fmt(o.amountRub)} ₽** (статус: оплачен)`),
114
+ 'Сертификаты лицензий и резервы франшиз автоматически обновлены.',
115
+ ].join('\n'),
116
+ actions: [{ type: 'refresh_admin' }, { type: 'admin_tab', tab: 'orders' }],
117
+ };
118
+ }
119
+
120
+ // 0.3 Команда администратора: сводка по реестру сервера лицензий / заказам / тикетам
121
+ if (/(сводк|реестр|статус сервер|обзор сервер|неоплачен\S*\s+заказ|покажи заказ|открыт\S*\s+тикет|покажи тикет|резерв\S*\s+франш)/i.test(lower)) {
122
+ const licenses = this.store.listLicenses();
123
+ const orders = Object.values(this.store.state.orders || {});
124
+ const partners = this.store.listPartners();
125
+ const reservations = Object.values(this.store.state.franchiseReservations || {});
126
+ const tickets = this.store.listTickets();
127
+ const totalSeats = licenses.reduce((s, l) => s + (l.totalSeats || 0), 0);
128
+ const paidOrders = orders.filter(o => o.status === 'paid');
129
+ const pendingOrders = orders.filter(o => o.status === 'pending');
130
+ const paidSum = paidOrders.reduce((s, o) => s + (Number(o.amountRub) || 0), 0);
131
+ const pendingSum = pendingOrders.reduce((s, o) => s + (Number(o.amountRub) || 0), 0);
132
+ const openTickets = tickets.filter(t => t.status !== 'answered' && t.status !== 'closed');
133
+
134
+ return {
135
+ reply: [
136
+ '**Сводка центра лицензирования и биллинга ODANT-WORK:**',
137
+ `• Выпущено лицензий ПАК: **${licenses.length}** (суммарно **${totalSeats}** активных мест, по ${PAK_PRODUCT.baseSeats} вечных в базе)`,
138
+ `• Оплачено заказов ЮKassa: **${paidOrders.length}** на сумму **${fmt(paidSum)} ₽**`,
139
+ `• Ожидают оплаты: **${pendingOrders.length}** на сумму **${fmt(pendingSum)} ₽**`,
140
+ `• Франшизы: **${partners.length}** действующих партнёров и **${reservations.length}** активных резервов`,
141
+ `• Техподдержка (MAX · TG · VK): **${openTickets.length}** открытых из **${tickets.length}** обращений`,
142
+ pendingOrders.length > 0 ? '\nНапишите «Проведи оплату заказов», чтобы подтвердить ожидающие платежи.' : '',
143
+ ].filter(Boolean).join('\n'),
144
+ actions: [{ type: 'refresh_admin' }],
145
+ };
146
+ }
147
+
71
148
  // 1. Франшиза в конкретном субъекте РФ
72
149
  const region = findRegionInText(text);
73
150
  if (region && /(франш|паушал|взнос|регион|вакант|квот|резерв|залог|партн|сколько|област|край|республ)/i.test(lower)) {
@@ -146,7 +223,7 @@ export class LicenseServerAiAgent {
146
223
  }
147
224
 
148
225
  // 5. Комплекс и аппаратная платформа
149
- if (/(ryzen|процессор|желез|характерист|radeon|8065|памят|192|модел|комплекс|продукт|сервер|(^|[^а-яё])ии([^а-яё]|$))/i.test(lower)) {
226
+ if (/(ryzen|процессор|желез|характерист|radeon|8065|памят|192|модел|комплекс|продукт|сервер|api|источник|(^|[^а-яё])ии([^а-яё]|$))/i.test(lower)) {
150
227
  const H = PAK_PRODUCT.hardware;
151
228
  return {
152
229
  reply: [
@@ -156,8 +233,9 @@ export class LicenseServerAiAgent {
156
233
  `• ИИ-ускорение: ${H.aiEngine}`,
157
234
  `• Память и диск: ${H.ram} · ${H.storage}`,
158
235
  `• Защита: ${H.security}`,
159
- '• Внутри: система учёта, документооборот, управление процессами, ИИ-консультант, мультиагентная система и 4 типа локальных моделей (языковая, голосовая, генерация изображений, векторный поиск).',
160
- `• В комплекте: вечная лицензия на ${PAK_PRODUCT.baseSeats} одновременных сессий + ${PAK_PRODUCT.includedSupportYears} год поддержки.`,
236
+ '• Внутри: система учёта, документооборот, управление процессами, ИИ-консультант, мультиагентная система и 4 типа интегрированных локальных моделей (языковая, голосовая, генерация изображений, векторный поиск).',
237
+ '• **Гибридный контур моделей:** интегрированные локальные модели предназначены для базовых ежедневных операций; если требуются более мощные модели ИИ, их можно подключить через API из любого источника.',
238
+ `• **В комплекте:** вечная лицензия на **${PAK_PRODUCT.baseSeats} одновременных сессий** + **${PAK_PRODUCT.includedSupportYears} год поддержки уровня Enterprise** (24/7, реакция ≤ 15 мин). Дальнейшее продление поддержки и докупка мест — **по желанию**.`,
161
239
  ].join('\n'),
162
240
  actions: [{ type: 'switch_stage', stage: 'product' }],
163
241
  };
@@ -174,7 +252,7 @@ export class LicenseServerAiAgent {
174
252
  `• Резерв субъекта: невозвратный залог **${fmt(FRANCHISE_PROGRAM.reservationDepositRub)} ₽** через ЮKassa, полная оплата паушального взноса — **в течение 1 года**`,
175
253
  `• Сейчас свободно **${totalVacant}** франшиз в **${all.length}** субъектах РФ (квота пропорциональна населению)`,
176
254
  `• Скидка партнёра: от ${FRANCHISE_PROGRAM.minDiscountPercent}% до ${FRANCHISE_PROGRAM.maxDiscountPercent}% от годового объёма продаж`,
177
- '• 100% дохода партнёра: оборудование и гарантия на него, NAS для резервных копий, серверы инференса, собственный API к моделям, поддержка, дообучение агентов и навыков.',
255
+ '• 100% дохода партнёра: оборудование и гарантия на него, NAS для резервных копий, серверы инференса, собственный API к мощным моделям, поддержка, дообучение агентов и навыков.',
178
256
  ].join('\n'),
179
257
  actions: [{ type: 'switch_stage', stage: 'franchise' }],
180
258
  };
@@ -184,10 +262,10 @@ export class LicenseServerAiAgent {
184
262
  return {
185
263
  reply: [
186
264
  'Я ИИ-агент лицензионного центра **ODANT-WORK**. Коротко об условиях:',
187
- `1. **Комплекс** — ${fmt(PAK_PRODUCT.basePriceRub)} ₽: AMD Ryzen™ AI Max+ PRO 495, Radeon™ 8065S, 192 ГБ, 4 ТБ, ${PAK_PRODUCT.baseSeats} вечных мест и год поддержки.`,
265
+ `1. **Комплекс** — ${fmt(PAK_PRODUCT.basePriceRub)} ₽: AMD Ryzen™ AI Max+ PRO 495, Radeon™ 8065S, 192 ГБ, 4 ТБ, **${PAK_PRODUCT.baseSeats} вечных лицензий** и **1 год поддержки Enterprise** в комплекте (дальше — по желанию). Интегрированные модели — для базовых операций, более мощные ИИ подключаются через API из любого источника.`,
188
266
  '2. **Доп. места на год:** Start 10 000 ₽ · Business 15 000 ₽ · Enterprise 20 000 ₽, скидка за количество до −25%.',
189
267
  `3. **Франшиза:** паушальный взнос ${fmt(FRANCHISE_PROGRAM.baseFranchiseFeeRub)} ₽ × индекс субъекта, залог за резерв ${fmt(FRANCHISE_PROGRAM.reservationDepositRub)} ₽, полная оплата в течение 1 года.`,
190
- 'Спросите, например: «Рассчитай 25 мест Business», «Франшиза в Рязанской области» или «Что умеют ИИ-агенты?»',
268
+ 'Спросите, например: «Рассчитай 25 мест Business», «Франшиза в Рязанской области», «Сводка по серверу» или «Выпусти лицензию».',
191
269
  ].join('\n'),
192
270
  actions: [],
193
271
  };
package/src/bots.js CHANGED
@@ -56,7 +56,7 @@ export class SupportBotsHub {
56
56
  '',
57
57
  'Доступные команды:',
58
58
  '• /bind <КОД> — привязать ПАК WORK к этому чату',
59
- '• /status — проверить рабочие места (5 вечных + годовые), SLA и остаток обращений',
59
+ '• /status — проверить рабочие места (10 вечных + годовые), SLA и остаток обращений',
60
60
  '• /buy <start|business|enterprise> <кол-во_мест> — докупить годовые лицензии через ЮKassa',
61
61
  '• Любое другое сообщение — создать обращение в инженерную поддержку по вашему SLA.',
62
62
  ].join('\n');
package/src/catalog.js CHANGED
@@ -9,10 +9,12 @@ export const PAK_PRODUCT = Object.freeze({
9
9
  name: 'ODANT-WORK',
10
10
  domain: 'work.odant.org',
11
11
  basePriceRub: 5_000_000,
12
- baseSeats: 5,
12
+ baseSeats: 10,
13
13
  perpetualBase: true,
14
14
  includedSupportYears: 1,
15
- includedSupportTier: 'business',
15
+ includedSupportTier: 'enterprise',
16
+ includedSupportTierName: 'Enterprise',
17
+ postYearPolicy: 'Дальнейшее продление поддержки и докупка мест — по желанию клиента (10 вечных лицензий работают бессрочно)',
16
18
  hardware: Object.freeze({
17
19
  cpu: 'AMD Ryzen™ AI Max+ PRO 495 (Zen 5, 16 ядер / 32 потока, до 5.2 ГГц, кэш 80 МБ)',
18
20
  gpu: 'AMD Radeon™ 8065S Graphics (40 ядер, 3000 МГц, до 61 TFLOPS FP16)',
@@ -25,16 +27,17 @@ export const PAK_PRODUCT = Object.freeze({
25
27
  }),
26
28
  software: Object.freeze({
27
29
  accounting: 'Встроенная система учёта, документооборота, управления процессами и приватный мультимодальный ИИ (справочники, план счетов, двойная запись, оборотно-сальдовая ведомость, анализ счёта, первичные документы и сквозные процессы)',
28
- seatModel: 'По одновременным активным сессиям (5 вечных рабочих мест в базовом комплекте + докупка срочных годовых лицензий со скидкой от количества)',
30
+ seatModel: 'По одновременным активным сессиям (10 вечных лицензий в базовом комплекте + 1 год поддержки Enterprise; дальше — продление и докупка мест по желанию)',
29
31
  hardwareBinding: 'Привязка вечной лицензии к Hardware ID материнской платы/CPU AMD Ryzen + криптографическому ключу узла (#system/node-key.json)',
30
32
  }),
33
+ modelsPolicy: 'Интегрированные локальные модели предназначены для базовых ежедневных операций внутри контура. Если требуются более мощные модели ИИ, их можно подключить через стандартный API из любого источника (облачные провайдеры, собственный GPU-кластер или API регионального партнёра).',
31
34
  preinstalledModels: Object.freeze([
32
35
  {
33
36
  id: 'llm-core',
34
37
  type: 'Большая языковая модель (LLM)',
35
- title: 'Интеллектуальное текстовое и аналитическое ядро',
36
- badge: 'Диалог · Учёт · Документы · Процессы',
37
- description: 'Ведение живого диалога, проведение хозяйственных операций, подготовка документов, управление бизнес-процессами, финансовый анализ и генерация программного кода.',
38
+ title: 'Базовое текстовое и учётное ядро',
39
+ badge: 'Базовые операции · Учёт · Документы',
40
+ description: 'Выполнение базовых операций: живой диалог, типовые хозяйственные проводки, шаблоны документов и повседневные задачи. Для глубокой аналитики и тяжёлых задач подключаются более мощные модели через API из любого источника.',
38
41
  ramGb: 18.5,
39
42
  },
40
43
  {
@@ -77,7 +80,7 @@ export const AI_PLATFORM = Object.freeze({
77
80
  'Действует в рамках роли и прав пользователя (ADMIN · BOSS · USER)',
78
81
  'Инструменты учётного ядра: описание класса, поиск объектов, проведение операций, отчёты',
79
82
  'Каждая консультация сохраняется задачей в личном кабинете и в ленте пользователя',
80
- 'Работает полностью локально на комплексе — данные не покидают контур предприятия',
83
+ 'Базовые операции выполняются интегрированными локальными моделями, а при необходимости более мощных ИИ — подключение через API из любого источника',
81
84
  ]),
82
85
  }),
83
86
  orchestration: Object.freeze([
@@ -236,7 +239,7 @@ export function calculateOrderQuote({
236
239
  const items = [];
237
240
  if (includeBasePak) {
238
241
  items.push({
239
- title: `${PAK_PRODUCT.name} (${PAK_PRODUCT.hardware.cpu}, ${PAK_PRODUCT.hardware.ramGb} ГБ RAM, SSD ${PAK_PRODUCT.hardware.ssdTb} ТБ, вечная лицензия на 5 мест + 1 год поддержки)`,
242
+ title: `${PAK_PRODUCT.name} (${PAK_PRODUCT.hardware.cpu}, ${PAK_PRODUCT.hardware.ramGb} ГБ RAM, SSD ${PAK_PRODUCT.hardware.ssdTb} ТБ, вечная лицензия на ${PAK_PRODUCT.baseSeats} мест + 1 год поддержки Enterprise)`,
240
243
  quantity: 1,
241
244
  unitPriceRub: PAK_PRODUCT.basePriceRub,
242
245
  totalRub: PAK_PRODUCT.basePriceRub,
@@ -536,7 +539,7 @@ export const PARTNER_TRAINING_TRACKS = Object.freeze([
536
539
  passScore: 75,
537
540
  requiredForFranchise: true,
538
541
  topics: [
539
- 'Экономическое обоснование внедрения ODANT-WORK (5 вечных мест + локальный ИИ в собственном контуре)',
542
+ 'Экономическое обоснование внедрения ODANT-WORK (10 вечных лицензий + 1 год поддержки Enterprise + локальный ИИ в собственном контуре)',
540
543
  'Лицензионная политика: расчёт конкурентных сессий со скидками от объёма и подбор тарифов SLA (Start / Business / Enterprise)',
541
544
  'Регламент первой и второй линии поддержки через боты MAX, Telegram и VK',
542
545
  ],
package/src/landing.js CHANGED
@@ -207,8 +207,8 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
207
207
  </div>
208
208
  <div class="trust">
209
209
  <span>${icon('shield')} Данные не покидают контур</span>
210
- <span>${icon('key')} 5 вечных рабочих мест</span>
211
- <span>${icon('check')} 1 год поддержки включён</span>
210
+ <span>${icon('key')} ${P.baseSeats} вечных лицензий</span>
211
+ <span>${icon('check')} 1 год поддержки Enterprise (далее — по желанию)</span>
212
212
  </div>
213
213
  </div>
214
214
  <div class="showcase" aria-hidden="true">
@@ -250,7 +250,7 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
250
250
  <span class="kicker">Паспорт комплекса</span>
251
251
  <h2 class="h2">ODANT-WORK · ${esc(P.sku)}</h2>
252
252
  <div class="price-big">${rub(P.basePriceRub)}</div>
253
- <p class="note">Вечная лицензия на ${P.baseSeats} одновременных рабочих сессий, привязанная к оборудованию, и ${P.includedSupportYears} год поддержки уровня Business — в комплекте.</p>
253
+ <p class="note">Вечная лицензия на ${P.baseSeats} одновременных рабочих сессий, привязанная к оборудованию, и ${P.includedSupportYears} год максимальной поддержки уровня Enterprise (24/7, реакция ≤ 15 мин) — в комплекте. Дальнейшее продление поддержки и докупка мест — по желанию.</p>
254
254
  <div class="hero-cta" style="margin-top:20px">
255
255
  <button class="btn btn-primary" data-stage="order">Оформить заказ</button>
256
256
  <button class="btn btn-ghost" data-stage="pricing">Тарифы поддержки</button>
@@ -270,12 +270,20 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
270
270
 
271
271
  <div class="sec-head reveal">
272
272
  <div>
273
- <span class="kicker">Приватный мультимодальный ИИ</span>
274
- <h2 class="h2">Четыре типа моделей уже установлены</h2>
273
+ <span class="kicker">Приватный и гибридный ИИ</span>
274
+ <h2 class="h2">Четыре типа базовых моделей уже установлены</h2>
275
275
  </div>
276
- <p>Модели работают на самом комплексе. Их можно дообучить на данных предприятия — с проверкой результатов учётным ядром.</p>
276
+ <p>Интегрированные локальные модели предназначены для базовых ежедневных операций внутри контура. Если требуются более мощные модели ИИ, их можно подключить через API из любого источника.</p>
277
277
  </div>
278
278
  <div class="grid g4">${renderModels()}</div>
279
+ <div class="card reveal" style="display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap">
280
+ <div>
281
+ <span class="kicker">Подключение мощных моделей ИИ по API</span>
282
+ <h3 class="h3" style="margin:4px 0 6px">Нужны более мощные модели? Подключайте из любого источника</h3>
283
+ <p class="note" style="margin:0">Предустановленные на комплексе модели закрывают базовые операции учёта, голоса, иллюстраций и поиска без внешних каналов. Для сложной аналитики, больших контекстов и специализированных агентов вы можете в один клик подключить по стандартному API модели из любого источника: внешних облачных провайдеров, корпоративного GPU-кластера или коммерческого API регионального партнёра.</p>
284
+ </div>
285
+ <button class="btn btn-ghost btn-sm" data-stage="agents">Подробнее об ИИ-агентах →</button>
286
+ </div>
279
287
  </section>
280
288
 
281
289
  <!-- ============ ИИ-АГЕНТЫ ============ -->
@@ -333,9 +341,9 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
333
341
  <div class="sec-head">
334
342
  <div>
335
343
  <span class="kicker">Лицензии и поддержка</span>
336
- <h2 class="h2">Платите только за дополнительные места</h2>
344
+ <h2 class="h2">10 вечных лицензий + 1 год Enterprise в базе, дальше — по желанию</h2>
337
345
  </div>
338
- <p>${P.baseSeats} рабочих мест входят в комплекс навсегда. Дополнительные места — годовые, цена зависит от уровня поддержки. Считаются одновременные активные сессии, а не учётные записи.</p>
346
+ <p>${P.baseSeats} рабочих мест входят в комплекс навсегда вместе с 1-м годом поддержки уровня Enterprise. Дальнейшее продление поддержки и докупка годовых мест — по желанию. Считаются одновременные активные сессии, а не учётные записи.</p>
339
347
  </div>
340
348
  <div class="tiers">${renderTiers()}</div>
341
349
  <div class="card">
@@ -351,7 +359,7 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
351
359
  <section class="stage" id="stage-order">
352
360
  <div class="sec-head">
353
361
  <div><span class="kicker">Заказ</span><h2 class="h2">Программно-аппаратный комплекс ODANT-WORK</h2></div>
354
- <p>Сервер AMD Ryzen™ AI Max+ PRO 495 · 192 ГБ · 4 ТБ с установленной платформой, вечной лицензией на ${P.baseSeats} мест и годом поддержки. Оплата через ЮKassa с чеком по 54-ФЗ.</p>
362
+ <p>Сервер AMD Ryzen™ AI Max+ PRO 495 · 192 ГБ · 4 ТБ с установленной платформой, вечной лицензией на ${P.baseSeats} мест и 1 годом поддержки Enterprise (дальше — по желанию). Оплата через ЮKassa с чеком по 54-ФЗ.</p>
355
363
  </div>
356
364
  <div class="split">
357
365
  <div class="card">
@@ -606,89 +614,130 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
606
614
  <div class="stat"><b id="admStatTickets">0</b><span id="admStatTicketsSub">открытых тикетов SLA</span></div>
607
615
  </div>
608
616
 
609
- <div class="card">
610
- <div class="sec-head" style="margin-bottom:14px">
611
- <div>
612
- <span class="kicker">Выпуск сертификата Ed25519</span>
613
- <h3 class="h3" style="margin:4px 0 0">Выпустить базовую лицензию комплекса (${P.baseSeats} вечных мест + 1 год поддержки)</h3>
617
+ <div class="admin-workspace" id="adminWorkspace">
618
+ <!-- Левая вкладка ИИ-агента и навигации админки -->
619
+ <aside class="admin-left">
620
+ <nav class="admin-nav" aria-label="Вкладки админ-панели">
621
+ <button class="adm-tab on" data-adm-tab="all">${icon('ai')}<span>ИИ-агент + Все разделы</span><i class="live-dot"></i></button>
622
+ <button class="adm-tab" data-adm-tab="agent">${icon('agents')}<span>Только ИИ-агент</span></button>
623
+ <button class="adm-tab" data-adm-tab="licenses">${icon('key')}<span>Лицензии (${P.baseSeats} вечн. + доп.)</span></button>
624
+ <button class="adm-tab" data-adm-tab="orders">${icon('cart')}<span>Заказы ЮKassa</span></button>
625
+ <button class="adm-tab" data-adm-tab="partners">${icon('map')}<span>Франшизы и партнёры</span></button>
626
+ <button class="adm-tab" data-adm-tab="tickets">${icon('chat')}<span>Тикеты SLA (MAX·TG·VK)</span></button>
627
+ </nav>
628
+
629
+ <div class="card adm-agent-card" id="admAgentCard">
630
+ <div class="ai-head" style="padding:0 0 12px;margin-bottom:10px">
631
+ <img src="/assets/odant-logo.png" alt="">
632
+ <div>
633
+ <b>ИИ-агент администратора</b>
634
+ <span>● управление реестром · в сети</span>
635
+ </div>
636
+ </div>
637
+ <div class="ai-msgs adm-ai-msgs" id="admAiMessages">
638
+ <div class="msg bot">Здравствуйте! Я ИИ-агент панели администратора ODANT-WORK. Помогу проанализировать реестр лицензий (${P.baseSeats} вечных мест + 1 год Enterprise), выпустить сертификат Ed25519, провести оплату заказов ЮKassa или проверить квоты франшиз по регионам РФ.</div>
639
+ </div>
640
+ <div class="ai-sugg" style="padding:8px 0;border-top:1px solid var(--line)">
641
+ <button data-adm-ask="Сводка по серверу лицензий">Сводка реестра</button>
642
+ <button data-adm-ask="Выпусти базовую лицензию для ООО «АгроТех» ИНН 6234001122">+ Выпустить лицензию</button>
643
+ <button data-adm-ask="Проведи оплату ожидающих заказов">Провести оплаты</button>
644
+ <button data-adm-ask="Сколько вакантных франшиз в Татарстане?">Квота Татарстана</button>
645
+ <button data-adm-ask="Какие модели входят в комплекс и как подключить мощные ИИ по API?">Модели и API</button>
646
+ </div>
647
+ <div class="ai-input" style="padding:10px 0 0">
648
+ <input type="text" id="admAiInput" placeholder="Команда или вопрос ИИ-агенту админки…" autocomplete="off">
649
+ <button class="btn btn-primary" data-action="adm-ai-send" aria-label="Отправить">${icon('send')}</button>
650
+ </div>
614
651
  </div>
615
- </div>
616
- <div class="grid g4" style="align-items:end">
617
- <div class="field" style="margin:0"><label for="admLicOrg">Организация</label><input type="text" id="admLicOrg" value="АО «РязаньАгроПром»"></div>
618
- <div class="field" style="margin:0"><label for="admLicInn">ИНН</label><input type="text" id="admLicInn" value="6234109876"></div>
619
- <div class="field" style="margin:0"><label for="admLicEmail">Email клиента</label><input type="email" id="admLicEmail" value="it@ryazan-agro.ru"></div>
620
- <div class="field" style="margin:0"><label for="admLicHwid">HWID комплекса (необязательно)</label><input type="text" id="admLicHwid" placeholder="WORK-HWID-..."></div>
621
- </div>
622
- <div style="display:flex;align-items:center;gap:14px;margin-top:14px;flex-wrap:wrap">
623
- <button class="btn btn-primary btn-sm" data-action="adm-issue-lic">${icon('key')} Выпустить и подписать сертификат</button>
624
- <span class="small muted" id="admIssueStatus"></span>
625
- </div>
626
- </div>
652
+ </aside>
627
653
 
628
- <div class="card">
629
- <h3 class="h3" style="margin:0 0 12px">Выпущенные лицензии комплексов ODANT-WORK</h3>
630
- <div class="table-wrap">
631
- <table class="t">
632
- <thead><tr>
633
- <th>Лицензия / Код бота</th>
634
- <th>Организация / ИНН</th>
635
- <th>Привязка оборудования</th>
636
- <th class="num">Сессии (вечные + доп.)</th>
637
- <th>SLA и срок</th>
638
- <th>Сертификат</th>
639
- </tr></thead>
640
- <tbody id="admLicensesBody"><tr><td colspan="6" class="muted">Загрузка…</td></tr></tbody>
641
- </table>
642
- </div>
643
- </div>
654
+ <!-- Правая область разделов админки -->
655
+ <div class="admin-main" id="adminMain">
656
+ <div class="card" data-adm-sec="licenses">
657
+ <div class="sec-head" style="margin-bottom:14px">
658
+ <div>
659
+ <span class="kicker">Выпуск сертификата Ed25519</span>
660
+ <h3 class="h3" style="margin:4px 0 0">Выпустить базовую лицензию комплекса (${P.baseSeats} вечных лицензий + 1 год поддержки Enterprise, далее — по желанию)</h3>
661
+ </div>
662
+ </div>
663
+ <div class="grid g4" style="align-items:end">
664
+ <div class="field" style="margin:0"><label for="admLicOrg">Организация</label><input type="text" id="admLicOrg" value="АО «РязаньАгроПром»"></div>
665
+ <div class="field" style="margin:0"><label for="admLicInn">ИНН</label><input type="text" id="admLicInn" value="6234109876"></div>
666
+ <div class="field" style="margin:0"><label for="admLicEmail">Email клиента</label><input type="email" id="admLicEmail" value="it@ryazan-agro.ru"></div>
667
+ <div class="field" style="margin:0"><label for="admLicHwid">HWID комплекса (необязательно)</label><input type="text" id="admLicHwid" placeholder="WORK-HWID-..."></div>
668
+ </div>
669
+ <div style="display:flex;align-items:center;gap:14px;margin-top:14px;flex-wrap:wrap">
670
+ <button class="btn btn-primary btn-sm" data-action="adm-issue-lic">${icon('key')} Выпустить и подписать сертификат</button>
671
+ <span class="small muted" id="admIssueStatus"></span>
672
+ </div>
673
+ </div>
644
674
 
645
- <div class="card">
646
- <h3 class="h3" style="margin:0 0 12px">Заказы и платежи ЮKassa</h3>
647
- <div class="table-wrap">
648
- <table class="t">
649
- <thead><tr>
650
- <th>Заказ / Платёж</th>
651
- <th>Назначение</th>
652
- <th>Плательщик</th>
653
- <th class="num">Сумма</th>
654
- <th>Статус</th>
655
- <th>Действия</th>
656
- </tr></thead>
657
- <tbody id="admOrdersBody"><tr><td colspan="6" class="muted">Загрузка…</td></tr></tbody>
658
- </table>
659
- </div>
660
- </div>
675
+ <div class="card" data-adm-sec="licenses">
676
+ <h3 class="h3" style="margin:0 0 12px">Выпущенные лицензии комплексов ODANT-WORK</h3>
677
+ <div class="table-wrap">
678
+ <table class="t">
679
+ <thead><tr>
680
+ <th>Лицензия / Код бота</th>
681
+ <th>Организация / ИНН</th>
682
+ <th>Привязка оборудования</th>
683
+ <th class="num">Сессии (вечные + доп.)</th>
684
+ <th>SLA и срок</th>
685
+ <th>Сертификат</th>
686
+ </tr></thead>
687
+ <tbody id="admLicensesBody"><tr><td colspan="6" class="muted">Загрузка…</td></tr></tbody>
688
+ </table>
689
+ </div>
690
+ </div>
661
691
 
662
- <div class="card">
663
- <h3 class="h3" style="margin:0 0 12px">Резервы региональных франшиз и действующие партнёры</h3>
664
- <div class="table-wrap">
665
- <table class="t">
666
- <thead><tr>
667
- <th>ID / Тип</th>
668
- <th>Субъект РФ</th>
669
- <th>Компания / ИНН</th>
670
- <th>Условия / Скидка</th>
671
- <th>Статус</th>
672
- <th>Действия</th>
673
- </tr></thead>
674
- <tbody id="admPartnersBody"><tr><td colspan="6" class="muted">Загрузка…</td></tr></tbody>
675
- </table>
676
- </div>
677
- </div>
692
+ <div class="card" data-adm-sec="orders">
693
+ <h3 class="h3" style="margin:0 0 12px">Заказы и платежи ЮKassa</h3>
694
+ <div class="table-wrap">
695
+ <table class="t">
696
+ <thead><tr>
697
+ <th>Заказ / Платёж</th>
698
+ <th>Назначение</th>
699
+ <th>Плательщик</th>
700
+ <th class="num">Сумма</th>
701
+ <th>Статус</th>
702
+ <th>Действия</th>
703
+ </tr></thead>
704
+ <tbody id="admOrdersBody"><tr><td colspan="6" class="muted">Загрузка…</td></tr></tbody>
705
+ </table>
706
+ </div>
707
+ </div>
678
708
 
679
- <div class="card">
680
- <h3 class="h3" style="margin:0 0 12px">Обращения техподдержки (MAX · Telegram · VK)</h3>
681
- <div class="table-wrap">
682
- <table class="t">
683
- <thead><tr>
684
- <th>Тикет / Канал</th>
685
- <th>Клиент / Лицензия</th>
686
- <th>SLA</th>
687
- <th>Переписка</th>
688
- <th>Ответ инженера</th>
689
- </tr></thead>
690
- <tbody id="admTicketsBody"><tr><td colspan="5" class="muted">Загрузка…</td></tr></tbody>
691
- </table>
709
+ <div class="card" data-adm-sec="partners">
710
+ <h3 class="h3" style="margin:0 0 12px">Резервы региональных франшиз и действующие партнёры</h3>
711
+ <div class="table-wrap">
712
+ <table class="t">
713
+ <thead><tr>
714
+ <th>ID / Тип</th>
715
+ <th>Субъект РФ</th>
716
+ <th>Компания / ИНН</th>
717
+ <th>Условия / Скидка</th>
718
+ <th>Статус</th>
719
+ <th>Действия</th>
720
+ </tr></thead>
721
+ <tbody id="admPartnersBody"><tr><td colspan="6" class="muted">Загрузка…</td></tr></tbody>
722
+ </table>
723
+ </div>
724
+ </div>
725
+
726
+ <div class="card" data-adm-sec="tickets">
727
+ <h3 class="h3" style="margin:0 0 12px">Обращения техподдержки (MAX · Telegram · VK)</h3>
728
+ <div class="table-wrap">
729
+ <table class="t">
730
+ <thead><tr>
731
+ <th>Тикет / Канал</th>
732
+ <th>Клиент / Лицензия</th>
733
+ <th>SLA</th>
734
+ <th>Переписка</th>
735
+ <th>Ответ инженера</th>
736
+ </tr></thead>
737
+ <tbody id="admTicketsBody"><tr><td colspan="5" class="muted">Загрузка…</td></tr></tbody>
738
+ </table>
739
+ </div>
740
+ </div>
692
741
  </div>
693
742
  </div>
694
743
  </section>
@@ -697,13 +746,13 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
697
746
  <div class="footer">
698
747
  <div>
699
748
  <div class="brand" style="cursor:default"><img src="/assets/odant-logo.png" alt="" style="width:34px;height:34px"><span class="brand-name">ODANT<b>·</b>WORK</span></div>
700
- <p style="margin-top:12px">Программно-аппаратный комплекс: встроенная система учёта, документооборот, управление процессами, ИИ-консультант и мультиагентная система внутри контура предприятия.</p>
749
+ <p style="margin-top:12px">Программно-аппаратный комплекс: встроенная система учёта, документооборот, управление процессами, ИИ-консультант и мультиагентная система. Интегрированные модели — для базовых операций, более мощные ИИ подключаются через API из любого источника.</p>
701
750
  </div>
702
751
  <div>
703
752
  <h4>Комплекс</h4>
704
753
  <p>AMD Ryzen™ AI Max+ PRO 495 · Radeon™ 8065S</p>
705
754
  <p>192 ГБ LPDDR5x-8533 · SSD 4 ТБ</p>
706
- <p>От ${rub(P.basePriceRub)} · ${P.baseSeats} вечных мест</p>
755
+ <p>От ${rub(P.basePriceRub)} · ${P.baseSeats} вечных лицензий + 1 год Enterprise</p>
707
756
  </div>
708
757
  <div>
709
758
  <h4>${esc(VENDOR_CONTACTS.company)}</h4>
package/src/store.js CHANGED
@@ -138,7 +138,7 @@ export class LicenseStore {
138
138
  perpetualBase: true,
139
139
  addonSubscriptions: Array.isArray(existing?.addonSubscriptions) ? existing.addonSubscriptions : [],
140
140
  support: {
141
- tier: existing?.support?.tier || includedSupportTier || 'business',
141
+ tier: existing?.support?.tier || includedSupportTier || PAK_PRODUCT.includedSupportTier || 'enterprise',
142
142
  includedUntil: existing?.support?.includedUntil || (issuedAt + ONE_YEAR_MS),
143
143
  ticketsUsed: Number(existing?.support?.ticketsUsed || 0),
144
144
  bindCode,
@@ -247,9 +247,9 @@ export class LicenseStore {
247
247
  const baseSupportActive = baseSupportUntil > now;
248
248
 
249
249
  let addonSeats = 0;
250
- let highestTier = baseSupportActive ? (lic.support?.tier || 'business') : null;
250
+ let highestTier = baseSupportActive ? (lic.support?.tier || PAK_PRODUCT.includedSupportTier || 'enterprise') : null;
251
251
  let ticketsQuota = baseSupportActive
252
- ? baseSeats * (SLA_TIERS[lic.support?.tier || 'business']?.ticketsLimitPerSeatYear || 48)
252
+ ? baseSeats * (SLA_TIERS[lic.support?.tier || PAK_PRODUCT.includedSupportTier || 'enterprise']?.ticketsLimitPerSeatYear || 9999)
253
253
  : 0;
254
254
 
255
255
  const rank = { start: 1, business: 2, enterprise: 3 };
package/src/yookassa.js CHANGED
@@ -39,7 +39,7 @@ export class YooKassaService {
39
39
  buildYooKassaPaymentPayload(order) {
40
40
  const valueStr = Number(order.amountRub || 0).toFixed(2);
41
41
  const description = order.includeBasePak
42
- ? `Заказ ${order.orderId}: ПАК WORK (5 вечных мест) + доп. лицензии (${order.addonSeats} мест, ${order.tierName})`
42
+ ? `Заказ ${order.orderId}: ПАК ODANT-WORK (10 вечных лицензий + 1 год Enterprise) + доп. места (${order.addonSeats} мест, ${order.tierName})`
43
43
  : `Заказ ${order.orderId}: Годовая лицензия WORK (${order.addonSeats} польз., уровень ${order.tierName}, ${order.years} г.)`;
44
44
 
45
45
  return {