work-license-server 1.0.0 → 1.0.1
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 +29 -14
- package/assets/landing.css +609 -0
- package/assets/landing.js +1187 -0
- package/assets/odant-logo.png +0 -0
- package/assets/russia-regions.json +1 -0
- package/package.json +2 -1
- package/src/agent.js +195 -0
- package/src/bots.js +2 -2
- package/src/catalog.js +315 -93
- package/src/index.js +6 -0
- package/src/landing.js +746 -882
- package/src/server.js +103 -5
- package/src/store.js +200 -2
package/src/landing.js
CHANGED
|
@@ -1,959 +1,825 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Витрина ODANT-WORK (work.odant.org).
|
|
3
|
+
*
|
|
4
|
+
* Сервер отдаёт готовую разметку всех вкладок (платформа, ИИ-агенты, тарифы, заказ комплекса,
|
|
5
|
+
* докупка лицензий — только после входа, франшиза с интерактивной картой субъектов РФ, поддержка),
|
|
6
|
+
* а поведение подключается из assets/landing.js. Стили — assets/landing.css, логотип — assets/odant-logo.png,
|
|
7
|
+
* контуры субъектов — assets/russia-regions.json.
|
|
4
8
|
*/
|
|
5
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
PAK_PRODUCT,
|
|
11
|
+
AI_PLATFORM,
|
|
12
|
+
VENDOR_CONTACTS,
|
|
13
|
+
FEDERAL_DISTRICTS,
|
|
14
|
+
SLA_TIERS,
|
|
15
|
+
SEAT_VOLUME_DISCOUNTS,
|
|
16
|
+
FRANCHISE_PROGRAM,
|
|
17
|
+
REGIONS_CATALOG,
|
|
18
|
+
PARTNER_TRAINING_TRACKS,
|
|
19
|
+
} from './catalog.js';
|
|
20
|
+
|
|
21
|
+
const ICON_PATHS = {
|
|
22
|
+
ledger: 'M4 5h16v14H4z M4 9h16 M9 9v10',
|
|
23
|
+
docs: 'M7 3h7l5 5v13H7z M14 3v5h5 M10 13h6 M10 17h6',
|
|
24
|
+
flow: 'M5 4h5v5H5z M14 15h5v5h-5z M7.5 9v3.5a2.5 2.5 0 0 0 2.5 2.5h4',
|
|
25
|
+
ai: 'M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z M19 15l.8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8z',
|
|
26
|
+
agents: 'M12 3.5a2 2 0 1 0 0 4a2 2 0 1 0 0-4 M5 16a2 2 0 1 0 0 4a2 2 0 1 0 0-4 M19 16a2 2 0 1 0 0 4a2 2 0 1 0 0-4 M12 7.5V12 M12 12l-5.6 4.4 M12 12l5.6 4.4',
|
|
27
|
+
shield: 'M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z M9 12l2 2 4-4',
|
|
28
|
+
chip: 'M7 7h10v10H7z M10 10h4v4h-4z M9 3v4 M15 3v4 M9 17v4 M15 17v4 M3 9h4 M3 15h4 M17 9h4 M17 15h4',
|
|
29
|
+
sun: 'M12 8a4 4 0 1 0 0 8a4 4 0 1 0 0-8 M12 2v2 M12 20v2 M4.9 4.9l1.4 1.4 M17.7 17.7l1.4 1.4 M2 12h2 M20 12h2 M4.9 19.1l1.4-1.4 M17.7 6.3l1.4-1.4',
|
|
30
|
+
moon: 'M20 14.5A8 8 0 1 1 9.5 4a6.5 6.5 0 0 0 10.5 10.5z',
|
|
31
|
+
user: 'M12 12a4 4 0 1 0 0-8a4 4 0 1 0 0 8 M4 21a8 8 0 0 1 16 0',
|
|
32
|
+
send: 'M4 12l16-8-6 16-2-7z',
|
|
33
|
+
check: 'M5 12l5 5L20 7',
|
|
34
|
+
plus: 'M12 5v14 M5 12h14',
|
|
35
|
+
minus: 'M5 12h14',
|
|
36
|
+
expand: 'M4 9V4h5 M20 9V4h-5 M4 15v5h5 M20 15v5h-5',
|
|
37
|
+
close: 'M6 6l12 12 M18 6L6 18',
|
|
38
|
+
phone: 'M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2',
|
|
39
|
+
pin: 'M12 21s-7-6.2-7-12a7 7 0 0 1 14 0c0 5.8-7 12-7 12z M12 11.5a2.5 2.5 0 1 0 0-5a2.5 2.5 0 1 0 0 5',
|
|
40
|
+
globe: 'M12 3a9 9 0 1 0 0 18a9 9 0 1 0 0-18 M3 12h18 M12 3c3 3.5 3 14.5 0 18 M12 3c-3 3.5-3 14.5 0 18',
|
|
41
|
+
lock: 'M6 11h12v10H6z M8 11V8a4 4 0 0 1 8 0v3',
|
|
42
|
+
mic: 'M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3 M5 11a7 7 0 0 0 14 0 M12 18v3',
|
|
43
|
+
bolt: 'M13 2L4 14h7l-1 8 9-12h-7z',
|
|
44
|
+
memory: 'M3 8h18v8H3z M7 8v8 M11 8v8 M15 8v8 M5 16v3 M19 16v3',
|
|
45
|
+
disk: 'M4 6a8 3 0 1 0 16 0a8 3 0 1 0-16 0 M4 6v12a8 3 0 0 0 16 0V6 M4 12a8 3 0 0 0 16 0',
|
|
46
|
+
chat: 'M4 5h16v11H8l-4 4z',
|
|
47
|
+
map: 'M9 4l-6 2v14l6-2 6 2 6-2V4l-6 2z M9 4v14 M15 6v14',
|
|
48
|
+
cert: 'M12 15a5 5 0 1 0 0-10a5 5 0 1 0 0 10 M8.5 13.5L7 21l5-2.5 5 2.5-1.5-7.5',
|
|
49
|
+
server: 'M4 4h16v6H4z M4 14h16v6H4z M8 7h.01 M8 17h.01',
|
|
50
|
+
image: 'M4 5h16v14H4z M4 16l5-5 4 4 3-3 4 4 M15 9h.01',
|
|
51
|
+
search: 'M11 4a7 7 0 1 0 0 14a7 7 0 1 0 0-14 M20 20l-4-4',
|
|
52
|
+
key: 'M8 14a4 4 0 1 0 0-8a4 4 0 1 0 0 8 M11 10h10 M17 10v4 M20 10v3',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export function icon(name) {
|
|
56
|
+
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="${ICON_PATHS[name] || ICON_PATHS.ai}"/></svg>`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, ch => ({ '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''' }[ch]));
|
|
60
|
+
const rub = (n) => Number(n || 0).toLocaleString('ru-RU').replace(/\u00a0/g, ' ') + ' ₽';
|
|
61
|
+
const jsonForScript = (v) => JSON.stringify(v).replace(/</g, '\\u003c').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
|
|
62
|
+
|
|
63
|
+
const MODEL_ICONS = { 'llm-core': 'ai', 'voice-core': 'mic', 'image-core': 'image', 'embedding-core': 'search' };
|
|
64
|
+
|
|
65
|
+
function renderModels() {
|
|
66
|
+
return PAK_PRODUCT.preinstalledModels.map(m => `
|
|
67
|
+
<article class="card spot reveal">
|
|
68
|
+
<div class="ico">${icon(MODEL_ICONS[m.id])}</div>
|
|
69
|
+
<div class="tag">${esc(m.type)}</div>
|
|
70
|
+
<h3 class="h3" style="margin-top:12px">${esc(m.title)}</h3>
|
|
71
|
+
<p class="muted small" style="margin:0 0 10px">${esc(m.badge)}</p>
|
|
72
|
+
<p class="note" style="margin:0">${esc(m.description)}</p>
|
|
73
|
+
</article>`).join('');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function renderTiers() {
|
|
77
|
+
return Object.values(SLA_TIERS).map(t => `
|
|
78
|
+
<article class="card spot tier ${t.id === 'business' ? 'card-glow featured' : ''}">
|
|
79
|
+
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px">
|
|
80
|
+
<h3 class="h3" style="margin:0;font-size:22px">${esc(t.name)}</h3>
|
|
81
|
+
<span class="tag ${t.id === 'business' ? 'ok' : ''}">${esc(t.badge)}</span>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="tier-price">${rub(t.pricePerUserYearRub)} <small>/ пользователь в год</small></div>
|
|
84
|
+
<div class="react">${icon('bolt')} Реакция ${esc(t.reactionLabel)} · ${esc(t.scheduleLabel)}</div>
|
|
85
|
+
<ul class="list">${t.features.map(f => `<li>${esc(f)}</li>`).join('')}</ul>
|
|
86
|
+
<button class="btn ${t.id === 'business' ? 'btn-primary' : 'btn-ghost'}" data-action="pick-tier" data-tier="${t.id}">Докупить места ${esc(t.name)}</button>
|
|
87
|
+
</article>`).join('');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function renderLadder(id) {
|
|
91
|
+
return `<div class="ladder" id="${id}">${[...SEAT_VOLUME_DISCOUNTS].reverse().map(d => `
|
|
92
|
+
<div data-min="${d.minSeats}"><b>−${d.discountPercent}%</b><span>от ${d.minSeats} ${d.minSeats === 1 ? 'места' : 'мест'}</span></div>`).join('')}</div>`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function renderStreams() {
|
|
96
|
+
return FRANCHISE_PROGRAM.partnerRevenueStreams.map(s => `
|
|
97
|
+
<article class="card spot">
|
|
98
|
+
<span class="tag ${/100%/.test(s.share) ? 'ok' : ''}">${esc(s.share)}</span>
|
|
99
|
+
<h3 class="h3" style="margin-top:12px">${esc(s.title)}</h3>
|
|
100
|
+
${s.description ? `<p class="note" style="margin:0">${esc(s.description)}</p>` : ''}
|
|
101
|
+
</article>`).join('');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function renderTracks() {
|
|
105
|
+
return PARTNER_TRAINING_TRACKS.map(t => `
|
|
106
|
+
<article class="card spot">
|
|
107
|
+
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
108
|
+
<span class="tag">${esc(t.code)}</span>
|
|
109
|
+
<span class="tag">${t.hours} ч</span>
|
|
110
|
+
${t.requiredForFranchise ? '<span class="tag ok">обязателен</span>' : ''}
|
|
111
|
+
</div>
|
|
112
|
+
<h3 class="h3" style="margin-top:12px;font-size:16px">${esc(t.title)}</h3>
|
|
113
|
+
<ul class="list">${t.topics.map(x => `<li>${esc(x)}</li>`).join('')}</ul>
|
|
114
|
+
</article>`).join('');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function renderOrchestration() {
|
|
118
|
+
return AI_PLATFORM.orchestration.map((o, i) => `
|
|
119
|
+
<article class="card spot reveal">
|
|
120
|
+
<div class="ico">${icon(['flow', 'shield', 'agents', 'server', 'check', 'cert'][i % 6])}</div>
|
|
121
|
+
<h3 class="h3">${esc(o.title)}</h3>
|
|
122
|
+
<p class="note" style="margin:0">${esc(o.description)}</p>
|
|
123
|
+
</article>`).join('');
|
|
124
|
+
}
|
|
6
125
|
|
|
7
|
-
export function renderLandingPage({ publicKey = '' } = {}) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
126
|
+
export function renderLandingPage({ publicKey = '', regions = null, assetVersion = '', initialStage = '' } = {}) {
|
|
127
|
+
const v = assetVersion ? '?v=' + encodeURIComponent(assetVersion) : '';
|
|
128
|
+
const P = PAK_PRODUCT;
|
|
129
|
+
const H = P.hardware;
|
|
130
|
+
const visibleRegions = (regions || REGIONS_CATALOG).filter(r => !r.hidden);
|
|
131
|
+
const totalVacant = visibleRegions.reduce((s, r) => s + (r.vacantFranchises ?? Math.max(1, Math.round(r.populationMillions))), 0);
|
|
132
|
+
|
|
133
|
+
const data = {
|
|
134
|
+
product: P,
|
|
135
|
+
tiers: Object.values(SLA_TIERS),
|
|
136
|
+
discounts: SEAT_VOLUME_DISCOUNTS,
|
|
137
|
+
franchise: FRANCHISE_PROGRAM,
|
|
138
|
+
regions: regions || REGIONS_CATALOG,
|
|
139
|
+
districts: FEDERAL_DISTRICTS,
|
|
140
|
+
ai: AI_PLATFORM,
|
|
141
|
+
vendor: VENDOR_CONTACTS,
|
|
142
|
+
icons: ICON_PATHS,
|
|
143
|
+
initialStage,
|
|
144
|
+
};
|
|
13
145
|
|
|
14
146
|
return /* html */`<!DOCTYPE html>
|
|
15
|
-
<html lang="ru">
|
|
147
|
+
<html lang="ru" data-theme="dark">
|
|
16
148
|
<head>
|
|
17
149
|
<meta charset="utf-8">
|
|
18
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
19
|
-
<title>WORK
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
--accent-2: #10b981;
|
|
30
|
-
--warn: #f59e0b;
|
|
31
|
-
}
|
|
32
|
-
* { box-sizing: border-box; }
|
|
33
|
-
body {
|
|
34
|
-
margin: 0;
|
|
35
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, sans-serif;
|
|
36
|
-
background: var(--bg);
|
|
37
|
-
color: var(--text);
|
|
38
|
-
line-height: 1.55;
|
|
39
|
-
}
|
|
40
|
-
a { color: var(--accent); text-decoration: none; }
|
|
41
|
-
.container {
|
|
42
|
-
max-width: 1180px;
|
|
43
|
-
margin: 0 auto;
|
|
44
|
-
padding: 0 24px;
|
|
45
|
-
}
|
|
46
|
-
header {
|
|
47
|
-
position: sticky;
|
|
48
|
-
top: 0;
|
|
49
|
-
z-index: 20;
|
|
50
|
-
backdrop-filter: blur(14px);
|
|
51
|
-
background: rgba(11, 17, 32, 0.84);
|
|
52
|
-
border-bottom: 1px solid var(--border);
|
|
53
|
-
}
|
|
54
|
-
.nav {
|
|
55
|
-
display: flex;
|
|
56
|
-
align-items: center;
|
|
57
|
-
justify-content: space-between;
|
|
58
|
-
height: 68px;
|
|
59
|
-
gap: 16px;
|
|
60
|
-
}
|
|
61
|
-
.brand {
|
|
62
|
-
display: flex;
|
|
63
|
-
align-items: center;
|
|
64
|
-
gap: 12px;
|
|
65
|
-
font-weight: 800;
|
|
66
|
-
font-size: 20px;
|
|
67
|
-
letter-spacing: .02em;
|
|
68
|
-
}
|
|
69
|
-
.brand-badge {
|
|
70
|
-
padding: 3px 10px;
|
|
71
|
-
border-radius: 999px;
|
|
72
|
-
font-size: 12px;
|
|
73
|
-
background: rgba(59, 130, 246, 0.2);
|
|
74
|
-
color: #60a5fa;
|
|
75
|
-
border: 1px solid rgba(59, 130, 246, 0.35);
|
|
76
|
-
}
|
|
77
|
-
.nav-links {
|
|
78
|
-
display: flex;
|
|
79
|
-
gap: 22px;
|
|
80
|
-
font-size: 14px;
|
|
81
|
-
color: var(--muted);
|
|
82
|
-
}
|
|
83
|
-
.nav-links a:hover { color: #fff; }
|
|
84
|
-
.hero {
|
|
85
|
-
padding: 76px 0 64px;
|
|
86
|
-
background:
|
|
87
|
-
radial-gradient(900px 420px at 80% 0%, rgba(59, 130, 246, 0.22), transparent 65%),
|
|
88
|
-
radial-gradient(700px 380px at 15% 20%, rgba(16, 185, 129, 0.14), transparent 65%);
|
|
89
|
-
border-bottom: 1px solid var(--border);
|
|
90
|
-
}
|
|
91
|
-
.hero-grid {
|
|
92
|
-
display: grid;
|
|
93
|
-
grid-template-columns: 1.25fr 0.95fr;
|
|
94
|
-
gap: 36px;
|
|
95
|
-
align-items: center;
|
|
96
|
-
}
|
|
97
|
-
@media (max-width: 920px) {
|
|
98
|
-
.hero-grid { grid-template-columns: 1fr; }
|
|
99
|
-
}
|
|
100
|
-
.eyebrow {
|
|
101
|
-
display: inline-block;
|
|
102
|
-
padding: 5px 12px;
|
|
103
|
-
border-radius: 999px;
|
|
104
|
-
background: rgba(16, 185, 129, 0.14);
|
|
105
|
-
color: #34d399;
|
|
106
|
-
font-size: 12px;
|
|
107
|
-
font-weight: 700;
|
|
108
|
-
letter-spacing: .06em;
|
|
109
|
-
text-transform: uppercase;
|
|
110
|
-
margin-bottom: 16px;
|
|
111
|
-
}
|
|
112
|
-
h1 {
|
|
113
|
-
margin: 0 0 18px;
|
|
114
|
-
font-size: clamp(2.1rem, 4.2vw, 3.25rem);
|
|
115
|
-
line-height: 1.1;
|
|
116
|
-
letter-spacing: -0.025em;
|
|
117
|
-
}
|
|
118
|
-
.lead {
|
|
119
|
-
color: var(--muted);
|
|
120
|
-
font-size: 17px;
|
|
121
|
-
margin: 0 0 28px;
|
|
122
|
-
}
|
|
123
|
-
.spec-card {
|
|
124
|
-
background: linear-gradient(145deg, #131c31, #0f172a);
|
|
125
|
-
border: 1px solid rgba(59, 130, 246, 0.32);
|
|
126
|
-
border-radius: 18px;
|
|
127
|
-
padding: 26px;
|
|
128
|
-
box-shadow: 0 24px 50px rgba(0, 0, 0, 0.45);
|
|
129
|
-
}
|
|
130
|
-
.spec-price {
|
|
131
|
-
font-size: 34px;
|
|
132
|
-
font-weight: 900;
|
|
133
|
-
color: #60a5fa;
|
|
134
|
-
margin: 6px 0;
|
|
135
|
-
}
|
|
136
|
-
.spec-list {
|
|
137
|
-
list-style: none;
|
|
138
|
-
padding: 0;
|
|
139
|
-
margin: 16px 0 0;
|
|
140
|
-
display: flex;
|
|
141
|
-
flex-direction: column;
|
|
142
|
-
gap: 10px;
|
|
143
|
-
font-size: 14px;
|
|
144
|
-
}
|
|
145
|
-
.spec-list li {
|
|
146
|
-
display: flex;
|
|
147
|
-
justify-content: space-between;
|
|
148
|
-
gap: 12px;
|
|
149
|
-
padding-bottom: 8px;
|
|
150
|
-
border-bottom: 1px dashed var(--border);
|
|
151
|
-
}
|
|
152
|
-
section {
|
|
153
|
-
padding: 64px 0;
|
|
154
|
-
border-bottom: 1px solid var(--border);
|
|
155
|
-
}
|
|
156
|
-
h2 {
|
|
157
|
-
margin: 0 0 10px;
|
|
158
|
-
font-size: 28px;
|
|
159
|
-
}
|
|
160
|
-
.sub {
|
|
161
|
-
color: var(--muted);
|
|
162
|
-
margin: 0 0 32px;
|
|
163
|
-
}
|
|
164
|
-
.grid-4 {
|
|
165
|
-
display: grid;
|
|
166
|
-
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
167
|
-
gap: 18px;
|
|
168
|
-
}
|
|
169
|
-
.grid-3 {
|
|
170
|
-
display: grid;
|
|
171
|
-
grid-template-columns: repeat(auto-fit, minmax(310px, 1fr));
|
|
172
|
-
gap: 20px;
|
|
173
|
-
}
|
|
174
|
-
.card {
|
|
175
|
-
background: var(--surface);
|
|
176
|
-
border: 1px solid var(--border);
|
|
177
|
-
border-radius: 14px;
|
|
178
|
-
padding: 22px;
|
|
179
|
-
display: flex;
|
|
180
|
-
flex-direction: column;
|
|
181
|
-
gap: 10px;
|
|
182
|
-
}
|
|
183
|
-
.card.featured {
|
|
184
|
-
border-color: var(--accent);
|
|
185
|
-
box-shadow: 0 0 0 1px var(--accent);
|
|
186
|
-
}
|
|
187
|
-
.pill {
|
|
188
|
-
align-self: flex-start;
|
|
189
|
-
padding: 3px 10px;
|
|
190
|
-
border-radius: 999px;
|
|
191
|
-
font-size: 11px;
|
|
192
|
-
font-weight: 700;
|
|
193
|
-
background: rgba(59, 130, 246, 0.16);
|
|
194
|
-
color: #93c5fd;
|
|
195
|
-
}
|
|
196
|
-
.tier-price {
|
|
197
|
-
font-size: 28px;
|
|
198
|
-
font-weight: 800;
|
|
199
|
-
color: #fff;
|
|
200
|
-
}
|
|
201
|
-
.tier-price span {
|
|
202
|
-
font-size: 14px;
|
|
203
|
-
font-weight: 400;
|
|
204
|
-
color: var(--muted);
|
|
205
|
-
}
|
|
206
|
-
.feature-ul {
|
|
207
|
-
margin: 8px 0 0;
|
|
208
|
-
padding-left: 18px;
|
|
209
|
-
color: var(--muted);
|
|
210
|
-
font-size: 14px;
|
|
211
|
-
display: flex;
|
|
212
|
-
flex-direction: column;
|
|
213
|
-
gap: 6px;
|
|
214
|
-
}
|
|
215
|
-
.calc-box {
|
|
216
|
-
display: grid;
|
|
217
|
-
grid-template-columns: 1.1fr 0.9fr;
|
|
218
|
-
gap: 24px;
|
|
219
|
-
background: var(--surface);
|
|
220
|
-
border: 1px solid var(--border);
|
|
221
|
-
border-radius: 16px;
|
|
222
|
-
padding: 28px;
|
|
223
|
-
}
|
|
224
|
-
@media (max-width: 860px) {
|
|
225
|
-
.calc-box { grid-template-columns: 1fr; }
|
|
226
|
-
}
|
|
227
|
-
.form-row {
|
|
228
|
-
display: flex;
|
|
229
|
-
flex-direction: column;
|
|
230
|
-
gap: 6px;
|
|
231
|
-
margin-bottom: 14px;
|
|
232
|
-
}
|
|
233
|
-
.form-row label {
|
|
234
|
-
font-size: 13px;
|
|
235
|
-
color: var(--muted);
|
|
236
|
-
font-weight: 600;
|
|
237
|
-
}
|
|
238
|
-
input[type="text"], input[type="email"], input[type="number"], select, textarea {
|
|
239
|
-
background: var(--surface-2);
|
|
240
|
-
border: 1px solid var(--border);
|
|
241
|
-
color: #fff;
|
|
242
|
-
padding: 10px 12px;
|
|
243
|
-
border-radius: 8px;
|
|
244
|
-
font-size: 14px;
|
|
245
|
-
width: 100%;
|
|
246
|
-
}
|
|
247
|
-
.btn {
|
|
248
|
-
display: inline-flex;
|
|
249
|
-
align-items: center;
|
|
250
|
-
justify-content: center;
|
|
251
|
-
gap: 8px;
|
|
252
|
-
padding: 12px 22px;
|
|
253
|
-
border-radius: 10px;
|
|
254
|
-
font-weight: 700;
|
|
255
|
-
font-size: 15px;
|
|
256
|
-
cursor: pointer;
|
|
257
|
-
border: none;
|
|
258
|
-
transition: opacity .15s, transform .1s;
|
|
259
|
-
}
|
|
260
|
-
.btn:hover { opacity: 0.94; }
|
|
261
|
-
.btn-primary {
|
|
262
|
-
background: var(--accent);
|
|
263
|
-
color: #fff;
|
|
264
|
-
}
|
|
265
|
-
.btn-success {
|
|
266
|
-
background: var(--accent-2);
|
|
267
|
-
color: #052e16;
|
|
268
|
-
}
|
|
269
|
-
.btn-outline {
|
|
270
|
-
background: transparent;
|
|
271
|
-
border: 1px solid var(--border);
|
|
272
|
-
color: var(--text);
|
|
273
|
-
}
|
|
274
|
-
.summary-panel {
|
|
275
|
-
background: var(--surface-2);
|
|
276
|
-
border-radius: 12px;
|
|
277
|
-
padding: 22px;
|
|
278
|
-
display: flex;
|
|
279
|
-
flex-direction: column;
|
|
280
|
-
justify-content: space-between;
|
|
281
|
-
gap: 16px;
|
|
282
|
-
}
|
|
283
|
-
.total-sum {
|
|
284
|
-
font-size: 32px;
|
|
285
|
-
font-weight: 900;
|
|
286
|
-
color: #34d399;
|
|
287
|
-
}
|
|
288
|
-
.bot-console {
|
|
289
|
-
display: grid;
|
|
290
|
-
grid-template-columns: 1fr 1fr;
|
|
291
|
-
gap: 20px;
|
|
292
|
-
}
|
|
293
|
-
@media (max-width: 860px) {
|
|
294
|
-
.bot-console { grid-template-columns: 1fr; }
|
|
295
|
-
}
|
|
296
|
-
.log-box {
|
|
297
|
-
background: #070b14;
|
|
298
|
-
border: 1px solid var(--border);
|
|
299
|
-
border-radius: 10px;
|
|
300
|
-
padding: 14px;
|
|
301
|
-
font-family: monospace;
|
|
302
|
-
font-size: 12.5px;
|
|
303
|
-
min-height: 180px;
|
|
304
|
-
max-height: 300px;
|
|
305
|
-
overflow-y: auto;
|
|
306
|
-
white-space: pre-wrap;
|
|
307
|
-
}
|
|
308
|
-
footer {
|
|
309
|
-
padding: 36px 0;
|
|
310
|
-
text-align: center;
|
|
311
|
-
color: var(--muted);
|
|
312
|
-
font-size: 13px;
|
|
313
|
-
}
|
|
314
|
-
</style>
|
|
150
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
151
|
+
<title>ODANT-WORK — учёт, документооборот, процессы и приватный ИИ в одном комплексе</title>
|
|
152
|
+
<meta name="description" content="ODANT-WORK — программно-аппаратный комплекс на AMD Ryzen™ AI Max+ PRO 495: встроенная система учёта, документооборот, управление процессами, ИИ-консультант и мультиагентная система, работающие полностью внутри контура предприятия.">
|
|
153
|
+
<meta name="theme-color" content="#04060b">
|
|
154
|
+
<link rel="icon" type="image/png" href="/favicon.ico">
|
|
155
|
+
<link rel="apple-touch-icon" href="/assets/odant-logo.png">
|
|
156
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
157
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
158
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Manrope:wght@600;700;800&display=swap">
|
|
159
|
+
<link rel="stylesheet" href="/assets/landing.css${v}">
|
|
160
|
+
<script>try{var t=localStorage.getItem('odant_work_theme');if(t)document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>
|
|
315
161
|
</head>
|
|
316
162
|
<body>
|
|
317
|
-
<
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
<
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
163
|
+
<div class="bg-aurora" aria-hidden="true"><i></i><i></i><i></i></div>
|
|
164
|
+
<canvas id="bgCanvas" aria-hidden="true"></canvas>
|
|
165
|
+
<div class="bg-grid" aria-hidden="true"></div>
|
|
166
|
+
<div class="bg-vignette" aria-hidden="true"></div>
|
|
167
|
+
|
|
168
|
+
<div class="shell">
|
|
169
|
+
<header class="topbar">
|
|
170
|
+
<button class="brand" data-stage="product" aria-label="ODANT-WORK — на главную">
|
|
171
|
+
<img src="/assets/odant-logo.png" alt="ODANT">
|
|
172
|
+
<span>
|
|
173
|
+
<span class="brand-name">ODANT<b>·</b>WORK</span><br>
|
|
174
|
+
<span class="brand-sub">учёт · документооборот · процессы · приватный ИИ</span>
|
|
175
|
+
</span>
|
|
176
|
+
</button>
|
|
177
|
+
|
|
178
|
+
<nav class="tabs" id="tabs" aria-label="Разделы">
|
|
179
|
+
<span class="tab-ind" id="tabInd"></span>
|
|
180
|
+
<button class="tab active" data-stage="product">Платформа</button>
|
|
181
|
+
<button class="tab" data-stage="agents">ИИ-агенты</button>
|
|
182
|
+
<button class="tab" data-stage="pricing">Тарифы</button>
|
|
183
|
+
<button class="tab" data-stage="order">Комплекс</button>
|
|
184
|
+
<button class="tab" id="tileAddons" data-stage="addons" style="display:none;">Докупка лицензий</button>
|
|
185
|
+
<button class="tab" data-stage="franchise">Франшиза</button>
|
|
186
|
+
<button class="tab" data-stage="support">Поддержка</button>
|
|
330
187
|
</nav>
|
|
331
|
-
</div>
|
|
332
|
-
</header>
|
|
333
|
-
|
|
334
|
-
<div class="hero" id="hardware">
|
|
335
|
-
<div class="container hero-grid">
|
|
336
|
-
<div>
|
|
337
|
-
<span class="eyebrow">Программно-аппаратный комплекс предприятия</span>
|
|
338
|
-
<h1>Учёт уровня 1С и приватный мультимодальный ИИ в одной коробке</h1>
|
|
339
|
-
<p class="lead">
|
|
340
|
-
Автономный сервер предприятия на базе <b>AMD Ryzen 495 8085 · 192 ГБ RAM · SSD 4 ТБ</b> с предустановленной типовой конфигурацией WORK, 4 локальными моделями ИИ и <b>вечной лицензией на 5 одновременных рабочих мест</b> (+1 год поддержки включён).
|
|
341
|
-
</p>
|
|
342
|
-
<div style="display:flex; gap:12px; flex-wrap:wrap;">
|
|
343
|
-
<a href="#checkout" class="btn btn-primary">Заказать ПАК / Докупить рабочие места</a>
|
|
344
|
-
<a href="#support" class="btn btn-outline">Подключить боты поддержки (MAX, TG, VK)</a>
|
|
345
|
-
</div>
|
|
346
|
-
</div>
|
|
347
|
-
|
|
348
|
-
<div class="spec-card">
|
|
349
|
-
<div style="font-size:13px; color:var(--muted); text-transform:uppercase; letter-spacing:.06em;">Базовый комплект ПАК WORK</div>
|
|
350
|
-
<div class="spec-price">5 000 000 ₽</div>
|
|
351
|
-
<div style="font-size:13px; color:#34d399; font-weight:600;">Вечная лицензия на 5 рабочих мест + 1 год поддержки в комплекте</div>
|
|
352
|
-
<ul class="spec-list">
|
|
353
|
-
<li><span>Процессор</span><strong>AMD Ryzen 495 8085</strong></li>
|
|
354
|
-
<li><span>Оперативная память</span><strong>192 ГБ Unified RAM</strong></li>
|
|
355
|
-
<li><span>Накопитель</span><strong>SSD 4 ТБ NVMe</strong></li>
|
|
356
|
-
<li><span>Учётная система</span><strong>Типовая конфигурация (аналог 1С)</strong></li>
|
|
357
|
-
<li><span>Локальный ИИ</span><strong>Qwen 3 30B + Голос + Картинки + Embedding</strong></li>
|
|
358
|
-
<li><span>Лицензирование</span><strong>5 вечных мест (конкурентные сессии)</strong></li>
|
|
359
|
-
<li><span>Защита лицензии</span><strong>HWID Ryzen + #system/node-key.json</strong></li>
|
|
360
|
-
</ul>
|
|
361
|
-
</div>
|
|
362
|
-
</div>
|
|
363
|
-
</div>
|
|
364
188
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
<section id="pricing">
|
|
374
|
-
<div class="container">
|
|
375
|
-
<h2>Дополнительные годовые лицензии и уровни поддержки (SLA)</h2>
|
|
376
|
-
<p class="sub">Базовые 5 рабочих мест — вечные. Дополнительные рабочие места лицензируются на 1 год и зависят от выбранного уровня поддержки.</p>
|
|
377
|
-
<div class="grid-3" id="tiersGrid"></div>
|
|
378
|
-
</div>
|
|
379
|
-
</section>
|
|
189
|
+
<div class="top-actions">
|
|
190
|
+
<button class="icon-btn" id="btnTheme" onclick="toggleTheme()" title="Светлая / тёмная тема" aria-label="Переключить тему">${icon('sun')}</button>
|
|
191
|
+
<button class="btn btn-ghost btn-sm" id="btnAdminTop" data-stage="admin" title="Админ-панель сервера лицензий">${icon('server')}<span>Админка</span></button>
|
|
192
|
+
<button class="btn btn-ghost btn-sm" id="btnAuth" data-action="auth">${icon('user')}<span id="btnAuthText">Войти</span></button>
|
|
193
|
+
<button class="btn btn-primary btn-sm" data-stage="order">Заказать</button>
|
|
194
|
+
</div>
|
|
195
|
+
</header>
|
|
380
196
|
|
|
381
|
-
|
|
382
|
-
<
|
|
383
|
-
<
|
|
384
|
-
<p class="sub">Оформите поставку базового ПАК WORK или докупите срочные годовые лицензии на дополнительных пользователей.</p>
|
|
385
|
-
<div class="calc-box">
|
|
197
|
+
<!-- ============ ПЛАТФОРМА ============ -->
|
|
198
|
+
<section class="stage active" id="stage-product">
|
|
199
|
+
<div class="hero">
|
|
386
200
|
<div>
|
|
387
|
-
<
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
</
|
|
201
|
+
<span class="pill"><span class="dot"></span>Программно-аппаратный комплекс на AMD Ryzen™ AI Max+ PRO 495</span>
|
|
202
|
+
<h1 class="h1">Учёт, документооборот, процессы и <span class="grad">приватный ИИ</span> — в одном комплексе</h1>
|
|
203
|
+
<p class="lead">ODANT-WORK — вычислительный центр предприятия, который ставится в серверную и работает автономно. Встроенная система учёта, единое пространство документов и задач, ИИ-консультант в каждой форме и команда ИИ-агентов — без облаков и передачи данных наружу.</p>
|
|
204
|
+
<div class="hero-cta">
|
|
205
|
+
<button class="btn btn-primary btn-lg" data-stage="order">Заказать комплекс · ${rub(P.basePriceRub)}</button>
|
|
206
|
+
<button class="btn btn-ghost btn-lg" data-stage="agents">${icon('agents')} Как работают ИИ-агенты</button>
|
|
392
207
|
</div>
|
|
393
|
-
<div class="
|
|
394
|
-
<
|
|
395
|
-
<
|
|
396
|
-
|
|
397
|
-
<option value="business" selected>Business — 40 000 ₽ / год за 1 пользователя (реакция до 2 ч, 48 обращений)</option>
|
|
398
|
-
<option value="enterprise">Enterprise — 100 000 ₽ / год за 1 пользователя (реакция до 15 мин 24/7, безлимит)</option>
|
|
399
|
-
</select>
|
|
208
|
+
<div class="trust">
|
|
209
|
+
<span>${icon('shield')} Данные не покидают контур</span>
|
|
210
|
+
<span>${icon('key')} 5 вечных рабочих мест</span>
|
|
211
|
+
<span>${icon('check')} 1 год поддержки включён</span>
|
|
400
212
|
</div>
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
213
|
+
</div>
|
|
214
|
+
<div class="showcase" aria-hidden="true">
|
|
215
|
+
<div class="showcase-ring"></div>
|
|
216
|
+
<div class="showcase-ring r2"></div>
|
|
217
|
+
<div class="showcase-logo"><img src="/assets/odant-logo.png" alt=""></div>
|
|
218
|
+
<div class="chipf c1"><b>132 TOPS</b>ИИ-производительность</div>
|
|
219
|
+
<div class="chipf c2"><b>192 ГБ</b>единой памяти LPDDR5x</div>
|
|
220
|
+
<div class="chipf c3"><b>16 / 32</b>ядра / потока Zen 5</div>
|
|
221
|
+
<div class="chipf c4"><b>4 ТБ</b>NVMe PCIe® 4.0</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<div class="kpis reveal">
|
|
226
|
+
<div class="kpi"><b data-count="132">132<small>TOPS</small></b><span>общая ИИ-производительность, NPU 55 TOPS</span></div>
|
|
227
|
+
<div class="kpi"><b data-count="61">61<small>TFLOPS</small></b><span>FP16 на графике Radeon™ 8065S</span></div>
|
|
228
|
+
<div class="kpi"><b data-count="192">192<small>ГБ</small></b><span>единая память для моделей и данных</span></div>
|
|
229
|
+
<div class="kpi"><b data-count="5.2" data-dec="1">5.2<small>ГГц</small></b><span>максимальная частота, 80 МБ кэша</span></div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div class="sec-head reveal">
|
|
233
|
+
<div>
|
|
234
|
+
<span class="kicker">Что внутри</span>
|
|
235
|
+
<h2 class="h2">Одна система вместо зоопарка программ</h2>
|
|
236
|
+
</div>
|
|
237
|
+
<p>Все данные предприятия — справочники, проводки, документы, переписка и задачи — живут в одной модели с едиными правами доступа. Поэтому ИИ видит полную картину и действует по правилам вашей организации.</p>
|
|
238
|
+
</div>
|
|
239
|
+
<div class="bento">
|
|
240
|
+
<article class="card spot reveal wide"><div class="ico">${icon('ledger')}</div><h3 class="h3">Встроенная система учёта</h3><p class="note">План счетов, справочники и аналитики, явная двойная запись с исправлением только через сторно. Оборотно-сальдовая ведомость, анализ, обороты и журнал проводок — в едином табличном стандарте.</p></article>
|
|
241
|
+
<article class="card spot reveal wide"><div class="ico">${icon('docs')}</div><h3 class="h3">Документооборот</h3><p class="note">Единое файловое пространство подразделений, первичные документы по шаблонам, согласования, версии и восстановление файлов. Офисные форматы открываются прямо в системе.</p></article>
|
|
242
|
+
<article class="card spot reveal"><div class="ico">${icon('flow')}</div><h3 class="h3">Управление процессами</h3><p class="note">Структура подразделений, роли, рабочие места, поручения со сроками и журнал всех фактов.</p></article>
|
|
243
|
+
<article class="card spot reveal"><div class="ico">${icon('ai')}</div><h3 class="h3">ИИ-консультант</h3><p class="note">Кнопка «?» в любой форме и голосовой диалог: объяснит, найдёт, посчитает и проведёт операцию.</p></article>
|
|
244
|
+
<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>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div class="card card-glow reveal">
|
|
248
|
+
<div class="spec-wrap">
|
|
249
|
+
<div>
|
|
250
|
+
<span class="kicker">Паспорт комплекса</span>
|
|
251
|
+
<h2 class="h2">ODANT-WORK · ${esc(P.sku)}</h2>
|
|
252
|
+
<div class="price-big">${rub(P.basePriceRub)}</div>
|
|
253
|
+
<p class="note">Вечная лицензия на ${P.baseSeats} одновременных рабочих сессий, привязанная к оборудованию, и ${P.includedSupportYears} год поддержки уровня Business — в комплекте.</p>
|
|
254
|
+
<div class="hero-cta" style="margin-top:20px">
|
|
255
|
+
<button class="btn btn-primary" data-stage="order">Оформить заказ</button>
|
|
256
|
+
<button class="btn btn-ghost" data-stage="pricing">Тарифы поддержки</button>
|
|
409
257
|
</div>
|
|
410
258
|
</div>
|
|
411
|
-
<
|
|
412
|
-
<div
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
</div>
|
|
416
|
-
<div
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
259
|
+
<dl class="spec">
|
|
260
|
+
<div><dt>Процессор</dt><dd>${esc(H.cpu)}</dd></div>
|
|
261
|
+
<div><dt>Графика</dt><dd>${esc(H.gpu)}</dd></div>
|
|
262
|
+
<div><dt>ИИ-ускорение</dt><dd>${esc(H.aiEngine)}</dd></div>
|
|
263
|
+
<div><dt>Память</dt><dd>${esc(H.ram)}</dd></div>
|
|
264
|
+
<div><dt>Накопитель</dt><dd>${esc(H.storage)}</dd></div>
|
|
265
|
+
<div><dt>Защита</dt><dd>${esc(H.security)}</dd></div>
|
|
266
|
+
<div><dt>Лицензия</dt><dd>По одновременным сессиям, привязка к HWID и ключу узла</dd></div>
|
|
267
|
+
</dl>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div class="sec-head reveal">
|
|
272
|
+
<div>
|
|
273
|
+
<span class="kicker">Приватный мультимодальный ИИ</span>
|
|
274
|
+
<h2 class="h2">Четыре типа моделей уже установлены</h2>
|
|
275
|
+
</div>
|
|
276
|
+
<p>Модели работают на самом комплексе. Их можно дообучить на данных предприятия — с проверкой результатов учётным ядром.</p>
|
|
277
|
+
</div>
|
|
278
|
+
<div class="grid g4">${renderModels()}</div>
|
|
279
|
+
</section>
|
|
280
|
+
|
|
281
|
+
<!-- ============ ИИ-АГЕНТЫ ============ -->
|
|
282
|
+
<section class="stage" id="stage-agents">
|
|
283
|
+
<div class="agents-hero">
|
|
284
|
+
<div>
|
|
285
|
+
<span class="kicker">ИИ-консультант</span>
|
|
286
|
+
<h2 class="h2" style="font-size:clamp(2rem,3.6vw,3.2rem)">Помощник, который <span class="grad">знает вашу организацию</span></h2>
|
|
287
|
+
<p class="lead">Консультант встроен в каждую форму системы. Он видит открытый документ, понимает структуру учёта и права пользователя — и не просто отвечает, а выполняет работу.</p>
|
|
288
|
+
<ul class="list">${AI_PLATFORM.consultant.features.map(f => `<li>${esc(f)}</li>`).join('')}</ul>
|
|
289
|
+
</div>
|
|
290
|
+
<div class="mock" id="consultantMock">
|
|
291
|
+
<div class="mock-bar">
|
|
292
|
+
<span class="dots"><i></i><i></i><i></i></span>
|
|
293
|
+
<span>Ведомость · счёт 41 «Товары»</span>
|
|
294
|
+
<span class="help" title="Спросить ИИ по этой форме">?</span>
|
|
420
295
|
</div>
|
|
421
|
-
<div
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
296
|
+
<div class="mock-body" id="mockBody"></div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<div class="card reveal">
|
|
301
|
+
<div class="orbit-wrap">
|
|
302
|
+
<svg class="orbit" id="agentsOrbit" viewBox="0 0 600 600" role="img" aria-label="Схема мультиагентной системы"></svg>
|
|
303
|
+
<div class="agent-detail">
|
|
304
|
+
<span class="kicker">Мультиагентная система</span>
|
|
305
|
+
<h3 class="h2" id="agentTitle">${AI_PLATFORM.agents.length} ролевых агентов под управлением основного</h3>
|
|
306
|
+
<p class="lead" id="agentDesc" style="font-size:15.5px">Основной агент принимает задачу, раскладывает её на поручения и раздаёт специалистам. Каждый агент — со своей ролью, моделью и строго ограниченным набором инструментов. Наведите на агента, чтобы узнать о нём больше.</p>
|
|
307
|
+
<div class="calc-out">
|
|
308
|
+
<div><b>${AI_PLATFORM.agents.length}</b><span>ролевых агентов</span></div>
|
|
309
|
+
<div><b>${AI_PLATFORM.skillsCount}+</b><span>готовых навыков</span></div>
|
|
310
|
+
<div><b>2</b><span>уровня делегирования</span></div>
|
|
429
311
|
</div>
|
|
430
312
|
</div>
|
|
431
313
|
</div>
|
|
314
|
+
</div>
|
|
432
315
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
316
|
+
<div class="sec-head reveal">
|
|
317
|
+
<div><span class="kicker">Оркестрация</span><h2 class="h2">Как агенты работают вместе</h2></div>
|
|
318
|
+
<p>Параллельность и скорость — без потери контроля: права, подтверждения и журнал действий заложены в код платформы, а не в текст подсказки.</p>
|
|
319
|
+
</div>
|
|
320
|
+
<div class="grid g3">${renderOrchestration()}</div>
|
|
321
|
+
|
|
322
|
+
<div class="card reveal">
|
|
323
|
+
<div class="sec-head" style="margin-bottom:18px">
|
|
324
|
+
<div><span class="kicker">Навыки</span><h2 class="h2">${AI_PLATFORM.skillsCount}+ проверенных рецептов работы</h2></div>
|
|
325
|
+
<p>Навык — описанный порядок решения типовой задачи. Удачный результат агента с одобрения сотрудника сохраняется как новый навык подразделения.</p>
|
|
443
326
|
</div>
|
|
327
|
+
<div class="skills">${AI_PLATFORM.skills.map(s => `<span>${esc(s)}</span>`).join('')}</div>
|
|
444
328
|
</div>
|
|
445
|
-
</
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
<h2>Партнёрская программа и Региональная франшиза WORK</h2>
|
|
451
|
-
<p class="sub">
|
|
452
|
-
Партнёр самостоятельно закупает оборудование ПАК, устанавливает стек WORK и модели ИИ, привязывает железо, выдаёт лицензии и предоставляет <b>годовую гарантию на оборудование</b>.
|
|
453
|
-
Фиксированная стоимость франшизы пропорциональна экономике региона, квота партнёров — населению, а партнёрская скидка растёт от <b>10%</b> до <b>50%</b> пропорционально годовому объёму продаж.
|
|
454
|
-
</p>
|
|
455
|
-
<div class="calc-box" style="margin-bottom:28px;">
|
|
329
|
+
</section>
|
|
330
|
+
|
|
331
|
+
<!-- ============ ТАРИФЫ ============ -->
|
|
332
|
+
<section class="stage" id="stage-pricing">
|
|
333
|
+
<div class="sec-head">
|
|
456
334
|
<div>
|
|
457
|
-
<
|
|
458
|
-
|
|
459
|
-
|
|
335
|
+
<span class="kicker">Лицензии и поддержка</span>
|
|
336
|
+
<h2 class="h2">Платите только за дополнительные места</h2>
|
|
337
|
+
</div>
|
|
338
|
+
<p>${P.baseSeats} рабочих мест входят в комплекс навсегда. Дополнительные места — годовые, цена зависит от уровня поддержки. Считаются одновременные активные сессии, а не учётные записи.</p>
|
|
339
|
+
</div>
|
|
340
|
+
<div class="tiers">${renderTiers()}</div>
|
|
341
|
+
<div class="card">
|
|
342
|
+
<div class="sec-head" style="margin-bottom:16px">
|
|
343
|
+
<div><span class="kicker">Скидка от количества</span><h3 class="h3" style="font-size:20px">Чем больше мест — тем ниже цена каждого</h3></div>
|
|
344
|
+
<p class="small">Скидка применяется ко всему пакету докупаемых мест.</p>
|
|
345
|
+
</div>
|
|
346
|
+
${renderLadder('ladderPricing')}
|
|
347
|
+
</div>
|
|
348
|
+
</section>
|
|
349
|
+
|
|
350
|
+
<!-- ============ ЗАКАЗ КОМПЛЕКСА ============ -->
|
|
351
|
+
<section class="stage" id="stage-order">
|
|
352
|
+
<div class="sec-head">
|
|
353
|
+
<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>
|
|
355
|
+
</div>
|
|
356
|
+
<div class="split">
|
|
357
|
+
<div class="card">
|
|
358
|
+
<div class="row2">
|
|
359
|
+
<div class="field"><label for="inpPakOrg">Организация</label><input type="text" id="inpPakOrg" value="ООО «Маслоторг»"></div>
|
|
360
|
+
<div class="field"><label for="inpPakInn">ИНН</label><input type="text" id="inpPakInn" value="7701234567" inputmode="numeric"></div>
|
|
460
361
|
</div>
|
|
461
|
-
<div class="
|
|
462
|
-
|
|
463
|
-
<
|
|
362
|
+
<div class="field"><label for="inpPakEmail">Email для счёта и личного кабинета</label><input type="email" id="inpPakEmail" value="admin@maslotorg.ru"></div>
|
|
363
|
+
<div class="field">
|
|
364
|
+
<label>Дополнительные годовые места сразу в поставку: <b id="lblPakSeats" style="color:var(--text)">0</b></label>
|
|
365
|
+
<input type="range" id="inpPakExtraSeats" min="0" max="150" value="0">
|
|
464
366
|
</div>
|
|
465
|
-
<div class="
|
|
466
|
-
<label
|
|
467
|
-
<div
|
|
468
|
-
<label style="display:flex; align-items:center; gap:8px; color:#fff; font-weight:400;">
|
|
469
|
-
<input type="checkbox" id="chkAddNas" checked onchange="recalcFranchise()">
|
|
470
|
-
<span>Поставка NAS для резервного копирования (+350 000 ₽)</span>
|
|
471
|
-
</label>
|
|
472
|
-
<label style="display:flex; align-items:center; gap:8px; color:#fff; font-weight:400;">
|
|
473
|
-
<input type="checkbox" id="chkAddInfer" onchange="recalcFranchise()">
|
|
474
|
-
<span>Доп. оборудование для инференса (+1 200 000 ₽)</span>
|
|
475
|
-
</label>
|
|
476
|
-
<label style="display:flex; align-items:center; gap:8px; color:#fff; font-weight:400;">
|
|
477
|
-
<input type="checkbox" id="chkAddApi" checked onchange="recalcFranchise()">
|
|
478
|
-
<span>Собственный платный API партнёра к моделям ИИ (+360 000 ₽/год)</span>
|
|
479
|
-
</label>
|
|
480
|
-
<label style="display:flex; align-items:center; gap:8px; color:#fff; font-weight:400;">
|
|
481
|
-
<input type="checkbox" id="chkAddSup" checked onchange="recalcFranchise()">
|
|
482
|
-
<span>Техническая поддержка и гарантийный сервис партнёра (+240 000 ₽/год)</span>
|
|
483
|
-
</label>
|
|
484
|
-
<label style="display:flex; align-items:center; gap:8px; color:#fff; font-weight:400;">
|
|
485
|
-
<input type="checkbox" id="chkAddSkills" checked onchange="recalcFranchise()">
|
|
486
|
-
<span>Дообучение ИИ-агентов (LoRA) и разработка навыков skills (+600 000 ₽)</span>
|
|
487
|
-
</label>
|
|
488
|
-
</div>
|
|
367
|
+
<div class="field">
|
|
368
|
+
<label>Уровень поддержки дополнительных мест</label>
|
|
369
|
+
<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>
|
|
489
370
|
</div>
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
371
|
+
<p class="note muted" style="margin:6px 0 0">Уже есть комплекс? <a href="#" data-action="addons">Войдите в кабинет</a>, чтобы докупить места со скидкой.</p>
|
|
372
|
+
</div>
|
|
373
|
+
<div class="card summary card-glow">
|
|
374
|
+
<span class="kicker">Итого</span>
|
|
375
|
+
<div class="sum-total" id="lblPakTotal">${rub(P.basePriceRub)}</div>
|
|
376
|
+
<div class="sum-lines" id="lblPakBreakdown"></div>
|
|
377
|
+
<button class="btn btn-primary btn-lg" style="width:100%" data-action="order-pak">Оформить и оплатить через ЮKassa</button>
|
|
378
|
+
<div class="status" id="pakOrderStatus"></div>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
</section>
|
|
382
|
+
|
|
383
|
+
<!-- ============ ДОКУПКА ЛИЦЕНЗИЙ (только после входа) ============ -->
|
|
384
|
+
<section class="stage" id="stage-addons">
|
|
385
|
+
<div class="sec-head">
|
|
386
|
+
<div><span class="kicker">Личный кабинет</span><h2 class="h2">Докупка пользовательских лицензий</h2></div>
|
|
387
|
+
<p id="userBadgeBox"></p>
|
|
388
|
+
</div>
|
|
389
|
+
<div class="split">
|
|
390
|
+
<div class="card">
|
|
391
|
+
<div class="field">
|
|
392
|
+
<label>Количество дополнительных мест</label>
|
|
393
|
+
<div style="display:flex;align-items:center;gap:16px">
|
|
394
|
+
<input type="range" id="rngAddonSeats" min="1" max="150" value="10" style="flex:1">
|
|
395
|
+
<input type="number" id="inpSeats" min="1" max="2000" value="10" style="width:110px">
|
|
498
396
|
</div>
|
|
499
397
|
</div>
|
|
500
|
-
<div
|
|
501
|
-
<
|
|
502
|
-
<
|
|
503
|
-
<button class="btn btn-outline" onclick="certifySpecialistAction()">Аттестовать и выдать сертификат</button>
|
|
398
|
+
<div class="field">
|
|
399
|
+
<label>Уровень поддержки</label>
|
|
400
|
+
<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>
|
|
504
401
|
</div>
|
|
505
|
-
<div
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
<div>
|
|
509
|
-
<div style="font-size:13px; color:var(--muted); text-transform:uppercase;">Фиксированный взнос региональной франшизы</div>
|
|
510
|
-
<div class="total-sum" id="lblFranchiseFee">1 400 000 ₽</div>
|
|
511
|
-
<div style="font-size:13px; color:#60a5fa; font-weight:700;" id="lblPartnerQuota"></div>
|
|
512
|
-
<div id="lblPartnerEcon" style="font-size:14px; color:var(--muted); margin-top:10px;"></div>
|
|
402
|
+
<div class="row2">
|
|
403
|
+
<div class="field"><label for="inpYears">Срок, лет</label><input type="number" id="inpYears" min="1" max="5" value="1"></div>
|
|
404
|
+
<div class="field"><label for="inpLicId">Лицензия комплекса</label><input type="text" id="inpLicId" readonly></div>
|
|
513
405
|
</div>
|
|
406
|
+
<div class="field" style="margin-top:6px"><label>Скидка от количества</label>${renderLadder('ladderAddons')}</div>
|
|
407
|
+
</div>
|
|
408
|
+
<div class="card summary card-glow">
|
|
409
|
+
<span class="kicker">К оплате</span>
|
|
410
|
+
<div class="sum-total" id="lblTotal">0 ₽</div>
|
|
411
|
+
<div class="small" id="lblDiscountBadge" style="color:var(--ok);font-weight:650"></div>
|
|
412
|
+
<div class="sum-lines" id="lblBreakdown"></div>
|
|
413
|
+
<button class="btn btn-primary btn-lg" style="width:100%" data-action="order-addons">Оплатить через ЮKassa</button>
|
|
414
|
+
<div class="status" id="orderStatus"></div>
|
|
514
415
|
</div>
|
|
515
416
|
</div>
|
|
417
|
+
</section>
|
|
516
418
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
419
|
+
<!-- ============ ФРАНШИЗА ============ -->
|
|
420
|
+
<section class="stage" id="stage-franchise">
|
|
421
|
+
<div class="sec-head">
|
|
422
|
+
<div><span class="kicker">Партнёрская программа</span><h2 class="h2">Региональная франшиза ODANT-WORK</h2></div>
|
|
423
|
+
<p>Число франшиз в субъекте пропорционально населению, стоимость — экономике региона. Резерв закрепляется сразу после невозвратного залога ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}.</p>
|
|
424
|
+
</div>
|
|
425
|
+
<div class="stats">
|
|
426
|
+
<div class="stat"><b id="statVacant">${totalVacant}</b><span>вакантных франшиз</span></div>
|
|
427
|
+
<div class="stat"><b>${visibleRegions.length}</b><span>субъектов РФ</span></div>
|
|
428
|
+
<div class="stat"><b>${rub(FRANCHISE_PROGRAM.baseFranchiseFeeRub)}</b><span>базовая стоимость франшизы</span></div>
|
|
429
|
+
<div class="stat"><b>${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</b><span>залог за резерв</span></div>
|
|
430
|
+
<div class="stat"><b>${FRANCHISE_PROGRAM.minDiscountPercent}→${FRANCHISE_PROGRAM.maxDiscountPercent}%</b><span>скидка партнёра от объёма</span></div>
|
|
431
|
+
</div>
|
|
524
432
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
<h3 style="margin:0;">Симулятор обращения в бот (MAX / Telegram / VK)</h3>
|
|
532
|
-
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
|
|
533
|
-
<div class="form-row">
|
|
534
|
-
<label>Мессенджер</label>
|
|
535
|
-
<select id="botChannel">
|
|
536
|
-
<option value="max">MAX (botapi.max.ru)</option>
|
|
537
|
-
<option value="telegram">Telegram</option>
|
|
538
|
-
<option value="vk">VK (ВКонтакте)</option>
|
|
539
|
-
</select>
|
|
433
|
+
<div class="map-layout">
|
|
434
|
+
<div class="card map-card">
|
|
435
|
+
<div class="map-tools">
|
|
436
|
+
<div class="chips" id="districtChips">
|
|
437
|
+
<button class="chip on" data-district="">Вся Россия</button>
|
|
438
|
+
${Object.keys(FEDERAL_DISTRICTS).map(d => `<button class="chip" data-district="${d}" title="${esc(FEDERAL_DISTRICTS[d])} федеральный округ">${d}</button>`).join('')}
|
|
540
439
|
</div>
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
440
|
+
</div>
|
|
441
|
+
<div class="map-box" id="mapBox">
|
|
442
|
+
<svg id="russiaMapSvg" viewBox="0 0 1000 551" role="img" aria-label="Интерактивная карта субъектов Российской Федерации">
|
|
443
|
+
<g id="mapRegions"></g>
|
|
444
|
+
<path id="mapHover" class="rg-hover" d=""></path>
|
|
445
|
+
<path id="mapSel" class="rg-sel" d=""></path>
|
|
446
|
+
<g id="mapDots"></g>
|
|
447
|
+
<g id="mapLabels" class="map-labels"></g>
|
|
448
|
+
<g id="mapPins"></g>
|
|
449
|
+
</svg>
|
|
450
|
+
<div class="map-zoom">
|
|
451
|
+
<button class="icon-btn" data-zoom="in" title="Приблизить">${icon('plus')}</button>
|
|
452
|
+
<button class="icon-btn" data-zoom="out" title="Отдалить">${icon('minus')}</button>
|
|
453
|
+
<button class="icon-btn" data-zoom="reset" title="Вся карта">${icon('expand')}</button>
|
|
454
|
+
</div>
|
|
455
|
+
<div class="map-legend">
|
|
456
|
+
Вакантных франшиз в субъекте
|
|
457
|
+
<div class="legend-bar"><i style="background:var(--m0)"></i><i style="background:var(--m1)"></i><i style="background:var(--m2)"></i><i style="background:var(--m3)"></i><i style="background:var(--m4)"></i><i style="background:var(--m5)"></i></div>
|
|
458
|
+
<div class="legend-scale"><span>0</span><span>1</span><span>2</span><span>3–4</span><span>5–7</span><span>8+</span></div>
|
|
544
459
|
</div>
|
|
460
|
+
<div class="map-tip" id="mapTip"></div>
|
|
461
|
+
</div>
|
|
462
|
+
<div class="map-hint"><span>Колесо — масштаб, перетаскивание — сдвиг, клик — выбор субъекта</span><span id="mapSource"></span></div>
|
|
463
|
+
</div>
|
|
464
|
+
|
|
465
|
+
<aside class="card reg-panel card-glow">
|
|
466
|
+
<span class="tag" id="mapRegDistrict">—</span>
|
|
467
|
+
<h3 class="reg-name" id="mapRegName">Выберите субъект на карте</h3>
|
|
468
|
+
<div class="small muted" id="mapRegMeta"></div>
|
|
469
|
+
<div class="vacancy">
|
|
470
|
+
<svg class="ringp" viewBox="0 0 80 80"><circle class="bg" cx="40" cy="40" r="32"/><circle class="fg" id="ringFg" cx="40" cy="40" r="32" stroke-dasharray="201" stroke-dashoffset="201"/><text x="40" y="40" id="ringTxt">0</text></svg>
|
|
471
|
+
<div><div class="small muted">Вакантно франшиз</div><div class="display" style="font-size:22px;font-weight:800" id="mapRegVacant">—</div><div class="small muted" id="mapRegOccupied"></div></div>
|
|
545
472
|
</div>
|
|
546
|
-
<div class="
|
|
547
|
-
<
|
|
548
|
-
<
|
|
473
|
+
<div class="kv">
|
|
474
|
+
<div><span>Стоимость франшизы</span><b id="mapRegFee">—</b></div>
|
|
475
|
+
<div><span>Залог за резерв</span><b>${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</b></div>
|
|
476
|
+
<div><span>Индекс экономики</span><b id="mapRegIndex">—</b></div>
|
|
477
|
+
<div><span>Остаток после залога</span><b id="mapRegRest">—</b></div>
|
|
549
478
|
</div>
|
|
550
|
-
<div
|
|
551
|
-
|
|
552
|
-
<
|
|
553
|
-
<
|
|
554
|
-
<button class="btn btn-outline" onclick="setBotCmd('Помогите настроить обмен с банком в модуле Register')">Тестовый тикет</button>
|
|
479
|
+
<div class="field"><label for="inpPartnerCompany">Компания</label><input type="text" id="inpPartnerCompany" value="ООО «УралТехИнтеграция»"></div>
|
|
480
|
+
<div class="row2">
|
|
481
|
+
<div class="field"><label for="inpPartnerInn">ИНН</label><input type="text" id="inpPartnerInn" value="6670123456" inputmode="numeric"></div>
|
|
482
|
+
<div class="field"><label for="inpPartnerEmail">Email</label><input type="email" id="inpPartnerEmail" value="partner@uraltech.ru"></div>
|
|
555
483
|
</div>
|
|
556
|
-
<
|
|
484
|
+
<button class="btn btn-primary" style="width:100%" id="btnReserveSelected" data-action="reserve-selected" disabled>Зарезервировать · залог ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</button>
|
|
485
|
+
<div class="status" id="partnerActionMsg"></div>
|
|
486
|
+
</aside>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<div class="card">
|
|
490
|
+
<div class="map-tools">
|
|
491
|
+
<h3 class="h3" style="margin:0">Все субъекты Российской Федерации</h3>
|
|
492
|
+
<input type="text" id="inpRegionFilter" placeholder="Поиск: Татарстан, Рязань, ДФО…" style="max-width:320px">
|
|
557
493
|
</div>
|
|
494
|
+
<div class="table-wrap">
|
|
495
|
+
<table class="t">
|
|
496
|
+
<thead><tr>
|
|
497
|
+
<th data-sort="name">Субъект<span class="arr">↕</span></th>
|
|
498
|
+
<th data-sort="district">Округ<span class="arr">↕</span></th>
|
|
499
|
+
<th data-sort="populationMillions" class="num">Население<span class="arr">↕</span></th>
|
|
500
|
+
<th data-sort="vacantFranchises">Вакантно<span class="arr">↕</span></th>
|
|
501
|
+
<th data-sort="franchiseFeeRub" class="num">Франшиза<span class="arr">↕</span></th>
|
|
502
|
+
<th></th>
|
|
503
|
+
</tr></thead>
|
|
504
|
+
<tbody id="regionsTableBody"></tbody>
|
|
505
|
+
</table>
|
|
506
|
+
</div>
|
|
507
|
+
</div>
|
|
558
508
|
|
|
509
|
+
<div class="split">
|
|
559
510
|
<div class="card">
|
|
560
|
-
<
|
|
561
|
-
<
|
|
562
|
-
|
|
563
|
-
|
|
511
|
+
<span class="kicker">Экономика партнёра</span>
|
|
512
|
+
<h3 class="h3" style="font-size:22px;margin-top:8px">Скидка растёт вместе с продажами</h3>
|
|
513
|
+
<p class="note">Партнёр сам закупает оборудование, устанавливает ODANT-WORK, привязывает лицензии и даёт клиенту ${FRANCHISE_PROGRAM.hardwareWarrantyYears} год гарантии на железо. Скидка — от ${FRANCHISE_PROGRAM.minDiscountPercent}% до ${FRANCHISE_PROGRAM.maxDiscountPercent}% пропорционально годовому объёму.</p>
|
|
514
|
+
<div class="field" style="margin-top:14px">
|
|
515
|
+
<label>Годовой объём продаж: <b id="lblPartnerSales" style="color:var(--text)">25 млн ₽</b></label>
|
|
516
|
+
<input type="range" id="rngPartnerSales" min="0" max="150" step="5" value="25">
|
|
517
|
+
</div>
|
|
518
|
+
<div class="calc-out">
|
|
519
|
+
<div><b id="outPartnerDisc">20%</b><span>скидка партнёра</span></div>
|
|
520
|
+
<div><b id="outPartnerMargin">1 000 000 ₽</b><span>доход с одного комплекса</span></div>
|
|
521
|
+
<div><b id="outPartnerLevel">Silver</b><span>статус партнёра</span></div>
|
|
564
522
|
</div>
|
|
565
|
-
|
|
523
|
+
</div>
|
|
524
|
+
<div class="card">
|
|
525
|
+
<span class="kicker">Как работает партнёр</span>
|
|
526
|
+
<ul class="list">${FRANCHISE_PROGRAM.partnerOperationsModel.map(s => `<li>${esc(s)}</li>`).join('')}</ul>
|
|
566
527
|
</div>
|
|
567
528
|
</div>
|
|
568
|
-
</div>
|
|
569
|
-
</section>
|
|
570
529
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
<div>WORK ПАК · work.odant.org · AMD Ryzen 495 8085 · 192 ГБ RAM · SSD 4 ТБ · Публичный ключ центра сертификации Ed25519: <code>${publicKey.slice(0, 28)}…</code></div>
|
|
574
|
-
</div>
|
|
575
|
-
</footer>
|
|
530
|
+
<div class="sec-head"><div><span class="kicker">Доходы партнёра</span><h2 class="h2">100% дохода с дополнительных услуг</h2></div></div>
|
|
531
|
+
<div class="grid g3">${renderStreams()}</div>
|
|
576
532
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const REGIONS = ${regionsJson};
|
|
581
|
-
const TRACKS = ${tracksJson};
|
|
582
|
-
const FRANCHISE = ${franchiseJson};
|
|
583
|
-
let lastRegisteredPartnerId = '';
|
|
584
|
-
|
|
585
|
-
function fmtRub(n) {
|
|
586
|
-
return Number(n || 0).toLocaleString('ru-RU') + ' ₽';
|
|
587
|
-
}
|
|
533
|
+
<div class="sec-head"><div><span class="kicker">Обучение и сертификация</span><h2 class="h2">Программа подготовки специалистов</h2></div></div>
|
|
534
|
+
<div class="grid g4">${renderTracks()}</div>
|
|
535
|
+
</section>
|
|
588
536
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
537
|
+
<!-- ============ ПОДДЕРЖКА ============ -->
|
|
538
|
+
<section class="stage" id="stage-support">
|
|
539
|
+
<div class="sec-head">
|
|
540
|
+
<div><span class="kicker">Сервис</span><h2 class="h2">Поддержка в мессенджерах MAX, Telegram и VK</h2></div>
|
|
541
|
+
<p>Боты принимают обращения, показывают статус лицензии, выставляют счета и передают вопросы инженеру по SLA вашего тарифа.</p>
|
|
542
|
+
</div>
|
|
543
|
+
<div class="grid g2">
|
|
592
544
|
<div class="card">
|
|
593
|
-
<
|
|
594
|
-
<
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
</div>
|
|
598
|
-
\`).join('');
|
|
599
|
-
|
|
600
|
-
const tGrid = document.getElementById('tiersGrid');
|
|
601
|
-
tGrid.innerHTML = TIERS.map(t => \`
|
|
602
|
-
<div class="card \${t.id === 'business' ? 'featured' : ''}">
|
|
603
|
-
<span class="pill">\${t.badge}</span>
|
|
604
|
-
<h3 style="margin:4px 0 0; font-size:22px;">\${t.name}</h3>
|
|
605
|
-
<div class="tier-price">\${fmtRub(t.pricePerUserYearRub)} <span>/ 1 польз. в год</span></div>
|
|
606
|
-
<div style="font-size:13px; color:#34d399; font-weight:600;">Реакция SLA: \${t.reactionLabel} (\${t.scheduleLabel})</div>
|
|
607
|
-
<ul class="feature-ul">
|
|
608
|
-
\${t.features.map(f => \`<li>\${f}</li>\`).join('')}
|
|
609
|
-
</ul>
|
|
610
|
-
<button class="btn \${t.id === 'business' ? 'btn-primary' : 'btn-outline'}" style="margin-top:auto;" onclick="chooseTier('\${t.id}')">Выбрать \${t.name}</button>
|
|
611
|
-
</div>
|
|
612
|
-
\`).join('');
|
|
613
|
-
|
|
614
|
-
const selReg = document.getElementById('selRegion');
|
|
615
|
-
if (selReg) {
|
|
616
|
-
selReg.innerHTML = REGIONS.map(r => \`
|
|
617
|
-
<option value="\${r.code}" \${r.code === 'RU-SVE' ? 'selected' : ''}>
|
|
618
|
-
\${r.name} (\${r.district}) — Население: \${r.populationMillions} млн чел. | Индекс экономики: \${r.economicIndex}
|
|
619
|
-
</option>
|
|
620
|
-
\`).join('');
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
const trGrid = document.getElementById('tracksGrid');
|
|
624
|
-
if (trGrid) {
|
|
625
|
-
trGrid.innerHTML = TRACKS.map(tr => \`
|
|
626
|
-
<div class="card">
|
|
627
|
-
<span class="pill">\${tr.code} · \${tr.hours} ак. часов</span>
|
|
628
|
-
<h4 style="margin:4px 0 0; font-size:16px;">\${tr.title}</h4>
|
|
629
|
-
<div style="font-size:12px; color:#34d399;">Проходной балл: \${tr.passScore}/100 \${tr.requiredForFranchise ? '· Обязателен для франшизы' : '· Профильный'}</div>
|
|
630
|
-
<ul class="feature-ul">
|
|
631
|
-
\${tr.topics.map(tp => \`<li>\${tp}</li>\`).join('')}
|
|
632
|
-
</ul>
|
|
545
|
+
<h3 class="h3">Консоль бота</h3>
|
|
546
|
+
<div class="row2" style="margin-top:12px">
|
|
547
|
+
<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>
|
|
548
|
+
<div class="field"><label for="botChatId">Chat ID</label><input type="text" id="botChatId" value="client-101"></div>
|
|
633
549
|
</div>
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const stGrid = document.getElementById('streamsGrid');
|
|
638
|
-
if (stGrid && FRANCHISE.partnerRevenueStreams) {
|
|
639
|
-
stGrid.innerHTML = FRANCHISE.partnerRevenueStreams.map(st => \`
|
|
640
|
-
<div class="card">
|
|
641
|
-
<span class="pill">\${st.share}</span>
|
|
642
|
-
<h4 style="margin:4px 0 0; font-size:16px;">\${st.title}</h4>
|
|
643
|
-
<p style="margin:0; color:var(--muted); font-size:13.5px;">\${st.description || st.share}</p>
|
|
644
|
-
<div style="margin-top:auto; padding-top:6px; font-size:13px; font-weight:700; color:#34d399;">Средний чек: ~\${fmtRub(st.recommendedAvgRub)}</div>
|
|
550
|
+
<div class="field"><label for="botText">Команда или текст</label>
|
|
551
|
+
<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>
|
|
645
552
|
</div>
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
553
|
+
<div class="chips" style="margin-bottom:12px">
|
|
554
|
+
<button class="chip" data-bot="/status">/status</button>
|
|
555
|
+
<button class="chip" data-bot="/buy business 10">/buy business 10</button>
|
|
556
|
+
<button class="chip" data-bot="/help">/help</button>
|
|
557
|
+
</div>
|
|
558
|
+
<div class="console" id="botReplyBox">Ответ бота появится здесь…</div>
|
|
559
|
+
</div>
|
|
560
|
+
<div class="card">
|
|
561
|
+
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap">
|
|
562
|
+
<h3 class="h3" style="margin:0">Реестр лицензий и резервов</h3>
|
|
563
|
+
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
564
|
+
<button class="btn btn-ghost btn-sm" data-action="registry">Обновить</button>
|
|
565
|
+
<button class="btn btn-ok btn-sm" data-action="demo-pak">Выпустить демо-лицензию</button>
|
|
566
|
+
<button class="btn btn-primary btn-sm" data-stage="admin">Открыть админку →</button>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
<div class="console" id="registryBox" style="margin-top:14px">Загрузка…</div>
|
|
570
|
+
</div>
|
|
571
|
+
</div>
|
|
572
|
+
<div class="card">
|
|
573
|
+
<div class="spec-wrap">
|
|
574
|
+
<div>
|
|
575
|
+
<span class="kicker">Разработчик платформы</span>
|
|
576
|
+
<h2 class="h2">${esc(VENDOR_CONTACTS.company)}</h2>
|
|
577
|
+
<p class="note">${esc(VENDOR_CONTACTS.description)}. Головной офис ODANT — в Рязани.</p>
|
|
578
|
+
</div>
|
|
579
|
+
<div>
|
|
580
|
+
<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>
|
|
581
|
+
<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>
|
|
582
|
+
<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>
|
|
583
|
+
</div>
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
</section>
|
|
649
587
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
let extraPerClient = 0;
|
|
663
|
-
if (document.getElementById('chkAddNas')?.checked) extraPerClient += 350_000;
|
|
664
|
-
if (document.getElementById('chkAddInfer')?.checked) extraPerClient += 1_200_000;
|
|
665
|
-
if (document.getElementById('chkAddApi')?.checked) extraPerClient += 360_000;
|
|
666
|
-
if (document.getElementById('chkAddSup')?.checked) extraPerClient += 240_000;
|
|
667
|
-
if (document.getElementById('chkAddSkills')?.checked) extraPerClient += 600_000;
|
|
668
|
-
|
|
669
|
-
const totalPerClient = marginPerPak + extraPerClient;
|
|
670
|
-
const paksCount = Math.max(1, Math.round(salesRub / PRODUCT.basePriceRub));
|
|
671
|
-
const annualTotalIncome = Math.round(salesRub * (discountPct / 100)) + (paksCount * extraPerClient);
|
|
672
|
-
|
|
673
|
-
document.getElementById('lblFranchiseFee').textContent = fmtRub(feeRub);
|
|
674
|
-
document.getElementById('lblPartnerQuota').textContent = 'Квота региона по населению (' + reg.populationMillions + ' млн чел.): макс. ' + quota + ' партнёр(ов)';
|
|
675
|
-
document.getElementById('lblPartnerEcon').innerHTML = [
|
|
676
|
-
'• Экономический индекс региона (' + reg.name + ', ВРП ' + reg.grpTrillionRub + ' трлн ₽): <b>' + reg.economicIndex + '</b>',
|
|
677
|
-
'• Скидка партнёра (от 10% до 50%): <b>' + discountPct + '%</b> (при объёме ' + salesM + ' млн ₽/год)',
|
|
678
|
-
'• Доход партнёра с лицензии 1 ПАК: <b>' + fmtRub(marginPerPak) + '</b>',
|
|
679
|
-
'• Доп. доход партнёра на клиенте (NAS, инференс, API моделей, поддержка, обучение агентов/навыков): <b>+' + fmtRub(extraPerClient) + '</b> (100% партнёру)',
|
|
680
|
-
'• Совокупный доход партнёра с 1 внедрения: <b>' + fmtRub(totalPerClient) + '</b>',
|
|
681
|
-
'• Прогноз совокупного годового дохода партнёра: <b style="color:#34d399;">' + fmtRub(annualTotalIncome) + '</b>',
|
|
682
|
-
].join('<br>');
|
|
683
|
-
}
|
|
588
|
+
<!-- ============ АДМИНКА СЕРВЕРА ЛИЦЕНЗИЙ (/admin) ============ -->
|
|
589
|
+
<section class="stage" id="stage-admin">
|
|
590
|
+
<div class="sec-head">
|
|
591
|
+
<div>
|
|
592
|
+
<span class="kicker">Центр сертификации и биллинга · /admin</span>
|
|
593
|
+
<h2 class="h2">Админ-панель сервера лицензий ODANT-WORK</h2>
|
|
594
|
+
</div>
|
|
595
|
+
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
596
|
+
<button class="btn btn-ghost btn-sm" data-action="registry">Обновить реестр</button>
|
|
597
|
+
<button class="btn btn-ok btn-sm" data-action="demo-pak">+ Демо-лицензия ПАК</button>
|
|
598
|
+
</div>
|
|
599
|
+
</div>
|
|
684
600
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
const res = await fetch('/api/partners/issue-license', {
|
|
693
|
-
method: 'POST',
|
|
694
|
-
headers: { 'Content-Type': 'application/json' },
|
|
695
|
-
body: JSON.stringify({
|
|
696
|
-
partnerId: lastRegisteredPartnerId,
|
|
697
|
-
customer: {
|
|
698
|
-
name: document.getElementById('inpOrg').value.trim() || 'ООО «Заказчик Регион»',
|
|
699
|
-
inn: document.getElementById('inpInn').value.trim() || '6601234567',
|
|
700
|
-
},
|
|
701
|
-
hardwareWarrantyYears: 1,
|
|
702
|
-
partnerAddons: {
|
|
703
|
-
nasBackup: document.getElementById('chkAddNas')?.checked,
|
|
704
|
-
inferenceHardware: document.getElementById('chkAddInfer')?.checked,
|
|
705
|
-
partnerModelApiUrl: document.getElementById('chkAddApi')?.checked ? 'https://ai.partner-node.ru/v1' : '',
|
|
706
|
-
techSupportContract: document.getElementById('chkAddSup')?.checked,
|
|
707
|
-
agentTrainingSkills: document.getElementById('chkAddSkills')?.checked,
|
|
708
|
-
},
|
|
709
|
-
}),
|
|
710
|
-
});
|
|
711
|
-
const data = await res.json();
|
|
712
|
-
if (!res.ok) throw new Error(data.error || 'Ошибка выдачи лицензии партнёром');
|
|
713
|
-
document.getElementById('inpLicId').value = data.license.licenseId;
|
|
714
|
-
msgEl.textContent = 'Партнёр установил ПАК, привязал HWID (' + data.license.hardware.hwid + '), предоставил 1 год гарантии на оборудование и выдал лицензию ' + data.license.licenseId + '! Новая скидка партнёра: ' + data.partner.discountPercent + '%';
|
|
715
|
-
refreshRegistry();
|
|
716
|
-
} catch (err) {
|
|
717
|
-
msgEl.textContent = 'Ошибка: ' + err.message;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
601
|
+
<div class="stats">
|
|
602
|
+
<div class="stat"><b id="admStatLic">0</b><span id="admStatLicSub">лицензий ПАК · 0 мест</span></div>
|
|
603
|
+
<div class="stat"><b id="admStatPaid">0 ₽</b><span id="admStatPaidSub">оплачено заказов: 0</span></div>
|
|
604
|
+
<div class="stat"><b id="admStatPending">0 ₽</b><span id="admStatPendingSub">ожидают оплаты: 0</span></div>
|
|
605
|
+
<div class="stat"><b id="admStatPartners">0 / 0</b><span>партнёров / резервов франшиз</span></div>
|
|
606
|
+
<div class="stat"><b id="admStatTickets">0</b><span id="admStatTicketsSub">открытых тикетов SLA</span></div>
|
|
607
|
+
</div>
|
|
720
608
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
msgEl.textContent = 'Отказ: ' + err.message;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
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>
|
|
614
|
+
</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>
|
|
742
627
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
});
|
|
760
|
-
const data = await res.json();
|
|
761
|
-
if (!res.ok) throw new Error(data.error || 'Ошибка сертификации');
|
|
762
|
-
msgEl.textContent = 'Выдан сертификат ' + data.certificate.certificateId + ' (' + data.certificate.trackTitle + ') специалисту ' + data.certificate.specialistName + '!';
|
|
763
|
-
refreshRegistry();
|
|
764
|
-
} catch (err) {
|
|
765
|
-
msgEl.textContent = 'Ошибка: ' + err.message;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
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>
|
|
768
644
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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>
|
|
774
661
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
if (seats > 0) {
|
|
792
|
-
lines.push('• Доп. годовые лицензии (' + seats + ' мест × ' + years + ' г., уровень ' + tier.name + ', реакция ' + tier.reactionLabel + '): ' + fmtRub(addonSum));
|
|
793
|
-
}
|
|
794
|
-
lines.push('• Итого одновременных рабочих мест после оплаты: ' + (5 + seats) + ' (5 вечных + ' + seats + ' годовых)');
|
|
795
|
-
document.getElementById('lblBreakdown').innerHTML = lines.join('<br>');
|
|
796
|
-
}
|
|
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>
|
|
797
678
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
email: document.getElementById('inpEmail').value.trim(),
|
|
815
|
-
},
|
|
816
|
-
}),
|
|
817
|
-
});
|
|
818
|
-
const data = await res.json();
|
|
819
|
-
if (!res.ok) throw new Error(data.error || 'Ошибка создания заказа');
|
|
820
|
-
document.getElementById('inpLicId').value = data.licenseId || '';
|
|
821
|
-
statusEl.innerHTML = 'Заказ <b>' + data.orderId + '</b> создан (лицензия <b>' + data.licenseId + '</b>). <a href="' + data.confirmationUrl + '" target="_blank" style="color:#34d399; font-weight:700; text-decoration:underline;">Оплатить в ЮKassa →</a>';
|
|
822
|
-
refreshRegistry();
|
|
823
|
-
} catch (err) {
|
|
824
|
-
statusEl.textContent = 'Ошибка: ' + err.message;
|
|
825
|
-
}
|
|
826
|
-
}
|
|
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>
|
|
692
|
+
</div>
|
|
693
|
+
</div>
|
|
694
|
+
</section>
|
|
827
695
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
696
|
+
<footer>
|
|
697
|
+
<div class="footer">
|
|
698
|
+
<div>
|
|
699
|
+
<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>
|
|
701
|
+
</div>
|
|
702
|
+
<div>
|
|
703
|
+
<h4>Комплекс</h4>
|
|
704
|
+
<p>AMD Ryzen™ AI Max+ PRO 495 · Radeon™ 8065S</p>
|
|
705
|
+
<p>192 ГБ LPDDR5x-8533 · SSD 4 ТБ</p>
|
|
706
|
+
<p>От ${rub(P.basePriceRub)} · ${P.baseSeats} вечных мест</p>
|
|
707
|
+
</div>
|
|
708
|
+
<div>
|
|
709
|
+
<h4>${esc(VENDOR_CONTACTS.company)}</h4>
|
|
710
|
+
<p>${esc(VENDOR_CONTACTS.address)}</p>
|
|
711
|
+
<p>${VENDOR_CONTACTS.phones.map(esc).join(', ')}</p>
|
|
712
|
+
<p><a href="${esc(VENDOR_CONTACTS.site)}" target="_blank" rel="noopener">${esc(VENDOR_CONTACTS.site.replace(/^https?:\/\//, ''))}</a></p>
|
|
713
|
+
</div>
|
|
714
|
+
</div>
|
|
715
|
+
<div class="footer-bottom">
|
|
716
|
+
<span>© ODANT · ${esc(P.domain)}</span>
|
|
717
|
+
<span>Ключ центра сертификации Ed25519: <code>${esc(publicKey.slice(0, 28))}…</code></span>
|
|
718
|
+
</div>
|
|
719
|
+
</footer>
|
|
720
|
+
</div>
|
|
832
721
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
722
|
+
<!-- Вход в кабинет -->
|
|
723
|
+
<div class="modal" id="authModal" role="dialog" aria-modal="true" aria-labelledby="authTitle">
|
|
724
|
+
<div class="card">
|
|
725
|
+
<div class="modal-head">
|
|
726
|
+
<img src="/assets/odant-logo.png" alt="">
|
|
727
|
+
<div><h3 class="h3" id="authTitle" style="margin:0">Кабинет владельца ODANT-WORK</h3><div class="small muted">Докупка лицензий доступна после входа</div></div>
|
|
728
|
+
</div>
|
|
729
|
+
<div class="field"><label for="authEmail">Корпоративный email</label><input type="email" id="authEmail" value="admin@maslotorg.ru"></div>
|
|
730
|
+
<div class="field"><label for="authCompany">Организация</label><input type="text" id="authCompany" value="ООО «Маслоторг»"></div>
|
|
731
|
+
<div class="field"><label for="authLicId">Номер лицензии (если уже выдан)</label><input type="text" id="authLicId" placeholder="Будет выпущен автоматически"></div>
|
|
732
|
+
<div style="display:flex;gap:10px;margin-top:8px">
|
|
733
|
+
<button class="btn btn-primary" style="flex:1" data-action="login">Войти</button>
|
|
734
|
+
<button class="btn btn-ghost" data-action="auth-close">Отмена</button>
|
|
735
|
+
</div>
|
|
736
|
+
</div>
|
|
737
|
+
</div>
|
|
847
738
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
document.getElementById('inpLicId').value = lic.licenseId;
|
|
863
|
-
document.getElementById('botText').value = '/bind ' + lic.licenseId;
|
|
864
|
-
}
|
|
865
|
-
refreshRegistry();
|
|
866
|
-
}
|
|
739
|
+
<!-- Просмотр подписанного сертификата Ed25519 -->
|
|
740
|
+
<div class="modal" id="certModal" role="dialog" aria-modal="true" aria-labelledby="certTitle">
|
|
741
|
+
<div class="card" style="max-width:640px">
|
|
742
|
+
<div class="modal-head">
|
|
743
|
+
<img src="/assets/odant-logo.png" alt="">
|
|
744
|
+
<div><h3 class="h3" id="certTitle" style="margin:0">Сертификат лицензии Ed25519</h3><div class="small muted" id="certSub">WORK-LIC-v1</div></div>
|
|
745
|
+
</div>
|
|
746
|
+
<pre class="console" id="certJsonBox" style="max-height:360px;margin:0"></pre>
|
|
747
|
+
<div style="display:flex;gap:10px;margin-top:14px">
|
|
748
|
+
<button class="btn btn-primary" data-action="cert-copy">Скопировать JSON</button>
|
|
749
|
+
<button class="btn btn-ghost" data-action="cert-close">Закрыть</button>
|
|
750
|
+
</div>
|
|
751
|
+
</div>
|
|
752
|
+
</div>
|
|
867
753
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
754
|
+
<!-- ИИ-агент лицензионного центра -->
|
|
755
|
+
<button class="ai-fab" data-action="ai-toggle" aria-controls="aiDrawer"><img src="/assets/odant-logo.png" alt=""><span class="t">ИИ-агент ODANT</span><span class="live"></span></button>
|
|
756
|
+
<div class="ai-drawer" id="aiDrawer" aria-live="polite">
|
|
757
|
+
<div class="ai-head">
|
|
758
|
+
<img src="/assets/odant-logo.png" alt="">
|
|
759
|
+
<div><b>ИИ-агент ODANT-WORK</b><span>● в сети</span></div>
|
|
760
|
+
<button class="icon-btn" data-action="ai-toggle" aria-label="Закрыть">${icon('close')}</button>
|
|
761
|
+
</div>
|
|
762
|
+
<div class="ai-msgs" id="aiMessages">
|
|
763
|
+
<div class="msg bot">Здравствуйте! Я ИИ-агент лицензионного центра ODANT-WORK. Расскажу о комплексе и ИИ-агентах, посчитаю лицензии со скидкой и подберу вакантную франшизу в вашем регионе.</div>
|
|
764
|
+
</div>
|
|
765
|
+
<div class="ai-sugg">
|
|
766
|
+
<button data-ask="Что умеют ИИ-консультант и агенты?">ИИ-агенты</button>
|
|
767
|
+
<button data-ask="Рассчитай 25 мест Business">25 мест Business</button>
|
|
768
|
+
<button data-ask="Сколько вакантных франшиз в Татарстане?">Франшиза в Татарстане</button>
|
|
769
|
+
<button data-ask="Характеристики комплекса">Железо</button>
|
|
770
|
+
<button data-ask="Контакты разработчика">Контакты</button>
|
|
771
|
+
</div>
|
|
772
|
+
<div class="ai-input">
|
|
773
|
+
<input type="text" id="aiInput" placeholder="Спросите ИИ-агента…" autocomplete="off">
|
|
774
|
+
<button class="btn btn-primary" data-action="ai-send" aria-label="Отправить">${icon('send')}</button>
|
|
775
|
+
</div>
|
|
776
|
+
</div>
|
|
882
777
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
recalcFranchise();
|
|
886
|
-
refreshRegistry();
|
|
887
|
-
</script>
|
|
778
|
+
<script>window.__ODANT__ = ${jsonForScript(data)};</script>
|
|
779
|
+
<script src="/assets/landing.js${v}" defer></script>
|
|
888
780
|
</body>
|
|
889
781
|
</html>`;
|
|
890
782
|
}
|
|
891
783
|
|
|
892
|
-
/**
|
|
893
|
-
* Страница встроенной песочницы ЮKassa (/pay/sandbox/:orderId) для удобной демонстрации и тестирования оплаты.
|
|
894
|
-
*/
|
|
895
784
|
export function renderSandboxPaymentPage(order) {
|
|
896
785
|
return /* html */`<!DOCTYPE html>
|
|
897
786
|
<html lang="ru">
|
|
898
787
|
<head>
|
|
899
788
|
<meta charset="utf-8">
|
|
900
|
-
<
|
|
789
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
790
|
+
<title>ЮKassa — оплата заказа ${esc(order.orderId)}</title>
|
|
791
|
+
<link rel="icon" type="image/png" href="/favicon.ico">
|
|
901
792
|
<style>
|
|
902
|
-
body {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}
|
|
913
|
-
.
|
|
914
|
-
|
|
915
|
-
border-radius: 16px;
|
|
916
|
-
max-width: 480px;
|
|
917
|
-
width: 100%;
|
|
918
|
-
padding: 28px;
|
|
919
|
-
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.08);
|
|
920
|
-
}
|
|
921
|
-
.amount {
|
|
922
|
-
font-size: 32px;
|
|
923
|
-
font-weight: 800;
|
|
924
|
-
color: #2563eb;
|
|
925
|
-
margin: 12px 0;
|
|
926
|
-
}
|
|
927
|
-
.btn {
|
|
928
|
-
width: 100%;
|
|
929
|
-
padding: 14px;
|
|
930
|
-
border-radius: 10px;
|
|
931
|
-
border: none;
|
|
932
|
-
font-size: 16px;
|
|
933
|
-
font-weight: 700;
|
|
934
|
-
cursor: pointer;
|
|
935
|
-
margin-top: 12px;
|
|
936
|
-
}
|
|
937
|
-
.btn-pay { background: #2563eb; color: #fff; }
|
|
938
|
-
.btn-cancel { background: #e2e8f0; color: #334155; }
|
|
793
|
+
body { margin: 0; min-height: 100vh; display: grid; place-items: center; padding: 20px; font-family: Inter, -apple-system, "Segoe UI", Roboto, sans-serif; color: #eef4fb;
|
|
794
|
+
background: radial-gradient(60vmax 40vmax at 70% 10%, rgba(0,163,224,.18), transparent 60%), #04060b; }
|
|
795
|
+
.card { width: 100%; max-width: 480px; padding: 30px; border-radius: 22px; background: rgba(12,18,31,.85); border: 1px solid rgba(148,170,200,.2); box-shadow: 0 30px 80px -20px rgba(0,0,0,.7); }
|
|
796
|
+
.head { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; }
|
|
797
|
+
.head img { width: 40px; height: 40px; }
|
|
798
|
+
.k { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: #4cc3f0; }
|
|
799
|
+
.amount { font-size: 40px; font-weight: 800; letter-spacing: -.03em; margin: 8px 0 16px; }
|
|
800
|
+
.row { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid rgba(148,170,200,.12); font-size: 14px; }
|
|
801
|
+
.row span { color: #7b8aa1; }
|
|
802
|
+
.btn { width: 100%; padding: 15px; border-radius: 14px; border: 0; font: inherit; font-weight: 700; font-size: 15px; cursor: pointer; margin-top: 12px; }
|
|
803
|
+
.pay { color: #fff; background: linear-gradient(135deg, #00a3e0, #1d6ff2); box-shadow: 0 12px 30px -10px rgba(0,163,224,.5); }
|
|
804
|
+
.cancel { color: #b4c2d4; background: rgba(148,170,200,.1); }
|
|
805
|
+
#msg { margin-top: 14px; font-weight: 600; color: #22c55e; min-height: 20px; }
|
|
939
806
|
</style>
|
|
940
807
|
</head>
|
|
941
808
|
<body>
|
|
942
809
|
<div class="card">
|
|
943
|
-
<div
|
|
944
|
-
<
|
|
810
|
+
<div class="head"><img src="/assets/odant-logo.png" alt=""><div><div class="k">ЮKassa · тестовая оплата</div><b>ODANT-WORK</b></div></div>
|
|
811
|
+
<div class="k" style="color:#7b8aa1">Заказ ${esc(order.orderId)}</div>
|
|
945
812
|
<div class="amount">${Number(order.amountRub).toLocaleString('ru-RU')} ₽</div>
|
|
946
|
-
<div
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
<
|
|
953
|
-
<button class="btn btn-cancel" onclick="confirmPay('payment.canceled')">Отменить платёж</button>
|
|
954
|
-
<div id="msg" style="margin-top:14px; font-size:14px; font-weight:600; color:#059669;"></div>
|
|
813
|
+
<div class="row"><span>Назначение</span><b>${esc(order.tierName)}</b></div>
|
|
814
|
+
<div class="row"><span>Плательщик</span><b>${esc(order.customer?.name || '—')}</b></div>
|
|
815
|
+
<div class="row"><span>ИНН</span><b>${esc(order.customer?.inn || '—')}</b></div>
|
|
816
|
+
<div class="row"><span>Статус</span><b id="st">${esc(order.status)}</b></div>
|
|
817
|
+
<button class="btn pay" onclick="confirmPay('payment.succeeded')">Оплатить</button>
|
|
818
|
+
<button class="btn cancel" onclick="confirmPay('payment.canceled')">Отменить платёж</button>
|
|
819
|
+
<div id="msg"></div>
|
|
955
820
|
</div>
|
|
956
821
|
<script>
|
|
822
|
+
const ORDER = ${jsonForScript({ orderId: order.orderId, paymentId: order.paymentId, licenseId: order.licenseId, amount: Number(order.amountRub).toFixed(2) })};
|
|
957
823
|
async function confirmPay(event) {
|
|
958
824
|
const res = await fetch('/api/payments/yookassa/webhook', {
|
|
959
825
|
method: 'POST',
|
|
@@ -962,18 +828,16 @@ export function renderSandboxPaymentPage(order) {
|
|
|
962
828
|
type: 'notification',
|
|
963
829
|
event,
|
|
964
830
|
object: {
|
|
965
|
-
id:
|
|
831
|
+
id: ORDER.paymentId,
|
|
966
832
|
status: event === 'payment.succeeded' ? 'succeeded' : 'canceled',
|
|
967
|
-
amount: { value:
|
|
968
|
-
metadata: { orderId:
|
|
969
|
-
}
|
|
970
|
-
})
|
|
833
|
+
amount: { value: ORDER.amount, currency: 'RUB' },
|
|
834
|
+
metadata: { orderId: ORDER.orderId, licenseId: ORDER.licenseId },
|
|
835
|
+
},
|
|
836
|
+
}),
|
|
971
837
|
});
|
|
972
838
|
const data = await res.json();
|
|
973
839
|
document.getElementById('st').textContent = data.status || event;
|
|
974
|
-
document.getElementById('msg').textContent = data.status === 'paid'
|
|
975
|
-
? 'Оплата прошла! Лицензия ' + data.licenseId + ' обновлена (всего мест: ' + data.totalSeats + ').'
|
|
976
|
-
: 'Платёж отменён.';
|
|
840
|
+
document.getElementById('msg').textContent = data.status === 'paid' ? 'Оплата проведена. Лицензия обновлена.' : 'Платёж отменён.';
|
|
977
841
|
}
|
|
978
842
|
</script>
|
|
979
843
|
</body>
|