work-license-server 1.0.7 → 1.0.9

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/src/landing.js CHANGED
@@ -180,122 +180,41 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
180
180
 
181
181
  <nav class="tabs" id="tabs" aria-label="Разделы">
182
182
  <span class="tab-ind" id="tabInd"></span>
183
- <button class="tab active" data-stage="product">Платформа</button>
184
- <button class="tab" data-stage="agents">ИИ-агенты</button>
185
- <button class="tab" data-stage="pricing">Тарифы</button>
186
- <button class="tab" data-stage="order">Комплекс</button>
187
- <button class="tab" id="tileAddons" data-stage="addons" style="display:none;">Личный кабинет</button>
183
+ <button class="tab active" data-stage="product">О платформе</button>
184
+ <button class="tab" data-stage="clients">Клиентам</button>
188
185
  <button class="tab" data-stage="franchise">Франшиза</button>
189
- <button class="tab" data-stage="support">Поддержка</button>
186
+ <button class="tab" id="tileAddons" data-stage="addons" style="display:none;">Личный кабинет</button>
190
187
  </nav>
191
188
 
192
189
  <div class="top-actions">
193
190
  <button class="icon-btn" id="btnTheme" onclick="toggleTheme()" title="Светлая / тёмная тема" aria-label="Переключить тему">${icon('sun')}</button>
194
- <button class="btn btn-ghost btn-sm" data-action="open-oferta" title="Публичный договор-оферта ODANT-WORK">${icon('docs')}<span>Оферта</span></button>
195
- <button class="btn btn-ghost btn-sm" id="btnAuth" data-action="auth">${icon('user')}<span id="btnAuthText">Войти / Регистрация</span></button>
196
- <button class="btn btn-primary btn-sm" data-stage="order">Заказать</button>
191
+ <button class="btn btn-primary btn-sm" id="btnAuth" data-action="auth">${icon('user')}<span id="btnAuthText">Войти / Регистрация</span></button>
197
192
  </div>
198
193
  </header>
199
194
 
200
- <!-- ============ ПЛАТФОРМА ============ -->
195
+ <!-- ============ О ПЛАТФОРМЕ (чистое описание без визуального шума) ============ -->
201
196
  <section class="stage active" id="stage-product">
202
- <div class="hero">
197
+ <div class="sec-head" style="margin-top:4px">
203
198
  <div>
204
- <span class="pill"><span class="dot"></span>Программно-аппаратный комплекс на AMD Ryzen™ AI Max+ PRO 495</span>
205
- <h1 class="h1">Учёт, документооборот, процессы и&nbsp;<span class="grad">приватный ИИ</span> — в&nbsp;одном комплексе</h1>
206
- <p class="lead">ODANT-WORK — вычислительный центр предприятия, который ставится в серверную и работает автономно. Встроенная система учёта, единое пространство документов и задач, ИИ-консультант в каждой форме и команда ИИ-агентов — без облаков и передачи данных наружу.</p>
207
- <div class="hero-cta">
208
- <button class="btn btn-primary btn-lg" data-stage="order">Заказать комплекс · ${rub(P.basePriceRub)}</button>
209
- <button class="btn btn-ghost btn-lg" data-stage="agents">${icon('agents')} Как работают ИИ-агенты</button>
210
- </div>
211
- <div class="trust">
212
- <span>${icon('shield')} Данные не покидают контур</span>
213
- <span>${icon('key')} ${P.baseSeats} вечных лицензий</span>
214
- <span>${icon('check')} 1 год поддержки Enterprise (далее — по желанию)</span>
215
- </div>
216
- </div>
217
- <div class="showcase" aria-hidden="true">
218
- <div class="showcase-ring"></div>
219
- <div class="showcase-ring r2"></div>
220
- <div class="showcase-logo"><img src="/assets/odant-logo.png" alt=""></div>
221
- <div class="chipf c1"><b>132 TOPS</b>ИИ-производительность</div>
222
- <div class="chipf c2"><b>192 ГБ</b>единой памяти LPDDR5x</div>
223
- <div class="chipf c3"><b>16 / 32</b>ядра / потока Zen 5</div>
224
- <div class="chipf c4"><b>4 ТБ</b>NVMe PCIe® 4.0</div>
199
+ <span class="kicker">Платформа цифровой организации</span>
200
+ <h1 class="h2" style="font-size:clamp(1.9rem,3vw,2.6rem);margin:4px 0 0">ODANT-WORK — учёт, документооборот, процессы и&nbsp;<span class="grad">приватный ИИ</span></h1>
225
201
  </div>
202
+ <p>Все данные предприятия — справочники, проводки, документы, переписка и задачи — живут в единой модели с общими правами доступа и работают автономно в вашем контуре без передачи данных наружу.</p>
226
203
  </div>
227
204
 
228
- <div class="kpis reveal">
229
- <div class="kpi"><b data-count="132">132<small>TOPS</small></b><span>общая ИИ-производительность, NPU 55 TOPS</span></div>
230
- <div class="kpi"><b data-count="61">61<small>TFLOPS</small></b><span>FP16 на графике Radeon™ 8065S</span></div>
231
- <div class="kpi"><b data-count="192">192<small>ГБ</small></b><span>единая память для моделей и данных</span></div>
232
- <div class="kpi"><b data-count="5.2" data-dec="1">5.2<small>ГГц</small></b><span>максимальная частота, 80 МБ кэша</span></div>
233
- </div>
234
-
235
- <div class="sec-head reveal">
236
- <div>
237
- <span class="kicker">Что внутри</span>
238
- <h2 class="h2">Одна система вместо зоопарка программ</h2>
239
- </div>
240
- <p>Все данные предприятия — справочники, проводки, документы, переписка и задачи — живут в одной модели с едиными правами доступа. Поэтому ИИ видит полную картину и действует по правилам вашей организации.</p>
241
- </div>
242
205
  <div class="bento">
