@coopenomics/desktop 2025.5.14 → 2025.6.14

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 (113) 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/src/app/App.vue +1 -0
  6. package/src/app/providers/routes/index.ts +12 -0
  7. package/src/app/styles/app.scss +1 -1
  8. package/src/app/styles/style.css +11 -0
  9. package/src/css/quasar.variables.scss +1 -1
  10. package/src/entities/Desktop/model/store.ts +68 -0
  11. package/src/entities/Desktop/model/types.ts +7 -0
  12. package/src/entities/Document/model/types.ts +1 -1
  13. package/src/entities/Meet/api/index.ts +2 -28
  14. package/src/entities/Meet/model/store.ts +7 -22
  15. package/src/entities/Wallet/api/index.ts +3 -3
  16. package/src/env.d.ts +1 -0
  17. package/src/features/Meet/CloseMeetWithDecision/model/index.ts +119 -17
  18. package/src/features/Meet/CreateMeet/model/index.ts +51 -9
  19. package/src/features/Meet/CreateMeet/ui/CreateMeet.vue +37 -6
  20. package/src/features/Meet/CreateMeet/ui/CreateMeetForm.vue +87 -65
  21. package/src/features/Meet/GenerateSovietDecision/model/index.ts +14 -4
  22. package/src/features/Meet/RestartMeet/model/index.ts +121 -3
  23. package/src/features/Meet/RestartMeet/ui/RestartMeet.vue +4 -6
  24. package/src/features/Meet/RestartMeet/ui/RestartMeetForm.vue +64 -28
  25. package/src/features/Meet/SignNotification/index.ts +2 -0
  26. package/src/features/Meet/SignNotification/model/index.ts +137 -0
  27. package/src/features/Meet/SignNotification/ui/SignNotificationButton.vue +61 -0
  28. package/src/features/Meet/SignNotification/ui/index.ts +1 -0
  29. package/src/features/Meet/VoteOnMeet/model/composable.ts +180 -0
  30. package/src/features/Meet/VoteOnMeet/model/index.ts +2 -17
  31. package/src/features/Meet/VoteOnMeet/model/types.ts +4 -0
  32. package/src/features/Meet/index.ts +6 -0
  33. package/src/features/User/LoginRedirect/ui/LoginRedirectPage.vue +15 -0
  34. package/src/features/User/LoginRedirect/ui/index.ts +1 -0
  35. package/src/features/User/LoginUser/ui/LoginForm/LoginForm.vue +38 -1
  36. package/src/features/User/LoginWithRedirect/ui/LoginRedirectForm/LoginRedirectForm.vue +0 -0
  37. package/src/pages/Cooperative/ListOfMeets/ui/ListOfMeetsPage.vue +22 -46
  38. package/src/pages/Cooperative/MeetDetails/ui/MeetDetailsPage.vue +84 -28
  39. package/src/pages/PermissionDenied/PermissionDenied.vue +1 -1
  40. package/src/processes/init-app/index.ts +12 -5
  41. package/src/processes/navigation-guard-setup/index.ts +37 -5
  42. package/src/processes/process-decisions/index.ts +7 -6
  43. package/src/shared/config/Environment.ts +10 -2
  44. package/src/shared/lib/composables/index.ts +1 -0
  45. package/src/shared/lib/composables/useMeetStatus.ts +96 -0
  46. package/src/shared/lib/consts/index.ts +1 -0
  47. package/src/shared/lib/consts/meet-statuses.ts +114 -0
  48. package/src/shared/lib/document/model/entity.ts +4 -2
  49. package/src/shared/lib/types/certificate/index.ts +6 -0
  50. package/src/shared/lib/types/document/index.ts +1 -1
  51. package/src/shared/lib/types/workspace.ts +24 -0
  52. package/src/shared/lib/utils/dates/index.ts +5 -0
  53. package/src/shared/lib/utils/dates/moment.ts +43 -2
  54. package/src/shared/lib/utils/dates/timezone.ts +75 -0
  55. package/src/shared/lib/utils/getNameFromCertificate.ts +108 -0
  56. package/src/shared/lib/utils/index.ts +1 -0
  57. package/src/shared/lib/utils/parseLinks.ts +10 -0
  58. package/src/shared/ui/AgendaNumberAvatar/AgendaNumberAvatar.vue +12 -0
  59. package/src/shared/ui/AgendaNumberAvatar/index.ts +1 -0
  60. package/src/shared/ui/BaseDocument/BaseDocument.vue +37 -8
  61. package/src/shared/ui/ExpandableDocument/ExpandableDocument.vue +49 -0
  62. package/src/shared/ui/ExpandableDocument/index.ts +1 -0
  63. package/src/shared/ui/MeetInfoCard/index.ts +1 -0
  64. package/src/shared/ui/MeetInfoCard/ui/MeetInfoCard.vue +62 -0
  65. package/src/shared/ui/MeetStatusBanner/index.ts +1 -0
  66. package/src/shared/ui/MeetStatusBanner/ui/MeetStatusBanner.vue +94 -0
  67. package/src/shared/ui/index.ts +1 -0
  68. package/src/widgets/Cooperative/Documents/ListOfDocuments/ui/DocumentsTable.vue +3 -3
  69. package/src/widgets/Cooperative/Orders/ListOfOrders/ui/ListOfOrdersWidget.vue +2 -2
  70. package/src/widgets/Cooperative/Payments/ListOfPayments/ui/ListOfPaymentsWidget.vue +2 -2
  71. package/src/widgets/Desktop/WorkspaceMenu/WorkspaceMenu.vue +74 -82
  72. package/src/widgets/Header/CommonHeader/CooperativeSettingsHeader.vue +1 -1
  73. package/src/widgets/Header/CommonHeader/ExtstoreHeader.vue +1 -1
  74. package/src/widgets/Header/CommonHeader/MainHeader.vue +6 -0
  75. package/src/widgets/Header/CommonHeader/UserSettingsHeader.vue +1 -1
  76. package/src/widgets/Meets/MeetCardsList/index.ts +1 -0
  77. package/src/widgets/Meets/MeetCardsList/ui/MeetCardsList.vue +55 -0
  78. package/src/widgets/Meets/MeetDetailsActions/MeetDetailsActions.vue +33 -15
  79. package/src/widgets/Meets/MeetDetailsAgenda/MeetDetailsAgenda.vue +34 -15
  80. package/src/widgets/Meets/MeetDetailsInfo/MeetDetailsInfo.vue +27 -0
  81. package/src/widgets/Meets/MeetDetailsInfo/index.ts +1 -0
  82. package/src/widgets/Meets/MeetDetailsResults/MeetDetailsResults.vue +129 -0
  83. package/src/widgets/Meets/MeetDetailsResults/index.ts +1 -0
  84. package/src/widgets/Meets/MeetDetailsVoting/MeetDetailsVoting.vue +221 -62
  85. package/src/widgets/Meets/MeetQuorumIndicator/MeetQuorumIndicator.vue +0 -0
  86. package/src/widgets/Meets/MeetQuorumIndicator/index.ts +1 -0
  87. package/src/widgets/Meets/MeetQuorumIndicator/ui/MeetQuorumIndicator.vue +35 -0
  88. package/src/widgets/Meets/MeetsTable/ui/MeetsTable.vue +56 -65
  89. package/src/widgets/NotificationCenter/NotificationCenter.vue +90 -0
  90. package/src/widgets/NotificationCenter/index.ts +1 -0
  91. package/src/widgets/Participants/ui/ParticipantsTable.vue +1 -1
  92. package/src/widgets/Questions/ui/QuestionsTable/QuestionsTable.vue +22 -10
  93. package/src/widgets/Questions/ui/VotingButtons/VotingButtons.vue +59 -14
  94. package/src/widgets/Registrator/AlreadyRegistered/AlreadyRegistered.vue +1 -1
  95. package/src/widgets/User/PaymentMethods/ui/PaymentMethods.vue +0 -1
  96. package/src-ssr/middlewares/injectEnv.ts +5 -1
  97. package/src/features/Meet/GenerateAgenda/index.ts +0 -1
  98. package/src/features/Meet/GenerateAgenda/model/index.ts +0 -18
  99. package/src/features/Meet/GenerateBallot/index.ts +0 -1
  100. package/src/features/Meet/GenerateBallot/model/index.ts +0 -18
  101. package/src/features/Meet/GenerateNotification/index.ts +0 -1
  102. package/src/features/Meet/GenerateNotification/model/index.ts +0 -18
  103. package/src/features/Meet/MeetDetailsManagement/index.ts +0 -1
  104. package/src/features/Meet/MeetDetailsManagement/model/index.ts +0 -121
  105. package/src/pages/Cooperative/ListOfMeets/model/index.ts +0 -1
  106. package/src/pages/Cooperative/ListOfMeets/model/model.ts +0 -117
  107. package/src/widgets/Meets/MeetDetailsActions/model.ts +0 -46
  108. package/src/widgets/Meets/MeetDetailsHeader/MeetDetailsHeader.vue +0 -40
  109. package/src/widgets/Meets/MeetDetailsHeader/index.ts +0 -1
  110. package/src/widgets/Meets/MeetDetailsVoting/model.ts +0 -117
  111. package/src/widgets/Meets/MeetInfoCard/ui/MeetInfoCard.vue +0 -38
  112. package/src/widgets/Meets/MeetInfoCard/ui/index.ts +0 -1
  113. /package/src/{widgets/Meets/MeetInfoCard → features/User/LoginRedirect}/index.ts +0 -0
