@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,35 @@
1
+ <template lang="pug">
2
+ div(flat bordered).info-card.hover
3
+ q-card(flat bordered).q-pa-md
4
+ div.text-h6.q-mb-xs.full-width.text-center Явка
5
+
6
+ div.row
7
+ div.col-12
8
+ q-linear-progress(
9
+ :value="(meet.processing?.meet?.current_quorum_percent ?? 0) / 100"
10
+ :buffer="(meet.processing?.meet?.quorum_percent ?? 0) / 100"
11
+ track-color="lime"
12
+ size="50px"
13
+ rounded
14
+ ).bg-grey
15
+ div.absolute-full.flex.flex-center
16
+ div.text-center
17
+ div.text-white.text-weight-medium(style="font-size: 22px; line-height: 1.2;")
18
+ | {{ (Math.round((meet.processing?.meet?.current_quorum_percent ?? 0) * 10) / 10).toFixed(1) }}%
19
+ div.q-mt-xs.full-width.text-center.text-caption.text-grey-7
20
+ | Собрание состоится при явке не менее {{ meet.processing?.meet?.quorum_percent ?? 0 }}% участников
21
+
22
+
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import type { IMeet } from 'src/entities/Meet'
27
+
28
+ defineProps<{
29
+ meet: IMeet
30
+ }>()
31
+ </script>
32
+
33
+ <style lang="scss" scoped>
34
+ @import 'src/shared/ui/CardStyles/index.scss';
35
+ </style>
@@ -1,57 +1,44 @@
1
1
  <template lang="pug">