243
206
  <article class="card spot reveal wide"><div class="ico">${icon('ledger')}</div><h3 class="h3">Встроенная система учёта</h3><p class="note">План счетов, справочники и аналитики, явная двойная запись с исправлением только через сторно. Оборотно-сальдовая ведомость, анализ, обороты и журнал проводок — в едином табличном стандарте.</p></article>
244
- <article class="card spot reveal wide"><div class="ico">${icon('docs')}</div><h3 class="h3">Документооборот</h3><p class="note">Единое файловое пространство подразделений, первичные документы по шаблонам, согласования, версии и восстановление файлов. Офисные форматы открываются прямо в системе.</p></article>
245
- <article class="card spot reveal"><div class="ico">${icon('flow')}</div><h3 class="h3">Управление процессами</h3><p class="note">Структура подразделений, роли, рабочие места, поручения со сроками и журнал всех фактов.</p></article>
207
+ <article class="card spot reveal wide"><div class="ico">${icon('docs')}</div><h3 class="h3">Документооборот и офис</h3><p class="note">Единое файловое пространство подразделений, первичные документы по шаблонам, согласования, версии и восстановление файлов. Таблицы, тексты и презентации редактируются прямо в системе.</p></article>
208
+ <article class="card spot reveal"><div class="ico">${icon('flow')}</div><h3 class="h3">Управление процессами</h3><p class="note">Структура подразделений, роли BOSS и USER, рабочие места, поручения со сроками и журнал всех фактов.</p></article>
246
209
  <article class="card spot reveal"><div class="ico">${icon('ai')}</div><h3 class="h3">ИИ-консультант</h3><p class="note">Кнопка «?» в любой форме и голосовой диалог: объяснит, найдёт, посчитает и проведёт операцию.</p></article>
247
210
  <article class="card spot reveal"><div class="ico">${icon('agents')}</div><h3 class="h3">Мультиагентная система</h3><p class="note">${AI_PLATFORM.agents.length} ролевых ИИ-агентов и ${AI_PLATFORM.skillsCount}+ навыков работают параллельно и строго в рамках прав.</p></article>
248
211
  </div>
249
212
 
250
- <div class="card card-glow reveal">
251
- <div class="spec-wrap">
252
- <div>
253
- <span class="kicker">Паспорт комплекса</span>
254
- <h2 class="h2">ODANT-WORK · ${esc(P.sku)}</h2>
255
- <div class="price-big">${rub(P.basePriceRub)}</div>
256
- <p class="note">Вечная лицензия на ${P.baseSeats} одновременных рабочих сессий, привязанная к оборудованию, и ${P.includedSupportYears} год максимальной поддержки уровня Enterprise (24/7, реакция ≤ 15 мин) — в комплекте. Дальнейшее продление поддержки и докупка мест — по желанию.</p>
257
- <div class="hero-cta" style="margin-top:20px">
258
- <button class="btn btn-primary" data-stage="order">Оформить заказ</button>
259
- <button class="btn btn-ghost" data-stage="pricing">Тарифы поддержки</button>
260
- </div>
261
- </div>
262
- <dl class="spec">
263
- <div><dt>Процессор</dt><dd>${esc(H.cpu)}</dd></div>
264
- <div><dt>Графика</dt><dd>${esc(H.gpu)}</dd></div>
265
- <div><dt>ИИ-ускорение</dt><dd>${esc(H.aiEngine)}</dd></div>
266
- <div><dt>Память</dt><dd>${esc(H.ram)}</dd></div>
267
- <div><dt>Накопитель</dt><dd>${esc(H.storage)}</dd></div>
268
- <div><dt>Защита</dt><dd>${esc(H.security)}</dd></div>
269
- <div><dt>Лицензия</dt><dd>По одновременным сессиям, привязка к HWID и ключу узла</dd></div>
270
- </dl>
271
- </div>
272
- </div>
273
-
274
- <div class="sec-head reveal">
275
- <div>
276
- <span class="kicker">Приватный и гибридный ИИ</span>
277
- <h2 class="h2">Четыре типа базовых моделей уже установлены</h2>
278
- </div>
279
- <p>Интегрированные локальные модели предназначены для базовых ежедневных операций внутри контура. Если требуются более мощные модели ИИ, их можно подключить через API из любого источника.</p>
280
- </div>
281
- <div class="grid g4">${renderModels()}</div>
282
- <div class="card reveal" style="display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap">
213
+ <div class="agents-hero" style="margin-top:12px">
283
214
  <div>
284
- <span class="kicker">Подключение мощных моделей ИИ по API</span>
285
- <h3 class="h3" style="margin:4px 0 6px">Нужны более мощные модели? Подключайте из любого источника</h3>
286
- <p class="note" style="margin:0">Предустановленные на комплексе модели закрывают базовые операции учёта, голоса, иллюстраций и поиска без внешних каналов. Для сложной аналитики, больших контекстов и специализированных агентов вы можете в один клик подключить по стандартному API модели из любого источника: внешних облачных провайдеров, корпоративного GPU-кластера или коммерческого API регионального партнёра.</p>
287
- </div>
288
- <button class="btn btn-ghost btn-sm" data-stage="agents">Подробнее об ИИ-агентах →</button>
289
- </div>
290
- </section>
291
-
292
- <!-- ============ ИИ-АГЕНТЫ ============ -->
293
- <section class="stage" id="stage-agents">
294
- <div class="agents-hero">
295
- <div>
296
- <span class="kicker">ИИ-консультант</span>
297
- <h2 class="h2" style="font-size:clamp(2rem,3.6vw,3.2rem)">Помощник, который <span class="grad">знает вашу организацию</span></h2>
298
- <p class="lead">Консультант встроен в каждую форму системы. Он видит открытый документ, понимает структуру учёта и права пользователя — и не просто отвечает, а выполняет работу.</p>
215
+ <span class="kicker">ИИ-консультант в каждой форме</span>
216
+ <h2 class="h2">Помощник, который <span class="grad">знает вашу организацию</span></h2>
217
+ <p class="lead">Консультант видит открытый документ, понимает структуру учёта и права пользователя — и не просто отвечает на вопросы, а выполняет работу.</p>
299
218
  <ul class="list">${AI_PLATFORM.consultant.features.map(f => `<li>${esc(f)}</li>`).join('')}</ul>