@@ -1,6 +1,14 @@
1
1
  import { client } from 'src/shared/api/client'
2
2
  import { useSignDocument } from 'src/shared/lib/document/model/entity'
3
- import { Mutations } from '@coopenomics/sdk'
3
+ import { Mutations, Zeus } from '@coopenomics/sdk'
4
+ import { computed, ref, type Ref } from 'vue'
5
+ import { useMeetStore } from 'src/entities/Meet'
6
+ import { useSessionStore } from 'src/entities/Session'
7
+ import { FailAlert, SuccessAlert } from 'src/shared/api'
8
+ import moment from 'moment-with-locales-es6'
9
+ import { useSystemStore } from 'src/entities/System/model'
10
+
11
+ moment.locale('ru')
4
12
 
5
13
  export type ISignBySecretaryResult = Mutations.Meet.SignBySecretaryOnAnnualGeneralMeet.IOutput[typeof Mutations.Meet.SignBySecretaryOnAnnualGeneralMeet.name]
6
14
  export type ISignByPresiderResult = Mutations.Meet.SignByPresiderOnAnnualGeneralMeet.IOutput[typeof Mutations.Meet.SignByPresiderOnAnnualGeneralMeet.name]
@@ -11,7 +19,7 @@ interface ICloseMeetWithDecisionInput {
11
19
  username: string
12
20
  }
