@coopenomics/desktop 2025.11.12-alpha-1 → 2025.11.13-alpha-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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coopenomics/desktop",
3
- "version": "2025.11.12-alpha-1",
3
+ "version": "2025.11.13-alpha-1",
4
4
  "description": "A Desktop Project",
5
5
  "productName": "Desktop App",
6
6
  "author": "Alex Ant <dacom.dark.sun@gmail.com>",
@@ -25,9 +25,9 @@
25
25
  "start": "node -r ./alias-resolver.js dist/ssr/index.js"
26
26
  },
27
27
  "dependencies": {
28
- "@coopenomics/controller": "2025.11.12-alpha-1",
29
- "@coopenomics/notifications": "2025.11.12-alpha-1",
30
- "@coopenomics/sdk": "2025.11.12-alpha-1",
28
+ "@coopenomics/controller": "2025.11.13-alpha-1",
29
+ "@coopenomics/notifications": "2025.11.13-alpha-1",
30
+ "@coopenomics/sdk": "2025.11.13-alpha-1",
31
31
  "@dicebear/collection": "^9.0.1",
32
32
  "@dicebear/core": "^9.0.1",
33
33
  "@editorjs/code": "^2.9.3",
@@ -59,7 +59,7 @@
59
59
  "@wharfkit/wallet-plugin-privatekey": "^1.1.0",
60
60
  "axios": "^1.2.1",
61
61
  "compression": "^1.7.4",
62
- "cooptypes": "2025.11.12-alpha-1",
62
+ "cooptypes": "2025.11.13-alpha-1",
63
63
  "dompurify": "^3.1.7",
64
64
  "dotenv": "^16.4.5",
65
65
  "email-regex": "^5.0.0",
@@ -123,5 +123,5 @@
123
123
  "npm": ">= 6.13.4",
124
124
  "yarn": ">= 1.21.1"
125
125
  },
126
- "gitHead": "b261f505aa7897cdc82ce88ed770fe0f6aee7afc"
126
+ "gitHead": "d0f73c32bb68d8f7451ecef3989bdff4629d71c4"
127
127
  }
