@coopenomics/desktop 2025.11.26-alpha-5 → 2025.11.28-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.
@@ -20,6 +20,17 @@ div
20
20
  text="Загрузка клиента..."
21
21
  )
22
22
 
23
+ // Заглушка для мобильных устройств
24
+ div(v-else-if="isMobile", class="mobile-stub")
25
+ div.mobile-stub-content
26
+ div.mobile-icon
27
+ i.fas.fa-mobile-alt
28
+ h2 Мобильный клиент доступен
29
+ p На мобильных устройствах кооперативный мессенджер работает только через мобильное приложение Element X.
30
+ p Перейдите на страницу с инструкциями по подключению.
31
+ q-btn(@click="goToMobileClient", class="mobile-button", color="primary", icon="fa-mobile-alt")
32
+ | Перейти к мобильному клиенту
33
+
23
34
  // Iframe с Matrix клиентом после полной загрузки
24
35
  iframe(
25
36
  v-else-if="chatcoopStore.accountStatus?.iframeUrl",
@@ -28,7 +39,7 @@ div
28
39
  class="matrix-iframe",
29
40
  frameborder="0",
30
41
  width="100%",
31
- :style="{ height: 'calc(100vh - 56px)' }"
42
+ :style="{ height: 'calc(100vh - 51px)' }"
32
43
  @load="onIframeLoaded"
33
44
  allow="camera; microphone; display-capture"
34
45
  )
@@ -36,11 +47,15 @@ div
36
47
 
37
48
  <script lang="ts" setup>
38
49
  import { ref, onMounted, watch } from 'vue';
50
+ import { useRouter } from 'vue-router';
39
51
  import { WindowLoader } from 'src/shared/ui/Loader';
40
52
  import { useChatCoopChatStore } from '../../../entities/ChatCoopChat/model';
41
53
  import { MatrixRegistration } from '../../../widgets/MatrixRegistration';
54
+ import { useWindowSize } from 'src/shared/hooks/useWindowSize';
42
55
 
43
56
  const chatcoopStore = useChatCoopChatStore();
57
+ const router = useRouter();
58
+ const { isMobile } = useWindowSize();
44
59
  const isIframeLoading = ref(true);
45
60
  let iframeLoadTimeout: number | null = null;
46
61
 
@@ -84,6 +99,10 @@ async function handleAccountCreated() {
84
99
  await chatcoopStore.loadAccountStatus();
85
100
  }
86
101
 