13
21
 
14
-
22
+ // Базовые функции API для работы с бэкендом
15
23
  export async function signBySecretaryOnAnnualGeneralMeetWithDecision(data: ICloseMeetWithDecisionInput): Promise<ISignBySecretaryResult> {
16
24
  const { signDocument } = useSignDocument()
17
25
 
@@ -19,6 +27,7 @@ export async function signBySecretaryOnAnnualGeneralMeetWithDecision(data: IClos
19
27
  data: {
20
28
  coopname: data.coopname,
21
29
  username: data.username,
30
+ meet_hash: data.hash
22
31
  }
23
32
  }
24
33
 
@@ -30,7 +39,7 @@ export async function signBySecretaryOnAnnualGeneralMeetWithDecision(data: IClos
30
39
  }
31
40
  )
32
41
 
33
- // Подписываем документ
42
+ // Подписываем документ первой подписью секретаря (signatureId = 1 по умолчанию)
34
43
  const signedDocument = await signDocument(generatedDocument, data.username)
35
44
 
36
45
  const variables2: Mutations.Meet.SignBySecretaryOnAnnualGeneralMeet.IInput = {
@@ -54,23 +63,35 @@ export async function signBySecretaryOnAnnualGeneralMeetWithDecision(data: IClos
54
63
 
55
64
  export async function signByPresiderOnAnnualGeneralMeetWithDecision(data: ICloseMeetWithDecisionInput): Promise<ISignByPresiderResult> {
56
65
  const { signDocument } = useSignDocument()
66
+ const meetStore = useMeetStore()
57
67
 
58
- const variables: Mutations.Meet.GenerateAnnualGeneralMeetDecisionDocument.IInput = {
59
- data: {
60
- coopname: data.coopname,
61
- username: data.username,
62
- }
68
+ // Получаем текущее собрание из store
69
+ const currentMeet = meetStore.currentMeet
70
+ if (!currentMeet?.processing?.meet) {
71
+ throw new Error('Собрание не найдено в store')
63
72
  }
64
- // Генерируем документ решения
65
- const { [Mutations.Meet.GenerateAnnualGeneralMeetDecisionDocument.name]: generatedDocument } = await client.Mutation(
66
- Mutations.Meet.GenerateAnnualGeneralMeetDecisionDocument.mutation,
67
- {
68
- variables
69
- }
70
- )
71
73
 
72
- // Подписываем документ
73
- const signedDocument = await signDocument(generatedDocument, data.username)
74
+ if (!currentMeet.processing.meet.decision1) {
75
+ throw new Error('Документ решения секретаря (decision1) не найден')
76
+ }
77
+
78
+ if (!currentMeet.processing.meet.decision1.rawDocument) {
79
+ throw new Error('Сырой документ решения секретаря (rawDocument) не найден')
80
+ }
81
+
82
+ if (!currentMeet.processing.meet.decision1.document) {
83
+ throw new Error('Подписанный документ решения секретаря (document) не найден')
84
+ }
85
+
86
+ // Используем существующий rawDocument из decision1 (созданный секретарем)
87
+ // и объединяем подпись секретаря с новой подписью председателя (signatureId = 2)
88
+ // В результате получается единый документ с обеими подписями
89
+ const signedDocument = await signDocument(
90
+ currentMeet.processing.meet.decision1.rawDocument,
91
+ data.username,
92
+ 2,
93
+ [currentMeet.processing.meet.decision1.document]
94
+ )
74
95
 
75
96
  const variables2: Mutations.Meet.SignByPresiderOnAnnualGeneralMeet.IInput = {
76
97
  data: {
@@ -80,6 +101,7 @@ export async function signByPresiderOnAnnualGeneralMeetWithDecision(data: IClose
80
101
  presider_decision: signedDocument
81
102
  }
82
103
  }
104
+
83
105
  // Закрываем собрание с подписанным решением председателя
84
106
  const { [Mutations.Meet.SignByPresiderOnAnnualGeneralMeet.name]: result } = await client.Mutation(
85
107
  Mutations.Meet.SignByPresiderOnAnnualGeneralMeet.mutation,
@@ -90,3 +112,83 @@ export async function signByPresiderOnAnnualGeneralMeetWithDecision(data: IClose
90
112
 
91
113
  return result
92
114
  }
115
+
116
+ // Композабл для использования в компонентах
117
+ export const useCloseMeet = (
118
+ isProcessing?: Ref<boolean>
119
+ ) => {
120
+ const { info } = useSystemStore()
121
+
122
+ const localIsProcessing = ref(false)
123
+ const processingRef = isProcessing || localIsProcessing
124
+
125
+ const meetStore = useMeetStore()
126
+ const sessionStore = useSessionStore()
127
+
128
+ const canCloseBySecretary = computed(() => {
129
+ const meet = meetStore.currentMeet
130
+ if (!meet?.processing?.meet) return false
131
+
132
+ const now = moment()
133
+ const closeAt = moment(meet.processing.meet.close_at)
134
+
135
+ const isAfterCloseDate = now.isAfter(closeAt)
136
+ const isQuorumPassed = meet.processing.meet.quorum_passed === true
137
+ const isAuthorized = meet.processing.extendedStatus === Zeus.ExtendedMeetStatus.VOTING_COMPLETED
138
+ const isSecretary = meet.processing.meet.secretary === sessionStore.username
139
+
140
+ return isAfterCloseDate && isQuorumPassed && isAuthorized && isSecretary
141
+ })
142
+
143
+ const canCloseByPresider = computed(() => {
144
+ const meet = meetStore.currentMeet
145
+ if (!meet?.processing?.meet) return false
146
+ const isPresider = meet.processing.meet.presider === sessionStore.username
147
+ return meet.processing.extendedStatus === Zeus.ExtendedMeetStatus.PRECLOSED && isPresider
148
+ })
149
+
150
+ const closeMeetBySecretary = async () => {
151
+ if (!meetStore.currentMeet) return
152
+ processingRef.value = true
153
+ try {
154
+ await signBySecretaryOnAnnualGeneralMeetWithDecision({
155
+ coopname: info.coopname,
156
+ hash: meetStore.currentMeet.hash,
157
+ username: sessionStore.username,
158
+ })
159
+
160
+ await meetStore.loadMeet({ coopname: info.coopname, hash: meetStore.currentMeet.hash })
161
+ SuccessAlert('Собрание успешно закрыто')
162
+ } catch (error: any) {
163
+ FailAlert(error)
164
+ } finally {
165
+ processingRef.value = false
166
+ }
167
+ }
168
+
169
+ const closeMeetByPresider = async () => {
170
+ if (!meetStore.currentMeet) return
171
+ processingRef.value = true
172
+ try {
173
+ await signByPresiderOnAnnualGeneralMeetWithDecision({
174
+ coopname: info.coopname,
175
+ hash: meetStore.currentMeet.hash,
176
+ username: sessionStore.username,
177
+ })
178
+
179
+ await meetStore.loadMeet({ coopname: info.coopname, hash: meetStore.currentMeet.hash })
180
+ SuccessAlert('Собрание успешно закрыто')
181
+ } catch (error: any) {
182
+ FailAlert(error)
183
+ } finally {
184
+ processingRef.value = false
185
+ }
186
+ }
187
+
188
+ return {
189
+ canCloseBySecretary,
190
+ canCloseByPresider,
191
+ closeMeetBySecretary,
192
+ closeMeetByPresider
193
+ }
194
+ }
@@ -1,12 +1,15 @@
1
1
  import { client } from 'src/shared/api/client'
2
2
  import { Mutations } from '@coopenomics/sdk'
3
- import { generateAgenda } from 'src/features/Meet/GenerateAgenda/model'
4
3
  import { useSignDocument } from 'src/shared/lib/document/model/entity'
5
- import { hashSHA256 } from 'src/shared/api/crypto'
4
+ import { getTimezone } from 'src/shared/lib/utils/dates/timezone'
5
+ import moment from 'moment-timezone'
6
6
 
7
7
  export type ICreateMeetInput = Mutations.Meet.CreateAnnualGeneralMeet.IInput['data']
8
8
  export type ICreateMeetResult = Mutations.Meet.CreateAnnualGeneralMeet.IOutput[typeof Mutations.Meet.CreateAnnualGeneralMeet.name]
9
9
 
10
+ export type IGenerateAgendaInput = Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.IInput['data']
11
+ export type IGenerateAgendaResult = Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.IOutput[typeof Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.name]
12
+
10
13
  export interface ICreateMeetWithAgendaInput {
11
14
  coopname: string
12
15
  initiator: string
@@ -22,6 +25,24 @@ export interface ICreateMeetWithAgendaInput {
22
25
  }[]
23
26
  }
24
27
 
28
+ /**
29
+ * Генерирует документ повестки собрания
30
+ * @private Внутренняя функция, не экспортируется
31
+ */
32
+ async function generateAgenda(data: IGenerateAgendaInput, options?: any): Promise<IGenerateAgendaResult> {
33
+ const { [Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.name]: generatedDocument } = await client.Mutation(
34
+ Mutations.Meet.GenerateAnnualGeneralMeetAgendaDocument.mutation,
35
+ {
36
+ variables: {
37
+ data,
38
+ options
39
+ }
40
+ }
41
+ );
42
+
43
+ return generatedDocument;
44
+ }
45
+
25
46
  export async function createMeet(data: ICreateMeetInput): Promise<ICreateMeetResult> {
26
47
  const { [Mutations.Meet.CreateAnnualGeneralMeet.name]: result } = await client.Mutation(
27
48
  Mutations.Meet.CreateAnnualGeneralMeet.mutation,
@@ -36,12 +57,38 @@ export async function createMeet(data: ICreateMeetInput): Promise<ICreateMeetRes
36
57
  }
37
58
 
38
59
  export async function createMeetWithAgenda(data: ICreateMeetWithAgendaInput): Promise<ICreateMeetResult> {
60
+
39
61
  const { signDocument } = useSignDocument()
40
- // Генерируем документ повестки
62
+
63
+ // Получаем московский часовой пояс для форматирования
64
+ const timezone = getTimezone()
65
+
66
+ // Преобразуем формат даты для документа, явно указывая московский часовой пояс
67
+ // Добавляем маркер '(Мск)' к датам, так как в шаблоне документа этот маркер не добавляется автоматически
68
+ const openAtFormatted = `${moment(data.open_at).tz(timezone).format('DD.MM.YYYY HH:mm')} (Мск)`
69
+ const closeAtFormatted = `${moment(data.close_at).tz(timezone).format('DD.MM.YYYY HH:mm')} (Мск)`
70
+
71
+ // Формируем вопросы повестки в требуемом формате
72
+ const questions = data.agenda_points.map((point, index) => ({
73
+ number: String(index + 1),
74
+ title: point.title,
75
+ decision: point.decision,
76
+ context: point.context || ''
77
+ }))
78
+
79
+ // Генерируем документ повестки с правильными параметрами согласно DTO
41
80
  const generatedDocument = await generateAgenda({
42
81
  coopname: data.coopname,
43
- username: data.username
82
+ username: data.username,
83
+ meet: {
84
+ type: 'regular', // По умолчанию очередное собрание
85
+ open_at_datetime: openAtFormatted,
86
+ close_at_datetime: closeAtFormatted
87
+ },
88
+ questions: questions
44
89
  })
90
+
91
+
45
92
  // Подписываем документ
46
93
  const signedDocument = await signDocument(generatedDocument, data.username)
47
94
 
@@ -54,11 +101,6 @@ export async function createMeetWithAgenda(data: ICreateMeetWithAgendaInput): Pr
54
101
  secretary: data.secretary,
55
102
  open_at: data.open_at,
56
103
  close_at: data.close_at,
57
- hash: await hashSHA256(JSON.stringify({
58
- coopname: data.coopname,
59
- initiator: data.initiator,
60
- timestamp: Date.now()
61
- })),
62
104
  proposal: signedDocument
63
105
  })
64
106
  return result
@@ -15,20 +15,51 @@ div
15
15
  <script setup lang="ts">
16
16
  import { ref } from 'vue'
17
17
  import { CreateMeetForm } from './index'
18
+ import { createMeetWithAgenda } from '../model'
19
+ import { useSessionStore } from 'src/entities/Session'
20
+ import { useMeetStore } from 'src/entities/Meet'
21
+ import { useRoute } from 'vue-router'
22
+ import { Notify } from 'quasar'
23
+ import { FailAlert } from 'src/shared/api'
24
+
25
+ const route = useRoute()
26
+ const sessionStore = useSessionStore()
27
+ const meetStore = useMeetStore()
18
28
 
19
29
  const showCreateMeetDialog = ref(false)
20
30
  const isCreating = ref(false)
21
31
 
22
- const emit = defineEmits<{
23
- (e: 'create', formData: any): void
24
- }>()
25
-
26
32
  const handleCreate = async (formData: any) => {
27
33
  isCreating.value = true
28
34
  try {
29
- emit('create', formData)
35
+ await createMeetWithAgenda({
36
+ coopname: route.params.coopname as string,
37
+ initiator: formData.initiator,
38
+ presider: formData.presider,
39
+ secretary: formData.secretary,
40
+ open_at: formData.open_at,
41
+ close_at: formData.close_at,
42
+ username: sessionStore.username,
43
+ agenda_points: formData.agenda_points
44
+ })
45
+
46
+ Notify.create({
47
+ message: 'Собрание успешно создано',
48
+ type: 'positive',
49
+ })
50
+
51
+ // Закрываем диалог
52
+ showCreateMeetDialog.value = false
53
+
54
+ // Обновляем список собраний через стор
55
+ await meetStore.loadMeets({ coopname: route.params.coopname as string })
56
+
57
+ return true
58
+ } catch (e: any) {
59
+ FailAlert(e)
60
+ return false
30
61
  } finally {
31
62
  isCreating.value = false
32
63
  }
33
64
  }
34
- </script>
65
+ </script>
@@ -8,66 +8,73 @@ q-dialog(:model-value="modelValue" @update:model-value="$emit('update:modelValue
8
8
 
9
9
  q-card-section
10
10
  q-form(@submit="handleSubmit")
11
- q-input(
12
- v-model="formData.initiator"
13
- label="Инициатор"
14
- :rules="[val => !!val || 'Обязательное поле']"
15
- dense
16
- )
17
11
  q-input(
18
12
  v-model="formData.presider"
19
- label="Председатель"
13
+ label="Имя аккаунта председателя собрания"
20
14
  :rules="[val => !!val || 'Обязательное поле']"
21
15
  dense
22
16
  )
23
17
  q-input(
24
18
  v-model="formData.secretary"
25
- label="Секретарь"
19
+ label="Имя аккаунта секретаря собрания"
26
20
  :rules="[val => !!val || 'Обязательное поле']"
27
21
  dense
28
22
  )
29
23
  q-input(
30
24
  v-model="formData.open_at"
31
- label="Дата и время открытия, UTC"
25
+ :label="env.NODE_ENV === 'development' ? `Дата и время открытия (мин. через 1 минуту, ${timezoneLabel})` : `Дата и время открытия (мин. через 15 дней, ${timezoneLabel})`"
32
26
  type="datetime-local"
33
27
  :rules="[val => !!val || 'Обязательное поле']"
34
28
  dense
35
29
  )
36
30
  q-input(
37
31
  v-model="formData.close_at"
38
- label="Дата и время закрытия, UTC"
32
+ :label="`Дата и время закрытия (${timezoneLabel})`"
39
33
  type="datetime-local"
40
34
  :rules="[val => !!val || 'Обязательное поле']"
41
35
  dense
42
36
  )
43
37
 
38
+
44
39
  div.text-h6.q-mt-md Повестка собрания
45
40
 
46
- div(v-for="(point, index) in formData.agenda_points" :key="index")
47
- div.row.q-mb-sm
48
- div.col
49
- q-input(
50
- v-model="point.title"
51
- label="Заголовок"
52
- :rules="[val => !!val || 'Обязательное поле']"
53
- dense
54
- )
55
- div.col
56
- q-input(
57
- v-model="point.context"
58
- label="Описание"
59
- :rules="[val => !!val || 'Обязательное поле']"
60
- dense
61
- )
62
- div.col
63
- q-input(
64
- v-model="point.decision"
65
- label="Решение"
66
- :rules="[val => !!val || 'Обязательное поле']"
67
- dense
68
- )
41
+ q-card(flat bordered v-for="(point, index) in formData.agenda_points" :key="index").q-mb-lg.q-pa-sm
42
+ div.row.items-center.q-mb-sm
43
+ div.text-subtitle1.q-mr-md № {{ index + 1 }}
69
44
  div.col-auto
70
- q-btn(flat icon="delete" @click="removeAgendaPoint(index)")
45
+ q-btn(flat icon="delete" size="sm" color="grey" @click="removeAgendaPoint(index)")
46
+
47
+ div.q-mb-sm
48
+ q-input(
49
+ v-model="point.title"
50
+ label="Вопрос"
51
+ :rules="[val => !!val || 'Обязательное поле']"
52
+ dense
53
+ type="textarea"
54
+ autogrow
55
+ )
56
+
57
+ div.q-mb-sm
58
+ q-input(
59
+ v-model="point.decision"
60
+ label="Проект Решения"
61
+ :rules="[val => !!val || 'Обязательное поле']"
62
+ dense
63
+ type="textarea"
64
+ autogrow
65
+ )
66
+
67
+ div.q-mb-sm
68
+ q-input(
69
+ v-model="point.context"
70
+ label="Приложения"
71
+ :rules="[val => !!val || 'Обязательное поле']"
72
+ dense
73
+ type="textarea"
74
+ autogrow
75
+ )
76
+
77
+ q-separator(v-if="index < formData.agenda_points.length - 1")
71
78
 
72
79
  div.text-center.q-mb-md
73
80
  q-btn(outline label="Добавить пункт повестки" @click="addAgendaPoint")
@@ -83,6 +90,10 @@ q-dialog(:model-value="modelValue" @update:model-value="$emit('update:modelValue
83
90
  <script setup lang="ts">
84
91
  import { reactive } from 'vue'
85
92
  import { useAgendaPoints } from 'src/shared/hooks/useAgendaPoints'
93
+ import { getCurrentLocalDateForForm, convertLocalDateToUTC, getTimezoneLabel, getFutureDateForForm } from 'src/shared/lib/utils/dates/timezone'
94
+ import { env } from 'src/shared/config/Environment'
95
+ import { useSessionStore } from 'src/entities/Session';
96
+ import { useSystemStore } from 'src/entities/System/model';
86
97
 
87
98
  defineProps<{
88
99
  modelValue: boolean,
@@ -94,43 +105,54 @@ const emit = defineEmits<{
94
105
  (e: 'create', data: any): void
95
106
  }>()
96
107
 
97
- // Форма для создания собрания
108
+ // Название часового пояса для отображения в лейблах
109
+ const timezoneLabel = getTimezoneLabel()
110
+ const session = useSessionStore()
111
+ const system = useSystemStore()
98
112
 
113
+ // Форма для создания собрания
99
114
  const formData = reactive(
100
- {
101
- 'coopname': 'voskhod',
102
- 'initiator': 'ant',
103
- 'presider': 'ant',
104
- 'secretary': 'ant',
105
- 'open_at': new Date(Date.now() + 10000).toISOString().slice(0, 16),
106
- 'close_at': new Date(Date.now() + 120000).toISOString().slice(0, 16),
107
- 'username': 'ant',
108
- 'agenda_points': [
109
- {
110
- 'title': 'test',
111
- 'context': 'testt',
112
- 'decision': 'testtt'
113
- }
114
- ]
115
- }
116
- // {
117
- // initiator: '',
118
- // presider: '',
119
- // secretary: '',
120
- // open_at: '',
121
- // close_at: '',
122
- // agenda_points: [{
123
- // title: '',
124
- // context: '',
125
- // decision: ''
126
- // }
127
- // ]
128
- // }
115
+ env.NODE_ENV === 'development'
116
+ ? {
117
+ coopname: system.info.coopname,
118
+ initiator: session.username,
119
+ presider: session.username,
120
+ secretary: session.username,
121
+ open_at: getCurrentLocalDateForForm(0.17), // ~10 секунд от текущего времени
122
+ close_at: getCurrentLocalDateForForm(2), // 2 минуты от текущего времени
123
+ username: session.username,
124
+ agenda_points: [
125
+ {
126
+ title: 'Тестовый вопрос',
127
+ context: 'Тут приложения к вопросу',
128
+ decision: 'Тут проект решения по вопросу',
129
+ },
130
+ ],
131
+ }
132
+ : {
133
+ coopname: system.info.coopname,
134
+ initiator: session.username,
135
+ presider: '',
136
+ secretary: '',
137
+ open_at: getFutureDateForForm(15, 6, 0), // через 15 дней, 6:00
138
+ close_at: getFutureDateForForm(18, 12, 0), // через 18 дней, 12:00
139
+ username: session.username,
140
+ agenda_points: [
141
+ // пустой массив, либо можно добавить пустой объект для вёрстки
142
+ ],
143
+ }
129
144
  )
130
145
 
131
146
  const { addAgendaPoint, removeAgendaPoint } = useAgendaPoints(formData.agenda_points)
132
147
 
133
148
  const handleSubmit = () => {
134
- emit('create', formData)
149
+ // Конвертируем локальные даты в UTC для отправки в блокчейн
150
+ const dataToSend = {
151
+ ...formData,
152
+ open_at: convertLocalDateToUTC(formData.open_at),
153
+ close_at: convertLocalDateToUTC(formData.close_at)
154
+ }
155
+
156
+ emit('create', dataToSend)
135
157
  }
136
158
  </script>
@@ -5,17 +5,18 @@ import { useSystemStore } from 'src/entities/System/model'
5
5
  export type IGenerateSovietDecisionInput = Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.IInput['data'];
6
6
  export type IGenerateSovietDecisionResult = Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.IOutput[typeof Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.name];
7
7
 
8
- export async function generateSovietDecision(data: IGenerateSovietDecisionInput): Promise<IGenerateSovietDecisionResult> {
9
- const { [Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.name]: result } = await client.Mutation(
8
+ export async function generateSovietDecision(data: IGenerateSovietDecisionInput, options?: any): Promise<IGenerateSovietDecisionResult> {
9
+ const { [Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.name]: generatedDocument } = await client.Mutation(
10
10
  Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.mutation,
11
11
  {
12
12
  variables: {
13
- data
13
+ data,
14
+ options
14
15
  }
15
16
  }
16
17
  );
17
18
 
18
- return result;
19
+ return generatedDocument;
19
20
  }
20
21
 
21
22
  export function useGenerateSovietDecisionOnAnnualMeet() {
@@ -27,6 +28,15 @@ export function useGenerateSovietDecisionOnAnnualMeet() {
27
28
  async function generateSovietDecisionOnAnnualMeet(
28
29
  data: Omit<IGenerateSovietDecisionInput, 'coopname'>
29
30
  ): Promise<IGenerateSovietDecisionResult> {
31
+ // Здесь необходимо убедиться, что переданы все обязательные параметры согласно DTO
32
+ if (!data.decision_id) {
33
+ throw new Error('Необходимо указать ID решения совета (decision_id)')
34
+ }
35
+
36
+ if (!data.meet_hash) {
37
+ throw new Error('Необходимо указать хеш собрания (meet_hash)')
38
+ }
39
+
30
40
  const { [Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.name]: result } = await client.Mutation(
31
41
  Mutations.Meet.GenerateSovietDecisionOnAnnualMeetDocument.mutation,
32
42
  {