@coopenomics/desktop 2025.5.14 → 2025.6.13

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.
Files changed (114) hide show
  1. package/.env-example +4 -0
  2. package/extensions/participant/install.ts +37 -25
  3. package/extensions/soviet/install.ts +23 -20
  4. package/package.json +7 -5
  5. package/quasar.config.cjs +1 -1
  6. package/src/app/App.vue +1 -0
  7. package/src/app/providers/routes/index.ts +12 -0
  8. package/src/app/styles/app.scss +1 -1
  9. package/src/app/styles/style.css +11 -0
  10. package/src/css/quasar.variables.scss +1 -1
  11. package/src/entities/Desktop/model/store.ts +68 -0
  12. package/src/entities/Desktop/model/types.ts +7 -0
  13. package/src/entities/Document/model/types.ts +1 -1
  14. package/src/entities/Meet/api/index.ts +2 -28
  15. package/src/entities/Meet/model/store.ts +7 -22
  16. package/src/entities/Wallet/api/index.ts +3 -3
  17. package/src/env.d.ts +1 -0
  18. package/src/features/Meet/CloseMeetWithDecision/model/index.ts +119 -17
  19. package/src/features/Meet/CreateMeet/model/index.ts +51 -9
  20. package/src/features/Meet/CreateMeet/ui/CreateMeet.vue +37 -6
  21. package/src/features/Meet/CreateMeet/ui/CreateMeetForm.vue +87 -65
  22. package/src/features/Meet/GenerateSovietDecision/model/index.ts +14 -4
  23. package/src/features/Meet/RestartMeet/model/index.ts +121 -3
  24. package/src/features/Meet/RestartMeet/ui/RestartMeet.vue +4 -6
  25. package/src/features/Meet/RestartMeet/ui/RestartMeetForm.vue +64 -28
  26. package/src/features/Meet/SignNotification/index.ts +2 -0
  27. package/src/features/Meet/SignNotification/model/index.ts +137 -0
  28. package/src/features/Meet/SignNotification/ui/SignNotificationButton.vue +61 -0
  29. package/src/features/Meet/SignNotification/ui/index.ts +1 -0
  30. package/src/features/Meet/VoteOnMeet/model/composable.ts +180 -0
  31. package/src/features/Meet/VoteOnMeet/model/index.ts +2 -17
  32. package/src/features/Meet/VoteOnMeet/model/types.ts +4 -0
  33. package/src/features/Meet/index.ts +6 -0
  34. package/src/features/User/LoginRedirect/ui/LoginRedirectPage.vue +15 -0
  35. package/src/features/User/LoginRedirect/ui/index.ts +1 -0
  36. package/src/features/User/LoginUser/ui/LoginForm/LoginForm.vue +38 -1
  37. package/src/features/User/LoginWithRedirect/ui/LoginRedirectForm/LoginRedirectForm.vue +0 -0
  38. package/src/pages/Cooperative/ListOfMeets/ui/ListOfMeetsPage.vue +22 -46
  39. package/src/pages/Cooperative/MeetDetails/ui/MeetDetailsPage.vue +84 -28
  40. package/src/pages/PermissionDenied/PermissionDenied.vue +1 -1
  41. package/src/processes/init-app/index.ts +12 -5
  42. package/src/processes/navigation-guard-setup/index.ts +37 -5
  43. package/src/processes/process-decisions/index.ts +7 -6
  44. package/src/shared/config/Environment.ts +35 -27
  45. package/src/shared/lib/composables/index.ts +1 -0
  46. package/src/shared/lib/composables/useMeetStatus.ts +96 -0
  47. package/src/shared/lib/consts/index.ts +1 -0
  48. package/src/shared/lib/consts/meet-statuses.ts +114 -0
  49. package/src/shared/lib/document/model/entity.ts +4 -2
  50. package/src/shared/lib/types/certificate/index.ts +6 -0
  51. package/src/shared/lib/types/document/index.ts +1 -1
  52. package/src/shared/lib/types/workspace.ts +24 -0
  53. package/src/shared/lib/utils/dates/index.ts +5 -0
  54. package/src/shared/lib/utils/dates/moment.ts +43 -2
  55. package/src/shared/lib/utils/dates/timezone.ts +75 -0
  56. package/src/shared/lib/utils/getNameFromCertificate.ts +108 -0
  57. package/src/shared/lib/utils/index.ts +1 -0
  58. package/src/shared/lib/utils/parseLinks.ts +10 -0
  59. package/src/shared/ui/AgendaNumberAvatar/AgendaNumberAvatar.vue +12 -0
  60. package/src/shared/ui/AgendaNumberAvatar/index.ts +1 -0
  61. package/src/shared/ui/BaseDocument/BaseDocument.vue +37 -8
  62. package/src/shared/ui/ExpandableDocument/ExpandableDocument.vue +49 -0
  63. package/src/shared/ui/ExpandableDocument/index.ts +1 -0
  64. package/src/shared/ui/MeetInfoCard/index.ts +1 -0
  65. package/src/shared/ui/MeetInfoCard/ui/MeetInfoCard.vue +62 -0
  66. package/src/shared/ui/MeetStatusBanner/index.ts +1 -0
  67. package/src/shared/ui/MeetStatusBanner/ui/MeetStatusBanner.vue +94 -0
  68. package/src/shared/ui/index.ts +1 -0
  69. package/src/widgets/Cooperative/Documents/ListOfDocuments/ui/DocumentsTable.vue +3 -3
  70. package/src/widgets/Cooperative/Orders/ListOfOrders/ui/ListOfOrdersWidget.vue +2 -2
  71. package/src/widgets/Cooperative/Payments/ListOfPayments/ui/ListOfPaymentsWidget.vue +2 -2
  72. package/src/widgets/Desktop/WorkspaceMenu/WorkspaceMenu.vue +74 -82
  73. package/src/widgets/Header/CommonHeader/CooperativeSettingsHeader.vue +1 -1
  74. package/src/widgets/Header/CommonHeader/ExtstoreHeader.vue +1 -1
  75. package/src/widgets/Header/CommonHeader/MainHeader.vue +6 -0
  76. package/src/widgets/Header/CommonHeader/UserSettingsHeader.vue +1 -1
  77. package/src/widgets/Meets/MeetCardsList/index.ts +1 -0
  78. package/src/widgets/Meets/MeetCardsList/ui/MeetCardsList.vue +55 -0
  79. package/src/widgets/Meets/MeetDetailsActions/MeetDetailsActions.vue +33 -15
  80. package/src/widgets/Meets/MeetDetailsAgenda/MeetDetailsAgenda.vue +34 -15
  81. package/src/widgets/Meets/MeetDetailsInfo/MeetDetailsInfo.vue +27 -0
  82. package/src/widgets/Meets/MeetDetailsInfo/index.ts +1 -0
  83. package/src/widgets/Meets/MeetDetailsResults/MeetDetailsResults.vue +129 -0
  84. package/src/widgets/Meets/MeetDetailsResults/index.ts +1 -0
  85. package/src/widgets/Meets/MeetDetailsVoting/MeetDetailsVoting.vue +221 -62
  86. package/src/widgets/Meets/MeetQuorumIndicator/MeetQuorumIndicator.vue +0 -0
  87. package/src/widgets/Meets/MeetQuorumIndicator/index.ts +1 -0
  88. package/src/widgets/Meets/MeetQuorumIndicator/ui/MeetQuorumIndicator.vue +35 -0
  89. package/src/widgets/Meets/MeetsTable/ui/MeetsTable.vue +56 -65
  90. package/src/widgets/NotificationCenter/NotificationCenter.vue +90 -0
  91. package/src/widgets/NotificationCenter/index.ts +1 -0
  92. package/src/widgets/Participants/ui/ParticipantsTable.vue +1 -1
  93. package/src/widgets/Questions/ui/QuestionsTable/QuestionsTable.vue +22 -10
  94. package/src/widgets/Questions/ui/VotingButtons/VotingButtons.vue +59 -14
  95. package/src/widgets/Registrator/AlreadyRegistered/AlreadyRegistered.vue +1 -1
  96. package/src/widgets/User/PaymentMethods/ui/PaymentMethods.vue +0 -1
  97. package/src-ssr/middlewares/injectEnv.ts +18 -12
  98. package/src/features/Meet/GenerateAgenda/index.ts +0 -1
  99. package/src/features/Meet/GenerateAgenda/model/index.ts +0 -18
  100. package/src/features/Meet/GenerateBallot/index.ts +0 -1
  101. package/src/features/Meet/GenerateBallot/model/index.ts +0 -18
  102. package/src/features/Meet/GenerateNotification/index.ts +0 -1
  103. package/src/features/Meet/GenerateNotification/model/index.ts +0 -18
  104. package/src/features/Meet/MeetDetailsManagement/index.ts +0 -1
  105. package/src/features/Meet/MeetDetailsManagement/model/index.ts +0 -121
  106. package/src/pages/Cooperative/ListOfMeets/model/index.ts +0 -1
  107. package/src/pages/Cooperative/ListOfMeets/model/model.ts +0 -117
  108. package/src/widgets/Meets/MeetDetailsActions/model.ts +0 -46
  109. package/src/widgets/Meets/MeetDetailsHeader/MeetDetailsHeader.vue +0 -40
  110. package/src/widgets/Meets/MeetDetailsHeader/index.ts +0 -1
  111. package/src/widgets/Meets/MeetDetailsVoting/model.ts +0 -117
  112. package/src/widgets/Meets/MeetInfoCard/ui/MeetInfoCard.vue +0 -38
  113. package/src/widgets/Meets/MeetInfoCard/ui/index.ts +0 -1
  114. /package/src/{widgets/Meets/MeetInfoCard → features/User/LoginRedirect}/index.ts +0 -0