300
219
  </div>
301
220
  <div class="mock" id="consultantMock">
@@ -324,95 +243,122 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
324
243
  </div>
325
244
  </div>
326
245
 
327
- <div class="sec-head reveal">
328
- <div><span class="kicker">Оркестрация</span><h2 class="h2">Как агенты работают вместе</h2></div>
329
- <p>Параллельность и скорость — без потери контроля: права, подтверждения и журнал действий заложены в код платформы, а не в текст подсказки.</p>
330
- </div>
331
246
  <div class="grid g3">${renderOrchestration()}</div>
332
247
 
333
- <div class="card reveal">
334
- <div class="sec-head" style="margin-bottom:18px">
335
- <div><span class="kicker">Навыки</span><h2 class="h2">${AI_PLATFORM.skillsCount}+ проверенных рецептов работы</h2></div>
336
- <p>Навык — описанный порядок решения типовой задачи. Удачный результат агента с одобрения сотрудника сохраняется как новый навык подразделения.</p>
337
- </div>
338
- <div class="skills">${AI_PLATFORM.skills.map(s => `<span>${esc(s)}</span>`).join('')}</div>
339
- </div>
340
- </section>
341
-
342
- <!-- ============ ТАРИФЫ ============ -->
343
- <section class="stage" id="stage-pricing">
344
- <div class="sec-head">
248
+ <div class="sec-head reveal">
345
249
  <div>
346
- <span class="kicker">Лицензии и поддержка</span>
347
- <h2 class="h2">10 вечных лицензий + 1 год Enterprise в базе, дальше — по желанию</h2>
250
+ <span class="kicker">Приватный и гибридный ИИ</span>
251
+ <h2 class="h2">Четыре типа базовых моделей уже установлены</h2>
348
252
  </div>
349
- <p>${P.baseSeats} рабочих мест входят в комплекс навсегда вместе с 1-м годом поддержки уровня Enterprise. Дальнейшее продление поддержки и докупка годовых мест — по желанию. Считаются одновременные активные сессии, а не учётные записи.</p>
253
+ <p>Интегрированные локальные модели предназначены для базовых ежедневных операций внутри контура. Если требуются более мощные модели ИИ, их можно подключить через API из любого источника.</p>
350
254
  </div>
351
- <div class="tiers">${renderTiers()}</div>
352
- <div class="card">
353
- <div class="sec-head" style="margin-bottom:16px">
354
- <div><span class="kicker">Скидка от количества</span><h3 class="h3" style="font-size:20px">Чем больше мест — тем ниже цена каждого</h3></div>
355
- <p class="small">Скидка применяется ко всему пакету докупаемых мест.</p>
255
+ <div class="grid g4">${renderModels()}</div>
256
+
257
+ <div class="card card-glow reveal">
258
+ <div class="spec-wrap">
259
+ <div>
260
+ <span class="kicker">Аппаратное исполнение или собственная инфраструктура</span>
261
+ <h2 class="h2">ODANT-WORK · ${esc(P.sku)}</h2>
262
+ <div class="price-big">${rub(P.basePriceRub)}</div>
263
+ <p class="note">Вечная лицензия на ${P.baseSeats} одновременных рабочих сессий и ${P.includedSupportYears} год поддержки уровня Enterprise — в комплекте с сервером (или ${rub(P.baseLicensePriceRub || 4_000_000)} за базовую лицензию без оборудования при установке через <code>npm</code>).</p>
264
+ <div class="hero-cta" style="margin-top:20px">
265
+ <button class="btn btn-primary" data-stage="clients">Перейти к покупке →</button>
266
+ <button class="btn btn-ghost" data-stage="franchise">Региональная франшиза</button>
267
+ </div>
268
+ </div>
269
+ <dl class="spec">
270
+ <div><dt>Процессор</dt><dd>${esc(H.cpu)}</dd></div>
271
+ <div><dt>Графика</dt><dd>${esc(H.gpu)}</dd></div>
272
+ <div><dt>ИИ-ускорение</dt><dd>${esc(H.aiEngine)}</dd></div>
273
+ <div><dt>Память</dt><dd>${esc(H.ram)}</dd></div>
274
+ <div><dt>Накопитель</dt><dd>${esc(H.storage)}</dd></div>
275
+ <div><dt>Защита</dt><dd>${esc(H.security)}</dd></div>
276
+ <div><dt>Лицензия</dt><dd>По одновременным сессиям, файлы .lic.json в #system</dd></div>
277
+ </dl>
356
278
  </div>
357
- ${renderLadder('ladderPricing')}
358
279
  </div>
359
280
  </section>
360
281
 
