@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.
- package/.env-example +4 -0
- package/extensions/participant/install.ts +37 -25
- package/extensions/soviet/install.ts +23 -20
- package/package.json +7 -5
- package/src/app/App.vue +1 -0
- package/src/app/providers/routes/index.ts +12 -0
- package/src/app/styles/app.scss +1 -1
- package/src/app/styles/style.css +11 -0
- package/src/css/quasar.variables.scss +1 -1
- package/src/entities/Desktop/model/store.ts +68 -0
- package/src/entities/Desktop/model/types.ts +7 -0
- package/src/entities/Document/model/types.ts +1 -1
- package/src/entities/Meet/api/index.ts +2 -28
- package/src/entities/Meet/model/store.ts +7 -22
- package/src/entities/Wallet/api/index.ts +3 -3
- package/src/env.d.ts +1 -0
- package/src/features/Meet/CloseMeetWithDecision/model/index.ts +119 -17
- package/src/features/Meet/CreateMeet/model/index.ts +51 -9
- package/src/features/Meet/CreateMeet/ui/CreateMeet.vue +37 -6
- package/src/features/Meet/CreateMeet/ui/CreateMeetForm.vue +87 -65
- package/src/features/Meet/GenerateSovietDecision/model/index.ts +14 -4
- package/src/features/Meet/RestartMeet/model/index.ts +121 -3
- package/src/features/Meet/RestartMeet/ui/RestartMeet.vue +4 -6
- package/src/features/Meet/RestartMeet/ui/RestartMeetForm.vue +64 -28
- package/src/features/Meet/SignNotification/index.ts +2 -0
- package/src/features/Meet/SignNotification/model/index.ts +137 -0
- package/src/features/Meet/SignNotification/ui/SignNotificationButton.vue +61 -0
- package/src/features/Meet/SignNotification/ui/index.ts +1 -0
- package/src/features/Meet/VoteOnMeet/model/composable.ts +180 -0
- package/src/features/Meet/VoteOnMeet/model/index.ts +2 -17
- package/src/features/Meet/VoteOnMeet/model/types.ts +4 -0
- package/src/features/Meet/index.ts +6 -0
- package/src/features/User/LoginRedirect/ui/LoginRedirectPage.vue +15 -0
- package/src/features/User/LoginRedirect/ui/index.ts +1 -0
- package/src/features/User/LoginUser/ui/LoginForm/LoginForm.vue +38 -1
- package/src/features/User/LoginWithRedirect/ui/LoginRedirectForm/LoginRedirectForm.vue +0 -0
- package/src/pages/Cooperative/ListOfMeets/ui/ListOfMeetsPage.vue +22 -46
- package/src/pages/Cooperative/MeetDetails/ui/MeetDetailsPage.vue +84 -28
- package/src/pages/PermissionDenied/PermissionDenied.vue +1 -1
- package/src/processes/init-app/index.ts +12 -5
- package/src/processes/navigation-guard-setup/index.ts +37 -5
- package/src/processes/process-decisions/index.ts +7 -6
- package/src/shared/config/Environment.ts +10 -2
- package/src/shared/lib/composables/index.ts +1 -0
- package/src/shared/lib/composables/useMeetStatus.ts +96 -0
- package/src/shared/lib/consts/index.ts +1 -0
- package/src/shared/lib/consts/meet-statuses.ts +114 -0
- package/src/shared/lib/document/model/entity.ts +4 -2
- package/src/shared/lib/types/certificate/index.ts +6 -0
- package/src/shared/lib/types/document/index.ts +1 -1
- package/src/shared/lib/types/workspace.ts +24 -0
- package/src/shared/lib/utils/dates/index.ts +5 -0
- package/src/shared/lib/utils/dates/moment.ts +43 -2
- package/src/shared/lib/utils/dates/timezone.ts +75 -0
- package/src/shared/lib/utils/getNameFromCertificate.ts +108 -0
- package/src/shared/lib/utils/index.ts +1 -0
- package/src/shared/lib/utils/parseLinks.ts +10 -0
- package/src/shared/ui/AgendaNumberAvatar/AgendaNumberAvatar.vue +12 -0
- package/src/shared/ui/AgendaNumberAvatar/index.ts +1 -0
- package/src/shared/ui/BaseDocument/BaseDocument.vue +37 -8
- package/src/shared/ui/ExpandableDocument/ExpandableDocument.vue +49 -0
- package/src/shared/ui/ExpandableDocument/index.ts +1 -0
- package/src/shared/ui/MeetInfoCard/index.ts +1 -0
- package/src/shared/ui/MeetInfoCard/ui/MeetInfoCard.vue +62 -0
- package/src/shared/ui/MeetStatusBanner/index.ts +1 -0
- package/src/shared/ui/MeetStatusBanner/ui/MeetStatusBanner.vue +94 -0
- package/src/shared/ui/index.ts +1 -0
- package/src/widgets/Cooperative/Documents/ListOfDocuments/ui/DocumentsTable.vue +3 -3
- package/src/widgets/Cooperative/Orders/ListOfOrders/ui/ListOfOrdersWidget.vue +2 -2
- package/src/widgets/Cooperative/Payments/ListOfPayments/ui/ListOfPaymentsWidget.vue +2 -2
- package/src/widgets/Desktop/WorkspaceMenu/WorkspaceMenu.vue +74 -82
- package/src/widgets/Header/CommonHeader/CooperativeSettingsHeader.vue +1 -1
- package/src/widgets/Header/CommonHeader/ExtstoreHeader.vue +1 -1
- package/src/widgets/Header/CommonHeader/MainHeader.vue +6 -0
- package/src/widgets/Header/CommonHeader/UserSettingsHeader.vue +1 -1
- package/src/widgets/Meets/MeetCardsList/index.ts +1 -0
- package/src/widgets/Meets/MeetCardsList/ui/MeetCardsList.vue +55 -0
- package/src/widgets/Meets/MeetDetailsActions/MeetDetailsActions.vue +33 -15
- package/src/widgets/Meets/MeetDetailsAgenda/MeetDetailsAgenda.vue +34 -15
- package/src/widgets/Meets/MeetDetailsInfo/MeetDetailsInfo.vue +27 -0
- package/src/widgets/Meets/MeetDetailsInfo/index.ts +1 -0
- package/src/widgets/Meets/MeetDetailsResults/MeetDetailsResults.vue +129 -0
- package/src/widgets/Meets/MeetDetailsResults/index.ts +1 -0
- package/src/widgets/Meets/MeetDetailsVoting/MeetDetailsVoting.vue +221 -62
- package/src/widgets/Meets/MeetQuorumIndicator/MeetQuorumIndicator.vue +0 -0
- package/src/widgets/Meets/MeetQuorumIndicator/index.ts +1 -0
- package/src/widgets/Meets/MeetQuorumIndicator/ui/MeetQuorumIndicator.vue +35 -0
- package/src/widgets/Meets/MeetsTable/ui/MeetsTable.vue +56 -65
- package/src/widgets/NotificationCenter/NotificationCenter.vue +90 -0
- package/src/widgets/NotificationCenter/index.ts +1 -0
- package/src/widgets/Participants/ui/ParticipantsTable.vue +1 -1
- package/src/widgets/Questions/ui/QuestionsTable/QuestionsTable.vue +22 -10
- package/src/widgets/Questions/ui/VotingButtons/VotingButtons.vue +59 -14
- package/src/widgets/Registrator/AlreadyRegistered/AlreadyRegistered.vue +1 -1
- package/src/widgets/User/PaymentMethods/ui/PaymentMethods.vue +0 -1
- package/src-ssr/middlewares/injectEnv.ts +5 -1
- package/src/features/Meet/GenerateAgenda/index.ts +0 -1
- package/src/features/Meet/GenerateAgenda/model/index.ts +0 -18
- package/src/features/Meet/GenerateBallot/index.ts +0 -1
- package/src/features/Meet/GenerateBallot/model/index.ts +0 -18
- package/src/features/Meet/GenerateNotification/index.ts +0 -1
- package/src/features/Meet/GenerateNotification/model/index.ts +0 -18
- package/src/features/Meet/MeetDetailsManagement/index.ts +0 -1
- package/src/features/Meet/MeetDetailsManagement/model/index.ts +0 -121
- package/src/pages/Cooperative/ListOfMeets/model/index.ts +0 -1
- package/src/pages/Cooperative/ListOfMeets/model/model.ts +0 -117
- package/src/widgets/Meets/MeetDetailsActions/model.ts +0 -46
- package/src/widgets/Meets/MeetDetailsHeader/MeetDetailsHeader.vue +0 -40
- package/src/widgets/Meets/MeetDetailsHeader/index.ts +0 -1
- package/src/widgets/Meets/MeetDetailsVoting/model.ts +0 -117
- package/src/widgets/Meets/MeetInfoCard/ui/MeetInfoCard.vue +0 -38
- package/src/widgets/Meets/MeetInfoCard/ui/index.ts +0 -1
- /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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
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 {
|
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
|
-
|
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="
|
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="
|
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
|
-
|
47
|
-
div.row.q-mb-sm
|
48
|
-
div.
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
//
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
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]:
|
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
|
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
|
{
|