@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.
- 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/quasar.config.cjs +1 -1
- 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 +35 -27
- 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 +18 -12
- 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
@@ -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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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: '
|
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:
|
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
|
-
|
100
|
-
|
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'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<template lang="pug">
|
2
|
-
div.scroll-area(style="height:
|
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(
|
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:
|
138
|
+
function getDecisionTitle(row: IAgenda) {
|
139
|
+
console.log('agenda', row)
|
129
140
|
// Используем только агрегаты документа
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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: '
|
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
|
-
|
5
|
-
q-
|
6
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
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:
|
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="
|
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
|
-
|
24
|
-
|
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'
|