361
- <!-- ============ ЗАКАЗ КОМПЛЕКСА ============ -->
362
- <section class="stage" id="stage-order">
363
- <div class="sec-head">
364
- <div><span class="kicker">Заказ</span><h2 class="h2">Программно-аппаратный комплекс ODANT-WORK</h2></div>
365
- <p>Сервер AMD Ryzen™ AI Max+ PRO 495 · 192 ГБ · 4 ТБ с установленной платформой, вечной лицензией на ${P.baseSeats} мест и 1 годом поддержки Enterprise (дальше — по желанию). Оплата через ЮKassa с чеком по 54-ФЗ.</p>
366
- </div>
367
- <div class="card oferta-banner" data-oferta-banner></div>
282
+ <!-- ============ КЛИЕНТАМ: СРАЗУ КАЛЬКУЛЯТОР СВЕРХУ ============ -->
283
+ <section class="stage" id="stage-clients">
368
284
  <div class="split">
369
285
  <div class="card">
370
286
  <div class="field">
371
- <label>Вариант базовой поставки</label>
287
+ <label>1. Базовая постоянная лицензия (10 вечных пользователей + ИИ-агенты)</label>
372
288
  <div class="seg" id="segPakVariant">
373
- <button data-variant="pak" class="on">Комплект с машиной<small>${rub(P.basePriceRub)} · железо + 10 мест</small></button>
374
- <button data-variant="license_only">Базовая лицензия без железа<small>${rub(P.baseLicensePriceRub || 4_000_000)} · 10 вечных мест</small></button>
289
+ <button data-variant="pak" class="on">С машинкой (ПАК)<small>${rub(P.basePriceRub)} · сервер + 10 вечных мест</small></button>
290
+ <button data-variant="license_only">Без машинки (лицензия)<small>${rub(P.baseLicensePriceRub || 4_000_000)} · 10 вечных мест</small></button>
291
+ <button data-variant="addons_only">Только доп. юзеры<small>0 ₽ · базовая уже есть</small></button>
375
292
  </div>
376
293
  </div>
377
- <div class="row2">
378
- <div class="field"><label for="inpPakOrg">Организация</label><input type="text" id="inpPakOrg" value="ООО «Маслоторг»"></div>
379
- <div class="field"><label for="inpPakInn">ИНН</label><input type="text" id="inpPakInn" value="7701234567" inputmode="numeric"></div>
380
- </div>
381
- <div class="field"><label for="inpPakEmail">Email для счёта и личного кабинета</label><input type="email" id="inpPakEmail" value="admin@maslotorg.ru"></div>
382
294
  <div class="field">
383
- <label>Дополнительные годовые места сразу в поставку: <b id="lblPakSeats" style="color:var(--text)">0</b></label>
384
- <input type="range" id="inpPakExtraSeats" min="0" max="150" value="0">
295
+ <label>2. Дополнительные временные пользователи (сверх 10 вечных)</label>
296
+ <div style="display:flex;align-items:center;gap:16px">
297
+ <input type="range" id="inpPakExtraSeats" min="0" max="150" value="0" style="flex:1">
298
+ <input type="number" id="inpPakExtraSeatsNum" min="0" max="2000" value="0" style="width:110px">
299
+ </div>
385
300
  </div>
386
- <div class="field">
387
- <label>Уровень поддержки дополнительных мест</label>
388
- <div class="seg" id="segPakTier">${Object.values(SLA_TIERS).map(t => `<button data-tier="${t.id}" class="${t.id === 'business' ? 'on' : ''}">${esc(t.name)}<small>${rub(t.pricePerUserYearRub)}/год</small></button>`).join('')}</div>
301
+ <div class="row2">
302
+ <div class="field">
303
+ <label>Уровень поддержки доп. мест</label>
304
+ <div class="seg" id="segPakTier">${Object.values(SLA_TIERS).map(t => `<button data-tier="${t.id}" class="${t.id === 'business' ? 'on' : ''}">${esc(t.name)}<small>${rub(t.pricePerUserYearRub)}/год</small></button>`).join('')}</div>
305
+ </div>
306
+ <div class="field">
307
+ <label for="inpPakYears">Срок временных мест, лет (1–5)</label>
308
+ <input type="number" id="inpPakYears" min="1" max="5" value="1">
309
+ </div>
389
310
  </div>
390
- <p class="note muted" style="margin:6px 0 0">Уже есть комплекс? <a href="#" data-action="addons">Войдите в кабинет и подпишите оферту</a>, чтобы докупить места со скидкой или получить токен привязки.</p>
311
+ <div class="field" style="margin-top:6px"><label>Скидка от количества дополнительных мест</label>${renderLadder('ladderPricing')}</div>
391
312
  </div>
313
+
392
314
  <div class="card summary card-glow">
393
- <span class="kicker">Итого</span>
315
+ <span class="kicker">Ваш выбор</span>
394
316
  <div class="sum-total" id="lblPakTotal">${rub(P.basePriceRub)}</div>
317
+ <div class="small" id="lblPakDiscount" style="color:var(--ok);font-weight:650">Включает 10 вечных мест и 1 год поддержки Enterprise</div>
395
318
  <div class="sum-lines" id="lblPakBreakdown"></div>
396
- <button class="btn btn-primary btn-lg" style="width:100%" data-action="order-pak">Оформить и оплатить через ЮKassa</button>
397
- <button class="btn btn-ghost btn-sm" style="width:100%;margin-top:8px" data-action="order-pak-invoice">Выставить счёт-договор (для юр. лиц)</button>
319
+ <button class="btn btn-primary btn-lg" style="width:100%" data-action="buy-client-config">Купить</button>
320
+ <p class="small muted" style="margin:8px 0 0;text-align:center">При нажатии «Купить» заказ добавится в ваш Личный кабинет, где вы сможете оплатить его через ЮKassa или выставить счёт-договор для юр. лица.</p>
398
321
  <div class="status" id="pakOrderStatus"></div>