2
+ div.scroll-area(style="height: calc(100% - $toolbar-min-height); overflow-y: auto;")
3
+ q-table(
4
+ ref="tableRef"
5
+ flat
6
+ :grid="isMobile"
7
+ :rows="meets"
8
+ :columns="columns"
9
+ :pagination="pagination"
10
+ virtual-scroll
11
+ @virtual-scroll="onScroll"
12
+ :virtual-scroll-target="'.scroll-area'"
13
+ :virtual-scroll-item-size="48"
14
+ :virtual-scroll-sticky-size-start="48"
15
+ :rows-per-page-options="[0]"
16
+ :loading='loading'
17
+ :no-data-label="'Собрания не найдены'"
18
+ )
19
+ template(#header="props")
20
+ q-tr(:props="props")
21
+ q-th(
22
+ v-for="col in props.cols"
23
+ :key="col.name"
24
+ :props="props"
25
+ ) {{ col.label }}
2
26
 
3
- q-table(
4
- ref="tableRef"
5
- flat
6
- :grid="isMobile"
7
- :rows="meets"
8
- :columns="columns"
9
- :pagination="pagination"
10
- virtual-scroll
11
- :virtual-scroll-item-size="48"
12
- :rows-per-page-options="[10]"
13
- :loading='loading'
14
- :no-data-label="'Собрания не найдены'"
15
- ).full-height
16
- template(#header="props")
17
- q-tr(:props="props")
18
- q-th(
19
- v-for="col in props.cols"
20
- :key="col.name"
21
- :props="props"
22
- ) {{ col.label }}
23
-
24
- template(#body="props")
25
- q-tr(:key="`m_${props.row?.hash}`" :props="props")
26
- q-td {{ props.row.hash.substring(0, 10) }}
27
- q-td {{ props.row.processing?.meet?.type }}
28
- q-td {{ props.row.processing?.meet?.status }}
29
- q-td {{ formatDate(props.row.processing?.meet?.open_at) }}
30
- q-td {{ formatDate(props.row.processing?.meet?.close_at) }}
31
- q-td
32
- q-btn(
33
- v-if="canVote(props.row)"
34
- size="sm"
35
- color="primary"
36
- icon="fa-solid fa-check-to-slot"
37
- flat
38
- @click="() => $emit('vote', props.row)"
39
- ) Голосовать
40
- q-btn(
41
- v-if="canVote(props.row)"
42
- size="sm"
43
- color="secondary"
44
- icon="fa-solid fa-eye"
45
- flat
46
- @click="() => $emit('view', props.row)"
47
- ) Участвовать
48
- q-btn(
49
- size="sm"
50
- color="accent"
51
- icon="fa-solid fa-arrow-right"
52
- flat
53
- @click="navigateToMeetDetails(props.row)"
54
- ) Подробнее
27
+ template(#body="props")
28
+ q-tr(:key="`m_${props.row?.hash}`" :props="props")
29
+ q-td {{ props.row.hash.substring(0, 10) }}
30
+ q-td {{ props.row.processing?.meet?.type }}
31
+ q-td {{ props.row.processing?.meet?.status }}
32
+ q-td {{ formatDate(props.row.processing?.meet?.open_at) }}
33
+ q-td {{ formatDate(props.row.processing?.meet?.close_at) }}
34
+ q-td
35
+ q-btn(
36
+ size="sm"
37
+ color="accent"
38
+ icon="fa-solid fa-arrow-right"
39
+ flat
40
+ @click="navigateToMeetDetails(props.row)"
41
+ ) Подробнее
55
42
  </template>
56
43
 
57
44
  <script setup lang="ts">
@@ -63,14 +50,13 @@ import { useRouter } from 'vue-router'
63
50
  import { useWindowSize } from 'src/shared/hooks'
64
51
  import { useDesktopStore } from 'src/entities/Desktop/model'
65
52
 
66
- defineProps<{
53
+ const props = defineProps<{
67
54
  meets: IMeet[],
68
55
  loading: boolean
69
56
  }>()
70
57
 
71
- defineEmits<{
72
- (e: 'vote', meet: IMeet): void
73
- (e: 'view', meet: IMeet): void
58
+ const emit = defineEmits<{
59
+ (e: 'load'): void
74
60
  }>()
75
61
 
76
62
  const router = useRouter()
@@ -88,7 +74,7 @@ const columns: QTableColumn<IMeet>[] = [
88
74
  ]
89
75
 
90
76
  const tableRef = ref(null)
91
- const pagination = ref({ rowsPerPage: 10 })
77
+ const pagination = ref({ rowsPerPage: 0 })
92
78
 
93
79
  // Форматирование даты
94
80
  const formatDate = (dateString: string) => {
@@ -96,23 +82,28 @@ const formatDate = (dateString: string) => {
96
82
  return date.formatDate(dateString, 'DD.MM.YYYY HH:mm')
97
83
  }
98
84
 
99
- const canVote = (meet: IMeet) => {
100
- return meet.processing?.meet?.status === 'open'
85
+ // Функция обработки виртуального скролла
86
+ const onScroll = ({ to }) => {
87
+ const lastIndex = props.meets.length - 1
88
+ // Если достигли последнего элемента в списке, эмитим событие load
89
+ if (props.meets.length > 0 && to === lastIndex) {
90
+ emit('load')
91
+ }
101
92
  }
102
93
 
103
94
  // Определение текущего воркспейса и подходящего маршрута для деталей собрания
104
95
  const navigateToMeetDetails = (meet: IMeet) => {
105
96
  const currentWorkspace = desktop.activeWorkspaceName
106
97
  const isSoviet = currentWorkspace === 'soviet'
107
-
98
+
108
99
  const routeName = isSoviet ? 'meet-details' : 'user-meet-details'
109
-
110
- router.push({
111
- name: routeName,
112
- params: {
100
+
101
+ router.push({
102
+ name: routeName,
103
+ params: {
113
104
  hash: meet.hash,
114
105
  coopname: router.currentRoute.value.params.coopname
115
- }
106
+ }
116
107
  })
117
108
  }
118
109
  </script>
@@ -0,0 +1,90 @@
1
+ <template lang="pug">div</template>
2
+ <!-- NotificationCenter.vue
3
+ <template lang="pug">
4
+ div#notification-inbox.notification-container
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, onMounted, watch } from 'vue'
9
+ import { NovuUI } from '@novu/js/ui'
10
+ import { dark } from '@novu/js/themes'
11
+ import { useCurrentUserStore } from 'src/entities/User'
12
+ import { useQuasar } from 'quasar'
13
+ import { env } from 'src/shared/config'
14
+ const currentUser = useCurrentUserStore()
15
+
16
+ const $q = useQuasar()
17
+ const isDark = computed(() => $q.dark.isActive)
18
+ let novu: any = null
19
+
20
+ function mountNovu() {
21
+ const subscriberId = currentUser.userAccount?.username || '68481006a874d6592b28c530'
22
+ const el = document.getElementById('notification-inbox')
23
+
24
+ if (el) el.innerHTML = ''
25
+ novu = new NovuUI({
26
+ options: {
27
+ applicationIdentifier: env.NOVU_APP_ID,
28
+ subscriberId,
29
+ backendUrl: env.NOVU_BACKEND_URL,
30
+ socketUrl: env.NOVU_SOCKET_URL,
31
+ },
32
+ appearance: {
33
+ baseTheme: isDark.value ? dark : undefined,
34
+ },
35
+ localization: {
36
+ 'inbox.filters.dropdownOptions.unread': 'Только непрочитанные',
37
+ 'inbox.filters.dropdownOptions.default': 'Все сообщения',
38
+ 'inbox.filters.dropdownOptions.archived': 'Архив',
39
+ 'inbox.filters.labels.unread': 'Непрочитанные',
40
+ 'inbox.filters.labels.default': 'Входящие',
41
+ 'inbox.filters.labels.archived': 'Архив',
42
+ 'notifications.emptyNotice': 'Здесь пока тихо. Загляните позже.',
43
+ 'notifications.actions.readAll': 'Прочитать все',
44
+ 'notifications.actions.archiveAll': 'Архивировать все',
45
+ 'notifications.actions.archiveRead': 'Архивировать прочитанные',
46
+ 'notifications.newNotifications': '{{notificationCount}} новое уведомление(ий)',
47
+ 'notification.actions.read.tooltip': 'Отметить как прочитанное',
48
+ 'notification.actions.unread.tooltip': 'Отметить как непрочитанное',
49
+ 'notification.actions.archive.tooltip': 'В архив',
50
+ 'notification.actions.unarchive.tooltip': 'Из архива',
51
+ 'preferences.title': 'Настройки',
52
+ 'preferences.emptyNotice': 'Пока нет настроек уведомлений.',
53
+ 'preferences.global': 'Общие настройки',
54
+ 'preferences.workflow.disabled.notice': 'Обратитесь к администратору для управления подпиской на это важное уведомление.',
55
+ 'preferences.workflow.disabled.tooltip': 'Для изменения обратитесь к администратору',
56
+ 'locale': 'ru-RU',
57
+ 'dynamic': {
58
+ 'comment-on-post': 'Комментарии к записи'
59
+ }
60
+ }
61
+ })
62
+ novu.mountComponent({
63
+ name: 'Inbox',
64
+ props: {},
65
+ element: el as HTMLDivElement,
66
+ })
67
+ }
68
+
69
+ onMounted(() => {
70
+ mountNovu()
71
+ })
72
+
73
+ watch(isDark, () => {
74
+ mountNovu()
75
+ })
76
+ </script>
77
+
78
+ <style scoped>
79
+ .notification-container {
80
+ display: flex;
81
+ align-items: center;
82
+ height: 100%;
83
+ padding: 0 10px;
84
+ }
85
+
86
+ :deep(.notification-bell-component) {
87
+ height: 24px;
88
+ width: 24px;
89
+ }
90
+ </style> -->
@@ -0,0 +1 @@
1
+ export { default as NotificationCenter } from './NotificationCenter.vue'
@@ -28,7 +28,7 @@ q-table(
28
28
  color="primary"
29
29
  round
30
30
  dense
31
- :icon="expanded.get(props.row.username) ? 'remove' : 'add'"
31
+ :icon="expanded.get(props.row.username) ? 'expand_more' : 'chevron_right'"
32
32
  @click="onToggleExpand(props.row.username)"
33
33
  )
34
34
 
@@ -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
@@ -46,10 +46,17 @@ div.scroll-area(style="height: 90vh; overflow-y: auto;")
46
46
  template(#body="props")
47
47
  q-tr(:key="`m_${props.row.table.id}`" :props="props")
48
48
  q-td(auto-width)
49
- q-btn(size="sm" color="primary" dense :icon="expanded.get(props.row.table.id) ? 'remove' : 'add'" round @click="toggleExpand(props.row.table.id)")
49
+ q-btn(
50
+ size="sm"
51
+ color="primary"
52
+ dense
53
+ :icon="expanded.get(props.row.table.id) ? 'expand_more' : 'chevron_right'"
54
+ round
55
+ @click="toggleExpand(props.row.table.id)"
56
+ )
50
57
 
51
58
  q-td {{ props.row.table.id }}
52
- q-td {{ props.row.table.username }}
59
+ q-td {{ getShortNameFromCertificate(props.row.table.username_certificate) || props.row.table.username }}
53
60
  q-td(style="max-width: 200px; word-wrap: break-word; white-space: normal;") {{ getDecisionTitle(props.row) }}
54
61
 
55
62
  q-td {{formatToFromNow(props.row.table.expired_at)}}
@@ -66,12 +73,13 @@ div.scroll-area(style="height: 90vh; overflow-y: auto;")
66
73
  q-btn(
67
74
  size="sm"
68
75
  color="teal"
76
+ push
69
77
  v-if="isChairman"
70
78
  :loading="isProcessing(props.row.table.id)"
71
79
  @click="onAuthorizeDecision(props.row)"
72
80
  ) утвердить
73
81
 
74
- q-tr(v-if="expanded.get(props.row.table.id)" :key="`e_${props.row.table.id}`" :props="props" class="q-virtual-scroll--with-prev")
82
+ q-tr(no-hover v-if="expanded.get(props.row.table.id)" :key="`e_${props.row.table.id}`" :props="props" class="q-virtual-scroll--with-prev")
75
83
  q-td(colspan="100%")
76
84
  ComplexDocument(:documents="props.row.documents")
77
85
  </template>
@@ -84,6 +92,8 @@ import { formatToFromNow } from 'src/shared/lib/utils/dates/formatToFromNow'
84
92
  import { QuestionCard } from '../QuestionCard'
85
93
  import { VotingButtons } from '../VotingButtons'
86
94
  import { useWindowSize } from 'src/shared/hooks'
95
+ import type { IAgenda } from 'src/entities/Agenda/model'
96
+ import { getShortNameFromCertificate } from 'src/shared/lib/utils/getNameFromCertificate'
87
97
 
88
98
  const props = defineProps({
89
99
  decisions: {
@@ -125,12 +135,14 @@ const emit = defineEmits(['authorize', 'vote-for', 'vote-against'])
125
135
  const { isMobile } = useWindowSize()
126
136
 
127
137
  // Получение заголовка для решения с поддержкой агрегатов документов
128
- function getDecisionTitle(row: any) {
138
+ function getDecisionTitle(row: IAgenda) {
139
+ console.log('agenda', row)
129
140
  // Используем только агрегаты документа
130
- if (row.documents?.statement?.documentAggregate?.rawDocument?.meta?.title) {
131
- const title = row.documents.statement.documentAggregate.rawDocument.meta.title
132
- const user = row.documents.statement.action.user
133
- return props.formatDecisionTitle(title, user)
141
+ const title = (row.documents?.statement?.documentAggregate?.rawDocument?.meta as any)?.title
142
+
143
+ if (title) {
144
+ const actor_certificate = row.documents?.statement?.action?.actor_certificate
145
+ return props.formatDecisionTitle(title, actor_certificate)
134
146
  }
135
147
 
136
148
  // Запасной вариант
@@ -140,7 +152,7 @@ function getDecisionTitle(row: any) {
140
152
  // Настройка таблицы
141
153
  const columns = [
142
154
  { name: 'id', align: 'left', label: '№', field: row => row.table.id, sortable: true },
143
- { name: 'username', align: 'left', label: 'Аккаунт', field: row => row.table.username, sortable: true },
155
+ { name: 'username', align: 'left', label: 'Заявитель', field: row => row.table.username, sortable: true },
144
156
  { name: 'caption', align: 'left', label: 'Пункт', field: row => getDecisionTitle(row), sortable: true },
145
157
  { name: 'expired_at', align: 'left', label: 'Истекает', field: row => row.table.expired_at, format: val => formatToFromNow(val), sortable: false },
146
158
  { name: 'approved', align: 'left', label: 'Голосование', field: row => row.table.approved, sortable: true },
@@ -1,28 +1,37 @@
1
1
  <template lang="pug">
2
2
  div.buttons-container
3
3
  div.votes-group
4
- q-btn(v-if="isVotedFor(decision) || !isVotedAny(decision)" :disabled="isVotedAny(decision)" dense flat @click="$emit('vote-against')").text-red
5
- q-icon(name="fa-regular fa-thumbs-down")
6
- span.vote-count {{decision.votes_against.length}}
4
+ div.button-wrapper
5
+ q-btn(v-if="isVotedFor(decision) || !isVotedAny(decision)" :disabled="isVotedAny(decision)" dense push @click="$emit('vote-against')").text-red
6
+ q-icon(name="fa-regular fa-thumbs-down")
7
+ span.vote-count {{decision.votes_against.length}}
7
8
 
8
- q-btn(v-if="isVotedAgainst(decision)" disabled dense flat).text-red
9
- q-icon(name="fas fa-thumbs-down")
10
- span.vote-count {{decision.votes_against.length}}
9
+ q-btn(v-if="isVotedAgainst(decision)" disabled dense push).text-red
10
+ q-icon(name="fas fa-thumbs-down")
11
+ span.vote-count {{decision.votes_against.length}}
11
12
 
12
- q-checkbox(v-model="approved" disable).q-mx-xs
13
+ div.voters-list
14
+ div.voter-name(v-for="certificate in decision.votes_against_certificates" :key="getVoterKey(certificate)") {{ getShortNameFromCertificate(certificate) }}
15
+
16
+ q-checkbox(v-model="approved" disable size="lg" style="margin-top: -9px;").q-mx-xs
13
17
 
14
18
  div.votes-group
15
- q-btn(v-if="isVotedAgainst(decision) || !isVotedAny(decision)" :disabled="isVotedAny(decision)" dense flat @click="$emit('vote-for')").text-green
16
- span.vote-count {{decision.votes_for.length}}
17
- q-icon(name="fa-regular fa-thumbs-up" style="transform: scaleX(-1)")
19
+ div.button-wrapper
20
+ q-btn(v-if="isVotedAgainst(decision) || !isVotedAny(decision)" :disabled="isVotedAny(decision)" dense push @click="$emit('vote-for')").text-green
21
+ span.vote-count {{decision.votes_for.length}}
22
+ q-icon(name="fa-regular fa-thumbs-up" style="transform: scaleX(-1)")
23
+
24
+ q-btn(v-if="isVotedFor(decision)" disabled dense push).text-green
25
+ span.vote-count {{decision.votes_for.length}}
26
+ q-icon(name="fas fa-thumbs-up" style="transform: scaleX(-1)")
18
27
 
19
- q-btn(v-if="isVotedFor(decision)" disabled dense flat).text-green
20
- span.vote-count {{decision.votes_for.length}}
21
- q-icon(name="fas fa-thumbs-up" style="transform: scaleX(-1)")
28
+ div.voters-list
29
+ div.voter-name(v-for="certificate in decision.votes_for_certificates" :key="getVoterKey(certificate)") {{ getShortNameFromCertificate(certificate) }}
22
30
  </template>
23
31
 
24
32
  <script setup lang="ts">
25
33
  import { computed } from 'vue'
34
+ import { getShortNameFromCertificate } from 'src/shared/lib/utils/getNameFromCertificate'
26
35
 
27
36
  const props = defineProps({
28
37
  decision: {
@@ -46,12 +55,21 @@ const props = defineProps({
46
55
  defineEmits(['vote-for', 'vote-against'])
47
56
 
48
57
  const approved = computed(() => props.decision.approved)
58
+
59
+ const getVoterKey = (certificate: any) => {
60
+ if (certificate.username) return certificate.username
61
+ if (certificate.first_name && certificate.last_name) {
62
+ return `${certificate.last_name}_${certificate.first_name}`
63
+ }
64
+ if (certificate.short_name) return certificate.short_name
65
+ return Math.random().toString()
66
+ }
49
67
  </script>
50
68
 
51
69
  <style scoped>
52
70
  .buttons-container {
53
71
  display: flex;
54
- align-items: center;
72
+ align-items: flex-start;
55
73
  justify-content: center;
56
74
  min-width: 150px;
57
75
  }
@@ -59,6 +77,14 @@ const approved = computed(() => props.decision.approved)
59
77
  .votes-group {
60
78
  width: 60px;
61
79
  display: flex;
80
+ flex-direction: column;
81
+ align-items: center;
82
+ }
83
+
84
+ .button-wrapper {
85
+ height: 32px;
86
+ display: flex;
87
+ align-items: center;
62
88
  justify-content: center;
63
89
  }
64
90
 
@@ -72,4 +98,23 @@ const approved = computed(() => props.decision.approved)
72
98
  min-width: 60px;
73
99
  height: 32px;
74
100
  }
101
+
102
+ .voters-list {
103
+ margin-top: 4px;
104
+ max-width: 100px;
105
+ min-height: 20px;
106
+ display: flex;
107
+ flex-direction: column;
108
+ align-items: center;
109
+ }
110
+
111
+ .voter-name {
112
+ font-size: 10px;
113
+ line-height: 1.2;
114
+ text-align: center;
115
+ margin-bottom: 2px;
116
+ color: #666;
117
+ word-wrap: break-word;
118
+ hyphens: auto;
119
+ }
75
120
  </style>
@@ -7,7 +7,7 @@ q-card(flat bordered).q-pa-md.q-mt-lg
7
7
  div(class="text-h5").q-mt-md Вы успешно зарегистрированы и авторизованы
8
8
 
9
9
  q-card-actions(align="center")
10
- q-btn(label="На главную" color="secondary" icon="fa fa-arrow-left" @click="goBack")
10
+ q-btn(label="На главную" color="primary" icon="fa fa-arrow-left" @click="goBack")
11
11
  </template>
12
12
  <script lang="ts" setup>
13
13
  import { useRouter } from 'vue-router'
@@ -85,7 +85,6 @@ import { computed } from 'vue';
85
85
  import { AddPaymentMethodButton } from 'src/features/PaymentMethod/AddPaymentMethod';
86
86
  import type { IBankTransferData, ISBPData } from 'src/features/PaymentMethod/AddPaymentMethod/model';
87
87
  import { DeletePaymentButton } from 'src/features/PaymentMethod/DeletePaymentMethod/ui';
88
- import 'src/shared/ui/CardStyles/index.scss';
89
88
 
90
89
  const props = defineProps({
91
90
  username: {
@@ -10,18 +10,24 @@ export default ssrMiddleware(({ app }) => {
10
10
  app.use((req, res, next) => {
11
11
  // Получаем переменные из process.env, которые нужны клиенту
12
12
  const envForClient: EnvVars = {
13
- NODE_ENV: process.env.NODE_ENV,
14
- BACKEND_URL: process.env.BACKEND_URL,
15
- CHAIN_URL: process.env.CHAIN_URL,
16
- CHAIN_ID: process.env.CHAIN_ID,
17
- CURRENCY: process.env.CURRENCY,
18
- COOP_SHORT_NAME: process.env.COOP_SHORT_NAME,
19
- SITE_DESCRIPTION: process.env.SITE_DESCRIPTION,
20
- SITE_IMAGE: process.env.SITE_IMAGE,
21
- STORAGE_URL: process.env.STORAGE_URL,
22
- UPLOAD_URL: process.env.UPLOAD_URL,
23
- VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE,
24
- VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE
13
+ NODE_ENV: process.env.NODE_ENV as string,
14
+ BACKEND_URL: process.env.BACKEND_URL as string,
15
+ CHAIN_URL: process.env.CHAIN_URL as string,
16
+ CHAIN_ID: process.env.CHAIN_ID as string,
17
+ CURRENCY: process.env.CURRENCY as string,
18
+ COOP_SHORT_NAME: process.env.COOP_SHORT_NAME as string,
19
+ SITE_DESCRIPTION: process.env.SITE_DESCRIPTION as string,
20
+ SITE_IMAGE: process.env.SITE_IMAGE as string,
21
+ STORAGE_URL: process.env.STORAGE_URL as string,
22
+ UPLOAD_URL: process.env.UPLOAD_URL as string,
23
+ TIMEZONE: process.env.TIMEZONE || 'Europe/Moscow',
24
+ CLIENT: process.env.CLIENT as unknown as boolean,
25
+ SERVER: process.env.SERVER as unknown as boolean,
26
+ VUE_ROUTER_MODE: process.env.VUE_ROUTER_MODE as string,
27
+ VUE_ROUTER_BASE: process.env.VUE_ROUTER_BASE as string,
28
+ NOVU_APP_ID: process.env.NOVU_APP_ID as string,
29
+ NOVU_BACKEND_URL: process.env.NOVU_BACKEND_URL as string,
30
+ NOVU_SOCKET_URL: process.env.NOVU_SOCKET_URL as string,
25
31
  };
26
32
 
27
33
  // Создаем скрипт, который добавит переменные в window.__ENV__
@@ -1 +0,0 @@
1
- export * from './model'
@@ -1,18 +0,0 @@
1
- import { client } from 'src/shared/api/client';
2
- import { Mutations } from '@coopenomics/sdk';
3
-
4
- export type IGenerateAgendaInput = Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.IInput['data'];
5
- export type IGenerateAgendaResult = Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.IOutput[typeof Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.name];
6
-
7
- export async function generateAgenda(data: IGenerateAgendaInput): Promise<IGenerateAgendaResult> {
8
- const { [Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.name]: generatedDocument } = await client.Mutation(
9
- Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.mutation,
10
- {
11
- variables: {
12
- data
13
- }
14
- }
15
- );
16
-
17
- return generatedDocument;
18
- }
@@ -1 +0,0 @@
1
- export * from './model'
@@ -1,18 +0,0 @@
1
- import { client } from 'src/shared/api/client';
2
- import { Mutations } from '@coopenomics/sdk';
3
-
4
- export type IGenerateBallotInput = Mutations.Meet.GenerateBallotForAnnualGeneralMeetDocument.IInput['data'];
5
- export type IGenerateBallotResult = Mutations.Meet.GenerateBallotForAnnualGeneralMeetDocument.IOutput[typeof Mutations.Meet.GenerateBallotForAnnualGeneralMeetDocument.name];
6
-
7
- export async function generateBallot(data: IGenerateBallotInput): Promise<IGenerateBallotResult> {
8
- const { [Mutations.Meet.GenerateBallotForAnnualGeneralMeetDocument.name]: result } = await client.Mutation(
9
- Mutations.Meet.GenerateBallotForAnnualGeneralMeetDocument.mutation,
10
- {
11
- variables: {
12
- data
13
- }
14
- }
15
- );
16
-
17
- return result;
18
- }
@@ -1 +0,0 @@
1
- export * from './model'
@@ -1,18 +0,0 @@
1
- import { client } from 'src/shared/api/client'
2
- import { Mutations } from '@coopenomics/sdk'
3
-
4
- export type IGenerateNotificationInput = Mutations.Meet.GenerateAnnualGeneralMeetNotificationDocument.IInput['data']
5
- export type IGenerateNotificationResult = Mutations.Meet.GenerateAnnualGeneralMeetNotificationDocument.IOutput[typeof Mutations.Meet.GenerateAnnualGeneralMeetNotificationDocument.name]
6
-
7
- export async function generateNotification(data: IGenerateNotificationInput): Promise<IGenerateNotificationResult> {
8
- const { [Mutations.Meet.GenerateAnnualGeneralMeetNotificationDocument.name]: result } = await client.Mutation(
9
- Mutations.Meet.GenerateAnnualGeneralMeetNotificationDocument.mutation,
10
- {
11
- variables: {
12
- data
13
- }
14
- }
15
- )
16
-
17
- return result
18
- }
@@ -1 +0,0 @@
1
- export * from './model'