@@ -139,6 +139,8 @@ export const useConnectionAgreementStore = defineStore(namespace, () => {
139
139
  console.log('Текущий инстанс загружен:', currentInstance.value)
140
140
  } catch (error: any) {
141
141
  currentInstanceError.value = error.message || 'Ошибка загрузки инстанса'
142
+ // Очищаем старые данные при ошибке - они больше не актуальны
143
+ currentInstance.value = null
142
144
  console.error('Ошибка при загрузке текущего инстанса:', error)
143
145
  } finally {
144
146
  currentInstanceLoading.value = false
@@ -45,6 +45,11 @@ let stopInstanceRefresh: (() => void) | null = null
45
45
 
46
46
  // Проверка завершения установки
47
47
  const isInstallationCompleted = computed(() => {
48
+ // Не показываем поздравление если идет загрузка или есть ошибка
49
+ if (connectionAgreement.currentInstanceLoading || connectionAgreement.currentInstanceError) {
50
+ return false
51
+ }
52
+
48
53
  const instance = connectionAgreement.currentInstance
49
54
  return instance?.progress === 100 && instance?.status === Zeus.InstanceStatus.ACTIVE
50
55
  })
@@ -72,6 +77,11 @@ const init = async () => {
72
77
 
73
78
  // Watch за изменением currentInstance для автоматического перехода между шагами
74
79
  watch(() => connectionAgreement.currentInstance, (instance) => {
80
+ // Не обрабатываем изменения если идет загрузка или есть ошибка
81
+ if (connectionAgreement.currentInstanceLoading || connectionAgreement.currentInstanceError) {
82
+ return
83
+ }
84
+
75
85
  if (!instance) return
76
86
 
77
87
  const currentStep = connectionAgreement.currentStep
@@ -14,7 +14,11 @@ const connectionAgreement = useConnectionAgreementStore()
14
14
  const handleContinue = (formData?: any) => {
15
15
  console.log('📝 FormStep: Продолжаем с данными формы:', formData)
16
16
 
17
- // Сохраняем данные формы в стор (уже сохранены напрямую)
17
+ // Сохраняем данные формы в стор
18
+ if (formData) {
19
+ connectionAgreement.setFormData(formData)
20
+ }
21
+
18
22
  // Переходим к следующему шагу (документ сгенерируется в watch)
19
23
  if (connectionAgreement.currentStep < 5) {
20
24
  connectionAgreement.setCurrentStep(connectionAgreement.currentStep + 1)
@@ -1,75 +1,97 @@
1
1
  <template lang="pug">
2
2
  q-step(
3
3
  :name="6"
4
- title="Установка"
4
+ title="Установка платформы"
5
5
  icon="cloud_download"
6
6
  :done="isDone"
7
7
  )
8
- .q-pa-md
8
+ .installation-container.q-pa-md
9
+
9
10
  //- Установка в процессе
10
11
  div(v-if="instance?.progress < 100")
11
- .text-h6.text-center.q-mb-lg Установка платформы
12
-
13
- //- Стильный прогресс-бар
14
- .q-mb-lg.q-px-md
15
- q-linear-progress(
16
- :value="instance?.progress / 100 || 0"
17
- color="primary"
18
- size="24px"
19
- rounded
20
- class="q-mb-md"
21
- )
22
- .absolute-full.flex.flex-center
23
- .text-body2.text-white.text-weight-medium {{ instance?.progress || 0 }}%
24
-
25
- //- Информационные тексты в стиле Windows
26
- q-card(flat).q-mb-lg.bg-grey-1
27
- q-card-section.q-pa-lg
28
- .text-body2.text-grey-8.q-mb-md(v-if="instance?.progress < 20")
29
- q-icon(name="settings" color="primary" size="20px").q-mr-sm
30
- | Подготовка серверного окружения...
31
- .text-body2.text-grey-8.q-mb-md(v-else-if="instance?.progress < 40")
32
- q-icon(name="download" color="primary" size="20px").q-mr-sm
33
- | Загрузка компонентов платформы...
34
- .text-body2.text-grey-8.q-mb-md(v-else-if="instance?.progress < 60")
35
- q-icon(name="storage" color="primary" size="20px").q-mr-sm
36
- | Настройка базы данных и хранилищ...
37
- .text-body2.text-grey-8.q-mb-md(v-else-if="instance?.progress < 80")
38
- q-icon(name="security" color="primary" size="20px").q-mr-sm
39
- | Конфигурация блокчейн-интеграции...
40
- .text-body2.text-grey-8.q-mb-md(v-else)
41
- q-icon(name="check_circle" color="primary" size="20px").q-mr-sm
42
- | Финализация установки...
43
-
44
- .text-caption.text-grey-6.q-mt-md
45
- | Платформа кооперативной экономики объединяет цифровые инструменты для управления, учета и взаимодействия членов кооператива.
46
-
47
- .text-body2.text-center.text-grey-7
48
- | Приблизительное время: 60 минут
12
+
13
+ //- Заголовок с градиентом
14
+ .installation-header
15
+ .text-h6.installation-title Установка платформы
16
+ .subtitle.text-body2.text-grey-7.q-mt-sm
17
+ | Создание кооперативной цифровой экосистемы
18
+
19
+ //- Основная карточка прогресса
20
+ .progress-card.q-mb-xl
21
+ .progress-header
22
+ .progress-title
23
+ q-icon(name="rocket_launch" size="24px" color="primary").q-mr-sm
24
+ span.text-subtitle1.text-weight-medium Прогресс установки
25
+
26
+ //- Живой прогресс-бар с анимацией
27
+ .progress-bar-container.q-mt-lg
28
+ .progress-bar-background
29
+ .progress-bar-fill(
30
+ :style="{ width: (instance?.progress || 0) + '%' }"
31
+ )
32
+ .progress-bar-shimmer
33
+ .progress-bar-glow(:style="{ width: (instance?.progress || 0) + '%' }")
34
+
35
+ //- Оставшееся время
36
+ .remaining-time.q-mt-md
37
+ .time-display
38
+ q-icon(name="schedule" size="18px" color="primary").q-mr-sm
39
+ span.text-body2 Осталось: {{ remainingTime }} мин
40
+
41
+ //- Статус текущего этапа
42
+ .current-stage.q-mt-lg
43
+ .stage-info
44
+ .stage-icon
45
+ q-icon(:name="currentStageIcon" size="32px" :color="currentStageColor")
46
+ .stage-details
47
+ .stage-title.text-body1.text-weight-medium {{ currentStageTitle }}
48
+ .stage-description.text-body2.text-grey-7.q-mt-xs {{ currentStageDescription }}
49
+
49
50
 
50
51
  //- Установка завершена
51
52
  div(v-else)
52
- .flex.column.items-center.q-pa-lg
53
- q-icon(name="celebration" color="positive" size="96px").q-mb-lg
54
- .text-h5.text-center.q-mb-md.text-positive Установка завершена!
55
- .text-body1.text-center.text-grey-7.q-mb-xl
53
+ .completion-container
54
+ .completion-card
55
+ .completion-header
56
+ .celebration-icon
57
+ q-icon(name="celebration" color="positive" size="80px")
58
+ .sparkle.sparkle-1
59
+ .sparkle.sparkle-2
60
+ .sparkle.sparkle-3
61
+ .completion-title.text-h5.text-positive.q-mt-lg Установка завершена!
62
+ .completion-subtitle.text-body1.text-grey-7.q-mt-sm
56
63
  | Ваш кооператив успешно развернут и готов к работе
57
64
 
58
- //- Информация о следующих шагах
59
- q-card(flat).q-mb-lg.bg-grey-1
60
- q-card-section.q-pa-lg
61
- .text-subtitle2.q-mb-md.text-weight-medium Дашборд подключения
62
- .text-body2.text-grey-8.q-mb-md
63
- | Дашборд откроется автоматически через несколько секунд. Здесь вы сможете управлять подписками, просматривать баланс кошелька AXON и настраивать параметры платформы.
64
-
65
- //- Навигация (только кнопка назад, если установка не завершена)
66
- q-stepper-navigation.q-gutter-sm(v-if="instance?.progress < 100")
67
- q-btn(
68
- color="grey-6"
69
- flat
70
- label="Назад"
71
- @click="handleBack"
72
- )
65
+ .completion-details.q-mt-xl
66
+ .detail-item
67
+ q-icon(name="check_circle" color="positive" size="24px").q-mr-md
68
+ .detail-text
69
+ .text-body2.text-weight-medium Серверная инфраструктура
70
+ .text-caption.text-grey-6 Полностью настроена и оптимизирована
71
+
72
+ .detail-item.q-mt-md
73
+ q-icon(name="check_circle" color="positive" size="24px").q-mr-md
74
+ .detail-text
75
+ .text-body2.text-weight-medium Блокчейн интеграция
76
+ .text-caption.text-grey-6 Подключена и протестирована
77
+
78
+ .detail-item.q-mt-md
79
+ q-icon(name="check_circle" color="positive" size="24px").q-mr-md
80
+ .detail-text
81
+ .text-body2.text-weight-medium База данных
82
+ .text-caption.text-grey-6 Инициализирована и готова к работе
83
+
84
+ .detail-item.q-mt-md
85
+ q-icon(name="check_circle" color="positive" size="24px").q-mr-md
86
+ .detail-text
87
+ .text-body2.text-weight-medium Платформенные сервисы
88
+ .text-caption.text-grey-6 Запущены и функционируют
89
+
90
+ .next-steps.q-mt-xl
91
+ .text-subtitle2.text-weight-medium.q-mb-md Что дальше?
92
+ .text-body2.text-grey-8
93
+ | Дашборд подключения откроется автоматически через несколько секунд. Здесь вы сможете управлять подписками, просматривать баланс кошелька AXON и настраивать параметры платформы.
94
+
73
95
  </template>
74
96
 
75
97
  <script setup lang="ts">
@@ -86,9 +108,447 @@ const instance = computed(() => connectionAgreement.currentInstance)
86
108
 
87
109
  const isDone = computed(() => props.isDone)
88
110
 
89
- const handleBack = () => {
90
- connectionAgreement.setCurrentStep(2)
91
- }
111
+ // Текущий этап (соответствует логике из startup-workflow.service.ts)
112
+ const currentStageInfo = computed(() => {
113
+ const progress = instance.value?.progress || 0
114
+
115
+ if (progress < 20) {
116
+ return {
117
+ icon: 'settings',
118
+ color: 'primary',
119
+ title: 'Подготовка серверного окружения',
120
+ description: 'Настраиваем инфраструктуру для развертывания платформы'
121
+ }
122
+ } else if (progress < 40) {
123
+ return {
124
+ icon: 'download',
125
+ color: 'info',
126
+ title: 'Загрузка компонентов платформы',
127
+ description: 'Устанавливаем необходимые компоненты и зависимости'
128
+ }
129
+ } else if (progress < 60) {
130
+ return {
131
+ icon: 'storage',
132
+ color: 'warning',
133
+ title: 'Настройка базы данных и хранилищ',
134
+ description: 'Инициализируем базы данных и настраиваем хранилища'
135
+ }
136
+ } else if (progress < 80) {
137
+ return {
138
+ icon: 'security',
139
+ color: 'secondary',
140
+ title: 'Конфигурация блокчейн-интеграции',
141
+ description: 'Подключаем и тестируем блокчейн функциональность'
142
+ }
143
+ } else {
144
+ return {
145
+ icon: 'check_circle',
146
+ color: 'positive',
147
+ title: 'Финализация установки',
148
+ description: 'Выполняем заключительные настройки и тестирование'
149
+ }
150
+ }
151
+ })
152
+
153
+ const currentStageIcon = computed(() => currentStageInfo.value.icon)
154
+ const currentStageColor = computed(() => currentStageInfo.value.color)
155
+ const currentStageTitle = computed(() => currentStageInfo.value.title)
156
+ const currentStageDescription = computed(() => currentStageInfo.value.description)
157
+
158
+ // Расчет оставшегося времени (общая установка 100 минут)
159
+ const remainingTime = computed(() => {
160
+ const progress = instance.value?.progress || 0
161
+ const totalMinutes = 100
162
+ const remaining = totalMinutes - progress
163
+ return Math.max(0, remaining) // Не показываем отрицательные значения
164
+ })
165
+
166
+ // Реактивно следим за изменениями прогресса
167
+ import { watch } from 'vue'
168
+
169
+ watch(() => instance.value?.progress, () => {
170
+ // Ничего не делаем, просто реактивно обновляем отображение
171
+ }, { immediate: true })
92
172
  </script>
93
173
 
174
+ <style scoped>
175
+ .installation-container {
176
+ max-width: 900px;
177
+ margin: 0 auto;
178
+ position: relative;
179
+ }
180
+
181
+ /* Заголовок с градиентом */
182
+ .installation-header {
183
+ text-align: center;
184
+ margin-bottom: 2rem;
185
+ }
186
+
187
+ .installation-title {
188
+ font-weight: 700;
189
+ letter-spacing: -0.5px;
190
+ background: linear-gradient(135deg, var(--q-primary) 0%, rgba(25, 118, 210, 0.8) 100%);
191
+ -webkit-background-clip: text;
192
+ -webkit-text-fill-color: transparent;
193
+ background-clip: text;
194
+ text-shadow: 0 2px 4px rgba(25, 118, 210, 0.3);
195
+ }
196
+
197
+ /* Основная карточка прогресса */
198
+ .progress-card {
199
+ border-radius: 20px;
200
+ padding: 2.5rem;
201
+ background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
202
+ border: 1px solid rgba(0, 0, 0, 0.05);
203
+ box-shadow:
204
+ 0 8px 32px rgba(0, 0, 0, 0.08),
205
+ 0 2px 8px rgba(0, 0, 0, 0.04);
206
+ position: relative;
207
+ overflow: hidden;
208
+ }
209
+
210
+ .progress-card::before {
211
+ content: '';
212
+ position: absolute;
213
+ top: 0;
214
+ left: 0;
215
+ right: 0;
216
+ height: 4px;
217
+ background: linear-gradient(90deg, var(--q-primary) 0%, var(--q-secondary) 50%, var(--q-accent) 100%);
218
+ background-size: 200% 100%;
219
+ animation: shimmer 4s ease-in-out infinite;
220
+ }
221
+
222
+ @keyframes shimmer {
223
+ 0% { background-position: -200% 0; }
224
+ 100% { background-position: 200% 0; }
225
+ }
226
+
227
+ .progress-header {
228
+ display: flex;
229
+ justify-content: space-between;
230
+ align-items: center;
231
+ margin-bottom: 1rem;
232
+ }
233
+
234
+ .progress-title {
235
+ display: flex;
236
+ align-items: center;
237
+ font-weight: 600;
238
+ color: var(--q-primary);
239
+ }
240
+
241
+
242
+ /* Живой прогресс-бар */
243
+ .progress-bar-container {
244
+ position: relative;
245
+ height: 12px;
246
+ background: rgba(0, 0, 0, 0.05);
247
+ border-radius: 20px;
248
+ overflow: hidden;
249
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
250
+ }
251
+
252
+ .progress-bar-background {
253
+ position: absolute;
254
+ top: 0;
255
+ left: 0;
256
+ height: 100%;
257
+ width: 100%;
258
+ background: linear-gradient(90deg, #e0e0e0 0%, #f0f0f0 100%);
259
+ }
260
+
261
+ .progress-bar-fill {
262
+ position: absolute;
263
+ top: 0;
264
+ left: 0;
265
+ height: 100%;
266
+ background: linear-gradient(135deg, var(--q-primary) 0%, var(--q-secondary) 100%);
267
+ border-radius: 20px;
268
+ transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
269
+ box-shadow:
270
+ 0 0 20px rgba(25, 118, 210, 0.3),
271
+ inset 0 1px 2px rgba(255, 255, 255, 0.2);
272
+ position: relative;
273
+ overflow: hidden;
274
+ }
275
+
276
+ .progress-bar-shimmer {
277
+ position: absolute;
278
+ top: 0;
279
+ left: -100%;
280
+ width: 100%;
281
+ height: 100%;
282
+ background: linear-gradient(
283
+ 90deg,
284
+ transparent 0%,
285
+ rgba(255, 255, 255, 0.6) 30%,
286
+ rgba(255, 255, 255, 0.8) 50%,
287
+ rgba(255, 255, 255, 0.6) 70%,
288
+ transparent 100%
289
+ );
290
+ animation: progress-shimmer 1.2s infinite cubic-bezier(0.25, 0.46, 0.45, 0.94);
291
+ }
292
+
293
+ @keyframes progress-shimmer {
294
+ 0% {
295
+ left: -100%;
296
+ opacity: 0;
297
+ }
298
+ 10% {
299
+ opacity: 1;
300
+ }
301
+ 90% {
302
+ opacity: 1;
303
+ }
304
+ 100% {
305
+ left: 100%;
306
+ opacity: 0;
307
+ }
308
+ }
309
+
310
+ .progress-bar-glow {
311
+ position: absolute;
312
+ top: -2px;
313
+ left: 0;
314
+ height: 16px;
315
+ background: linear-gradient(135deg, var(--q-primary) 0%, rgba(25, 118, 210, 0.3) 100%);
316
+ border-radius: 20px;
317
+ filter: blur(4px);
318
+ opacity: 0.6;
319
+ transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
320
+ }
321
+
322
+ /* Текущий этап */
323
+ .current-stage {
324
+ margin-top: 2rem;
325
+ padding: 1.5rem;
326
+ background: rgba(25, 118, 210, 0.02);
327
+ border-radius: 16px;
328
+ border: 1px solid rgba(25, 118, 210, 0.1);
329
+ }
330
+
331
+ .stage-info {
332
+ display: flex;
333
+ align-items: center;
334
+ gap: 1rem;
335
+ }
336
+
337
+ .stage-icon {
338
+ background: linear-gradient(135deg, var(--q-primary) 0%, rgba(25, 118, 210, 0.8) 100%);
339
+ border-radius: 50%;
340
+ padding: 0.75rem;
341
+ box-shadow: 0 4px 12px rgba(25, 118, 210, 0.2);
342
+ }
343
+
344
+ .stage-details {
345
+ flex: 1;
346
+ }
347
+
348
+ .stage-title {
349
+ color: var(--q-primary);
350
+ margin-bottom: 0.25rem;
351
+ }
352
+
353
+ .stage-description {
354
+ color: #666;
355
+ }
356
+
357
+ /* Оставшееся время */
358
+ .remaining-time {
359
+ display: flex;
360
+ justify-content: center;
361
+ align-items: center;
362
+ }
363
+
364
+ .time-display {
365
+ display: flex;
366
+ align-items: center;
367
+ padding: 0.75rem 1.5rem;
368
+ background: rgba(25, 118, 210, 0.05);
369
+ border-radius: 12px;
370
+ border: 1px solid rgba(25, 118, 210, 0.1);
371
+ }
372
+
373
+
374
+ /* Экран завершения */
375
+ .completion-container {
376
+ display: flex;
377
+ justify-content: center;
378
+ align-items: center;
379
+ min-height: 500px;
380
+ }
381
+
382
+ .completion-card {
383
+ max-width: 600px;
384
+ width: 100%;
385
+ text-align: center;
386
+ padding: 3rem;
387
+ border-radius: 24px;
388
+ background: linear-gradient(135deg, #ffffff 0%, #f8fff9 100%);
389
+ border: 1px solid rgba(76, 175, 80, 0.1);
390
+ box-shadow:
391
+ 0 12px 40px rgba(0, 0, 0, 0.08),
392
+ 0 4px 16px rgba(0, 0, 0, 0.04);
393
+ position: relative;
394
+ overflow: hidden;
395
+ }
396
+
397
+ .completion-card::before {
398
+ content: '';
399
+ position: absolute;
400
+ top: 0;
401
+ left: 0;
402
+ right: 0;
403
+ height: 4px;
404
+ background: linear-gradient(90deg, var(--q-positive) 0%, #4CAF50 50%, var(--q-positive) 100%);
405
+ background-size: 200% 100%;
406
+ animation: completion-shimmer 3s ease-in-out infinite;
407
+ }
408
+
409
+ @keyframes completion-shimmer {
410
+ 0%, 100% { background-position: -200% 0; }
411
+ 50% { background-position: 200% 0; }
412
+ }
413
+
414
+ .completion-header {
415
+ position: relative;
416
+ }
417
+
418
+ .celebration-icon {
419
+ position: relative;
420
+ display: inline-block;
421
+ margin-bottom: 1rem;
422
+ }
423
+
424
+ .sparkle {
425
+ position: absolute;
426
+ width: 8px;
427
+ height: 8px;
428
+ background: var(--q-positive);
429
+ border-radius: 50%;
430
+ animation: sparkle 2s infinite ease-in-out;
431
+ }
432
+
433
+ .sparkle-1 {
434
+ top: -10px;
435
+ right: -10px;
436
+ animation-delay: 0s;
437
+ }
438
+
439
+ .sparkle-2 {
440
+ top: 20px;
441
+ left: -15px;
442
+ animation-delay: 0.5s;
443
+ }
444
+
445
+ .sparkle-3 {
446
+ bottom: -5px;
447
+ right: 15px;
448
+ animation-delay: 1s;
449
+ }
450
+
451
+ @keyframes sparkle {
452
+ 0%, 100% {
453
+ opacity: 0;
454
+ transform: scale(0);
455
+ }
456
+ 50% {
457
+ opacity: 1;
458
+ transform: scale(1);
459
+ }
460
+ }
461
+
462
+ .completion-title {
463
+ font-weight: 700;
464
+ letter-spacing: -0.5px;
465
+ margin-bottom: 0.5rem;
466
+ background: linear-gradient(135deg, var(--q-positive) 0%, #4CAF50 100%);
467
+ -webkit-background-clip: text;
468
+ -webkit-text-fill-color: transparent;
469
+ background-clip: text;
470
+ text-shadow: 0 2px 4px rgba(76, 175, 80, 0.3);
471
+ }
472
+
473
+ .completion-details {
474
+ margin-top: 2rem;
475
+ text-align: left;
476
+ }
477
+
478
+ .detail-item {
479
+ display: flex;
480
+ align-items: center;
481
+ padding: 1rem;
482
+ background: rgba(76, 175, 80, 0.02);
483
+ border-radius: 12px;
484
+ border: 1px solid rgba(76, 175, 80, 0.1);
485
+ transition: all 0.3s ease;
486
+ }
487
+
488
+ .detail-item:hover {
489
+ transform: translateX(4px);
490
+ background: rgba(76, 175, 80, 0.05);
491
+ box-shadow: 0 4px 12px rgba(76, 175, 80, 0.1);
492
+ }
493
+
494
+ .detail-text {
495
+ margin-left: 1rem;
496
+ }
94
497
 
498
+ .next-steps {
499
+ margin-top: 2rem;
500
+ text-align: left;
501
+ padding: 1.5rem;
502
+ background: rgba(0, 0, 0, 0.02);
503
+ border-radius: 12px;
504
+ }
505
+
506
+ /* Адаптивность */
507
+ @media (max-width: 768px) {
508
+ .progress-card {
509
+ padding: 2rem;
510
+ }
511
+
512
+ .progress-header {
513
+ flex-direction: column;
514
+ gap: 1rem;
515
+ text-align: center;
516
+ }
517
+
518
+
519
+ .stage-info {
520
+ flex-direction: column;
521
+ text-align: center;
522
+ gap: 0.75rem;
523
+ }
524
+
525
+ .completion-card {
526
+ padding: 2rem;
527
+ }
528
+
529
+ .detail-item {
530
+ flex-direction: column;
531
+ text-align: center;
532
+ gap: 0.5rem;
533
+ }
534
+
535
+ .time-display {
536
+ padding: 0.5rem 1rem;
537
+ }
538
+ }
539
+
540
+ @media (max-width: 480px) {
541
+ .installation-container {
542
+ padding: 1rem;
543
+ }
544
+
545
+ .progress-card {
546
+ padding: 1.5rem;
547
+ }
548
+
549
+ .time-display {
550
+ padding: 0.5rem 0.75rem;
551
+ font-size: 0.875rem;
552
+ }
553
+ }
554
+ </style>