@@ -0,0 +1,12 @@
1
+ <template lang="pug">
2
+ q-avatar(color="primary" text-color="white" size="40px") {{ number }}
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ defineProps({
7
+ number: {
8
+ type: [String, Number],
9
+ required: true,
10
+ },
11
+ })
12
+ </script>
@@ -0,0 +1 @@
1
+ export { default as AgendaNumberAvatar } from './AgendaNumberAvatar.vue';
@@ -29,7 +29,7 @@ q-card(:flat="isMobile" style="word-break: break-all !important; white-space: no
29
29
  q-expansion-item(
30
30
  v-for="(signature, index) in documentAggregate.document.signatures"
31
31
  :key="index"
32
- :label="`Подпись ${index + 1}: ${getSignerName(signature.signer_info)}`"
32
+ :label="`Подпись ${index + 1}: ${getSignerName(signature.signer_certificate)}`"
33
33
  header-class="signature-header"
34
34
  dense
35
35
  )
@@ -38,7 +38,7 @@ q-card(:flat="isMobile" style="word-break: break-all !important; white-space: no
38
38
  div.q-mb-sm
39
39
  q-badge(:color="signature.is_valid ? 'teal' : 'red'").text-center.q-pa-xs
40
40
  span Подписант
41
- p.q-mt-sm.q-ml-lg {{ getSignerName(signature.signer_info) }}
41
+ p.q-mt-sm.q-ml-lg {{ getSignerName(signature.signer_certificate) }}
42
42
 
43
43
  div(v-if="signature.public_key").q-mb-sm
44
44
  q-badge(:color="signature.is_valid ? 'teal' : 'red'").text-center.q-pa-xs
@@ -68,9 +68,9 @@ import { useGlobalStore } from 'src/shared/store';
68
68
  import DOMPurify from 'dompurify';
69
69
  import { DigitalDocument } from 'src/shared/lib/document';
70
70
  import { FailAlert, SuccessAlert } from 'src/shared/api';
71
- import { getNameFromUserData } from 'src/shared/lib/utils/getNameFromUserData';
72
71
  import { useWindowSize } from 'src/shared/hooks';
73
72
  import type { IDocumentAggregate } from 'src/entities/Document/model';
73
+ import { getNameFromCertificate } from 'src/shared/lib/utils/getNameFromCertificate';
74
74
 
75
75
  const props = defineProps({
76
76
  documentAggregate: {
@@ -106,7 +106,10 @@ const regenerate = async() => {
106
106
 
107
107
  // Функция для декодирования и очистки HTML
108
108
  function sanitizeHtml(html: string) {
109
- return DOMPurify.sanitize(html);
109
+ return DOMPurify.sanitize(html, {
110
+ ADD_TAGS: ['style'],
111
+ ADD_ATTR: ['class', 'id']
112
+ });
110
113
  }
111
114
 
112
115
  const safeHtml = computed(() => sanitizeHtml(doc.value?.html ?? ''));
@@ -131,10 +134,10 @@ const hashBuffer = async () => {
131
134
  }
132
135
  }
133
136
 
134
- // Получение ФИО подписанта
135
- const getSignerName = (signer: any) => {
136
- if (!signer) return 'Неизвестный подписант';
137
- return getNameFromUserData(signer) || signer;
137
+ // Получение ФИО/названия подписанта по сертификату
138
+ const getSignerName = (signer_certificate: any) => {
139
+ if (!signer_certificate) return 'Неизвестный подписант';
140
+ return getNameFromCertificate(signer_certificate) || 'Неизвестный подписант';
138
141
  }
139
142
 
140
143
  // Верификация всех подписей из агрегата
@@ -228,4 +231,30 @@ const hasInvalidSignature = computed(() => props.documentAggregate?.document?.si
228
231
  padding: 10px !important;
229
232
  }
230
233
  }
234
+ /*
235
+ /* Стили для таблиц внутри документа */
236
+ .description table {
237
+ width: 100%;
238
+ border-collapse: collapse;
239
+ }
240
+
241
+ .description th,
242
+ .description td {
243
+ border: 1px solid #ccc;
244
+ padding: 8px;
245
+ text-align: left;
246
+ word-wrap: break-word;
247
+ overflow-wrap: break-word;
248
+ }
249
+
250
+ .description th {
251
+ background-color: #f4f4f4;
252
+ width: 30% !important;
253
+ max-width: 30% !important;
254
+ word-break: break-word !important;
255
+ }
256
+ .q-table--no-wrap th, .q-table--no-wrap td {
257
+ white-space: break-spaces !important;
258
+ word-break: break-word !important;
259
+ }
231
260
  </style>
@@ -0,0 +1,49 @@
1
+ <template lang="pug">
2
+ q-card(flat).expandable-document.q-mb-md
3
+ q-expansion-item(
4
+ switch-toggle-side
5
+ :label="title || 'Электронный документ'"
6
+ icon="article"
7
+ header-class="document-header"
8
+ expand-icon-class="text-primary"
9
+ )
10
+ q-card
11
+ q-card-section
12
+ div.row.justify-center
13
+ BaseDocument(
14
+ :documentAggregate="documentAggregate"
15
+ ).col-xs-12.col-md-10
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import { BaseDocument } from '../BaseDocument'
20
+ import type { IDocumentAggregate } from 'src/entities/Document/model'
21
+
22
+ defineProps({
23
+ documentAggregate: {
24
+ type: Object as () => IDocumentAggregate,
25
+ required: true
26
+ },
27
+ title: {
28
+ type: String,
29
+ default: 'Документ собрания'
30
+ }
31
+ })
32
+ </script>
33
+
34
+ <style lang="scss" scoped>
35
+ .expandable-document {
36
+ width: 100%;
37
+ border: 1px solid #f0f0f0;
38
+ border-radius: 8px;
39
+
40
+ :deep(.document-header) {
41
+ font-weight: 500;
42
+ border-radius: 8px;
43
+
44
+ .q-icon {
45
+ color: var(--q-primary);
46
+ }
47
+ }
48
+ }
49
+ </style>
@@ -0,0 +1 @@
1
+ export { default as ExpandableDocument } from './ExpandableDocument.vue'
@@ -0,0 +1 @@
1
+ export { default as MeetInfoCard } from './ui/MeetInfoCard.vue'
@@ -0,0 +1,62 @@
1
+ <template lang="pug">
2
+ div
3
+ div.row.q-mb-sm
4
+ div.col-12
5
+ div.text-h5.q-mb-xs.full-width.text-center Общее собрание № {{ meet.processing?.meet?.id }}
6
+
7
+
8
+ div.row.justify-around.q-mt-md
9
+ div.col-xs-12.col-md-5
10
+
11
+ div.row.q-mb-sm.items-baseline
12
+ div.col-xs-12.col-sm-5.card-label Председатель:
13
+ div.col-xs-12.col-sm-7.card-value {{ getNameFromCertificate(meet.processing?.meet?.presider_certificate) }}
14
+
15
+ div.row.q-mb-sm.items-baseline
16
+ div.col-xs-12.col-sm-5.card-label Секретарь:
17
+ div.col-xs-12.col-sm-7.card-value {{ getNameFromCertificate(meet.processing?.meet?.secretary_certificate) }}
18
+
19
+ div.row.q-mb-sm.items-baseline
20
+ div.col-xs-12.col-sm-5.card-label Открытие:
21
+ div.col-xs-12.col-sm-7.card-value {{ meetStatus.formattedOpenDate }} ({{ timezoneLabel }})
22
+
23
+ div.col-xs-12.col-md-5
24
+ div.row.q-mb-sm.items-baseline
25
+ div.col-xs-12.col-sm-5.card-label Кворум:
26
+ div.col-xs-12.col-sm-7.card-value {{ meet.processing?.meet?.quorum_percent }}%
27
+
28
+ div.row.q-mb-sm.items-baseline
29
+ div.col-xs-12.col-sm-5.card-label Явка:
30
+ div.col-xs-12.col-sm-7.card-value {{ (Math.round((meet.processing?.meet?.current_quorum_percent ?? 0) * 10) / 10).toFixed(1) }}%
31
+
32
+
33
+ div.row.q-mb-sm.items-baseline
34
+ div.col-xs-12.col-sm-5.card-label Закрытие:
35
+ div.col-xs-12.col-sm-7.card-value {{ meetStatus.formattedCloseDate }} ({{ timezoneLabel }})
36
+
37
+ div.row.justify-around.q-mt-lg
38
+ MeetStatusBanner(:meet="meet")
39
+
40
+
41
+ </template>
42
+
43
+ <script setup lang="ts">
44
+ import type { IMeet } from 'src/entities/Meet'
45
+ import { useMeetStatus } from 'src/shared/lib/composables'
46
+ import { getNameFromCertificate } from 'src/shared/lib/utils/getNameFromCertificate'
47
+ import { MeetStatusBanner } from 'src/shared/ui/MeetStatusBanner'
48
+ import { getTimezoneLabel } from 'src/shared/lib/utils/dates/timezone'
49
+
50
+ const props = defineProps<{
51
+ meet: IMeet
52
+ }>()
53
+
54
+ // Название часового пояса для отображения в лейблах
55
+ const timezoneLabel = getTimezoneLabel()
56
+
57
+ const meetStatus = useMeetStatus(props.meet)
58
+ </script>
59
+
60
+ <style lang="scss" scoped>
61
+ @import 'src/shared/ui/CardStyles/index.scss';
62
+ </style>
@@ -0,0 +1 @@
1
+ export { default as MeetStatusBanner } from './ui/MeetStatusBanner.vue'
@@ -0,0 +1,94 @@
1
+ <template lang="pug">
2
+ // Баннер с информацией о статусе собрания
3
+ div.full-width
4
+ q-banner(
5
+ v-if="extendedStatus && extendedStatus !== 'NONE'",
6
+ rounded,
7
+ :class="bannerClasses"
8
+ :style="bannerStyle"
9
+ ).q-pa-md
10
+ template(v-slot:avatar)
11
+ q-icon(:name="bannerConfig.icon" :color="iconColor")
12
+ div {{ statusText }} {{ timeText }}
13
+
14
+
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { computed } from 'vue'
19
+ import type { IMeet } from 'src/entities/Meet'
20
+ import { EXTENDED_STATUS_MAP, STATUS_BANNER_CONFIG } from 'src/shared/lib/consts'
21
+ import { formatDateFromNow } from 'src/shared/lib/utils/dates/timezone'
22
+
23
+ const props = defineProps<{
24
+ meet: IMeet
25
+ }>()
26
+
27
+ // Получаем расширенный статус собрания
28
+ const extendedStatus = computed(() => {
29
+ return props.meet?.processing?.extendedStatus || 'NONE'
30
+ })
31
+
32
+ // Текст статуса собрания
33
+ const statusText = computed(() => {
34
+ if (!props.meet?.processing?.extendedStatus) return 'Неизвестный статус'
35
+ return EXTENDED_STATUS_MAP[props.meet.processing.extendedStatus] || 'Неизвестный статус'
36
+ })
37
+
38
+ // Конфигурация баннера
39
+ const bannerConfig = computed(() => {
40
+ if (!props.meet?.processing?.extendedStatus) {
41
+ return STATUS_BANNER_CONFIG['NONE']
42
+ }
43
+ return (
44
+ STATUS_BANNER_CONFIG[props.meet.processing.extendedStatus] || STATUS_BANNER_CONFIG['NONE']
45
+ )
46
+ })
47
+
48
+ const bannerClasses = computed(() => {
49
+ const config = bannerConfig.value
50
+ if (config.outline) {
51
+ return 'bg-grey-1 outlined-banner'
52
+ }
53
+ return `bg-${config.color} text-white`
54
+ })
55
+
56
+ const bannerStyle = computed(() => {
57
+ const config = bannerConfig.value
58
+ if (config.outline) {
59
+ return { borderColor: `var(--q-color-${config.color})` }
60
+ }
61
+ return {}
62
+ })
63
+
64
+ const iconColor = computed(() => {
65
+ const config = bannerConfig.value
66
+ return config.outline ? config.color : 'white'
67
+ })
68
+
69
+ // Форматированное время до открытия/закрытия с учетом часового пояса
70
+ const timeText = computed(() => {
71
+ if (!bannerConfig.value.needTime) return ''
72
+
73
+ // Для статуса WAITING_FOR_OPENING
74
+ if (props.meet?.processing?.extendedStatus === 'WAITING_FOR_OPENING') {
75
+ if (!props.meet?.processing?.meet?.open_at) return ''
76
+ return formatDateFromNow(props.meet.processing.meet.open_at)
77
+ }
78
+
79
+ // Для статуса VOTING_IN_PROGRESS
80
+ if (props.meet?.processing?.extendedStatus === 'VOTING_IN_PROGRESS') {
81
+ if (!props.meet?.processing?.meet?.close_at) return ''
82
+ return formatDateFromNow(props.meet.processing.meet.close_at)
83
+ }
84
+
85
+ return ''
86
+ })
87
+ </script>
88
+
89
+ <style lang="scss" scoped>
90
+ .outlined-banner {
91
+ border-width: 1px;
92
+ border-style: solid;
93
+ }
94
+ </style>
@@ -1 +1,2 @@
1
1
  export * from './CopyableInput';
2
+ export * from './ExpandableDocument';
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- div.scroll-area(style="height: 90vh; overflow-y: auto;")
2
+ div.scroll-area(style="height: calc(100% - $toolbar-min-height); overflow-y: auto;")
3
3
  q-table(
4
4
  ref="tableRef"
5
5
  flat
@@ -45,7 +45,7 @@ div.scroll-area(style="height: 90vh; overflow-y: auto;")
45
45
  color="primary"
46
46
  round
47
47
  dense
48
- :icon="expanded.get(props.row?.id || props.row?.statement?.action?.global_sequence) ? 'remove' : 'add'"
48
+ :icon="expanded.get(props.row?.id || props.row?.statement?.action?.global_sequence) ? 'expand_more' : 'chevron_right'"
49
49
  @click="toggleExpand(props.row?.id || props.row?.statement?.action?.global_sequence)"
50
50
  )
51
51
 
@@ -160,4 +160,4 @@ const toggleExpand = (id: any) => {
160
160
  .documents-table .q-table__top{
161
161
  padding: 0px !important;
162
162
  }
163
- </style>
163
+ </style>
@@ -1,7 +1,7 @@
1
1
  <template lang="pug">
2
2
  div.row.justify-center
3
3
  div.col-12
4
- div.scroll-area(style="height: 90vh; overflow-y: auto;")
4
+ div.scroll-area(style="height: calc(100% - $toolbar-min-height); overflow-y: auto;")
5
5
  q-table(
6
6
  v-if="payments && payments.items"
7
7
  ref="tableRef"
@@ -48,7 +48,7 @@
48
48
  color="primary"
49
49
  round
50
50
  dense
51
- :icon="expanded.get(props.row.id) ? 'remove' : 'add'"
51
+ :icon="expanded.get(props.row.id) ? 'expand_more' : 'chevron_right'"
52
52
  @click="toggleExpand(props.row.id)"
53
53
  )
54
54
  q-td {{props.row.id}}
@@ -1,7 +1,7 @@
1
1
  <template lang="pug">
2
2
  div.row.justify-center
3
3
  div.col-12
4
- div.scroll-area(style="height: 90vh; overflow-y: auto;")
4
+ div.scroll-area(style="height: calc(100% - $toolbar-min-height); overflow-y: auto;")
5
5
  q-table(
6
6
  v-if="payments && payments.items"
7
7
  ref="tableRef"
@@ -47,7 +47,7 @@
47
47
  color="primary"
48
48
  round
49
49
  dense
50
- :icon="expanded.get(props.row.id) ? 'remove' : 'add'"
50
+ :icon="expanded.get(props.row.id) ? 'expand_more' : 'chevron_right'"
51
51
  @click="toggleExpand(props.row.id)"
52
52
  )
53
53
  q-td(style="max-width: 150px; word-wrap: break-word; white-space: normal;") {{ getName(props.row.account) }}
@@ -53,99 +53,91 @@ div
53
53
  div.btn-font.text-center {{ item.title }}
54
54
  </template>
55
55
 
56
- <script setup lang="ts">
57
- import { ref, computed, onMounted, watch } from 'vue'
58
- import { useRouter } from 'vue-router'
59
- import { useSystemStore } from 'src/entities/System/model'
60
- import { useCurrentUserStore } from 'src/entities/User'
61
- import { useDesktopStore } from 'src/entities/Desktop/model'
62
- import { useSessionStore } from 'src/entities/Session'
56
+ <script setup lang="ts">
57
+ import { ref, computed, onMounted, watch } from 'vue'
58
+ import { useRouter } from 'vue-router'
59
+ import { useCurrentUserStore } from 'src/entities/User'
60
+ import { useDesktopStore } from 'src/entities/Desktop/model'
63
61
 
64
- const router = useRouter()
65
- const system = useSystemStore()
66
- const user = useCurrentUserStore()
67
- const session = useSessionStore()
68
- const desktop = useDesktopStore()
69
- const { info } = system
62
+ const router = useRouter()
63
+ const user = useCurrentUserStore()
64
+ const desktop = useDesktopStore()
70
65
 
71
- const slideIndex = ref(0)
72
- const showDialog = ref(false)
66
+ const slideIndex = ref(0)
67
+ const showDialog = ref(false)
73
68
 
74
- // workspaceMenus – список рабочих столов из store
75
- const workspaceMenus = computed(() => desktop.workspaceMenus)
69
+ // workspaceMenus – список рабочих столов из store
70
+ const workspaceMenus = computed(() => desktop.workspaceMenus)
76
71
 
77
- // Фильтрация по ролям (если требуется)
78
- const menuWorkspaces = computed(() => {
79
- const userRole = user.userAccount?.role || 'user'
80
- return workspaceMenus.value.filter(item =>
81
- item.meta.roles?.includes(userRole) || !item.meta.roles || item.meta.roles.length === 0
82
- )
83
- })
84
-
85
- const headerClass = (item: any) => {
86
- return desktop?.activeWorkspaceName === item.workspaceName ? 'text-white bg-teal' : ''
87
- }
88
-
89
-
90
- // Функция навигации: переходим в рамках маршрутов рабочего стола
91
- const open = (route: any) => {
92
- if (route && route.children && route.children.length)
93
- router.push({ name: route.children[0].name, params: { coopname: info.coopname } })
94
- else if (route)
95
- router.push({ name: route.name, params: { coopname: info.coopname } })
96
- }
72
+ // Фильтрация по ролям (если требуется)
73
+ const menuWorkspaces = computed(() => {
74
+ const userRole = user.userAccount?.role || 'user'
75
+ return workspaceMenus.value.filter(item =>
76
+ item.meta.roles?.includes(userRole) || !item.meta.roles || item.meta.roles.length === 0
77
+ )
78
+ })
97
79
 
98
- const handleClick = (item: any, index: number) => {
99
- if (slideIndex.value === index) {
100
- showDialog.value = true
101
- } else {
102
- slideIndex.value = index
103
- // Обновляем активный рабочий стол
104
- desktop.selectWorkspace(item.workspaceName)
105
- if (item.mainRoute) open(item.mainRoute)
106
- }
107
- }
80
+ const headerClass = (item: any) => {
81
+ return desktop?.activeWorkspaceName === item.workspaceName ? 'text-white bg-teal' : ''
82
+ }
108
83
 
109
- const selectFromDialog = (index: number) => {
110
- showDialog.value = false
84
+ const handleClick = (item: any, index: number) => {
85
+ if (slideIndex.value === index) {
86
+ showDialog.value = true
87
+ } else {
111
88
  slideIndex.value = index
112
- const item = menuWorkspaces.value[index]
89
+ // Обновляем активный рабочий стол
113
90
  desktop.selectWorkspace(item.workspaceName)
114
- if (item.mainRoute) open(item.mainRoute)
91
+ // Переходим на маршрут по умолчанию для выбранного рабочего стола
92
+ desktop.goToDefaultPage(router)
115
93
  }
94
+ }
116
95
 
117
- onMounted(() => {
118
- if (menuWorkspaces.value.length > 0 && session.isAuth && user.isRegistrationComplete) {
119
- slideIndex.value = 0
120
- const firstItem = menuWorkspaces.value[0]
121
- desktop.selectWorkspace(firstItem.workspaceName)
122
- if (firstItem.mainRoute) open(firstItem.mainRoute)
123
- }
124
- })
96
+ const selectFromDialog = (index: number) => {
97
+ showDialog.value = false
98
+ slideIndex.value = index
99
+ const item = menuWorkspaces.value[index]
100
+ desktop.selectWorkspace(item.workspaceName)
101
+ // Переходим на маршрут по умолчанию для выбранного рабочего стола
102
+ desktop.goToDefaultPage(router)
103
+ }
125
104
 
126
- // При изменении slideIndex обновляем активный рабочий стол
127
- watch(slideIndex, (newIndex) => {
128
- const item = menuWorkspaces.value[newIndex]
129
- if (item) {
130
- desktop.selectWorkspace(item.workspaceName)
131
- if (item.mainRoute) open(item.mainRoute)
105
+ onMounted(() => {
106
+ // Находим индекс активного рабочего стола
107
+ const activeWorkspaceName = desktop.activeWorkspaceName
108
+ if (activeWorkspaceName) {
109
+ const activeIndex = menuWorkspaces.value.findIndex(
110
+ item => item.workspaceName === activeWorkspaceName
111
+ )
112
+ if (activeIndex !== -1) {
113
+ slideIndex.value = activeIndex
132
114
  }
133
- })
134
- </script>
135
-
136
- <style lang="scss">
137
- .btn-menu {
138
- height: 100px;
139
- width: 100px;
140
- border-radius: 0 !important;
141
- }
142
- .btn-icon {
143
- font-size: 20px !important;
144
115
  }
145
- .btn-font {
146
- font-size: 8px !important;
147
- }
148
- .workspace-menu .q-carousel__slide {
149
- padding: 0px !important;
116
+ })
117
+
118
+ // При изменении slideIndex обновляем активный рабочий стол
119
+ watch(slideIndex, (newIndex) => {
120
+ const item = menuWorkspaces.value[newIndex]
121
+ if (item) {
122
+ desktop.selectWorkspace(item.workspaceName)
123
+ desktop.goToDefaultPage(router)
150
124
  }
151
- </style>
125
+ })
126
+ </script>
127
+
128
+ <style lang="scss">
129
+ .btn-menu {
130
+ height: 100px;
131
+ width: 100px;
132
+ border-radius: 0 !important;
133
+ }
134
+ .btn-icon {
135
+ font-size: 20px !important;
136
+ }
137
+ .btn-font {
138
+ font-size: 8px !important;
139
+ }
140
+ .workspace-menu .q-carousel__slide {
141
+ padding: 0px !important;
142
+ }
143
+ </style>
@@ -11,7 +11,7 @@ q-header(bordered).bg-gradient-dark
11
11
  active-class="bg-teal"
12
12
  active-bg-color="teal"
13
13
  active-color="white"
14
- indicator-color="secondary"
14
+ indicator-color="primary"
15
15
  class="header-tab-container"
16
16
  )
17
17
  q-route-tab(name="Совет" label="Совет" :to="{name: 'members'}" )
@@ -11,7 +11,7 @@ q-header(bordered).bg-gradient-dark
11
11
  active-class="bg-teal"
12
12
  active-bg-color="teal"
13
13
  active-color="white"
14
- indicator-color="secondary"
14
+ indicator-color="primary"
15
15
  class="header-tab-container"
16
16
  )
17
17
  q-route-tab(name="Все приложения" label="Все приложения" :to="{name: 'extstore-showcase'}" )
@@ -6,6 +6,9 @@ q-header(bordered :class="headerClass").header
6
6
  BackButton(v-if="loggedIn")
7
7
  q-toolbar-title()
8
8
 
9
+ // Добавляем компонент уведомлений, если пользователь авторизован
10
+ NotificationCenter(v-if="loggedIn && isClient")
11
+
9
12
  SettingsDropdown(:isMobile="isMobile" :isChairman="isChairman" :isMember="isMember")
10
13
 
11
14
  template(v-if="!loggedIn")
@@ -40,6 +43,7 @@ import config from 'src/app/config'
40
43
  import { useWindowSize } from 'src/shared/hooks'
41
44
  import { SettingsDropdown } from 'src/widgets/Header/SettingsDropdown'
42
45
  import { BackButton } from 'src/widgets/Header/BackButton'
46
+ import { NotificationCenter } from 'src/widgets/NotificationCenter'
43
47
  import './HeaderStyles.scss'
44
48
  // import { env } from 'src/shared/config'
45
49
 
@@ -50,6 +54,8 @@ const session = useSessionStore()
50
54
  const { isMobile } = useWindowSize()
51
55
  const emit = defineEmits(['toggle-left-drawer'])
52
56
 
57
+ const isClient = computed(() => typeof process !== 'undefined' && process.env.CLIENT)
58
+
53
59
  // Получаем информацию для навигации назад
54
60
  // const coopTitle = computed(() => env.COOP_SHORT_NAME)
55
61
 
@@ -11,7 +11,7 @@ q-header(bordered).flex.bg-gradient-dark
11
11
  active-class="bg-teal"
12
12
  active-bg-color="teal"
13
13
  active-color="white"
14
- indicator-color="secondary"
14
+ indicator-color="primary"
15
15
  class="header-tab-container"
16
16
  )
17
17
  q-route-tab(name="Реквизиты" label="Реквизиты" :to="{name: 'user-payment-methods'}" )
@@ -0,0 +1 @@
1
+ export { default as MeetCardsList } from './ui/MeetCardsList.vue'