102
+ function goToMobileClient() {
103
+ router.push({ name: 'chatcoop-mobile' });
104
+ }
105
+
87
106
  onMounted(async () => {
88
107
  await chatcoopStore.loadAccountStatus();
89
108
  // Инициализируем загрузку iframe после получения статуса аккаунта
@@ -103,7 +122,7 @@ onMounted(async () => {
103
122
  flex-direction: column;
104
123
  align-items: center;
105
124
  justify-content: center;
106
- height: calc(100vh - 56px);
125
+ height: calc(100vh - 51px);
107
126
  text-align: center;
108
127
  padding: 2rem;
109
128
  }
@@ -127,4 +146,42 @@ onMounted(async () => {
127
146
  .retry-button:hover {
128
147
  background-color: #0056b3;
129
148
  }
149
+
150
+ .mobile-stub {
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ height: calc(100vh - 51px);
155
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
156
+ color: white;
157
+ }
158
+
159
+ .mobile-stub-content {
160
+ text-align: center;
161
+ padding: 2rem;
162
+ max-width: 400px;
163
+ }
164
+
165
+ .mobile-icon {
166
+ font-size: 4rem;
167
+ margin-bottom: 1.5rem;
168
+ opacity: 0.9;
169
+ }
170
+
171
+ .mobile-stub-content h2 {
172
+ margin: 0 0 1rem 0;
173
+ font-size: 2rem;
174
+ font-weight: 600;
175
+ }
176
+
177
+ .mobile-stub-content p {
178
+ margin: 0 0 1.5rem 0;
179
+ opacity: 0.9;
180
+ line-height: 1.6;
181
+ font-size: 1.1rem;
182
+ }
183
+
184
+ .mobile-button {
185
+ margin-top: 1rem;
186
+ }
130
187
  </style>
@@ -196,7 +196,7 @@ const handleSubmit = async () => {
196
196
  padding: 3rem 2.5rem;
197
197
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
198
198
  width: 100%;
199
- max-width: 420px;
199
+ max-width: 500px;
200
200
  border: 1px solid rgba(255, 255, 255, 0.2);
201
201
  transition: all 0.3s ease;
202
202
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coopenomics/desktop",
3
- "version": "2025.11.26-alpha-5",
3
+ "version": "2025.11.28-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.26-alpha-5",
29
- "@coopenomics/notifications": "2025.11.26-alpha-5",
30
- "@coopenomics/sdk": "2025.11.26-alpha-5",
28
+ "@coopenomics/controller": "2025.11.28-alpha-1",
29
+ "@coopenomics/notifications": "2025.11.28-alpha-1",
30
+ "@coopenomics/sdk": "2025.11.28-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.26-alpha-5",
62
+ "cooptypes": "2025.11.28-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": "53a1c103d691570643c7f98e0f656efc42edaee6"
126
+ "gitHead": "7128e26a2741c2a5d4fd8bdbbcab363541341d79"
127
127
  }
@@ -12,7 +12,7 @@ const namespace = 'connection-agreement'
12
12
 
13
13
  export const useConnectionAgreementStore = defineStore(namespace, () => {
14
14
  // State
15
- const currentStep = ref<number>(1)
15
+ const currentStep = ref<number>(0)
16
16
  const selectedTariff = ref<ITariff | null>(null)
17
17
  const isInitialized = ref<boolean>(false)
18
18
  const document = ref<any>(null)
@@ -137,10 +137,9 @@ export const useConnectionAgreementStore = defineStore(namespace, () => {
137
137
  try {
138
138
  currentInstanceLoading.value = true
139
139
  currentInstanceError.value = null
140
- const freshInstance = await getCurrentInstance()
141
-
142
- // При успешной загрузке сбрасываем флаг Bad Gateway
140
+ // Сбрасываем флаг Bad Gateway при начале каждой загрузки
143
141
  isBadGateway.value = false
142
+ const freshInstance = await getCurrentInstance()
144
143
 
145
144
  // Обновляем данные только если получили свежие данные
146
145
  // При ошибке оставляем старые данные в currentInstance (они могут быть из localStorage)
@@ -150,9 +149,14 @@ export const useConnectionAgreementStore = defineStore(namespace, () => {
150
149
 
151
150
  console.log('Текущий инстанс загружен:', currentInstance.value)
152
151
  } catch (error: any) {
153
- isBadGateway.value = true
154
152
 
155
153
  currentInstanceError.value = extractGraphQLErrorMessages(error)
154
+
155
+ // Не устанавливаем Bad Gateway для ошибки "Инстанс не найден"
156
+ if (!currentInstanceError.value?.includes('Инстанс не найден')) {
157
+ isBadGateway.value = true
158
+ }
159
+
156
160
  // НЕ очищаем старые данные при ошибке - они остаются актуальными из localStorage
157
161
  // currentInstance.value остается как есть
158
162
  console.warn('Ошибка при загрузке текущего инстанса:', error)
@@ -15,23 +15,23 @@ div
15
15
  label="Собственное название организации"
16
16
  placeholder="Ромашка"
17
17
  standout="bg-teal text-white"
18
- hint="Название без кавычек, которое будет добавляться к основе"
18
+ hint="Название кооператива без кавычек, которое будет добавляться к ОПФ+"
19
19
  :rules="[val => notEmpty(val)]"
20
20
  )
21
21
 
22
22
  q-input.q-mt-md(
23
23
  v-model="installStore.vars.full_abbr"
24
- label="Основа наименования в именительном падеже"
25
- placeholder="Потребительский Кооператив Социального Комплекса"
24
+ label="ОПФ+ в именительном падеже"
25
+ hint="Например: Потребительский Кооператив Социального Комплекса"
26
26
  standout="bg-teal text-white"
27
27
  :rules="[val => notEmpty(val)]"
28
28
  )
29
- .text-caption.text-grey Вся часть до названия. Если уточнения нет - просто ОПФ
29
+
30
30
 
31
31
  q-input.q-mt-md(
32
32
  v-model="installStore.vars.full_abbr_genitive"
33
33
  label="Основа наименования в родительном падеже"
34
- placeholder="Потребительского Кооператива Социального Комплекса"
34
+ hint="Например: Потребительского Кооператива Социального Комплекса"
35
35
  standout="bg-teal text-white"
36
36
  :rules="[val => notEmpty(val)]"
37
37
  )
@@ -39,7 +39,7 @@ div
39
39
  q-input.q-mt-md(
40
40
  v-model="installStore.vars.full_abbr_dative"
41
41
  label="Основа наименования в дательном падеже"
42
- placeholder="Потребительскому Кооперативу Социального Комплекса"
42
+ hint="Например: Потребительскому Кооперативу Социального Комплекса"
43
43
  standout="bg-teal text-white"
44
44
  :rules="[val => notEmpty(val)]"
45
45
  )
@@ -47,7 +47,7 @@ div
47
47
  q-input.q-mt-md(
48
48
  v-model="installStore.vars.short_abbr"
49
49
  label="Краткая аббревиатура основы наименования"
50
- placeholder="ПКСК"
50
+ hint="Например: ПКСК"
51
51
  standout="bg-teal text-white"
52
52
  :rules="[val => notEmpty(val)]"
53
53
  )
@@ -1,6 +1,7 @@
1
1
  <template lang="pug">
2
2
  div.row.q-pa-md
3
3
  div.col-md-12.col-xs-12
4
+
4
5
  // Лоадер пока идет загрузка данных или технические работы у провайдера
5
6
  WindowLoader(
6
7
  v-if="isLoading || connectionAgreement.isBadGateway",
@@ -108,7 +109,6 @@ const init = async () => {
108
109
  isLoading.value = false;
109
110
  return;
110
111
  }
111
- console.log('SYSTEM.info.is_unioned', system.info.is_unioned, connectionAgreement.isInitialized);
112
112
 
113
113
  // Запускаем автообновление инстанса каждые 30 секунд (включает начальную загрузку)
114
114
  // Ждем завершения первой загрузки, чтобы корректно определить состояние isBadGateway
@@ -135,29 +135,29 @@ const init = async () => {
135
135
  return;
136
136
  }
137
137
 
138
+ // Определяем шаг на основе текущего состояния
139
+ let targetStep: number;
140
+
138
141
  if (hasInstanceError) {
139
- // Если есть ошибка загрузки инстанса, но установки не было завершено ранее,
140
- // начинаем с шага 1 по умолчанию
141
- console.log('❌ Ошибка загрузки инстанса, устанавливаем шаг 1 по умолчанию');
142
- connectionAgreement.setCurrentStep(1);
142
+ // Если есть ошибка загрузки инстанса, определяем шаг на основе членства в союзе
143
+ targetStep = system.info.is_unioned ? 0 : 1;
143
144
  } else if (instance && typeof instance.progress === 'number' && instance.progress > 0) {
144
145
  // Если установка уже идет (прогресс > 0), переходим к шагу установки
145
- console.log('🔄 Установка уже идет, прогресс:', instance.progress, '→ шаг 6');
146
- connectionAgreement.setCurrentStep(6);
146
+ targetStep = 6;
147
147
  } else {
148
148
  // Если инстанса нет ИЛИ его прогресс = 0, определяем шаг на основе членства в союзе
149
- const hasNoInstance = instance === null;
150
149
  if (system.info.is_unioned) {
151
150
  // Если кооператив не является членом союза, начинаем с нулевого шага
152
- console.log(hasNoInstance ? 'ℹ️ Инстанс отсутствует, кооператив не в союзе → шаг 0' : '🔄 Установка не начата, кооператив не в союзе → шаг 0');
153
- connectionAgreement.setCurrentStep(0);
151
+ targetStep = 0;
154
152
  } else {
155
153
  // Если кооператив уже член союза, начинаем с первого шага
156
- console.log(hasNoInstance ? 'ℹ️ Инстанс отсутствует, кооператив в союзе → шаг 1' : '🔄 Установка не начата, кооператив в союзе → шаг 1');
157
- connectionAgreement.setCurrentStep(1);
154
+ targetStep = 1;
158
155
  }
159
156
  }
160
157
 
158
+ // Устанавливаем определенный шаг
159
+ connectionAgreement.setCurrentStep(targetStep);
160
+
161
161
  // Скрываем лоадер после загрузки данных
162
162
  isLoading.value = false;
163
163
  };
@@ -0,0 +1,47 @@
1
+ import type { IWorkspaceConfig } from 'src/shared/lib/types/workspace';
2
+
3
+ // Импортируем все функции установки расширений
4
+ import capitalInstall from '../../../extensions/capital/install';
5
+ import chairmanInstall from '../../../extensions/chairman/install';
6
+ import chatcoopInstall from '../../../extensions/chatcoop/install';
7
+ import marketInstall from '../../../extensions/market/install';
8
+ import marketAdminInstall from '../../../extensions/market-admin/install';
9
+ import participantInstall from '../../../extensions/participant/install';
10
+ import powerupInstall from '../../../extensions/powerup/install';
11
+ import sovietInstall from '../../../extensions/soviet/install';
12
+
13
+ /**
14
+ * Единый регистр всех доступных расширений
15
+ * Ключ - имя расширения, значение - функция установки
16
+ */
17
+ export const extensionsRegistry: Record<string, () => Promise<IWorkspaceConfig[]>> = {
18
+ capital: capitalInstall,
19
+ chairman: chairmanInstall,
20
+ chatcoop: chatcoopInstall,
21
+ market: marketInstall,
22
+ 'market-admin': marketAdminInstall,
23
+ participant: participantInstall,
24
+ powerup: powerupInstall,
25
+ soviet: sovietInstall,
26
+ };
27
+
28
+ /**
29
+ * Получить список всех доступных расширений
30
+ */
31
+ export function getAvailableExtensions(): string[] {
32
+ return Object.keys(extensionsRegistry);
33
+ }
34
+
35
+ /**
36
+ * Проверить, существует ли расширение
37
+ */
38
+ export function isExtensionAvailable(extensionName: string): boolean {
39
+ return extensionName in extensionsRegistry;
40
+ }
41
+
42
+ /**
43
+ * Получить функцию установки расширения
44
+ */
45
+ export function getExtensionInstaller(extensionName: string): (() => Promise<IWorkspaceConfig[]>) | undefined {
46
+ return extensionsRegistry[extensionName];
47
+ }
@@ -1,35 +1,62 @@
1
1
  import type { Router } from 'vue-router';
2
2
  import { useDesktopStore } from 'src/entities/Desktop/model';
3
3
  import type { IWorkspaceConfig } from 'src/shared/lib/types/workspace';
4
+ import { extensionsRegistry, getAvailableExtensions } from './extensions-registry';
4
5
 
5
6
  export async function useInitExtensionsProcess(router: Router) {
6
7
  const store = useDesktopStore();
7
- // Загружаем все модули расширений
8
- const modules = import.meta.glob('../../../extensions/**/install.{ts,js}');
9
8
 
10
- for (const path in modules) {
11
- const mod = await modules[path]();
12
- if (mod?.default) {
13
- // Ожидаем, что расширение возвращает массив IWorkspaceConfig[]
14
- const result = await mod.default();
9
+ console.log('📦 [InitExtensions] Starting initialization with extensions registry');
10
+
11
+ // Получаем список всех доступных расширений
12
+ const availableExtensions = getAvailableExtensions();
13
+
14
+ console.log('📦 [InitExtensions] Available extensions:', availableExtensions);
15
+
16
+ // Загружаем все расширения из регистра
17
+ for (const extensionName of availableExtensions) {
18
+ try {
19
+ console.log(`📦 [InitExtensions] Loading extension: ${extensionName}`);
20
+
21
+ const installFunction = extensionsRegistry[extensionName];
22
+ const result = await installFunction();
15
23
 
16
24
  // Поддержка обоих форматов: массив или одиночный объект (для обратной совместимости)
17
25
  const workspaceConfigs: IWorkspaceConfig[] = Array.isArray(result) ? result : [result];
18
26
 
27
+ console.log(`📦 [InitExtensions] Extension "${extensionName}" loaded, configs:`, workspaceConfigs.length);
28
+
19
29
  // Обрабатываем каждый workspace из расширения
20
30
  for (const config of workspaceConfigs) {
21
31
  if (config?.workspace && config?.routes?.length) {
32
+ console.log(`📦 [InitExtensions] Setting routes for workspace: ${config.workspace}`);
33
+
22
34
  // Записываем маршруты в соответствующий workspace
23
35
  store.setRoutes(config.workspace, config.routes as any);
36
+
24
37
  // Регистрируем маршруты в router, добавляя их в базовый родительский маршрут
25
38
  const baseRoute = router.getRoutes().find((r) => r.name === 'base');
26
39
  if (baseRoute) {
27
- config.routes.forEach((r: any) => router.addRoute('base', r));
40
+ config.routes.forEach((r: any) => {
41
+ // Проверяем, не зарегистрирован ли уже маршрут
42
+ const existingRoute = router.getRoutes().find((route) => route.name === r.name);
43
+ if (!existingRoute) {
44
+ console.log(`📦 [InitExtensions] Adding route to router: ${r.name}`);
45
+ router.addRoute('base', r);
46
+ } else {
47
+ console.log(`📦 [InitExtensions] Route already exists, skipping: ${r.name}`);
48
+ }
49
+ });
28
50
  }
29
51
  }
30
52
  }
53
+ } catch (error) {
54
+ console.error(`📦 [InitExtensions] Failed to load extension "${extensionName}":`, error);
55
+ // Продолжаем загрузку других расширений даже если одно не загрузилось
31
56
  }
32
57
  }
58
+
59
+ console.log('📦 [InitExtensions] All extensions initialization completed');
33
60
  }
34
61
 
35
62
  // Функция для динамической загрузки маршрутов конкретного расширения
@@ -42,72 +69,59 @@ export async function loadExtensionRoutes(
42
69
  try {
43
70
  console.log('📦 [LoadExtensionRoutes] Starting to load routes for extension:', extensionName);
44
71
 
45
- // Получаем все доступные модули расширений
46
- const allModules = import.meta.glob(
47
- '../../../extensions/**/install.{ts,js}',
48
- );
49
-
50
- // Находим путь к модулю нужного расширения
51
- const modulePath = Object.keys(allModules).find((path) => {
52
- // Извлекаем имя расширения из пути: ../../../extensions/{extensionName}/install.{ts,js}
53
- const pathParts = path.split('/');
54
- const extName = pathParts[pathParts.length - 2]; // предпоследний элемент - имя папки
55
- return extName === extensionName;
56
- });
72
+ // Получаем функцию установки из регистра
73
+ const installFunction = extensionsRegistry[extensionName];
57
74
 
58
- if (!modulePath) {
59
- console.warn(`📦 [LoadExtensionRoutes] No module found for extension "${extensionName}"`);
75
+ if (!installFunction) {
76
+ console.warn(`📦 [LoadExtensionRoutes] Extension "${extensionName}" not found in registry`);
60
77
  return;
61
78
  }
62
79
 
63
- console.log('📦 [LoadExtensionRoutes] Found module path:', modulePath);
64
-
65
- const module = await allModules[modulePath]();
66
- if (module?.default) {
67
- const result = await module.default();
68
- console.log('📦 [LoadExtensionRoutes] Module loaded, result:', result);
69
-
70
- // Поддержка обоих форматов: массив или одиночный объект (для обратной совместимости)
71
- const workspaceConfigs: IWorkspaceConfig[] = Array.isArray(result) ? result : [result];
72
-
73
- console.log('📦 [LoadExtensionRoutes] Processing workspace configs:', workspaceConfigs.length);
74
-
75
- // Обрабатываем каждый workspace из расширения
76
- for (const config of workspaceConfigs) {
77
- console.log('📦 [LoadExtensionRoutes] Processing workspace config:', {
78
- workspace: config.workspace,
79
- routesCount: config.routes?.length,
80
- routes: config.routes?.map(r => ({ name: r.name, meta: r.meta }))
81
- });
82
-
83
- if (config?.workspace && config?.routes?.length) {
84
- // Записываем маршруты в соответствующий workspace
85
- console.log('📦 [LoadExtensionRoutes] Setting routes for workspace:', config.workspace);
86
- store.setRoutes(config.workspace, config.routes as any);
87
-
88
- // Регистрируем маршруты в router
89
- const baseRoute = router.getRoutes().find((r) => r.name === 'base');
90
- if (baseRoute) {
91
- config.routes.forEach((r: any) => {
92
- // Проверяем, не зарегистрирован ли уже маршрут
93
- const existingRoute = router
94
- .getRoutes()
95
- .find((route) => route.name === r.name);
96
- if (!existingRoute) {
97
- console.log('📦 [LoadExtensionRoutes] Adding route to router:', r.name);
98
- router.addRoute('base', r);
99
- } else {
100
- console.log('📦 [LoadExtensionRoutes] Route already exists, skipping:', r.name);
101
- }
102
- });
103
- }
80
+ console.log('📦 [LoadExtensionRoutes] Found extension in registry, loading...');
81
+
82
+ const result = await installFunction();
83
+ console.log('📦 [LoadExtensionRoutes] Extension loaded, result:', result);
84
+
85
+ // Поддержка обоих форматов: массив или одиночный объект (для обратной совместимости)
86
+ const workspaceConfigs: IWorkspaceConfig[] = Array.isArray(result) ? result : [result];
87
+
88
+ console.log('📦 [LoadExtensionRoutes] Processing workspace configs:', workspaceConfigs.length);
89
+
90
+ // Обрабатываем каждый workspace из расширения
91
+ for (const config of workspaceConfigs) {
92
+ console.log('📦 [LoadExtensionRoutes] Processing workspace config:', {
93
+ workspace: config.workspace,
94
+ routesCount: config.routes?.length,
95
+ routes: config.routes?.map(r => ({ name: r.name, meta: r.meta }))
96
+ });
97
+
98
+ if (config?.workspace && config?.routes?.length) {
99
+ // Записываем маршруты в соответствующий workspace
100
+ console.log('📦 [LoadExtensionRoutes] Setting routes for workspace:', config.workspace);
101
+ store.setRoutes(config.workspace, config.routes as any);
102
+
103
+ // Регистрируем маршруты в router
104
+ const baseRoute = router.getRoutes().find((r) => r.name === 'base');
105
+ if (baseRoute) {
106
+ config.routes.forEach((r: any) => {
107
+ // Проверяем, не зарегистрирован ли уже маршрут
108
+ const existingRoute = router
109
+ .getRoutes()
110
+ .find((route) => route.name === r.name);
111
+ if (!existingRoute) {
112
+ console.log('📦 [LoadExtensionRoutes] Adding route to router:', r.name);
113
+ router.addRoute('base', r);
114
+ } else {
115
+ console.log('📦 [LoadExtensionRoutes] Route already exists, skipping:', r.name);
116
+ }
117
+ });
104
118
  }
105
119
  }
106
-
107
- console.log(
108
- `📦 [LoadExtensionRoutes] Routes for extension "${extensionName}" loaded successfully (${workspaceConfigs.length} workspace(s))`,
109
- );
110
120
  }
121
+
122
+ console.log(
123
+ `📦 [LoadExtensionRoutes] Routes for extension "${extensionName}" loaded successfully (${workspaceConfigs.length} workspace(s))`,
124
+ );
111
125
  } catch (error) {
112
126
  console.error(
113
127
  `📦 [LoadExtensionRoutes] Failed to load routes for extension "${extensionName}":`,
@@ -1,3 +1,4 @@
1
1
  export { useMeetStatus } from './useMeetStatus';
2
2
  export { useExpandableState } from './useExpandableState';
3
3
  export { useDataPoller } from './useDataPoller';
4
+ export { useMobileDrawer } from './useMobileDrawer';
@@ -0,0 +1,20 @@
1
+ import { useDesktopStore } from 'src/entities/Desktop/model';
2
+
3
+ /**
4
+ * Composable для управления drawer на мобильных устройствах
5
+ * Используется для автоматического закрытия drawer при определенных действиях
6
+ */
7
+ export function useMobileDrawer() {
8
+ const desktop = useDesktopStore();
9
+
10
+ /**
11
+ * Закрывает левый drawer только на мобильных устройствах
12
+ */
13
+ const closeDrawerOnMobile = () => {
14
+ desktop.closeLeftDrawerOnMobile();
15
+ };
16
+
17
+ return {
18
+ closeDrawerOnMobile,
19
+ };
20
+ }
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- .absolute-full.flex.flex-center.z-top
2
+ .absolute-full.flex.flex-center.z-plus
3
3
  Loader(size='50px', color='grey', style='margin: auto', :text='text')
4
4
  </template>
5
5
 
@@ -14,3 +14,8 @@ defineProps({
14
14
  },
15
15
  });
16
16
  </script>
17
+ <style>
18
+ .z-plus {
19
+ z-index: 100;
20
+ }
21
+ </style>
@@ -25,9 +25,8 @@
25
25
  .status-info.q-mt-lg
26
26
  .status-description.text-body1.text-on-surface
27
27
  | Все технические подготовки завершены. Ваш Цифровой Кооператив готов к установке и подключению к платформе Кооперативной Экономики.
28
- br
29
- | Теперь мы ожидаем официального подтверждения от союза кооперативов о вашем членстве.
30
-
28
+ | Теперь мы ожидаем официального подтверждения от союза о вашем активном членстве.
29
+ | Когда оно будет получено - установка продолжится автоматически.
31
30
 
32
31
  //- Навигация
33
32
  q-stepper-navigation.q-gutter-sm
@@ -48,7 +48,7 @@ q-dialog.cmdk-dialog(
48
48
  // Заголовок группы (воркспейс)
49
49
  .cmdk-group-header(
50
50
  :class="{ 'selected': cmdkStore.selectedIndex === groupIndex }"
51
- @click="cmdkStore.selectGroup(groupIndex)"
51
+ @click="handleSelectGroup(groupIndex)"
52
52
  )
53
53
  q-icon.cmdk-group-icon(:name="group.icon")
54
54
  .cmdk-group-title {{ group.title }}
@@ -60,14 +60,14 @@ q-dialog.cmdk-dialog(
60
60
  v-for="(page, pageIndex) in group.pages",
61
61
  :key="page.name",
62
62
  :class="{ 'selected': cmdkStore.selectedIndex === groupIndex && cmdkStore.selectedPageIndex === pageIndex }"
63
- @click="cmdkStore.selectPage(group.workspaceName, page)"
63
+ @click="handleSelectPage(group.workspaceName, page)"
64
64
  )
65
65
  q-icon.cmdk-page-icon(:name="page.meta.icon || 'circle'")
66
66
  .cmdk-page-title {{ page.meta.title }}
67
67
  .cmdk-page-shortcut(v-if="page.shortcut") {{ page.shortcut }}
68
68
 
69
69
  // Подсказки
70
- .cmdk-footer
70
+ .cmdk-footer(v-if="!isMobile")
71
71
  .cmdk-hint
72
72
  kbd ↑↓
73
73
  span для навигации
@@ -80,11 +80,26 @@ q-dialog.cmdk-dialog(
80
80
  <script setup lang="ts">
81
81
  import { ref, onMounted, onUnmounted, watchEffect } from 'vue';
82
82
  import { useCmdkMenuStore } from 'src/entities/CmdkMenu/model';
83
+ import { useMobileDrawer } from 'src/shared/lib/composables/useMobileDrawer';
84
+ import { useWindowSize } from 'src/shared/hooks/useWindowSize';
83
85
 
84
86
  const cmdkStore = useCmdkMenuStore();
87
+ const { isMobile } = useWindowSize();
88
+ const { closeDrawerOnMobile } = useMobileDrawer();
85
89
  const searchInput = ref<HTMLInputElement | null>(null);
86
90
  const contentRef = ref<HTMLElement | null>(null);
87
91
 
92
+ // Обработчики для выбора элементов с закрытием drawer на мобильных устройствах
93
+ const handleSelectGroup = (groupIndex: number) => {
94
+ closeDrawerOnMobile();
95
+ cmdkStore.selectGroup(groupIndex);
96
+ };
97
+
98
+ const handleSelectPage = (workspaceName: string, page: any) => {
99
+ closeDrawerOnMobile();
100
+ cmdkStore.selectPage(workspaceName, page);
101
+ };
102
+
88
103
  // Следим за contentRef и устанавливаем его в store
89
104
  watchEffect(() => {
90
105
  if (contentRef.value) {
@@ -110,6 +125,7 @@ onUnmounted(() => {
110
125
  <style lang="scss">
111
126
  .cmdk-dialog {
112
127
  max-width: 100%;
128
+ z-index: 10000;
113
129
  }
114
130
 
115
131
  .cmdk-panel {