399
322
  </div>
400
323
  </div>
324
+
325
+ <div class="sec-head" style="margin-top:16px">
326
+ <div>
327
+ <span class="kicker">Тарифы поддержки и конкурентных мест</span>
328
+ <h2 class="h2">Условия лицензирования и SLA</h2>
329
+ </div>
330
+ <p>Сразу после установки из <code>npm</code> система работает бесплатно в режиме настройки (1 сессия BOSS, ИИ-агенты отключены). Базовая постоянная лицензия открывает <b>${P.baseSeats} вечных одновременных сессий</b> и включает агентскую ИИ-систему.</p>
331
+ </div>
332
+ <div class="tiers">${renderTiers()}</div>
401
333
  </section>
402
334
 
403
- <!-- ============ ДОКУПКА ЛИЦЕНЗИЙ И ЛИЧНЫЙ КАБИНЕТ (только после входа и подписания оферты) ============ -->
335
+ <!-- ============ ЛИЧНЫЙ КАБИНЕТ (открывается после входа / регистрации) ============ -->
404
336
  <section class="stage" id="stage-addons">
405
337
  <div class="sec-head">
406
- <div><span class="kicker">Личный кабинет владельца</span><h2 class="h2">Токен привязки, ваши лицензии и покупка</h2></div>
338
+ <div><span class="kicker">Личный кабинет владельца</span><h2 class="h2">Заказы, токен привязки и лицензии</h2></div>
407
339
  <p id="userBadgeBox"></p>
408
340
  </div>
409
- <div class="card oferta-banner" data-oferta-banner></div>
410
341
 
411
- <div class="split" style="margin-bottom:20px">
342
+ <div class="card" style="margin-bottom:20px">
343
+ <div style="display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap">
344
+ <div>
345
+ <span class="kicker">Заказы и оплата</span>
346
+ <h3 class="h3" style="margin:4px 0 0">Ваши заказы в Личном кабинете</h3>
347
+ </div>
348
+ <div style="display:flex;gap:8px;flex-wrap:wrap">
349
+ <button class="btn btn-ghost btn-sm" data-stage="clients">+ Добавить лицензию / ПАК</button>
350
+ <button class="btn btn-ghost btn-sm" data-stage="franchise">+ Зарезервировать франшизу</button>
351
+ <button class="btn btn-ghost btn-sm" data-action="refresh-cabinet">Обновить</button>
352
+ </div>
353
+ </div>
354
+ <div id="cabinetOrdersBox" style="margin-top:14px"></div>
355
+ </div>
356
+
357
+ <div class="split">
412
358
  <div class="card">
413
- <span class="kicker">Шаг 1 · Привязка вашего локального сервера WORK</span>
359
+ <span class="kicker">Привязка локального сервера WORK</span>
414
360
  <h3 class="h3" style="margin:4px 0 8px">Токен владельца (содержит URL сервера поддержки)</h3>
415
- <p class="small" style="margin:0 0 12px">Скопируйте этот токен и вставьте его в разделе <b>WORK → Лицензия</b> вашей локальной системы. По этому токену система автоматически скачивает и обновляет ваши лицензии в папку <code>#system</code>.</p>
361
+ <p class="small" style="margin:0 0 12px">Скопируйте этот токен и вставьте его в разделе <b>WORK → Лицензия</b> вашей локальной системы. По этому токену система сама скачивает и обновляет оплаченные файлы лицензий в папку <code>#system</code>.</p>
416
362
  <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
417
363
  <input type="text" id="inpCabinetHubToken" readonly placeholder="owk1..." style="flex:1;min-width:220px;font-family:monospace">
418
364
  <button class="btn btn-primary btn-sm" data-action="copy-hub-token">Скопировать токен</button>
@@ -422,72 +368,15 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
422
368
  </div>
423
369
 
424
370
  <div class="card">
425
- <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap">
426
- <div>
427
- <span class="kicker">Шаг 2 · Ваши выпущенные лицензии</span>
428
- <h3 class="h3" style="margin:4px 0 6px">Файлы лицензий в личном кабинете</h3>
429
- </div>
430
- <button class="btn btn-ghost btn-sm" data-action="refresh-cabinet">Обновить</button>
431
- </div>
371
+ <span class="kicker">Действующие лицензии (.lic.json) и франшизы</span>
372
+ <h3 class="h3" style="margin:4px 0 6px">Выпущенные файлы лицензий</h3>
432
373
  <div id="cabinetLicensesBox" class="small" style="margin-top:8px;line-height:1.55"></div>
433
374
  </div>
434
375
  </div>
435
-
436
- <div class="split">
437
- <div class="card">
438
- <span class="kicker">Шаг 3 · Приобретение базовой и дополнительных лицензий</span>
439
- <div class="field" style="margin-top:8px">
440
- <label>Базовая постоянная лицензия (10 вечных пользователей + включение ИИ-агентов)</label>
441
- <div class="seg" id="segCabinetBase">
442
- <button data-base="license_only" class="on">Базовая лицензия (без железа)<small>${rub(P.baseLicensePriceRub || 4_000_000)} · 10 вечных мест</small></button>
443
- <button data-base="pak">Комплект с машиной<small>${rub(P.basePriceRub)} · сервер + 10 мест</small></button>
444
- <button data-base="none">Только доп. временные<small>0 ₽ · базовая уже куплена</small></button>
445
- </div>
446
- </div>
447
- <div class="field">
448
- <label>Дополнительные временные места (сверх 10 вечных)</label>
449
- <div style="display:flex;align-items:center;gap:16px">
450
- <input type="range" id="rngAddonSeats" min="0" max="150" value="0" style="flex:1">
451
- <input type="number" id="inpSeats" min="0" max="2000" value="0" style="width:110px">
452
- </div>
453
- </div>
454
- <div class="field">
455
- <label>Уровень поддержки дополнительных мест</label>
456
- <div class="seg" id="segTier">${Object.values(SLA_TIERS).map(t => `<button data-tier="${t.id}" class="${t.id === 'business' ? 'on' : ''}">${esc(t.name)}<small>${rub(t.pricePerUserYearRub)} · ${esc(t.reactionLabel)}</small></button>`).join('')}</div>
457
- </div>
458
- <div class="row2">
459
- <div class="field"><label for="inpYears">Срок временных лицензий, лет (кратно годам)</label><input type="number" id="inpYears" min="1" max="5" value="1"></div>
460
- <div class="field"><label for="inpLicId">Идентификатор лицензии</label><input type="text" id="inpLicId" readonly></div>
461
- </div>
462
- <div class="field" style="margin-top:6px"><label>Скидка от количества доп. мест</label>${renderLadder('ladderAddons')}</div>
463
- </div>
464
- <div class="card summary card-glow">
465
- <span class="kicker">К оплате</span>
466
- <div class="sum-total" id="lblTotal">0 ₽</div>
467
- <div class="small" id="lblDiscountBadge" style="color:var(--ok);font-weight:650"></div>
468
- <div class="sum-lines" id="lblBreakdown"></div>
469
- <button class="btn btn-primary btn-lg" style="width:100%" data-action="order-addons">Оплатить через ЮKassa</button>
470
- <button class="btn btn-ghost btn-sm" style="width:100%;margin-top:8px" data-action="order-addons-invoice">Выставить счёт-договор (для юр. лиц)</button>
471
- <div class="status" id="orderStatus"></div>
472
- </div>
473
- </div>
474
376
  </section>
475
377
 
476
- <!-- ============ ФРАНШИЗА ============ -->
378
+ <!-- ============ ФРАНШИЗА: СРАЗУ КАРТА СВЕРХУ, ВСЯ ПРОЧАЯ ИНФОРМАЦИЯ ВНИЗУ ============ -->
477
379
  <section class="stage" id="stage-franchise">
478
- <div class="sec-head">
479
- <div><span class="kicker">Партнёрская программа</span><h2 class="h2">Региональная франшиза ODANT-WORK</h2></div>
480
- <p>Число франшиз в субъекте пропорционально населению, паушальный взнос — экономике региона. Резерв закрепляется сразу после невозвратного залога ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}, полная оплата паушального взноса — в течение 1 года.</p>
481
- </div>
482
- <div class="card oferta-banner" data-oferta-banner></div>
483
- <div class="stats">
484
- <div class="stat"><b id="statVacant">${totalVacant}</b><span>вакантных франшиз</span></div>
485
- <div class="stat"><b>${visibleRegions.length}</b><span>субъектов РФ</span></div>
486
- <div class="stat"><b>${rub(FRANCHISE_PROGRAM.baseFranchiseFeeRub)}</b><span>базовый паушальный взнос</span></div>
487
- <div class="stat"><b>${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</b><span>залог за резерв</span></div>
488
- <div class="stat"><b>${FRANCHISE_PROGRAM.minDiscountPercent}→${FRANCHISE_PROGRAM.maxDiscountPercent}%</b><span>скидка партнёра от объёма</span></div>
489
- </div>
490
-
491
380
  <div class="map-layout">
492
381
  <div class="card map-card">
493
382
  <div class="map-tools">
@@ -534,16 +423,25 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
534
423
  <div><span>Индекс экономики</span><b id="mapRegIndex">—</b></div>
535
424
  <div><span>Остаток (в течение года)</span><b id="mapRegRest">—</b></div>
536
425
  </div>
537
- <div class="field"><label for="inpPartnerCompany">Компания</label><input type="text" id="inpPartnerCompany" value="ООО «УралТехИнтеграция»"></div>
538
- <div class="row2">
539
- <div class="field"><label for="inpPartnerInn">ИНН</label><input type="text" id="inpPartnerInn" value="6670123456" inputmode="numeric"></div>
540
- <div class="field"><label for="inpPartnerEmail">Email</label><input type="email" id="inpPartnerEmail" value="partner@uraltech.ru"></div>
541
- </div>
542
- <button class="btn btn-primary" style="width:100%" id="btnReserveSelected" data-action="reserve-selected" disabled>Зарезервировать · залог ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</button>
426
+ <button class="btn btn-primary" style="width:100%;margin-top:8px" id="btnReserveSelected" data-action="reserve-selected" disabled>Зарезервировать · залог ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</button>
427
+ <p class="small muted" style="margin:8px 0 0;text-align:center">При нажатии «Зарезервировать» заявка на регион добавится как заказ в ваш Личный кабинет.</p>
543
428
  <div class="status" id="partnerActionMsg"></div>
544
429
  </aside>
545
430
  </div>
546
431
 
432
+ <div class="sec-head" style="margin-top:8px">
433
+ <div><span class="kicker">Партнёрская программа</span><h2 class="h2">Региональная франшиза ODANT-WORK</h2></div>
434
+ <p>Число франшиз в субъекте пропорционально населению, паушальный взнос — экономике региона. Резерв закрепляется сразу после невозвратного залога ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}, полная оплата паушального взноса — в течение 1 года.</p>
435
+ </div>
436
+
437
+ <div class="stats">
438
+ <div class="stat"><b id="statVacant">${totalVacant}</b><span>вакантных франшиз</span></div>
439
+ <div class="stat"><b>${visibleRegions.length}</b><span>субъектов РФ</span></div>
440
+ <div class="stat"><b>${rub(FRANCHISE_PROGRAM.baseFranchiseFeeRub)}</b><span>базовый паушальный взнос</span></div>
441
+ <div class="stat"><b>${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</b><span>залог за резерв</span></div>
442
+ <div class="stat"><b>${FRANCHISE_PROGRAM.minDiscountPercent}→${FRANCHISE_PROGRAM.maxDiscountPercent}%</b><span>скидка партнёра от объёма</span></div>
443
+ </div>
444
+
547
445
  <div class="card">
548
446
  <div class="map-tools">
549
447
  <h3 class="h3" style="margin:0">Все субъекты Российской Федерации</h3>
@@ -592,56 +490,6 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
592
490
  <div class="grid g4">${renderTracks()}</div>
593
491
  </section>
594
492
 
595
- <!-- ============ ПОДДЕРЖКА ============ -->
596
- <section class="stage" id="stage-support">
597
- <div class="sec-head">
598
- <div><span class="kicker">Сервис</span><h2 class="h2">Поддержка в мессенджерах MAX, Telegram и VK</h2></div>
599
- <p>Боты принимают обращения, показывают статус лицензии, выставляют счета и передают вопросы инженеру по SLA вашего тарифа.</p>
600
- </div>
601
- <div class="grid g2">
602
- <div class="card">
603
- <h3 class="h3">Консоль бота</h3>
604
- <div class="row2" style="margin-top:12px">
605
- <div class="field"><label for="botChannel">Канал</label><select id="botChannel"><option value="max">MAX</option><option value="telegram">Telegram</option><option value="vk">VK</option></select></div>
606
- <div class="field"><label for="botChatId">Chat ID</label><input type="text" id="botChatId" value="client-101"></div>
607
- </div>
608
- <div class="field"><label for="botText">Команда или текст</label>
609
- <div style="display:flex;gap:8px"><input type="text" id="botText" value="/start"><button class="btn btn-primary" data-action="bot-send">${icon('send')}</button></div>
610
- </div>
611
- <div class="chips" style="margin-bottom:12px">
612
- <button class="chip" data-bot="/status">/status</button>
613
- <button class="chip" data-bot="/buy business 10">/buy business 10</button>
614
- <button class="chip" data-bot="/help">/help</button>
615
- </div>
616
- <div class="console" id="botReplyBox">Ответ бота появится здесь…</div>
617
- </div>
618
- <div class="card">
619
- <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap">
620
- <h3 class="h3" style="margin:0">Ваша лицензия и привязка мессенджеров</h3>
621
- <div style="display:flex;gap:8px;flex-wrap:wrap">
622
- <button class="btn btn-ghost btn-sm" data-action="auth">Личный кабинет</button>
623
- <button class="btn btn-ghost btn-sm" data-action="open-oferta">Договор-оферта</button>
624
- </div>
625
- </div>
626
- <div class="console" id="clientLicenseBox" style="margin-top:14px">Войдите в личный кабинет и подпишите договор-оферту, чтобы получить код привязки вашего комплекса к ботам MAX, Telegram и VK.</div>
627
- </div>
628
- </div>
629
- <div class="card">
630
- <div class="spec-wrap">
631
- <div>
632
- <span class="kicker">Разработчик платформы</span>
633
- <h2 class="h2">${esc(VENDOR_CONTACTS.company)}</h2>
634
- <p class="note">${esc(VENDOR_CONTACTS.description)}. Головной офис ODANT — в Рязани.</p>
635
- </div>
636
- <div>
637
- <div class="contact-row">${icon('pin')}<div><b>${esc(VENDOR_CONTACTS.address)}</b><br><a href="https://yandex.ru/maps/?pt=${VENDOR_CONTACTS.geo.lon},${VENDOR_CONTACTS.geo.lat}&z=17&l=map" target="_blank" rel="noopener">Открыть на карте</a></div></div>
638
- <div class="contact-row">${icon('phone')}<div>${VENDOR_CONTACTS.phones.map(p => `<a href="tel:${p.replace(/[^\d+]/g, '')}">${esc(p)}</a>`).join('<br>')}</div></div>
639
- <div class="contact-row">${icon('globe')}<div><a href="${esc(VENDOR_CONTACTS.site)}" target="_blank" rel="noopener">${esc(VENDOR_CONTACTS.site.replace(/^https?:\/\//, ''))}</a> · <a href="https://${esc(P.domain)}">${esc(P.domain)}</a></div></div>
640
- </div>
641
- </div>
642
- </div>
643
- </section>
644
-
645
493
  <footer>
646
494
  <div class="footer">
647
495
  <div>
@@ -668,39 +516,54 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
668
516
  </footer>
669
517
  </div>
670
518
 
671
- <!-- Регистрация / Вход в кабинет и подписание публичной оферты ПЭП -->
519
+ <!-- Вход / Регистрация в Личный кабинет по одноразовому коду на Email -->
672
520
  <div class="modal" id="authModal" role="dialog" aria-modal="true" aria-labelledby="authTitle">
673
- <div class="card" style="max-width:540px">
521
+ <div class="card" style="max-width:460px">
674
522
  <div class="modal-head">
675
523
  <img src="/assets/odant-logo.png" alt="">
676
524
  <div>
677
- <h3 class="h3" id="authTitle" style="margin:0">Регистрация и подписание оферты</h3>
678
- <div class="small muted" id="authSub">Заказ комплекса, докупка лицензий и резервирование франшизы доступны после регистрации и акцепта оферты (ПЭП)</div>
525
+ <h3 class="h3" id="authTitle" style="margin:0">Вход / Регистрация</h3>
526
+ <div class="small muted" id="authSub">На ваш Email придёт одноразовый 6-значный код подтверждения</div>
679
527
  </div>
680
528
  </div>
681
- <div class="row2">
682
- <div class="field"><label for="authEmail">Корпоративный email *</label><input type="email" id="authEmail" value="admin@maslotorg.ru" required></div>
683
- <div class="field"><label for="authInn">ИНН организации *</label><input type="text" id="authInn" value="7701234567" inputmode="numeric" required></div>
684
- </div>
685
- <div class="row2">
686
- <div class="field"><label for="authCompany">Наименование организации *</label><input type="text" id="authCompany" value="ООО «Маслоторг»" required></div>
687
- <div class="field"><label for="authSigner">ФИО и должность подписанта *</label><input type="text" id="authSigner" value="Перепелкин Р.А., Генеральный директор" required></div>
529
+
530
+ <div id="authIntentNotice" class="small" style="display:none;padding:10px 12px;border-radius:10px;background:rgba(0,163,224,.12);border:1px solid rgba(0,163,224,.35);margin-bottom:4px"></div>
531
+
532
+ <div id="authStepEmail">
533
+ <div class="field">
534
+ <label for="authEmail">Ваш Email *</label>
535
+ <input type="email" id="authEmail" value="" placeholder="Введите ваш email (например, name@company.ru)" autocomplete="email" required>
536
+ </div>
537
+
538
+ <div class="oferta-check-box" style="margin-top:10px">
539
+ <label class="oferta-check-label">
540
+ <input type="checkbox" id="authOfertaCheck" checked>
541
+ <span>
542
+ Регистрируясь и входя в Личный кабинет, я принимаю условия <a href="#" data-action="open-oferta">Публичного договора-оферты ODANT-WORK (${esc(PUBLIC_OFERTA.version)})</a>.
543
+ </span>
544
+ </label>
545
+ </div>
546
+
547
+ <button class="btn btn-primary" style="width:100%;margin-top:12px" id="btnSendCode" data-action="send-code">Получить код на Email</button>
688
548
  </div>
689
- <div class="field"><label for="authLicId">Номер лицензии ПАК (если уже выдан)</label><input type="text" id="authLicId" placeholder="Будет привязан или выпущен автоматически"></div>
690
-
691
- <div class="oferta-check-box">
692
- <label class="oferta-check-label">
693
- <input type="checkbox" id="authOfertaCheck" checked>
694
- <span>
695
- Я ознакомился с условиями <a href="#" data-action="open-oferta">Публичного договора-оферты ODANT-WORK (${esc(PUBLIC_OFERTA.version)})</a> и подписываю его простой электронной подписью (ПЭП) от имени указанной организации.
696
- </span>
697
- </label>
549
+
550
+ <div id="authStepCode" style="display:none">
551
+ <div class="small" id="authCodeSentNotice" style="padding:10px 12px;border-radius:10px;background:rgba(16,185,129,.12);color:var(--ok);margin-bottom:10px"></div>
552
+ <div class="field">
553
+ <label for="authCodeInput">Одноразовый код из письма (6 цифр) *</label>
554
+ <input type="text" id="authCodeInput" value="" placeholder="000000" maxlength="6" inputmode="numeric" autocomplete="one-time-code" style="font-size:20px;letter-spacing:6px;text-align:center;font-family:monospace">
555
+ </div>
556
+ <button class="btn btn-primary" style="width:100%;margin-top:10px" id="btnVerifyCode" data-action="verify-code">${icon('check')} Подтвердить код и войти</button>
557
+ <div style="display:flex;justify-content:space-between;margin-top:8px">
558
+ <button class="btn btn-ghost btn-sm" data-action="reset-auth-step">← Изменить Email</button>
559
+ <button class="btn btn-ghost btn-sm" data-action="send-code">Отправить код повторно</button>
560
+ </div>
698
561
  </div>
562
+
699
563
  <div class="status" id="authErrorMsg" style="color:var(--danger);min-height:18px;margin-top:6px"></div>
700
- <div style="display:flex;gap:10px;margin-top:10px;flex-wrap:wrap">
701
- <button class="btn btn-primary" style="flex:1" data-action="login">${icon('check')} Зарегистрироваться и подписать оферту</button>
702
- <button class="btn btn-ghost" id="btnLogoutModal" data-action="logout" style="display:none">Выйти</button>
703
- <button class="btn btn-ghost" data-action="auth-close">Закрыть</button>
564
+ <div style="display:flex;gap:10px;margin-top:6px;justify-content:flex-end;flex-wrap:wrap">
565
+ <button class="btn btn-ghost btn-sm" id="btnLogoutModal" data-action="logout" style="display:none">Выйти из аккаунта</button>
566
+ <button class="btn btn-ghost btn-sm" data-action="auth-close">Закрыть</button>
704
567
  </div>
705
568
  </div>
706
569
  </div>
@@ -728,7 +591,7 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
728
591
  </div>
729
592
  </div>
730
593
  <div style="display:flex;gap:10px;margin-top:14px;justify-content:flex-end;flex-wrap:wrap">
731
- <button class="btn btn-primary" data-action="accept-from-oferta">${icon('check')} Принять условия и перейти к подписанию ПЭП</button>
594
+ <button class="btn btn-primary" data-action="accept-from-oferta">${icon('check')} Принять условия оферты</button>
732
595
  <button class="btn btn-ghost" data-action="oferta-close">Закрыть</button>
733
596
  </div>
734
597
  </div>