From 48ecb49a59809f1cc2a45ab1d03e1f922318b235 Mon Sep 17 00:00:00 2001 From: Alex Ant Date: Sat, 28 Mar 2026 21:09:57 +0500 Subject: [PATCH] calendar of events --- components/controller/schema.gql | 107 ++++++- .../dto/generation/update-story-input.dto.ts | 9 + .../services/generation.service.ts | 25 +- .../services/github-sync.service.ts | 36 ++- .../domain/services/file-format.service.ts | 38 ++- .../services/github-sync-scheduler.service.ts | 2 +- .../infrastructure/services/github.service.ts | 10 +- .../chatcoop-calendar-feed.controller.ts | 42 +++ .../chatcoop/application/dto/calendar.dto.ts | 115 +++++++ .../resolvers/chatcoop-calendar.resolver.ts | 139 ++++++++ .../chatcoop-calendar-application.service.ts | 217 +++++++++++++ .../chatcoop/chatcoop-extension.module.ts | 22 +- .../domain/entities/calendar-event.entity.ts | 32 ++ .../repositories/calendar-event.repository.ts | 20 ++ .../calendar-ics-subscription.repository.ts | 17 + .../database/chatcoop-database.module.ts | 4 + .../entities/calendar-event.typeorm-entity.ts | 36 +++ ...alendar-ics-subscription.typeorm-entity.ts | 17 + ...hatcoop-inter-chatcoop-calendar.adapter.ts | 15 + .../mappers/calendar-event.mapper.ts | 19 ++ .../calendar-event.typeorm-repository.ts | 97 ++++++ ...dar-ics-subscription.typeorm-repository.ts | 61 ++++ .../inter-communication-bridge.module.ts | 17 +- components/controller/zeus/const.ts | 49 ++- components/controller/zeus/index.ts | 301 +++++++++++++++--- .../capital/entities/Project/model/store.ts | 15 + .../features/Issue/DeleteIssue/index.ts | 1 + .../DeleteIssue/ui/DeleteIssueButton.vue | 75 +++++ .../features/Issue/DeleteIssue/ui/index.ts | 1 + .../Issue/SetCreator/ui/SetCreatorButton.vue | 1 + .../Project/DeleteProject/model/index.ts | 5 + .../ui/DeleteProjectSidebarButton.vue | 86 +++++ .../Project/DeleteProject/ui/index.ts | 1 + .../features/Story/CreateStory/model/index.ts | 2 + .../ui/EditRequirementPanel.vue | 21 +- .../pages/ComponentPage/ui/ComponentPage.vue | 15 + .../capital/pages/IssuePage/ui/IssuePage.vue | 15 + .../pages/ProjectPage/ui/ProjectPage.vue | 7 + .../CommitsListWidget/CommitsListWidget.vue | 2 +- .../ComponentSidebarWidget.vue | 10 + .../ComponentsListWidget.vue | 4 +- .../ui/InvitationsListWidget.vue | 2 +- .../IssueSidebarWidget/IssueSidebarWidget.vue | 11 + .../IssuesListWidget/ui/IssuesListWidget.vue | 7 +- .../ListResultProjectsWidget.vue | 2 +- .../ListVotingProjectWidget.vue | 2 +- .../ProjectSidebarWidget.vue | 11 + .../ProjectVotingSegmentsWidget.vue | 2 +- .../ProjectsListWidget/ProjectsListWidget.vue | 4 +- .../ui/RequirementsListWidget.vue | 4 +- .../ResultSubmissionSegmentsWidget.vue | 2 +- .../widgets/StoryWidget/ui/StoriesWidget.vue | 3 + .../TimeIssuesWidget/TimeIssuesWidget.vue | 2 +- .../TimeStatsWidget/TimeStatsWidget.vue | 2 +- .../entities/ChatCoopCalendar/api/index.ts | 61 ++++ .../entities/ChatCoopCalendar/index.ts | 2 + .../entities/ChatCoopCalendar/model/index.ts | 2 + .../entities/ChatCoopCalendar/model/store.ts | 53 +++ .../entities/ChatCoopCalendar/model/types.ts | 4 + .../extensions/chatcoop/entities/index.ts | 5 +- .../features/Calendar/DeleteEvent/index.ts | 1 + .../ui/DeleteCalendarEventDialog.vue | 56 ++++ .../features/Calendar/DeleteEvent/ui/index.ts | 1 + .../features/Calendar/SaveEvent/index.ts | 1 + .../SaveEvent/ui/SaveCalendarEventDialog.vue | 124 ++++++++ .../features/Calendar/SaveEvent/ui/index.ts | 1 + .../desktop/extensions/chatcoop/install.ts | 15 +- .../chatcoop/pages/CalendarPage/index.ts | 1 + .../pages/CalendarPage/ui/CalendarPage.vue | 55 ++++ .../chatcoop/pages/CalendarPage/ui/index.ts | 1 + .../pages/ChatCoopPage/ui/ChatCoopPage.vue | 28 +- .../extensions/chatcoop/pages/index.ts | 1 + .../chatcoop/shared/lib/calendarDateFormat.ts | 20 ++ .../shared/lib/matrixIframeDeepLink.ts | 12 + .../shared/lib/useCalendarBoardPermissions.ts | 11 + .../widgets/CalendarErrorBanner/index.ts | 1 + .../ui/CalendarErrorBanner.vue | 14 + .../widgets/CalendarErrorBanner/ui/index.ts | 1 + .../widgets/CalendarEventsTable/index.ts | 1 + .../ui/CalendarEventsTable.vue | 77 +++++ .../widgets/CalendarEventsTable/ui/index.ts | 1 + .../widgets/CalendarPageToolbar/index.ts | 1 + .../ui/CalendarPageToolbar.vue | 67 ++++ .../widgets/CalendarPageToolbar/ui/index.ts | 1 + .../inter/src/chatcoop-calendar.port.ts | 34 ++ components/inter/src/index.ts | 12 +- components/inter/src/tokens.ts | 3 + .../mutations/chatcoop/createCalendarEvent.ts | 19 ++ .../chatcoop/createCalendarIcsSubscription.ts | 17 + .../mutations/chatcoop/deleteCalendarEvent.ts | 18 ++ .../sdk/src/mutations/chatcoop/index.ts | 4 + .../mutations/chatcoop/updateCalendarEvent.ts | 19 ++ components/sdk/src/queries/chatcoop/index.ts | 2 + .../queries/chatcoop/listCalendarEvents.ts | 17 + .../src/queries/chatcoop/listCalendarRooms.ts | 17 + .../sdk/src/selectors/chatcoop/index.ts | 45 +++ components/sdk/src/zeus/const.ts | 49 ++- components/sdk/src/zeus/index.ts | 295 ++++++++++++++--- 98 files changed, 2749 insertions(+), 183 deletions(-) create mode 100644 components/controller/src/extensions/chatcoop/application/controllers/chatcoop-calendar-feed.controller.ts create mode 100644 components/controller/src/extensions/chatcoop/application/dto/calendar.dto.ts create mode 100644 components/controller/src/extensions/chatcoop/application/resolvers/chatcoop-calendar.resolver.ts create mode 100644 components/controller/src/extensions/chatcoop/application/services/chatcoop-calendar-application.service.ts create mode 100644 components/controller/src/extensions/chatcoop/domain/entities/calendar-event.entity.ts create mode 100644 components/controller/src/extensions/chatcoop/domain/repositories/calendar-event.repository.ts create mode 100644 components/controller/src/extensions/chatcoop/domain/repositories/calendar-ics-subscription.repository.ts create mode 100644 components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-event.typeorm-entity.ts create mode 100644 components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-ics-subscription.typeorm-entity.ts create mode 100644 components/controller/src/extensions/chatcoop/infrastructure/inter/chatcoop-inter-chatcoop-calendar.adapter.ts create mode 100644 components/controller/src/extensions/chatcoop/infrastructure/mappers/calendar-event.mapper.ts create mode 100644 components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-event.typeorm-repository.ts create mode 100644 components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-ics-subscription.typeorm-repository.ts create mode 100644 components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/DeleteIssueButton.vue create mode 100644 components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/index.ts create mode 100644 components/desktop/extensions/capital/features/Project/DeleteProject/ui/DeleteProjectSidebarButton.vue create mode 100644 components/desktop/extensions/chatcoop/entities/ChatCoopCalendar/api/index.ts create mode 100644 components/desktop/extensions/chatcoop/entities/ChatCoopCalendar/index.ts create mode 100644 components/desktop/extensions/chatcoop/entities/ChatCoopCalendar/model/index.ts create mode 100644 components/desktop/extensions/chatcoop/entities/ChatCoopCalendar/model/store.ts create mode 100644 components/desktop/extensions/chatcoop/entities/ChatCoopCalendar/model/types.ts create mode 100644 components/desktop/extensions/chatcoop/features/Calendar/DeleteEvent/index.ts create mode 100644 components/desktop/extensions/chatcoop/features/Calendar/DeleteEvent/ui/DeleteCalendarEventDialog.vue create mode 100644 components/desktop/extensions/chatcoop/features/Calendar/DeleteEvent/ui/index.ts create mode 100644 components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/index.ts create mode 100644 components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/SaveCalendarEventDialog.vue create mode 100644 components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/index.ts create mode 100644 components/desktop/extensions/chatcoop/pages/CalendarPage/index.ts create mode 100644 components/desktop/extensions/chatcoop/pages/CalendarPage/ui/CalendarPage.vue create mode 100644 components/desktop/extensions/chatcoop/pages/CalendarPage/ui/index.ts create mode 100644 components/desktop/extensions/chatcoop/shared/lib/calendarDateFormat.ts create mode 100644 components/desktop/extensions/chatcoop/shared/lib/matrixIframeDeepLink.ts create mode 100644 components/desktop/extensions/chatcoop/shared/lib/useCalendarBoardPermissions.ts create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarErrorBanner/index.ts create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarErrorBanner/ui/CalendarErrorBanner.vue create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarErrorBanner/ui/index.ts create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/index.ts create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/CalendarEventsTable.vue create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/index.ts create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/index.ts create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/CalendarPageToolbar.vue create mode 100644 components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/index.ts create mode 100644 components/inter/src/chatcoop-calendar.port.ts create mode 100644 components/sdk/src/mutations/chatcoop/createCalendarEvent.ts create mode 100644 components/sdk/src/mutations/chatcoop/createCalendarIcsSubscription.ts create mode 100644 components/sdk/src/mutations/chatcoop/deleteCalendarEvent.ts create mode 100644 components/sdk/src/mutations/chatcoop/updateCalendarEvent.ts create mode 100644 components/sdk/src/queries/chatcoop/listCalendarEvents.ts create mode 100644 components/sdk/src/queries/chatcoop/listCalendarRooms.ts diff --git a/components/controller/schema.gql b/components/controller/schema.gql index af115d2f2c0..6cf9810aaba 100644 --- a/components/controller/schema.gql +++ b/components/controller/schema.gql @@ -1568,6 +1568,10 @@ type CallTranscription { endedAt: DateTime id: String! matrixRoomId: String! + + """ + Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id + """ participants: [String!]! roomId: String! roomName: String! @@ -1881,12 +1885,12 @@ type CapitalContributor { """ID в блокчейне""" id: Int - """Является ли внешним контрактом""" - is_external_contract: Boolean - """Соглашение Благорост предоставлено при импорте (внешний документ)""" is_external_blagorost_agreement: Boolean + """Является ли внешним контрактом""" + is_external_contract: Boolean + """Последнее обновление энергии""" last_energy_update: String @@ -3297,12 +3301,12 @@ type CapitalStory { """Номер блока крайней синхронизации с блокчейном""" block_num: Float - """Имя аккаунта кооператива""" - coopname: String! - """Формат содержимого (markdown-текст или BPMN 2.0 XML в description)""" content_format: CapitalStoryContentFormat! + """Имя аккаунта кооператива""" + coopname: String! + """Имя пользователя, создавшего историю""" created_by: String! @@ -3331,6 +3335,16 @@ type CapitalStory { title: String! } +""" +Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) +""" +enum CapitalStoryContentFormat { + BPMN + DRAWIO + MARKDOWN + MERMAID +} + """Параметры фильтрации для запросов историй CAPITAL""" input CapitalStoryFilter { """Фильтр по названию кооператива""" @@ -3610,6 +3624,29 @@ type ChartOfAccountsItem { writeoff: String! } +type ChatCoopCalendarEvent { + createdAt: DateTime! + createdByUsername: String! + description: String + endsAt: DateTime + icsSequence: Int! + id: String! + matrixRoomId: String! + startsAt: DateTime! + title: String! + updatedAt: DateTime! +} + +type ChatCoopCalendarIcsUrlResponse { + """Полный URL ленты ICS с секретом в query (без JWT)""" + icsUrl: String! +} + +type ChatCoopCalendarRoomOption { + displayLabel: String! + matrixRoomId: String! +} + input CheckMatrixUsernameInput { username: String! } @@ -4147,6 +4184,14 @@ input CreateBranchInput { trustee: String! } +input CreateChatCoopCalendarEventInput { + description: String + endsAt: DateTime + matrixRoomId: String! + startsAt: DateTime! + title: String! +} + input CreateChildOrderInput { """Имя кооператива""" coopname: String! @@ -4614,12 +4659,12 @@ input CreateSovietIndividualDataInput { } input CreateStoryInput { - """Имя аккаунта кооператива""" - coopname: String! - """Формат содержимого; по умолчанию MARKDOWN""" content_format: CapitalStoryContentFormat = MARKDOWN + """Имя аккаунта кооператива""" + coopname: String! + """Описание истории""" description: String @@ -7049,6 +7094,18 @@ type Mutation { """Создать Matrix аккаунт с именем пользователя и паролем""" chatcoopCreateAccount(data: CreateMatrixAccountInputDTO!): Boolean! + """Создать событие календаря""" + chatcoopCreateCalendarEvent(data: CreateChatCoopCalendarEventInput!): ChatCoopCalendarEvent! + + """Выдать или обновить персональный URL подписки ICS (секрет в query)""" + chatcoopCreateCalendarIcsSubscription: ChatCoopCalendarIcsUrlResponse! + + """Удалить событие календаря""" + chatcoopDeleteCalendarEvent(id: String!): Boolean! + + """Обновить событие календаря""" + chatcoopUpdateCalendarEvent(data: UpdateChatCoopCalendarEventInput!): ChatCoopCalendarEvent! + """Выполнить шаг онбординга capital (создание предложения повестки)""" completeCapitalOnboardingStep(data: CapitalOnboardingStepInput!): CapitalOnboardingState! @@ -8855,6 +8912,14 @@ type Query { """Получить список транскрипций звонков""" chatcoopGetTranscriptions(data: GetTranscriptionsInput): [CallTranscription!]! + """Список событий календаря кооператива""" + chatcoopListCalendarEvents: [ChatCoopCalendarEvent!]! + + """ + Незашифрованные комнаты из реестра ChatCoop для привязки события календаря + """ + chatcoopListCalendarRooms: [ChatCoopCalendarRoomOption!]! + """Получить сводную информацию о аккаунте""" getAccount(data: GetAccountInput!): Account! @@ -10265,14 +10330,6 @@ enum StoryStatus { PENDING } -"""Формат содержимого требования (истории) в CAPITAL""" -enum CapitalStoryContentFormat { - BPMN - DRAWIO - MARKDOWN - MERMAID -} - input SubmitVoteInput { """Имя аккаунта кооператива""" coopname: String! @@ -10434,7 +10491,11 @@ type TranscriptionSegment { createdAt: DateTime! endOffset: Float! id: String! + + """Канонический Matrix user id (@localpart:server)""" speakerIdentity: String! + + """Отображаемое имя из Synapse (displayname)""" speakerName: String! startOffset: Float! text: String! @@ -10518,6 +10579,15 @@ input UpdateBankAccountInput { username: String! } +input UpdateChatCoopCalendarEventInput { + description: String + endsAt: DateTime + id: String! + matrixRoomId: String! + startsAt: DateTime! + title: String! +} + input UpdateEntrepreneurDataInput { """Дата рождения""" birthdate: String! @@ -10710,6 +10780,9 @@ input UpdateSettingsInput { } input UpdateStoryInput { + """Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID)""" + content_format: CapitalStoryContentFormat + """Описание истории""" description: String diff --git a/components/controller/src/extensions/capital/application/dto/generation/update-story-input.dto.ts b/components/controller/src/extensions/capital/application/dto/generation/update-story-input.dto.ts index 24eb6fb492a..9659f9d11e9 100644 --- a/components/controller/src/extensions/capital/application/dto/generation/update-story-input.dto.ts +++ b/components/controller/src/extensions/capital/application/dto/generation/update-story-input.dto.ts @@ -1,6 +1,7 @@ import { Field, InputType, Int } from '@nestjs/graphql'; import { IsNotEmpty, IsString, IsOptional, IsEnum, Min } from 'class-validator'; import { StoryStatus } from '../../../domain/enums/story-status.enum'; +import { StoryContentFormat } from '../../../domain/enums/story-content-format.enum'; /** * GraphQL Input DTO для обновления истории @@ -38,6 +39,14 @@ export class UpdateStoryInputDTO { @IsEnum(StoryStatus, { message: 'Неверный статус истории' }) status?: StoryStatus; + @Field(() => StoryContentFormat, { + nullable: true, + description: 'Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID)', + }) + @IsOptional() + @IsEnum(StoryContentFormat, { message: 'Неверный формат содержимого истории' }) + content_format?: StoryContentFormat; + @Field(() => String, { nullable: true, description: 'Хеш проекта (если история привязана к проекту)', diff --git a/components/controller/src/extensions/capital/application/services/generation.service.ts b/components/controller/src/extensions/capital/application/services/generation.service.ts index ca8e4bebf59..1d5f98dc37c 100644 --- a/components/controller/src/extensions/capital/application/services/generation.service.ts +++ b/components/controller/src/extensions/capital/application/services/generation.service.ts @@ -33,7 +33,6 @@ import { StoryStatus } from '../../domain/enums/story-status.enum'; import { StoryContentFormat } from '../../domain/enums/story-content-format.enum'; import { normalizeBpmnStoryDescription } from '../../domain/utils/bpmn-story-description.util'; import { EMPTY_BPMN_STORY_XML } from '../constants/empty-bpmn-story-xml'; -import { EMPTY_DRAWIO_STORY_XML } from '../constants/empty-drawio-story-xml'; import { DEFAULT_MERMAID_STORY_SOURCE } from '../constants/default-mermaid-story'; import { IssuePriority } from '../../domain/enums/issue-priority.enum'; import { IssueStatus } from '../../domain/enums/issue-status.enum'; @@ -401,13 +400,18 @@ export class GenerationService { // Определяем issue_hash с нормализацией const issueHash = data.issue_hash ?? existingStory.issue_hash; + const nextContentFormat = data.content_format ?? existingStory.content_format; + let nextDescription = data.description ?? existingStory.description; - if ( - (existingStory.content_format === StoryContentFormat.BPMN || - existingStory.content_format === StoryContentFormat.DRAWIO) && - data.description !== undefined - ) { - nextDescription = normalizeBpmnStoryDescription(data.description, existingStory.content_format); + if (data.description !== undefined) { + if ( + nextContentFormat === StoryContentFormat.BPMN || + nextContentFormat === StoryContentFormat.DRAWIO + ) { + nextDescription = normalizeBpmnStoryDescription(data.description, nextContentFormat); + } else { + nextDescription = data.description; + } } const nextTitle = data.title ?? existingStory.title; @@ -420,7 +424,7 @@ export class GenerationService { coopname: existingStory.coopname, title: nextTitle, description: nextDescription, - content_format: existingStory.content_format, + content_format: nextContentFormat, status: data.status ?? existingStory.status, project_hash: data.project_hash ?? existingStory.project_hash, // Нормализация: пустая строка или undefined преобразуется в undefined @@ -734,7 +738,6 @@ export class GenerationService { currentUser?.role ); } - console.log('inputData', data) // Создаем обновленные данные для доменной сущности const updatedIssueDatabaseData: IIssueDatabaseData = { _id: existingIssue._id, @@ -758,8 +761,6 @@ export class GenerationService { }, present: existingIssue.present, }; - console.log('updatedIssueDatabaseData', updatedIssueDatabaseData) - // Создаем доменную сущность с обновленными данными const issueEntity = new IssueDomainEntity(updatedIssueDatabaseData); @@ -775,7 +776,7 @@ export class GenerationService { // Сохраняем через репозиторий const updatedIssue = await this.issueRepository.update(issueEntity); - console.log('updatedIssue', updatedIssue) + // Рассчитываем права доступа для задачи const permissions = await this.permissionsService.calculateIssuePermissions(updatedIssue, currentUser); diff --git a/components/controller/src/extensions/capital/application/services/github-sync.service.ts b/components/controller/src/extensions/capital/application/services/github-sync.service.ts index 57ee556e89c..8e9ae53e2e4 100644 --- a/components/controller/src/extensions/capital/application/services/github-sync.service.ts +++ b/components/controller/src/extensions/capital/application/services/github-sync.service.ts @@ -365,13 +365,16 @@ export class GitHubSyncService { await this.githubService.deleteFile(this.owner, this.repo, oldPath, `Rename: remove old file ${oldPath}`, oldSha); } - // Создаём новый файл + // Если по целевому пути файл уже есть (другой поток синка, ручной коммит), нужен blob-sha для обновления + const newPathExistingSha = await this.githubService.getFileSha(this.owner, this.repo, newPath); + const newSha = await this.githubService.createOrUpdateFile( this.owner, this.repo, newPath, newContent, - `Rename: create new file ${newPath}` + `Rename: create new file ${newPath}`, + newPathExistingSha || undefined ); // Обновляем индекс @@ -477,7 +480,6 @@ export class GitHubSyncService { if (changedFiles.length === 0) { this.logger.debug('Нет изменённых файлов для синхронизации'); - // Обновляем SHA как последний синхронизированный await this.fileIndexRepository.upsert({ coopname: this.coopname, entity_type: 'project', @@ -517,7 +519,6 @@ export class GitHubSyncService { if (mdFiles.length === 0) { this.logger.log('В репозитории нет файлов проектов для синхронизации'); - // Сохраняем SHA как последний синхронизированный, чтобы не повторять попытки await this.fileIndexRepository.upsert({ coopname: this.coopname, entity_type: 'project', @@ -549,7 +550,6 @@ export class GitHubSyncService { } catch (error: any) { this.logger.error(`Ошибка первичной синхронизации: ${error.message}`); - // Если репозиторий пустой, просто сохраняем маркер синхронизации await this.fileIndexRepository.upsert({ coopname: this.coopname, entity_type: 'project', @@ -667,6 +667,15 @@ export class GitHubSyncService { const existing = await this.projectRepository.findByHash(projectData.hash); if (existing) { + const titleMatches = (projectData.title || '') === (existing.title || ''); + const descMatches = (projectData.description || '') === (existing.description || ''); + if (titleMatches && descMatches) { + this.logger.debug( + `Проект ${projectData.hash}: title и description совпадают с БД — пропуск обновления из GitHub` + ); + return; + } + // Проверяем, нужно ли обновление (по дате) const githubUpdatedAt = projectData.updated_at ? new Date(projectData.updated_at) : null; const dbUpdatedAt = existing._updated_at ? new Date(existing._updated_at) : null; @@ -740,6 +749,8 @@ export class GitHubSyncService { labels: issueData.labels, submaster: issueData.submaster, creators: issueData.creators, + sort_order: issueData.sort_order, + cycle_id: issueData.cycle_id, }, chairman.username, chairman @@ -754,8 +765,13 @@ export class GitHubSyncService { title: issueData.title, description: issueData.description, priority: issueData.priority as any, + status: issueData.status as any, estimate: issueData.estimate, labels: issueData.labels, + submaster: issueData.submaster, + sort_order: issueData.sort_order, + cycle_id: issueData.cycle_id, + ...(issueData.creators?.length ? { creators: issueData.creators } : {}), }, chairman.username, chairman @@ -792,8 +808,13 @@ export class GitHubSyncService { title: storyData.title, description: storyData.description, status: storyData.status as any, + content_format: storyData.content_format, + project_hash: storyData.project_hash, + issue_hash: storyData.issue_hash, + sort_order: storyData.sort_order, }, - chairman.username + chairman.username, + chairman ); } else { // Создаём новое требование @@ -806,6 +827,9 @@ export class GitHubSyncService { issue_hash: storyData.issue_hash, title: storyData.title, description: storyData.description, + content_format: storyData.content_format, + status: storyData.status as any, + sort_order: storyData.sort_order, }, chairman ); diff --git a/components/controller/src/extensions/capital/domain/services/file-format.service.ts b/components/controller/src/extensions/capital/domain/services/file-format.service.ts index dd57cfc3db1..fa87f228556 100644 --- a/components/controller/src/extensions/capital/domain/services/file-format.service.ts +++ b/components/controller/src/extensions/capital/domain/services/file-format.service.ts @@ -5,6 +5,7 @@ import type { IssueDomainEntity } from '../entities/issue.entity'; import type { StoryDomainEntity } from '../entities/story.entity'; import type { ResultDomainEntity } from '../entities/result.entity'; import type { SegmentDomainEntity } from '../entities/segment.entity'; +import { StoryContentFormat } from '../enums/story-content-format.enum'; /** * Интерфейс для результата парсинга markdown @@ -34,7 +35,6 @@ export interface ProjectMarkdownData { export interface IssueMarkdownData { type: 'issue'; title: string; - id: string; hash: string; project_hash: string; cycle_id?: string; @@ -58,6 +58,7 @@ export interface StoryMarkdownData { type: 'story'; title: string; hash: string; + content_format: StoryContentFormat; project_hash?: string; issue_hash?: string; status: string; @@ -198,6 +199,21 @@ export class FileFormatService { return /[а-яё]/i.test(text); } + /** + * Нормализует content_format из frontmatter; неизвестные значения → MARKDOWN. + */ + parseStoryContentFormat(raw: unknown): StoryContentFormat { + if (raw === undefined || raw === null || raw === '') { + return StoryContentFormat.MARKDOWN; + } + const s = String(raw).trim(); + const allowed = Object.values(StoryContentFormat) as string[]; + if (allowed.includes(s)) { + return s as StoryContentFormat; + } + return StoryContentFormat.MARKDOWN; + } + /** * Генерирует slug из заголовка * Для русских названий делает транслитерацию в английский @@ -276,7 +292,6 @@ export class FileFormatService { const frontmatter: Record = { type: 'issue', title: issue.title, - id: issue.id, hash: issue.issue_hash, project_hash: issue.project_hash, status: issue.status, @@ -316,17 +331,22 @@ export class FileFormatService { markdownToIssue(content: string): IssueMarkdownData { const { frontmatter, body } = this.parseMarkdownFile(content); + const estimateRaw = frontmatter.estimate; + const estimate = + estimateRaw === undefined || estimateRaw === null || estimateRaw === '' + ? 0 + : Number(estimateRaw); + return { type: 'issue', title: frontmatter.title, - id: frontmatter.id, hash: frontmatter.hash, project_hash: frontmatter.project_hash, cycle_id: frontmatter.cycle_id, - status: frontmatter.status, - priority: frontmatter.priority, - estimate: frontmatter.estimate, - created_by: frontmatter.created_by, + status: frontmatter.status ?? 'backlog', + priority: frontmatter.priority ?? 'medium', + estimate: Number.isFinite(estimate) ? estimate : 0, + created_by: frontmatter.created_by ?? '', submaster: frontmatter.submaster, creators: frontmatter.creators || [], labels: frontmatter.labels || [], @@ -347,6 +367,7 @@ export class FileFormatService { type: 'story', title: story.title, hash: story.story_hash, + content_format: story.content_format, status: story.status, created_by: story.created_by, sort_order: story.sort_order, @@ -384,9 +405,10 @@ export class FileFormatService { type: 'story', title: frontmatter.title, hash: frontmatter.hash, + content_format: this.parseStoryContentFormat(frontmatter.content_format), project_hash: frontmatter.project_hash, issue_hash: frontmatter.issue_hash, - status: frontmatter.status, + status: frontmatter.status ?? 'pending', created_by: frontmatter.created_by, description: body, created_at: frontmatter.created_at, diff --git a/components/controller/src/extensions/capital/infrastructure/services/github-sync-scheduler.service.ts b/components/controller/src/extensions/capital/infrastructure/services/github-sync-scheduler.service.ts index 40647cf67a1..53c3b89d94c 100644 --- a/components/controller/src/extensions/capital/infrastructure/services/github-sync-scheduler.service.ts +++ b/components/controller/src/extensions/capital/infrastructure/services/github-sync-scheduler.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger, OnModuleInit, OnModuleDestroy, Inject } from '@nestjs/common' +import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common' import { GitHubSyncService } from '../../application/services/github-sync.service' import * as cron from 'node-cron' import { config } from '~/config' diff --git a/components/controller/src/extensions/capital/infrastructure/services/github.service.ts b/components/controller/src/extensions/capital/infrastructure/services/github.service.ts index 2455976490d..12f835a7837 100644 --- a/components/controller/src/extensions/capital/infrastructure/services/github.service.ts +++ b/components/controller/src/extensions/capital/infrastructure/services/github.service.ts @@ -230,7 +230,15 @@ export class GitHubService { if (error.status === 409 && retryOnConflict) { const freshSha = await this.getFileSha(owner, repo, path, branch); if (freshSha) { - // Рекурсивный вызов с свежим SHA, но без повторных попыток + return await this.createOrUpdateFile(owner, repo, path, content, message, freshSha, branch, false); + } + } + + // 422: файл уже существует, а sha не передали — подставляем актуальный blob-sha и повторяем + const msg = String(error?.message ?? ''); + if (error.status === 422 && retryOnConflict && !sha && msg.includes('sha') && msg.includes("wasn't supplied")) { + const freshSha = await this.getFileSha(owner, repo, path, branch); + if (freshSha) { return await this.createOrUpdateFile(owner, repo, path, content, message, freshSha, branch, false); } } diff --git a/components/controller/src/extensions/chatcoop/application/controllers/chatcoop-calendar-feed.controller.ts b/components/controller/src/extensions/chatcoop/application/controllers/chatcoop-calendar-feed.controller.ts new file mode 100644 index 00000000000..e72cbf73811 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/application/controllers/chatcoop-calendar-feed.controller.ts @@ -0,0 +1,42 @@ +import { Controller, Get, Query, Res, Logger } from '@nestjs/common'; +import type { Response } from 'express'; +import { ChatCoopCalendarApplicationService } from '../services/chatcoop-calendar-application.service'; + +/** + * Публичная лента ICS по секрету в query (без JWT). Google Calendar и др. клиенты опрашивают URL напрямую. + */ +@Controller('v1/extensions/chatcoop/calendar') +export class ChatCoopCalendarFeedController { + private readonly logger = new Logger(ChatCoopCalendarFeedController.name); + + constructor(private readonly calendar: ChatCoopCalendarApplicationService) {} + + @Get('feed.ics') + async feed( + @Query('id') subscriptionId: string, + @Query('secret') secret: string, + @Res() res: Response + ): Promise { + if (typeof subscriptionId !== 'string' || subscriptionId.length === 0) { + res.status(400).send('Missing id'); + return; + } + if (typeof secret !== 'string' || secret.length === 0) { + res.status(400).send('Missing secret'); + return; + } + try { + const body = await this.calendar.buildIcsDocumentForSubscription(subscriptionId, secret); + if (!body) { + res.status(404).send('Not found'); + return; + } + res.setHeader('Content-Type', 'text/calendar; charset=utf-8'); + res.setHeader('Cache-Control', 'private, max-age=300'); + res.status(200).send(body); + } catch (e) { + this.logger.warn(`ICS feed error: ${String(e)}`); + res.status(500).send('Internal error'); + } + } +} diff --git a/components/controller/src/extensions/chatcoop/application/dto/calendar.dto.ts b/components/controller/src/extensions/chatcoop/application/dto/calendar.dto.ts new file mode 100644 index 00000000000..af588fddc21 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/application/dto/calendar.dto.ts @@ -0,0 +1,115 @@ +import { Field, GraphQLISODateTime, InputType, Int, ObjectType } from '@nestjs/graphql'; +import { IsOptional, IsString, IsUUID, MaxLength, MinLength } from 'class-validator'; +import { Type } from 'class-transformer'; + +@ObjectType('ChatCoopCalendarRoomOption') +export class ChatCoopCalendarRoomOptionDTO { + @Field() + matrixRoomId!: string; + + @Field() + displayLabel!: string; +} + +@ObjectType('ChatCoopCalendarEvent') +export class ChatCoopCalendarEventDTO { + @Field() + id!: string; + + @Field() + matrixRoomId!: string; + + @Field() + title!: string; + + @Field(() => String, { nullable: true }) + description!: string | null; + + @Field(() => GraphQLISODateTime) + startsAt!: Date; + + @Field(() => GraphQLISODateTime, { nullable: true }) + endsAt!: Date | null; + + @Field() + createdByUsername!: string; + + @Field(() => Int) + icsSequence!: number; + + @Field(() => GraphQLISODateTime) + createdAt!: Date; + + @Field(() => GraphQLISODateTime) + updatedAt!: Date; +} + +@ObjectType('ChatCoopCalendarIcsUrlResponse') +export class ChatCoopCalendarIcsUrlResponseDTO { + @Field({ description: 'Полный URL ленты ICS с секретом в query (без JWT)' }) + icsUrl!: string; +} + +@InputType('CreateChatCoopCalendarEventInput') +export class CreateChatCoopCalendarEventInputDTO { + @Field() + @IsString() + @MinLength(1) + @MaxLength(255) + matrixRoomId!: string; + + @Field() + @IsString() + @MinLength(1) + @MaxLength(500) + title!: string; + + @Field(() => String, { nullable: true }) + @IsOptional() + @IsString() + @MaxLength(8000) + description?: string | null; + + @Field(() => GraphQLISODateTime) + @Type(() => Date) + startsAt!: Date; + + @Field(() => GraphQLISODateTime, { nullable: true }) + @IsOptional() + @Type(() => Date) + endsAt?: Date | null; +} + +@InputType('UpdateChatCoopCalendarEventInput') +export class UpdateChatCoopCalendarEventInputDTO { + @Field() + @IsUUID('4') + id!: string; + + @Field() + @IsString() + @MinLength(1) + @MaxLength(255) + matrixRoomId!: string; + + @Field() + @IsString() + @MinLength(1) + @MaxLength(500) + title!: string; + + @Field(() => String, { nullable: true }) + @IsOptional() + @IsString() + @MaxLength(8000) + description?: string | null; + + @Field(() => GraphQLISODateTime) + @Type(() => Date) + startsAt!: Date; + + @Field(() => GraphQLISODateTime, { nullable: true }) + @IsOptional() + @Type(() => Date) + endsAt?: Date | null; +} diff --git a/components/controller/src/extensions/chatcoop/application/resolvers/chatcoop-calendar.resolver.ts b/components/controller/src/extensions/chatcoop/application/resolvers/chatcoop-calendar.resolver.ts new file mode 100644 index 00000000000..3059f102b25 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/application/resolvers/chatcoop-calendar.resolver.ts @@ -0,0 +1,139 @@ +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; +import { UseGuards } from '@nestjs/common'; +import { GqlJwtAuthGuard } from '~/application/auth/guards/graphql-jwt-auth.guard'; +import { CurrentUser } from '~/application/auth/decorators/current-user.decorator'; +import type { MonoAccountDomainInterface } from '~/domain/account/interfaces/mono-account-domain.interface'; +import { AuthRoles } from '~/application/auth/decorators/auth.decorator'; +import { RolesGuard } from '~/application/auth/guards/roles.guard'; +import { ChatCoopCalendarApplicationService } from '../services/chatcoop-calendar-application.service'; +import { + ChatCoopCalendarEventDTO, + ChatCoopCalendarIcsUrlResponseDTO, + ChatCoopCalendarRoomOptionDTO, + CreateChatCoopCalendarEventInputDTO, + UpdateChatCoopCalendarEventInputDTO, +} from '../dto/calendar.dto'; + +function toEventDto(e: { + id: string; + matrixRoomId: string; + title: string; + description: string | null; + startsAt: Date; + endsAt: Date | null; + createdByUsername: string; + icsSequence: number; + createdAt: Date; + updatedAt: Date; +}): ChatCoopCalendarEventDTO { + const dto = new ChatCoopCalendarEventDTO(); + dto.id = e.id; + dto.matrixRoomId = e.matrixRoomId; + dto.title = e.title; + dto.description = e.description; + dto.startsAt = e.startsAt; + dto.endsAt = e.endsAt; + dto.createdByUsername = e.createdByUsername; + dto.icsSequence = e.icsSequence; + dto.createdAt = e.createdAt; + dto.updatedAt = e.updatedAt; + return dto; +} + +@Resolver() +export class ChatCoopCalendarResolver { + constructor(private readonly calendar: ChatCoopCalendarApplicationService) {} + + @Query(() => [ChatCoopCalendarRoomOptionDTO], { + name: 'chatcoopListCalendarRooms', + description: 'Незашифрованные комнаты из реестра ChatCoop для привязки события календаря', + }) + @UseGuards(GqlJwtAuthGuard, RolesGuard) + @AuthRoles(['chairman', 'member']) + async listRooms(): Promise { + const rows = await this.calendar.listPlaintextRoomsForPicker(); + return rows.map((r) => { + const dto = new ChatCoopCalendarRoomOptionDTO(); + dto.matrixRoomId = r.matrixRoomId; + dto.displayLabel = r.displayLabel; + return dto; + }); + } + + @Query(() => [ChatCoopCalendarEventDTO], { + name: 'chatcoopListCalendarEvents', + description: 'Список событий календаря кооператива', + }) + @UseGuards(GqlJwtAuthGuard, RolesGuard) + @AuthRoles(['chairman', 'member', 'user']) + async listEvents(): Promise { + const list = await this.calendar.listEvents(); + return list.map(toEventDto); + } + + @Mutation(() => ChatCoopCalendarEventDTO, { + name: 'chatcoopCreateCalendarEvent', + description: 'Создать событие календаря', + }) + @UseGuards(GqlJwtAuthGuard, RolesGuard) + @AuthRoles(['chairman', 'member']) + async create( + @CurrentUser() user: MonoAccountDomainInterface, + @Args('data', { type: () => CreateChatCoopCalendarEventInputDTO }) data: CreateChatCoopCalendarEventInputDTO + ): Promise { + const ev = await this.calendar.createEvent(user.username, { + matrixRoomId: data.matrixRoomId, + title: data.title, + description: data.description ?? null, + startsAt: data.startsAt, + endsAt: data.endsAt ?? null, + }); + return toEventDto(ev); + } + + @Mutation(() => ChatCoopCalendarEventDTO, { + name: 'chatcoopUpdateCalendarEvent', + description: 'Обновить событие календаря', + }) + @UseGuards(GqlJwtAuthGuard, RolesGuard) + @AuthRoles(['chairman', 'member']) + async update( + @Args('data', { type: () => UpdateChatCoopCalendarEventInputDTO }) data: UpdateChatCoopCalendarEventInputDTO + ): Promise { + const ev = await this.calendar.updateEvent({ + id: data.id, + matrixRoomId: data.matrixRoomId, + title: data.title, + description: data.description ?? null, + startsAt: data.startsAt, + endsAt: data.endsAt ?? null, + }); + return toEventDto(ev); + } + + @Mutation(() => Boolean, { + name: 'chatcoopDeleteCalendarEvent', + description: 'Удалить событие календаря', + }) + @UseGuards(GqlJwtAuthGuard, RolesGuard) + @AuthRoles(['chairman', 'member']) + async delete(@Args('id') id: string): Promise { + await this.calendar.deleteEvent(id); + return true; + } + + @Mutation(() => ChatCoopCalendarIcsUrlResponseDTO, { + name: 'chatcoopCreateCalendarIcsSubscription', + description: 'Выдать или обновить персональный URL подписки ICS (секрет в query)', + }) + @UseGuards(GqlJwtAuthGuard, RolesGuard) + @AuthRoles(['chairman', 'member', 'user']) + async createIcs( + @CurrentUser() user: MonoAccountDomainInterface + ): Promise { + const url = await this.calendar.createOrRotatePersonalIcsUrl(user.username); + const dto = new ChatCoopCalendarIcsUrlResponseDTO(); + dto.icsUrl = url; + return dto; + } +} diff --git a/components/controller/src/extensions/chatcoop/application/services/chatcoop-calendar-application.service.ts b/components/controller/src/extensions/chatcoop/application/services/chatcoop-calendar-application.service.ts new file mode 100644 index 00000000000..9228f3908dd --- /dev/null +++ b/components/controller/src/extensions/chatcoop/application/services/chatcoop-calendar-application.service.ts @@ -0,0 +1,217 @@ +import { Inject, Injectable } from '@nestjs/common'; +import * as crypto from 'crypto'; +import config from '~/config/config'; +import type { + InterCalendarEventWindow, + InterCoopCalendarEventRead, +} from '@coopenomics/inter'; +import type { ChatcoopManagedMatrixRoomRepository } from '../../domain/repositories/managed-matrix-room.repository'; +import { CHATCOOP_MANAGED_MATRIX_ROOM_REPOSITORY } from '../../domain/repositories/managed-matrix-room.repository'; +import type { ChatCoopCalendarEventRepository } from '../../domain/repositories/calendar-event.repository'; +import { CHATCOOP_CALENDAR_EVENT_REPOSITORY } from '../../domain/repositories/calendar-event.repository'; +import type { ChatCoopCalendarIcsSubscriptionRepository } from '../../domain/repositories/calendar-ics-subscription.repository'; +import { CHATCOOP_CALENDAR_ICS_SUBSCRIPTION_REPOSITORY } from '../../domain/repositories/calendar-ics-subscription.repository'; +import type { ChatCoopCalendarEventDomainEntity } from '../../domain/entities/calendar-event.entity'; + +function sha256Hex(plain: string): string { + return crypto.createHash('sha256').update(plain, 'utf8').digest('hex'); +} + +function timingSafeEqualHex(a: string, b: string): boolean { + try { + const ba = Buffer.from(a, 'hex'); + const bb = Buffer.from(b, 'hex'); + if (ba.length !== bb.length) { + return false; + } + return crypto.timingSafeEqual(ba, bb); + } catch { + return false; + } +} + +/** Форматирование даты для ICS в UTC (базовый формат DTSTART/DTEND). */ +function formatIcsUtc(d: Date): string { + const y = d.getUTCFullYear(); + const m = String(d.getUTCMonth() + 1).padStart(2, '0'); + const day = String(d.getUTCDate()).padStart(2, '0'); + const h = String(d.getUTCHours()).padStart(2, '0'); + const min = String(d.getUTCMinutes()).padStart(2, '0'); + const s = String(d.getUTCSeconds()).padStart(2, '0'); + return `${y}${m}${day}T${h}${min}${s}Z`; +} + +function escapeIcsText(text: string): string { + return text + .replace(/\\/g, '\\\\') + .replace(/\n/g, '\\n') + .replace(/;/g, '\\;') + .replace(/,/g, '\\,'); +} + +function foldIcsLine(line: string): string { + const max = 75; + if (line.length <= max) { + return line; + } + const parts: string[] = []; + let rest = line; + parts.push(rest.slice(0, max)); + rest = rest.slice(max); + while (rest.length > 0) { + const chunk = rest.slice(0, max - 1); + parts.push(` ${chunk}`); + rest = rest.slice(max - 1); + } + return parts.join('\r\n'); +} + +@Injectable() +export class ChatCoopCalendarApplicationService { + constructor( + @Inject(CHATCOOP_MANAGED_MATRIX_ROOM_REPOSITORY) + private readonly managedRooms: ChatcoopManagedMatrixRoomRepository, + @Inject(CHATCOOP_CALENDAR_EVENT_REPOSITORY) + private readonly events: ChatCoopCalendarEventRepository, + @Inject(CHATCOOP_CALENDAR_ICS_SUBSCRIPTION_REPOSITORY) + private readonly icsSubs: ChatCoopCalendarIcsSubscriptionRepository + ) {} + + async listPlaintextRoomsForPicker(): Promise<{ matrixRoomId: string; displayLabel: string }[]> { + const rows = await this.managedRooms.findEligibleForSecretaryTranscription(); + return rows.map((r) => ({ matrixRoomId: r.matrixRoomId, displayLabel: r.displayLabel })); + } + + async listEvents(): Promise { + return this.events.listAll(); + } + + async assertPlaintextManagedRoom(matrixRoomId: string): Promise { + const room = await this.managedRooms.findByMatrixRoomId(matrixRoomId); + if (!room || room.encrypted) { + throw new Error('Комната не найдена в реестре или недоступна для календаря (только незашифрованные комнаты)'); + } + } + + async createEvent( + coopUsername: string, + input: { + matrixRoomId: string; + title: string; + description: string | null; + startsAt: Date; + endsAt: Date | null; + } + ): Promise { + await this.assertPlaintextManagedRoom(input.matrixRoomId); + return this.events.create({ + matrixRoomId: input.matrixRoomId, + title: input.title, + description: input.description, + startsAt: input.startsAt, + endsAt: input.endsAt, + createdByUsername: coopUsername.toLowerCase(), + }); + } + + async updateEvent(input: { + id: string; + matrixRoomId: string; + title: string; + description: string | null; + startsAt: Date; + endsAt: Date | null; + }): Promise { + await this.assertPlaintextManagedRoom(input.matrixRoomId); + return this.events.update({ + id: input.id, + matrixRoomId: input.matrixRoomId, + title: input.title, + description: input.description, + startsAt: input.startsAt, + endsAt: input.endsAt, + }); + } + + async deleteEvent(id: string): Promise { + await this.events.deleteById(id); + } + + /** + * Создаёт или обновляет секрет подписки и возвращает полный URL ленты ICS (без JWT). + */ + async createOrRotatePersonalIcsUrl(coopUsername: string): Promise { + const rawSecret = crypto.randomBytes(32).toString('hex'); + const hash = sha256Hex(rawSecret); + const sub = await this.icsSubs.rotateSecretForUser(coopUsername, hash); + const base = config.base_url.replace(/\/$/, ''); + return `${base}/v1/extensions/chatcoop/calendar/feed.ics?id=${encodeURIComponent(sub.id)}&secret=${encodeURIComponent(rawSecret)}`; + } + + async buildIcsDocumentForSubscription(subscriptionId: string, rawSecret: string): Promise { + const sub = await this.icsSubs.findById(subscriptionId); + if (!sub) { + return null; + } + if (!timingSafeEqualHex(sub.secretSha256Hex, sha256Hex(rawSecret))) { + return null; + } + const all = await this.events.listAll(); + const coopname = config.coopname; + const baseUrl = config.base_url.replace(/\/$/, ''); + const lines: string[] = [ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//Coopenomics//ChatCoop//RU', + 'CALSCALE:GREGORIAN', + 'METHOD:PUBLISH', + ]; + const now = new Date(); + const stamp = formatIcsUtc(now); + for (const ev of all) { + const room = await this.managedRooms.findByMatrixRoomId(ev.matrixRoomId); + const roomLabel = room?.displayLabel ?? ev.matrixRoomId; + const deepLink = `${baseUrl}/#/${coopname}/chatcoop/chat?matrix_room=${encodeURIComponent(ev.matrixRoomId)}`; + const descParts = [ + ev.description ? escapeIcsText(ev.description) : '', + escapeIcsText(`Комната: ${roomLabel}`), + escapeIcsText(`Стол связи: ${deepLink}`), + ].filter((p) => p.length > 0); + const description = descParts.join('\\n\\n'); + const end = ev.endsAt ?? new Date(ev.startsAt.getTime() + 60 * 60 * 1000); + lines.push('BEGIN:VEVENT'); + lines.push(foldIcsLine(`UID:chatcoop-calendar-${ev.id}@${coopname}`)); + lines.push(`DTSTAMP:${stamp}`); + lines.push(`DTSTART:${formatIcsUtc(ev.startsAt)}`); + lines.push(`DTEND:${formatIcsUtc(end)}`); + lines.push(`SEQUENCE:${ev.icsSequence}`); + lines.push(foldIcsLine(`SUMMARY:${escapeIcsText(ev.title)}`)); + lines.push(foldIcsLine(`DESCRIPTION:${description}`)); + lines.push('END:VEVENT'); + } + lines.push('END:VCALENDAR'); + return `${lines.join('\r\n')}\r\n`; + } + + async listEventsForInterPort( + projectHash: string, + window?: InterCalendarEventWindow + ): Promise { + const w = + window !== undefined + ? { from: new Date(window.fromInclusive), to: new Date(window.toExclusive) } + : undefined; + const list = await this.events.listByManagedRoomProjectHashes([projectHash], w); + return list.map((ev) => ({ + id: ev.id, + matrixRoomId: ev.matrixRoomId, + projectHash, + title: ev.title, + description: ev.description, + startsAtIso: ev.startsAt.toISOString(), + endsAtIso: ev.endsAt ? ev.endsAt.toISOString() : null, + createdByUsername: ev.createdByUsername, + icsSequence: ev.icsSequence, + })); + } +} diff --git a/components/controller/src/extensions/chatcoop/chatcoop-extension.module.ts b/components/controller/src/extensions/chatcoop/chatcoop-extension.module.ts index 9e181785759..977a00f934a 100644 --- a/components/controller/src/extensions/chatcoop/chatcoop-extension.module.ts +++ b/components/controller/src/extensions/chatcoop/chatcoop-extension.module.ts @@ -8,6 +8,8 @@ import { UnionChatService } from './domain/services/union-chat.service'; import { UnionChatTypeormRepository } from './infrastructure/repositories/union-chat.typeorm-repository'; import { UNION_CHAT_REPOSITORY } from './domain/repositories/union-chat.repository'; import { ChatCoopResolver } from './application/resolvers/chatcoop.resolver'; +import { ChatCoopCalendarResolver } from './application/resolvers/chatcoop-calendar.resolver'; +import { ChatCoopCalendarFeedController } from './application/controllers/chatcoop-calendar-feed.controller'; import { TranscriptionResolver } from './application/resolvers/transcription.resolver'; import { WinstonLoggerService } from '~/application/logger/logger-app.service'; import { ConfigModule } from '@nestjs/config'; @@ -44,6 +46,12 @@ import { ChatCoopSecretaryMatrixTokenService } from './application/services/chat import { MatrixRoomMessageHistoryCronService } from './application/services/matrix-room-message-history-cron.service'; import { ChatcoopInterProjectCommunicationArtifactsAdapter } from './infrastructure/inter/chatcoop-inter-project-communication-artifacts.adapter'; import { ChatcoopInterMatrixRoomMessagingAdapter } from './infrastructure/inter/chatcoop-inter-matrix-room-messaging.adapter'; +import { ChatcoopInterChatCoopCalendarAdapter } from './infrastructure/inter/chatcoop-inter-chatcoop-calendar.adapter'; +import { ChatCoopCalendarApplicationService } from './application/services/chatcoop-calendar-application.service'; +import { CalendarEventTypeormRepository } from './infrastructure/repositories/calendar-event.typeorm-repository'; +import { CalendarIcsSubscriptionTypeormRepository } from './infrastructure/repositories/calendar-ics-subscription.typeorm-repository'; +import { CHATCOOP_CALENDAR_EVENT_REPOSITORY } from './domain/repositories/calendar-event.repository'; +import { CHATCOOP_CALENDAR_ICS_SUBSCRIPTION_REPOSITORY } from './domain/repositories/calendar-ics-subscription.repository'; import { CHATCOOP_STATE_REPOSITORY } from './domain/repositories/chatcoop-state.repository'; import type { ChatcoopStateRepository } from './domain/repositories/chatcoop-state.repository'; import { ChatcoopStateTypeormRepository } from './infrastructure/repositories/chatcoop-state.typeorm-repository'; @@ -503,8 +511,8 @@ export class ChatCoopPlugin extends BaseExtModule { AccountInfrastructureModule, ], controllers: [ - // REST контроллер для LiveKit webhook LiveKitWebhookController, + ChatCoopCalendarFeedController, ], providers: [ // Plugin @@ -521,6 +529,8 @@ export class ChatCoopPlugin extends BaseExtModule { MatrixRoomMessageHistoryCronService, ChatcoopInterProjectCommunicationArtifactsAdapter, ChatcoopInterMatrixRoomMessagingAdapter, + ChatcoopInterChatCoopCalendarAdapter, + ChatCoopCalendarApplicationService, // Repositories { @@ -568,9 +578,18 @@ export class ChatCoopPlugin extends BaseExtModule { provide: ROOM_MESSAGE_HISTORY_REPOSITORY, useClass: RoomMessageHistoryTypeormRepository, }, + { + provide: CHATCOOP_CALENDAR_EVENT_REPOSITORY, + useClass: CalendarEventTypeormRepository, + }, + { + provide: CHATCOOP_CALENDAR_ICS_SUBSCRIPTION_REPOSITORY, + useClass: CalendarIcsSubscriptionTypeormRepository, + }, // GraphQL Resolvers ChatCoopResolver, + ChatCoopCalendarResolver, TranscriptionResolver, ], exports: [ @@ -578,6 +597,7 @@ export class ChatCoopPlugin extends BaseExtModule { ChatCoopApplicationService, ChatcoopInterProjectCommunicationArtifactsAdapter, ChatcoopInterMatrixRoomMessagingAdapter, + ChatcoopInterChatCoopCalendarAdapter, ], }) export class ChatCoopPluginModule { diff --git a/components/controller/src/extensions/chatcoop/domain/entities/calendar-event.entity.ts b/components/controller/src/extensions/chatcoop/domain/entities/calendar-event.entity.ts new file mode 100644 index 00000000000..632965c0ea8 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/domain/entities/calendar-event.entity.ts @@ -0,0 +1,32 @@ +/** Доменная сущность события календаря ChatCoop (кооперативный календарь в незашифрованных Matrix-комнатах). */ +export interface ChatCoopCalendarEventDomainEntity { + id: string; + matrixRoomId: string; + title: string; + description: string | null; + startsAt: Date; + endsAt: Date | null; + createdByUsername: string; + /** Для поля SEQUENCE в ICS при обновлениях */ + icsSequence: number; + createdAt: Date; + updatedAt: Date; +} + +export interface CreateChatCoopCalendarEventDomainInput { + matrixRoomId: string; + title: string; + description: string | null; + startsAt: Date; + endsAt: Date | null; + createdByUsername: string; +} + +export interface UpdateChatCoopCalendarEventDomainInput { + id: string; + matrixRoomId: string; + title: string; + description: string | null; + startsAt: Date; + endsAt: Date | null; +} diff --git a/components/controller/src/extensions/chatcoop/domain/repositories/calendar-event.repository.ts b/components/controller/src/extensions/chatcoop/domain/repositories/calendar-event.repository.ts new file mode 100644 index 00000000000..5c706356758 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/domain/repositories/calendar-event.repository.ts @@ -0,0 +1,20 @@ +import type { + ChatCoopCalendarEventDomainEntity, + CreateChatCoopCalendarEventDomainInput, + UpdateChatCoopCalendarEventDomainInput, +} from '../entities/calendar-event.entity'; + +export interface ChatCoopCalendarEventRepository { + create(input: CreateChatCoopCalendarEventDomainInput): Promise; + update(input: UpdateChatCoopCalendarEventDomainInput): Promise; + deleteById(id: string): Promise; + findById(id: string): Promise; + listAll(): Promise; + /** События в комнатах с заданным project_hash в реестре управляемых комнат */ + listByManagedRoomProjectHashes( + projectHashes: string[], + window?: { from: Date; to: Date } + ): Promise; +} + +export const CHATCOOP_CALENDAR_EVENT_REPOSITORY = Symbol('ChatCoopCalendarEventRepository'); diff --git a/components/controller/src/extensions/chatcoop/domain/repositories/calendar-ics-subscription.repository.ts b/components/controller/src/extensions/chatcoop/domain/repositories/calendar-ics-subscription.repository.ts new file mode 100644 index 00000000000..0b6c8b566c7 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/domain/repositories/calendar-ics-subscription.repository.ts @@ -0,0 +1,17 @@ +/** Подписка ICS: один секрет на пользователя кооператива (username пайщика). */ +export interface ChatCoopCalendarIcsSubscriptionDomainEntity { + id: string; + coopUsername: string; + secretSha256Hex: string; + createdAt: Date; +} + +export interface ChatCoopCalendarIcsSubscriptionRepository { + create(coopUsername: string, secretSha256Hex: string): Promise; + findById(id: string): Promise; + /** Заменить секрет существующей подписки пользователя (ротация URL). */ + rotateSecretForUser(coopUsername: string, secretSha256Hex: string): Promise; + findByUsername(coopUsername: string): Promise; +} + +export const CHATCOOP_CALENDAR_ICS_SUBSCRIPTION_REPOSITORY = Symbol('ChatCoopCalendarIcsSubscriptionRepository'); diff --git a/components/controller/src/extensions/chatcoop/infrastructure/database/chatcoop-database.module.ts b/components/controller/src/extensions/chatcoop/infrastructure/database/chatcoop-database.module.ts index f37973a8401..72c37ec6102 100644 --- a/components/controller/src/extensions/chatcoop/infrastructure/database/chatcoop-database.module.ts +++ b/components/controller/src/extensions/chatcoop/infrastructure/database/chatcoop-database.module.ts @@ -8,6 +8,8 @@ import { TranscriptionSegmentTypeormEntity } from '../entities/transcription-seg import { ManagedMatrixRoomTypeormEntity } from '../entities/managed-matrix-room.typeorm-entity'; import { RoomMessageHistoryTypeormEntity } from '../entities/room-message-history.typeorm-entity'; import { ChatcoopStateTypeormEntity } from '../entities/chatcoop-state.typeorm-entity'; +import { CalendarEventTypeormEntity } from '../entities/calendar-event.typeorm-entity'; +import { CalendarIcsSubscriptionTypeormEntity } from '../entities/calendar-ics-subscription.typeorm-entity'; // Все TypeORM-сущности расширения chatcoop const CHATCOOP_ENTITIES = [ @@ -19,6 +21,8 @@ const CHATCOOP_ENTITIES = [ ManagedMatrixRoomTypeormEntity, RoomMessageHistoryTypeormEntity, ChatcoopStateTypeormEntity, + CalendarEventTypeormEntity, + CalendarIcsSubscriptionTypeormEntity, ]; @Module({ diff --git a/components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-event.typeorm-entity.ts b/components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-event.typeorm-entity.ts new file mode 100644 index 00000000000..349ba6ef2a2 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-event.typeorm-entity.ts @@ -0,0 +1,36 @@ +import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, Index } from 'typeorm'; + +@Entity('chatcoop_calendar_events') +export class CalendarEventTypeormEntity { + @PrimaryGeneratedColumn('uuid') + id!: string; + + @Index('IDX_chatcoop_calendar_events_matrix_room') + @Column({ name: 'matrix_room_id', type: 'varchar', length: 255 }) + matrixRoomId!: string; + + @Column({ type: 'varchar', length: 500 }) + title!: string; + + @Column({ type: 'text', nullable: true }) + description!: string | null; + + @Column({ name: 'starts_at', type: 'timestamptz' }) + startsAt!: Date; + + @Column({ name: 'ends_at', type: 'timestamptz', nullable: true }) + endsAt!: Date | null; + + @Index('IDX_chatcoop_calendar_events_created_by') + @Column({ name: 'created_by_username', type: 'varchar', length: 255 }) + createdByUsername!: string; + + @Column({ name: 'ics_sequence', type: 'int', default: 0 }) + icsSequence!: number; + + @CreateDateColumn({ name: 'created_at' }) + createdAt!: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt!: Date; +} diff --git a/components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-ics-subscription.typeorm-entity.ts b/components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-ics-subscription.typeorm-entity.ts new file mode 100644 index 00000000000..d2879c76750 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/infrastructure/entities/calendar-ics-subscription.typeorm-entity.ts @@ -0,0 +1,17 @@ +import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, Index } from 'typeorm'; + +@Entity('chatcoop_calendar_ics_subscriptions') +export class CalendarIcsSubscriptionTypeormEntity { + @PrimaryGeneratedColumn('uuid') + id!: string; + + @Index({ unique: true }) + @Column({ name: 'coop_username', type: 'varchar', length: 255 }) + coopUsername!: string; + + @Column({ name: 'secret_sha256_hex', type: 'varchar', length: 64 }) + secretSha256Hex!: string; + + @CreateDateColumn({ name: 'created_at' }) + createdAt!: Date; +} diff --git a/components/controller/src/extensions/chatcoop/infrastructure/inter/chatcoop-inter-chatcoop-calendar.adapter.ts b/components/controller/src/extensions/chatcoop/infrastructure/inter/chatcoop-inter-chatcoop-calendar.adapter.ts new file mode 100644 index 00000000000..7cee4ab56f6 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/infrastructure/inter/chatcoop-inter-chatcoop-calendar.adapter.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import type { InterChatCoopCalendarPort, InterCalendarEventWindow } from '@coopenomics/inter'; +import { ChatCoopCalendarApplicationService } from '../../application/services/chatcoop-calendar-application.service'; + +@Injectable() +export class ChatcoopInterChatCoopCalendarAdapter implements InterChatCoopCalendarPort { + constructor(private readonly calendar: ChatCoopCalendarApplicationService) {} + + async listEventsByProjectHash(input: { + projectHash: string; + window?: InterCalendarEventWindow; + }) { + return this.calendar.listEventsForInterPort(input.projectHash, input.window); + } +} diff --git a/components/controller/src/extensions/chatcoop/infrastructure/mappers/calendar-event.mapper.ts b/components/controller/src/extensions/chatcoop/infrastructure/mappers/calendar-event.mapper.ts new file mode 100644 index 00000000000..acaf43bd157 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/infrastructure/mappers/calendar-event.mapper.ts @@ -0,0 +1,19 @@ +import type { ChatCoopCalendarEventDomainEntity } from '../../domain/entities/calendar-event.entity'; +import type { CalendarEventTypeormEntity } from '../entities/calendar-event.typeorm-entity'; + +export class CalendarEventMapper { + static toDomain(row: CalendarEventTypeormEntity): ChatCoopCalendarEventDomainEntity { + return { + id: row.id, + matrixRoomId: row.matrixRoomId, + title: row.title, + description: row.description, + startsAt: row.startsAt, + endsAt: row.endsAt, + createdByUsername: row.createdByUsername, + icsSequence: row.icsSequence, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + }; + } +} diff --git a/components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-event.typeorm-repository.ts b/components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-event.typeorm-repository.ts new file mode 100644 index 00000000000..e0d7f1261f1 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-event.typeorm-repository.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import type { + ChatCoopCalendarEventRepository, +} from '../../domain/repositories/calendar-event.repository'; +import type { + ChatCoopCalendarEventDomainEntity, + CreateChatCoopCalendarEventDomainInput, + UpdateChatCoopCalendarEventDomainInput, +} from '../../domain/entities/calendar-event.entity'; +import { CalendarEventTypeormEntity } from '../entities/calendar-event.typeorm-entity'; +import { ManagedMatrixRoomTypeormEntity } from '../entities/managed-matrix-room.typeorm-entity'; +import { CalendarEventMapper } from '../mappers/calendar-event.mapper'; + +@Injectable() +export class CalendarEventTypeormRepository implements ChatCoopCalendarEventRepository { + constructor( + @InjectRepository(CalendarEventTypeormEntity) + private readonly repo: Repository + ) {} + + async create(input: CreateChatCoopCalendarEventDomainInput): Promise { + const row = this.repo.create({ + matrixRoomId: input.matrixRoomId, + title: input.title, + description: input.description, + startsAt: input.startsAt, + endsAt: input.endsAt, + createdByUsername: input.createdByUsername, + icsSequence: 0, + }); + const saved = await this.repo.save(row); + return CalendarEventMapper.toDomain(saved); + } + + async update(input: UpdateChatCoopCalendarEventDomainInput): Promise { + const existing = await this.repo.findOne({ where: { id: input.id } }); + if (!existing) { + throw new Error('Событие календаря не найдено'); + } + const nextSeq = existing.icsSequence + 1; + await this.repo.update( + { id: input.id }, + { + matrixRoomId: input.matrixRoomId, + title: input.title, + description: input.description, + startsAt: input.startsAt, + endsAt: input.endsAt, + icsSequence: nextSeq, + } + ); + const updated = await this.repo.findOneOrFail({ where: { id: input.id } }); + return CalendarEventMapper.toDomain(updated); + } + + async deleteById(id: string): Promise { + await this.repo.delete({ id }); + } + + async findById(id: string): Promise { + const row = await this.repo.findOne({ where: { id } }); + return row ? CalendarEventMapper.toDomain(row) : null; + } + + async listAll(): Promise { + const rows = await this.repo.find({ order: { startsAt: 'ASC' } }); + return rows.map(CalendarEventMapper.toDomain); + } + + async listByManagedRoomProjectHashes( + projectHashes: string[], + window?: { from: Date; to: Date } + ): Promise { + if (projectHashes.length === 0) { + return []; + } + const qb = this.repo + .createQueryBuilder('e') + .innerJoin( + ManagedMatrixRoomTypeormEntity, + 'r', + 'r.matrixRoomId = e.matrixRoomId AND r.encrypted = :enc AND r.projectHash IN (:...hashes)', + { enc: false, hashes: projectHashes } + ) + .orderBy('e.startsAt', 'ASC'); + + if (window) { + qb.andWhere('(e.endsAt IS NULL OR e.endsAt > :from)', { from: window.from }); + qb.andWhere('e.startsAt < :to', { to: window.to }); + } + + const rows = await qb.getMany(); + return rows.map(CalendarEventMapper.toDomain); + } +} diff --git a/components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-ics-subscription.typeorm-repository.ts b/components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-ics-subscription.typeorm-repository.ts new file mode 100644 index 00000000000..421b6a82b62 --- /dev/null +++ b/components/controller/src/extensions/chatcoop/infrastructure/repositories/calendar-ics-subscription.typeorm-repository.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import type { + ChatCoopCalendarIcsSubscriptionDomainEntity, + ChatCoopCalendarIcsSubscriptionRepository, +} from '../../domain/repositories/calendar-ics-subscription.repository'; +import { CalendarIcsSubscriptionTypeormEntity } from '../entities/calendar-ics-subscription.typeorm-entity'; + +@Injectable() +export class CalendarIcsSubscriptionTypeormRepository implements ChatCoopCalendarIcsSubscriptionRepository { + constructor( + @InjectRepository(CalendarIcsSubscriptionTypeormEntity) + private readonly repo: Repository + ) {} + + async create( + coopUsername: string, + secretSha256Hex: string + ): Promise { + const row = this.repo.create({ + coopUsername: coopUsername.toLowerCase(), + secretSha256Hex, + }); + const saved = await this.repo.save(row); + return this.toDomain(saved); + } + + async findById(id: string): Promise { + const row = await this.repo.findOne({ where: { id } }); + return row ? this.toDomain(row) : null; + } + + async findByUsername(coopUsername: string): Promise { + const row = await this.repo.findOne({ where: { coopUsername: coopUsername.toLowerCase() } }); + return row ? this.toDomain(row) : null; + } + + async rotateSecretForUser( + coopUsername: string, + secretSha256Hex: string + ): Promise { + const u = coopUsername.toLowerCase(); + const existing = await this.repo.findOne({ where: { coopUsername: u } }); + if (!existing) { + return this.create(coopUsername, secretSha256Hex); + } + await this.repo.update({ id: existing.id }, { secretSha256Hex }); + const refreshed = await this.repo.findOneOrFail({ where: { id: existing.id } }); + return this.toDomain(refreshed); + } + + private toDomain(row: CalendarIcsSubscriptionTypeormEntity): ChatCoopCalendarIcsSubscriptionDomainEntity { + return { + id: row.id, + coopUsername: row.coopUsername, + secretSha256Hex: row.secretSha256Hex, + createdAt: row.createdAt, + }; + } +} diff --git a/components/controller/src/extensions/inter-communication-bridge.module.ts b/components/controller/src/extensions/inter-communication-bridge.module.ts index b03ac5e4e20..9a6a466c270 100644 --- a/components/controller/src/extensions/inter-communication-bridge.module.ts +++ b/components/controller/src/extensions/inter-communication-bridge.module.ts @@ -1,8 +1,13 @@ import { Global, Module } from '@nestjs/common'; -import { INTER_MATRIX_ROOM_MESSAGING, INTER_PROJECT_COMMUNICATION_ARTIFACTS } from '@coopenomics/inter'; +import { + INTER_CHATCOOP_CALENDAR, + INTER_MATRIX_ROOM_MESSAGING, + INTER_PROJECT_COMMUNICATION_ARTIFACTS, +} from '@coopenomics/inter'; import { ChatCoopPluginModule } from './chatcoop/chatcoop-extension.module'; import { ChatcoopInterProjectCommunicationArtifactsAdapter } from './chatcoop/infrastructure/inter/chatcoop-inter-project-communication-artifacts.adapter'; import { ChatcoopInterMatrixRoomMessagingAdapter } from './chatcoop/infrastructure/inter/chatcoop-inter-matrix-room-messaging.adapter'; +import { ChatcoopInterChatCoopCalendarAdapter } from './chatcoop/infrastructure/inter/chatcoop-inter-chatcoop-calendar.adapter'; /** * Глобальная привязка порта @coopenomics/inter к реализации ChatCoop. @@ -20,7 +25,15 @@ import { ChatcoopInterMatrixRoomMessagingAdapter } from './chatcoop/infrastructu provide: INTER_MATRIX_ROOM_MESSAGING, useExisting: ChatcoopInterMatrixRoomMessagingAdapter, }, + { + provide: INTER_CHATCOOP_CALENDAR, + useExisting: ChatcoopInterChatCoopCalendarAdapter, + }, + ], + exports: [ + INTER_PROJECT_COMMUNICATION_ARTIFACTS, + INTER_MATRIX_ROOM_MESSAGING, + INTER_CHATCOOP_CALENDAR, ], - exports: [INTER_PROJECT_COMMUNICATION_ARTIFACTS, INTER_MATRIX_ROOM_MESSAGING], }) export class InterCommunicationBridgeModule {} diff --git a/components/controller/zeus/const.ts b/components/controller/zeus/const.ts index ff0c8b1e8f2..ff5e23b790e 100644 --- a/components/controller/zeus/const.ts +++ b/components/controller/zeus/const.ts @@ -171,6 +171,7 @@ export const AllTypesProps: Record = { CapitalSegmentFilter:{ status:"SegmentStatus" }, + CapitalStoryContentFormat: "enum" as const, CapitalStoryFilter:{ status:"StoryStatus" }, @@ -301,6 +302,14 @@ export const AllTypesProps: Record = { }, CreateMatrixAccountInputDTO:{ + }, + CreateChatCoopCalendarEventInput:{ + endsAt:"DateTime", + startsAt:"DateTime" + }, + UpdateChatCoopCalendarEventInput:{ + endsAt:"DateTime", + startsAt:"DateTime" }, CreateOrganizationDataInput:{ bank_account:"BankAccountInput", @@ -854,6 +863,15 @@ export const AllTypesProps: Record = { chatcoopCreateAccount:{ data:"CreateMatrixAccountInputDTO" }, + chatcoopCreateCalendarEvent:{ + data:"CreateChatCoopCalendarEventInput" + }, + chatcoopDeleteCalendarEvent:{ + + }, + chatcoopUpdateCalendarEvent:{ + data:"UpdateChatCoopCalendarEventInput" + }, completeCapitalOnboardingStep:{ data:"CapitalOnboardingStepInput" }, @@ -1649,7 +1667,6 @@ export const AllTypesProps: Record = { }, StoryStatus: "enum" as const, - CapitalStoryContentFormat: "enum" as const, SubmitVoteInput:{ votes:"VoteDistributionInput" }, @@ -1707,6 +1724,7 @@ export const AllTypesProps: Record = { }, UpdateStoryInput:{ + content_format:"CapitalStoryContentFormat", status:"StoryStatus" }, UserStatus: "enum" as const, @@ -2143,8 +2161,8 @@ export const ReturnTypes: Record = { generator_offer_hash:"String", hours_per_day:"Float", id:"Int", - is_external_contract:"Boolean", is_external_blagorost_agreement:"Boolean", + is_external_contract:"Boolean", last_energy_update:"String", level:"Int", main_wallet:"ProgramWallet", @@ -2568,8 +2586,8 @@ export const ReturnTypes: Record = { _id:"String", _updated_at:"DateTime", block_num:"Float", - coopname:"String", content_format:"CapitalStoryContentFormat", + coopname:"String", created_by:"String", description:"String", issue_hash:"String", @@ -2993,6 +3011,25 @@ export const ReturnTypes: Record = { iframeUrl:"String", matrixUsername:"String" }, + ChatCoopCalendarEvent:{ + createdAt:"DateTime", + createdByUsername:"String", + description:"String", + endsAt:"DateTime", + icsSequence:"Int", + id:"String", + matrixRoomId:"String", + startsAt:"DateTime", + title:"String", + updatedAt:"DateTime" + }, + ChatCoopCalendarIcsUrlResponse:{ + icsUrl:"String" + }, + ChatCoopCalendarRoomOption:{ + displayLabel:"String", + matrixRoomId:"String" + }, Meet:{ authorization:"DocumentAggregate", close_at:"DateTime", @@ -3169,6 +3206,10 @@ export const ReturnTypes: Record = { chairmanConfirmApprove:"Approval", chairmanDeclineApprove:"Approval", chatcoopCreateAccount:"Boolean", + chatcoopCreateCalendarIcsSubscription:"ChatCoopCalendarIcsUrlResponse", + chatcoopCreateCalendarEvent:"ChatCoopCalendarEvent", + chatcoopDeleteCalendarEvent:"Boolean", + chatcoopUpdateCalendarEvent:"ChatCoopCalendarEvent", completeCapitalOnboardingStep:"CapitalOnboardingState", completeChairmanAgendaStep:"ChairmanOnboardingState", completeChairmanGeneralMeetStep:"ChairmanOnboardingState", @@ -3672,6 +3713,8 @@ export const ReturnTypes: Record = { chairmanApprovals:"PaginatedChairmanApprovalsPaginationResult", chatcoopCheckUsernameAvailability:"Boolean", chatcoopGetAccountStatus:"MatrixAccountStatusResponseDTO", + chatcoopListCalendarEvents:"ChatCoopCalendarEvent", + chatcoopListCalendarRooms:"ChatCoopCalendarRoomOption", chatcoopGetTranscription:"CallTranscriptionWithSegments", chatcoopGetTranscriptions:"CallTranscription", getAccount:"Account", diff --git a/components/controller/zeus/index.ts b/components/controller/zeus/index.ts index 0f37accad5e..03663154dd0 100644 --- a/components/controller/zeus/index.ts +++ b/components/controller/zeus/index.ts @@ -2240,6 +2240,7 @@ export type ValueTypes = { endedAt?:boolean | `@${string}`, id?:boolean | `@${string}`, matrixRoomId?:boolean | `@${string}`, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants?:boolean | `@${string}`, roomId?:boolean | `@${string}`, roomName?:boolean | `@${string}`, @@ -2480,10 +2481,10 @@ export type ValueTypes = { hours_per_day?:boolean | `@${string}`, /** ID в блокчейне */ id?:boolean | `@${string}`, - /** Является ли внешним контрактом */ - is_external_contract?:boolean | `@${string}`, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?:boolean | `@${string}`, + /** Является ли внешним контрактом */ + is_external_contract?:boolean | `@${string}`, /** Последнее обновление энергии */ last_energy_update?:boolean | `@${string}`, /** Уровень участника */ @@ -3492,10 +3493,10 @@ export type ValueTypes = { _updated_at?:boolean | `@${string}`, /** Номер блока крайней синхронизации с блокчейном */ block_num?:boolean | `@${string}`, - /** Имя аккаунта кооператива */ - coopname?:boolean | `@${string}`, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format?:boolean | `@${string}`, + /** Имя аккаунта кооператива */ + coopname?:boolean | `@${string}`, /** Имя пользователя, создавшего историю */ created_by?:boolean | `@${string}`, /** Описание истории */ @@ -3517,6 +3518,8 @@ export type ValueTypes = { __typename?: boolean | `@${string}`, ['...on CapitalStory']?: Omit }>; + /** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +["CapitalStoryContentFormat"]:CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -4271,6 +4274,21 @@ export type ValueTypes = { ["CreateMatrixAccountInputDTO"]: { password: string | Variable, username: string | Variable +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null | Variable, + endsAt?: ValueTypes["DateTime"] | undefined | null | Variable, + matrixRoomId: string | Variable, + startsAt: ValueTypes["DateTime"] | Variable, + title: string | Variable +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null | Variable, + endsAt?: ValueTypes["DateTime"] | undefined | null | Variable, + id: string | Variable, + matrixRoomId: string | Variable, + startsAt: ValueTypes["DateTime"] | Variable, + title: string | Variable }; ["CreateOrganizationDataInput"]: { /** Банковский счет организации */ @@ -4406,10 +4424,10 @@ export type ValueTypes = { phone: string | Variable }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string | Variable, /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: ValueTypes["CapitalStoryContentFormat"] | undefined | null | Variable, + /** Имя аккаунта кооператива */ + coopname: string | Variable, /** Описание истории */ description?: string | undefined | null | Variable, /** Хеш задачи (если история привязана к задаче) */ @@ -5724,6 +5742,32 @@ export type ValueTypes = { matrixUsername?:boolean | `@${string}`, __typename?: boolean | `@${string}`, ['...on MatrixAccountStatusResponseDTO']?: Omit +}>; + ["ChatCoopCalendarEvent"]: AliasType<{ + createdAt?:boolean | `@${string}`, + createdByUsername?:boolean | `@${string}`, + description?:boolean | `@${string}`, + endsAt?:boolean | `@${string}`, + icsSequence?:boolean | `@${string}`, + id?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + startsAt?:boolean | `@${string}`, + title?:boolean | `@${string}`, + updatedAt?:boolean | `@${string}`, + __typename?: boolean | `@${string}`, + ['...on ChatCoopCalendarEvent']?: Omit +}>; + ["ChatCoopCalendarIcsUrlResponse"]: AliasType<{ + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl?:boolean | `@${string}`, + __typename?: boolean | `@${string}`, + ['...on ChatCoopCalendarIcsUrlResponse']?: Omit +}>; + ["ChatCoopCalendarRoomOption"]: AliasType<{ + displayLabel?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + __typename?: boolean | `@${string}`, + ['...on ChatCoopCalendarRoomOption']?: Omit }>; /** Данные о собрании кооператива */ ["Meet"]: AliasType<{ @@ -6012,6 +6056,11 @@ capitalUpdateStory?: [{ data: ValueTypes["UpdateStoryInput"] | Variable},ValueTypes["Approval"]], chairmanDeclineApprove?: [{ data: ValueTypes["DeclineApproveInput"] | Variable},ValueTypes["Approval"]], chatcoopCreateAccount?: [{ data: ValueTypes["CreateMatrixAccountInputDTO"] | Variable},boolean | `@${string}`], + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription?:ValueTypes["ChatCoopCalendarIcsUrlResponse"], +chatcoopCreateCalendarEvent?: [{ data: ValueTypes["CreateChatCoopCalendarEventInput"] | Variable},ValueTypes["ChatCoopCalendarEvent"]], +chatcoopDeleteCalendarEvent?: [{ id: string | Variable},boolean | `@${string}`], +chatcoopUpdateCalendarEvent?: [{ data: ValueTypes["UpdateChatCoopCalendarEventInput"] | Variable},ValueTypes["ChatCoopCalendarEvent"]], completeCapitalOnboardingStep?: [{ data: ValueTypes["CapitalOnboardingStepInput"] | Variable},ValueTypes["CapitalOnboardingState"]], completeChairmanAgendaStep?: [{ data: ValueTypes["ChairmanOnboardingAgendaInput"] | Variable},ValueTypes["ChairmanOnboardingState"]], completeChairmanGeneralMeetStep?: [{ data: ValueTypes["ChairmanOnboardingGeneralMeetInput"] | Variable},ValueTypes["ChairmanOnboardingState"]], @@ -7182,6 +7231,10 @@ chairmanApprovals?: [{ filter?: ValueTypes["ApprovalFilter"] | undefined | null chatcoopCheckUsernameAvailability?: [{ data: ValueTypes["CheckMatrixUsernameInput"] | Variable},boolean | `@${string}`], /** Проверить статус Matrix аккаунта пользователя и получить iframe URL */ chatcoopGetAccountStatus?:ValueTypes["MatrixAccountStatusResponseDTO"], + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents?:ValueTypes["ChatCoopCalendarEvent"], + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms?:ValueTypes["ChatCoopCalendarRoomOption"], chatcoopGetTranscription?: [{ data: ValueTypes["GetTranscriptionInput"] | Variable},ValueTypes["CallTranscriptionWithSegments"]], chatcoopGetTranscriptions?: [{ data?: ValueTypes["GetTranscriptionsInput"] | undefined | null | Variable},ValueTypes["CallTranscription"]], getAccount?: [{ data: ValueTypes["GetAccountInput"] | Variable},ValueTypes["Account"]], @@ -8144,8 +8197,6 @@ searchPrivateAccounts?: [{ data: ValueTypes["SearchPrivateAccountsInput"] | Vari }; /** Статус истории в системе CAPITAL */ ["StoryStatus"]:StoryStatus; - /** Формат содержимого требования (истории) в CAPITAL */ -["CapitalStoryContentFormat"]:CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string | Variable, @@ -8270,7 +8321,9 @@ searchPrivateAccounts?: [{ data: ValueTypes["SearchPrivateAccountsInput"] | Vari createdAt?:boolean | `@${string}`, endOffset?:boolean | `@${string}`, id?:boolean | `@${string}`, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity?:boolean | `@${string}`, + /** Отображаемое имя из Synapse (displayname) */ speakerName?:boolean | `@${string}`, startOffset?:boolean | `@${string}`, text?:boolean | `@${string}`, @@ -8462,6 +8515,8 @@ searchPrivateAccounts?: [{ data: ValueTypes["SearchPrivateAccountsInput"] | Vari provider_name?: string | undefined | null | Variable }; ["UpdateStoryInput"]: { + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: ValueTypes["CapitalStoryContentFormat"] | undefined | null | Variable, /** Описание истории */ description?: string | undefined | null | Variable, /** Хеш задачи (если история привязана к задаче) */ @@ -9759,6 +9814,7 @@ export type ResolverInputTypes = { endedAt?:boolean | `@${string}`, id?:boolean | `@${string}`, matrixRoomId?:boolean | `@${string}`, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants?:boolean | `@${string}`, roomId?:boolean | `@${string}`, roomName?:boolean | `@${string}`, @@ -9992,10 +10048,10 @@ export type ResolverInputTypes = { hours_per_day?:boolean | `@${string}`, /** ID в блокчейне */ id?:boolean | `@${string}`, - /** Является ли внешним контрактом */ - is_external_contract?:boolean | `@${string}`, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?:boolean | `@${string}`, + /** Является ли внешним контрактом */ + is_external_contract?:boolean | `@${string}`, /** Последнее обновление энергии */ last_energy_update?:boolean | `@${string}`, /** Уровень участника */ @@ -10981,10 +11037,10 @@ export type ResolverInputTypes = { _updated_at?:boolean | `@${string}`, /** Номер блока крайней синхронизации с блокчейном */ block_num?:boolean | `@${string}`, - /** Имя аккаунта кооператива */ - coopname?:boolean | `@${string}`, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format?:boolean | `@${string}`, + /** Имя аккаунта кооператива */ + coopname?:boolean | `@${string}`, /** Имя пользователя, создавшего историю */ created_by?:boolean | `@${string}`, /** Описание истории */ @@ -11005,6 +11061,8 @@ export type ResolverInputTypes = { title?:boolean | `@${string}`, __typename?: boolean | `@${string}` }>; + /** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +["CapitalStoryContentFormat"]:CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -11750,6 +11808,21 @@ export type ResolverInputTypes = { ["CreateMatrixAccountInputDTO"]: { password: string, username: string +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ResolverInputTypes["DateTime"] | undefined | null, + matrixRoomId: string, + startsAt: ResolverInputTypes["DateTime"], + title: string +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ResolverInputTypes["DateTime"] | undefined | null, + id: string, + matrixRoomId: string, + startsAt: ResolverInputTypes["DateTime"], + title: string }; ["CreateOrganizationDataInput"]: { /** Банковский счет организации */ @@ -11885,10 +11958,10 @@ export type ResolverInputTypes = { phone: string }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: ResolverInputTypes["CapitalStoryContentFormat"] | undefined | null, + /** Имя аккаунта кооператива */ + coopname: string, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -13169,6 +13242,29 @@ export type ResolverInputTypes = { iframeUrl?:boolean | `@${string}`, matrixUsername?:boolean | `@${string}`, __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarEvent"]: AliasType<{ + createdAt?:boolean | `@${string}`, + createdByUsername?:boolean | `@${string}`, + description?:boolean | `@${string}`, + endsAt?:boolean | `@${string}`, + icsSequence?:boolean | `@${string}`, + id?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + startsAt?:boolean | `@${string}`, + title?:boolean | `@${string}`, + updatedAt?:boolean | `@${string}`, + __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarIcsUrlResponse"]: AliasType<{ + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl?:boolean | `@${string}`, + __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarRoomOption"]: AliasType<{ + displayLabel?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + __typename?: boolean | `@${string}` }>; /** Данные о собрании кооператива */ ["Meet"]: AliasType<{ @@ -13450,6 +13546,11 @@ capitalUpdateStory?: [{ data: ResolverInputTypes["UpdateStoryInput"]},ResolverIn chairmanConfirmApprove?: [{ data: ResolverInputTypes["ConfirmApproveInput"]},ResolverInputTypes["Approval"]], chairmanDeclineApprove?: [{ data: ResolverInputTypes["DeclineApproveInput"]},ResolverInputTypes["Approval"]], chatcoopCreateAccount?: [{ data: ResolverInputTypes["CreateMatrixAccountInputDTO"]},boolean | `@${string}`], + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription?:ResolverInputTypes["ChatCoopCalendarIcsUrlResponse"], +chatcoopCreateCalendarEvent?: [{ data: ResolverInputTypes["CreateChatCoopCalendarEventInput"]},ResolverInputTypes["ChatCoopCalendarEvent"]], +chatcoopDeleteCalendarEvent?: [{ id: string},boolean | `@${string}`], +chatcoopUpdateCalendarEvent?: [{ data: ResolverInputTypes["UpdateChatCoopCalendarEventInput"]},ResolverInputTypes["ChatCoopCalendarEvent"]], completeCapitalOnboardingStep?: [{ data: ResolverInputTypes["CapitalOnboardingStepInput"]},ResolverInputTypes["CapitalOnboardingState"]], completeChairmanAgendaStep?: [{ data: ResolverInputTypes["ChairmanOnboardingAgendaInput"]},ResolverInputTypes["ChairmanOnboardingState"]], completeChairmanGeneralMeetStep?: [{ data: ResolverInputTypes["ChairmanOnboardingGeneralMeetInput"]},ResolverInputTypes["ChairmanOnboardingState"]], @@ -14571,6 +14672,10 @@ chairmanApprovals?: [{ filter?: ResolverInputTypes["ApprovalFilter"] | undefined chatcoopCheckUsernameAvailability?: [{ data: ResolverInputTypes["CheckMatrixUsernameInput"]},boolean | `@${string}`], /** Проверить статус Matrix аккаунта пользователя и получить iframe URL */ chatcoopGetAccountStatus?:ResolverInputTypes["MatrixAccountStatusResponseDTO"], + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents?:ResolverInputTypes["ChatCoopCalendarEvent"], + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms?:ResolverInputTypes["ChatCoopCalendarRoomOption"], chatcoopGetTranscription?: [{ data: ResolverInputTypes["GetTranscriptionInput"]},ResolverInputTypes["CallTranscriptionWithSegments"]], chatcoopGetTranscriptions?: [{ data?: ResolverInputTypes["GetTranscriptionsInput"] | undefined | null},ResolverInputTypes["CallTranscription"]], getAccount?: [{ data: ResolverInputTypes["GetAccountInput"]},ResolverInputTypes["Account"]], @@ -15515,8 +15620,6 @@ searchPrivateAccounts?: [{ data: ResolverInputTypes["SearchPrivateAccountsInput" }; /** Статус истории в системе CAPITAL */ ["StoryStatus"]:StoryStatus; - /** Формат содержимого требования (истории) в CAPITAL */ -["CapitalStoryContentFormat"]:CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string, @@ -15634,7 +15737,9 @@ searchPrivateAccounts?: [{ data: ResolverInputTypes["SearchPrivateAccountsInput" createdAt?:boolean | `@${string}`, endOffset?:boolean | `@${string}`, id?:boolean | `@${string}`, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity?:boolean | `@${string}`, + /** Отображаемое имя из Synapse (displayname) */ speakerName?:boolean | `@${string}`, startOffset?:boolean | `@${string}`, text?:boolean | `@${string}`, @@ -15825,6 +15930,8 @@ searchPrivateAccounts?: [{ data: ResolverInputTypes["SearchPrivateAccountsInput" provider_name?: string | undefined | null }; ["UpdateStoryInput"]: { + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: ResolverInputTypes["CapitalStoryContentFormat"] | undefined | null, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -17095,6 +17202,7 @@ export type ModelTypes = { endedAt?: ModelTypes["DateTime"] | undefined | null, id: string, matrixRoomId: string, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants: Array, roomId: string, roomName: string, @@ -17321,10 +17429,10 @@ export type ModelTypes = { hours_per_day?: number | undefined | null, /** ID в блокчейне */ id?: number | undefined | null, - /** Является ли внешним контрактом */ - is_external_contract?: boolean | undefined | null, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?: boolean | undefined | null, + /** Является ли внешним контрактом */ + is_external_contract?: boolean | undefined | null, /** Последнее обновление энергии */ last_energy_update?: string | undefined | null, /** Уровень участника */ @@ -18287,10 +18395,10 @@ export type ModelTypes = { _updated_at: ModelTypes["DateTime"], /** Номер блока крайней синхронизации с блокчейном */ block_num?: number | undefined | null, - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format: ModelTypes["CapitalStoryContentFormat"], + /** Имя аккаунта кооператива */ + coopname: string, /** Имя пользователя, создавшего историю */ created_by: string, /** Описание истории */ @@ -18310,6 +18418,7 @@ export type ModelTypes = { /** Название истории */ title: string }; + ["CapitalStoryContentFormat"]:CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -19043,6 +19152,21 @@ export type ModelTypes = { ["CreateMatrixAccountInputDTO"]: { password: string, username: string +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ModelTypes["DateTime"] | undefined | null, + matrixRoomId: string, + startsAt: ModelTypes["DateTime"], + title: string +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ModelTypes["DateTime"] | undefined | null, + id: string, + matrixRoomId: string, + startsAt: ModelTypes["DateTime"], + title: string }; ["CreateOrganizationDataInput"]: { /** Банковский счет организации */ @@ -19178,10 +19302,10 @@ export type ModelTypes = { phone: string }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: ModelTypes["CapitalStoryContentFormat"] | undefined | null, + /** Имя аккаунта кооператива */ + coopname: string, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -20417,6 +20541,26 @@ export type ModelTypes = { hasAccount: boolean, iframeUrl?: string | undefined | null, matrixUsername?: string | undefined | null +}; + ["ChatCoopCalendarEvent"]: { + createdAt: ModelTypes["DateTime"], + createdByUsername: string, + description?: string | undefined | null, + endsAt?: ModelTypes["DateTime"] | undefined | null, + icsSequence: number, + id: string, + matrixRoomId: string, + startsAt: ModelTypes["DateTime"], + title: string, + updatedAt: ModelTypes["DateTime"] +}; + ["ChatCoopCalendarIcsUrlResponse"]: { + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl: string +}; + ["ChatCoopCalendarRoomOption"]: { + displayLabel: string, + matrixRoomId: string }; /** Данные о собрании кооператива */ ["Meet"]: { @@ -20771,6 +20915,14 @@ export type ModelTypes = { chairmanDeclineApprove: ModelTypes["Approval"], /** Создать Matrix аккаунт с именем пользователя и паролем */ chatcoopCreateAccount: boolean, + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription: ModelTypes["ChatCoopCalendarIcsUrlResponse"], + /** Создать событие календаря */ + chatcoopCreateCalendarEvent: ModelTypes["ChatCoopCalendarEvent"], + /** Удалить событие календаря */ + chatcoopDeleteCalendarEvent: boolean, + /** Обновить событие календаря */ + chatcoopUpdateCalendarEvent: ModelTypes["ChatCoopCalendarEvent"], /** Выполнить шаг онбординга capital (создание предложения повестки) */ completeCapitalOnboardingStep: ModelTypes["CapitalOnboardingState"], /** Выполнить один из шагов онбординга (создание предложения повестки) */ @@ -21952,6 +22104,10 @@ export type ModelTypes = { chatcoopCheckUsernameAvailability: boolean, /** Проверить статус Matrix аккаунта пользователя и получить iframe URL */ chatcoopGetAccountStatus: ModelTypes["MatrixAccountStatusResponseDTO"], + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents: Array, + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms: Array, /** Получить детальную транскрипцию с сегментами */ chatcoopGetTranscription?: ModelTypes["CallTranscriptionWithSegments"] | undefined | null, /** Получить список транскрипций звонков */ @@ -22902,7 +23058,6 @@ export type ModelTypes = { project_hash: string }; ["StoryStatus"]:StoryStatus; - ["CapitalStoryContentFormat"]:CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string, @@ -23012,7 +23167,9 @@ export type ModelTypes = { createdAt: ModelTypes["DateTime"], endOffset: number, id: string, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity: string, + /** Отображаемое имя из Synapse (displayname) */ speakerName: string, startOffset: number, text: string @@ -23201,6 +23358,8 @@ export type ModelTypes = { provider_name?: string | undefined | null }; ["UpdateStoryInput"]: { + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: ModelTypes["CapitalStoryContentFormat"] | undefined | null, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -24516,6 +24675,7 @@ export type GraphQLTypes = { endedAt?: GraphQLTypes["DateTime"] | undefined | null, id: string, matrixRoomId: string, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants: Array, roomId: string, roomName: string, @@ -24756,10 +24916,10 @@ export type GraphQLTypes = { hours_per_day?: number | undefined | null, /** ID в блокчейне */ id?: number | undefined | null, - /** Является ли внешним контрактом */ - is_external_contract?: boolean | undefined | null, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?: boolean | undefined | null, + /** Является ли внешним контрактом */ + is_external_contract?: boolean | undefined | null, /** Последнее обновление энергии */ last_energy_update?: string | undefined | null, /** Уровень участника */ @@ -25768,10 +25928,10 @@ export type GraphQLTypes = { _updated_at: GraphQLTypes["DateTime"], /** Номер блока крайней синхронизации с блокчейном */ block_num?: number | undefined | null, - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format: GraphQLTypes["CapitalStoryContentFormat"], + /** Имя аккаунта кооператива */ + coopname: string, /** Имя пользователя, создавшего историю */ created_by: string, /** Описание истории */ @@ -25792,6 +25952,8 @@ export type GraphQLTypes = { title: string, ['...on CapitalStory']: Omit }; + /** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +["CapitalStoryContentFormat"]: CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -26546,6 +26708,21 @@ export type GraphQLTypes = { ["CreateMatrixAccountInputDTO"]: { password: string, username: string +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: GraphQLTypes["DateTime"] | undefined | null, + matrixRoomId: string, + startsAt: GraphQLTypes["DateTime"], + title: string +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: GraphQLTypes["DateTime"] | undefined | null, + id: string, + matrixRoomId: string, + startsAt: GraphQLTypes["DateTime"], + title: string }; ["CreateOrganizationDataInput"]: { /** Банковский счет организации */ @@ -26681,10 +26858,10 @@ export type GraphQLTypes = { phone: string }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string, - /** Формат содержимого; по умолчанию MARKDOWN */ + /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: GraphQLTypes["CapitalStoryContentFormat"] | undefined | null, + /** Имя аккаунта кооператива */ + coopname: string, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -27999,6 +28176,32 @@ export type GraphQLTypes = { iframeUrl?: string | undefined | null, matrixUsername?: string | undefined | null, ['...on MatrixAccountStatusResponseDTO']: Omit +}; + ["ChatCoopCalendarEvent"]: { + __typename: "ChatCoopCalendarEvent", + createdAt: GraphQLTypes["DateTime"], + createdByUsername: string, + description?: string | undefined | null, + endsAt?: GraphQLTypes["DateTime"] | undefined | null, + icsSequence: number, + id: string, + matrixRoomId: string, + startsAt: GraphQLTypes["DateTime"], + title: string, + updatedAt: GraphQLTypes["DateTime"], + ['...on ChatCoopCalendarEvent']: Omit +}; + ["ChatCoopCalendarIcsUrlResponse"]: { + __typename: "ChatCoopCalendarIcsUrlResponse", + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl: string, + ['...on ChatCoopCalendarIcsUrlResponse']: Omit +}; + ["ChatCoopCalendarRoomOption"]: { + __typename: "ChatCoopCalendarRoomOption", + displayLabel: string, + matrixRoomId: string, + ['...on ChatCoopCalendarRoomOption']: Omit }; /** Данные о собрании кооператива */ ["Meet"]: { @@ -28368,6 +28571,14 @@ export type GraphQLTypes = { chairmanDeclineApprove: GraphQLTypes["Approval"], /** Создать Matrix аккаунт с именем пользователя и паролем */ chatcoopCreateAccount: boolean, + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription: GraphQLTypes["ChatCoopCalendarIcsUrlResponse"], + /** Создать событие календаря */ + chatcoopCreateCalendarEvent: GraphQLTypes["ChatCoopCalendarEvent"], + /** Удалить событие календаря */ + chatcoopDeleteCalendarEvent: boolean, + /** Обновить событие календаря */ + chatcoopUpdateCalendarEvent: GraphQLTypes["ChatCoopCalendarEvent"], /** Выполнить шаг онбординга capital (создание предложения повестки) */ completeCapitalOnboardingStep: GraphQLTypes["CapitalOnboardingState"], /** Выполнить один из шагов онбординга (создание предложения повестки) */ @@ -29668,6 +29879,10 @@ export type GraphQLTypes = { chatcoopCheckUsernameAvailability: boolean, /** Проверить статус Matrix аккаунта пользователя и получить iframe URL */ chatcoopGetAccountStatus: GraphQLTypes["MatrixAccountStatusResponseDTO"], + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents: Array, + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms: Array, /** Получить детальную транскрипцию с сегментами */ chatcoopGetTranscription?: GraphQLTypes["CallTranscriptionWithSegments"] | undefined | null, /** Получить список транскрипций звонков */ @@ -30656,8 +30871,6 @@ export type GraphQLTypes = { }; /** Статус истории в системе CAPITAL */ ["StoryStatus"]: StoryStatus; - /** Формат содержимого требования (истории) в CAPITAL */ -["CapitalStoryContentFormat"]: CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string, @@ -30783,7 +30996,9 @@ export type GraphQLTypes = { createdAt: GraphQLTypes["DateTime"], endOffset: number, id: string, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity: string, + /** Отображаемое имя из Synapse (displayname) */ speakerName: string, startOffset: number, text: string, @@ -30974,7 +31189,9 @@ export type GraphQLTypes = { provider_name?: string | undefined | null }; ["UpdateStoryInput"]: { - /** Описание истории */ + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: GraphQLTypes["CapitalStoryContentFormat"] | undefined | null, + /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ issue_hash?: string | undefined | null, @@ -31192,6 +31409,13 @@ export enum CapitalOnboardingStep { generator_offer_template = "generator_offer_template", generator_program_template = "generator_program_template" } +/** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +export enum CapitalStoryContentFormat { + BPMN = "BPMN", + DRAWIO = "DRAWIO", + MARKDOWN = "MARKDOWN", + MERMAID = "MERMAID" +} export enum ChairmanOnboardingAgendaStep { participant_application = "participant_application", privacy_agreement = "privacy_agreement", @@ -31473,13 +31697,6 @@ export enum StoryStatus { COMPLETED = "COMPLETED", PENDING = "PENDING" } -/** Формат содержимого требования (истории) в CAPITAL */ -export enum CapitalStoryContentFormat { - BPMN = "BPMN", - DRAWIO = "DRAWIO", - MARKDOWN = "MARKDOWN", - MERMAID = "MERMAID" -} /** Состояние контроллера кооператива */ export enum SystemStatus { active = "active", @@ -31559,6 +31776,7 @@ type ZEUS_VARIABLES = { ["CapitalOnboardingStepInput"]: ValueTypes["CapitalOnboardingStepInput"]; ["CapitalProjectFilter"]: ValueTypes["CapitalProjectFilter"]; ["CapitalSegmentFilter"]: ValueTypes["CapitalSegmentFilter"]; + ["CapitalStoryContentFormat"]: ValueTypes["CapitalStoryContentFormat"]; ["CapitalStoryFilter"]: ValueTypes["CapitalStoryFilter"]; ["CapitalTimeEntriesFilter"]: ValueTypes["CapitalTimeEntriesFilter"]; ["CapitalTimeStatsInput"]: ValueTypes["CapitalTimeStatsInput"]; @@ -31601,6 +31819,8 @@ type ZEUS_VARIABLES = { ["CreateInitialPaymentInput"]: ValueTypes["CreateInitialPaymentInput"]; ["CreateIssueInput"]: ValueTypes["CreateIssueInput"]; ["CreateMatrixAccountInputDTO"]: ValueTypes["CreateMatrixAccountInputDTO"]; + ["CreateChatCoopCalendarEventInput"]: ValueTypes["CreateChatCoopCalendarEventInput"]; + ["UpdateChatCoopCalendarEventInput"]: ValueTypes["UpdateChatCoopCalendarEventInput"]; ["CreateOrganizationDataInput"]: ValueTypes["CreateOrganizationDataInput"]; ["CreateParentOfferInput"]: ValueTypes["CreateParentOfferInput"]; ["CreateProcessTemplateInput"]: ValueTypes["CreateProcessTemplateInput"]; @@ -31794,7 +32014,6 @@ type ZEUS_VARIABLES = { ["StartVotingInput"]: ValueTypes["StartVotingInput"]; ["StopProjectInput"]: ValueTypes["StopProjectInput"]; ["StoryStatus"]: ValueTypes["StoryStatus"]; - ["CapitalStoryContentFormat"]: ValueTypes["CapitalStoryContentFormat"]; ["SubmitVoteInput"]: ValueTypes["SubmitVoteInput"]; ["SupplyOnRequestInput"]: ValueTypes["SupplyOnRequestInput"]; ["SystemStatus"]: ValueTypes["SystemStatus"]; diff --git a/components/desktop/extensions/capital/entities/Project/model/store.ts b/components/desktop/extensions/capital/entities/Project/model/store.ts index acdf5abcea2..fd77945b401 100644 --- a/components/desktop/extensions/capital/entities/Project/model/store.ts +++ b/components/desktop/extensions/capital/entities/Project/model/store.ts @@ -26,6 +26,7 @@ interface IProjectStore { projects: Ref; loadProjects: (data: IGetProjectsInput, append?: boolean) => Promise; addProjectToList: (projectData: IProject) => void; + removeProjectFromList: (projectHash: string) => void; loadProject: (data: IGetProjectInput) => Promise; projectWithRelations: Ref; loadProjectWithRelations: ( @@ -91,6 +92,19 @@ export const useProjectStore = defineStore(namespace, (): IProjectStore => { } }; + const removeProjectFromList = (projectHash: string) => { + const idx = projects.value.items.findIndex( + (p) => p.project_hash === projectHash, + ); + if (idx !== -1) { + projects.value.items.splice(idx, 1); + projects.value.totalCount = Math.max(0, projects.value.totalCount - 1); + } + if (projectWithRelations.value?.project_hash === projectHash) { + projectWithRelations.value = null; + } + }; + const loadProject = async (data: IGetProjectInput): Promise => { const loadedData = await api.loadProject(data); if (!loadedData) return; @@ -172,6 +186,7 @@ export const useProjectStore = defineStore(namespace, (): IProjectStore => { projectWithRelations, loadProjects, addProjectToList, + removeProjectFromList, loadProject, loadProjectWithRelations, loadProjectLogs, diff --git a/components/desktop/extensions/capital/features/Issue/DeleteIssue/index.ts b/components/desktop/extensions/capital/features/Issue/DeleteIssue/index.ts index a7e4ad32cce..f07009fa6d9 100644 --- a/components/desktop/extensions/capital/features/Issue/DeleteIssue/index.ts +++ b/components/desktop/extensions/capital/features/Issue/DeleteIssue/index.ts @@ -1 +1,2 @@ export { useDeleteIssue } from './model'; +export * from './ui'; diff --git a/components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/DeleteIssueButton.vue b/components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/DeleteIssueButton.vue new file mode 100644 index 00000000000..960a966e6cd --- /dev/null +++ b/components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/DeleteIssueButton.vue @@ -0,0 +1,75 @@ + + + diff --git a/components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/index.ts b/components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/index.ts new file mode 100644 index 00000000000..5a2f26b87e5 --- /dev/null +++ b/components/desktop/extensions/capital/features/Issue/DeleteIssue/ui/index.ts @@ -0,0 +1 @@ +export { default as DeleteIssueButton } from './DeleteIssueButton.vue'; diff --git a/components/desktop/extensions/capital/features/Issue/SetCreator/ui/SetCreatorButton.vue b/components/desktop/extensions/capital/features/Issue/SetCreator/ui/SetCreatorButton.vue index e05530af828..c3ebc218202 100644 --- a/components/desktop/extensions/capital/features/Issue/SetCreator/ui/SetCreatorButton.vue +++ b/components/desktop/extensions/capital/features/Issue/SetCreator/ui/SetCreatorButton.vue @@ -10,6 +10,7 @@ ContributorSelector( placeholder='' class='creators-selector' label='Исполнители' + style="width: 220px;" ) diff --git a/components/desktop/extensions/capital/features/Project/DeleteProject/model/index.ts b/components/desktop/extensions/capital/features/Project/DeleteProject/model/index.ts index 3a02340e92f..0420a807bf2 100644 --- a/components/desktop/extensions/capital/features/Project/DeleteProject/model/index.ts +++ b/components/desktop/extensions/capital/features/Project/DeleteProject/model/index.ts @@ -1,11 +1,14 @@ import { ref, type Ref } from 'vue'; import type { Mutations } from '@coopenomics/sdk'; import { api } from '../api'; +import { useProjectStore } from 'app/extensions/capital/entities/Project/model'; export type IDeleteProjectInput = Mutations.Capital.DeleteProject.IInput['data']; export function useDeleteProject() { + const projectStore = useProjectStore(); + const initialDeleteProjectInput: IDeleteProjectInput = { coopname: '', project_hash: '', @@ -26,6 +29,8 @@ export function useDeleteProject() { async function deleteProject(data: IDeleteProjectInput) { const transaction = await api.deleteProject(data); + projectStore.removeProjectFromList(data.project_hash); + // Сбрасываем deleteProjectInput после выполнения deleteProject resetInput(deleteProjectInput, initialDeleteProjectInput); diff --git a/components/desktop/extensions/capital/features/Project/DeleteProject/ui/DeleteProjectSidebarButton.vue b/components/desktop/extensions/capital/features/Project/DeleteProject/ui/DeleteProjectSidebarButton.vue new file mode 100644 index 00000000000..2655705ca29 --- /dev/null +++ b/components/desktop/extensions/capital/features/Project/DeleteProject/ui/DeleteProjectSidebarButton.vue @@ -0,0 +1,86 @@ + + + diff --git a/components/desktop/extensions/capital/features/Project/DeleteProject/ui/index.ts b/components/desktop/extensions/capital/features/Project/DeleteProject/ui/index.ts index 3d48542851e..972bbee04b1 100644 --- a/components/desktop/extensions/capital/features/Project/DeleteProject/ui/index.ts +++ b/components/desktop/extensions/capital/features/Project/DeleteProject/ui/index.ts @@ -1 +1,2 @@ export { default as DeleteProjectButton } from './DeleteProjectButton.vue'; +export { default as DeleteProjectSidebarButton } from './DeleteProjectSidebarButton.vue'; diff --git a/components/desktop/extensions/capital/features/Story/CreateStory/model/index.ts b/components/desktop/extensions/capital/features/Story/CreateStory/model/index.ts index 884edbd161f..c6131809860 100644 --- a/components/desktop/extensions/capital/features/Story/CreateStory/model/index.ts +++ b/components/desktop/extensions/capital/features/Story/CreateStory/model/index.ts @@ -1,4 +1,5 @@ import { ref, type Ref } from 'vue'; +import { Zeus } from '@coopenomics/sdk'; import { useStoryStore, type ICreateStoryInput, @@ -15,6 +16,7 @@ export function useCreateStory() { story_hash: '', coopname: '', title: '', + content_format: Zeus.CapitalStoryContentFormat.MARKDOWN, }; const createStoryInput = ref({ diff --git a/components/desktop/extensions/capital/features/Story/EditRequirement/ui/EditRequirementPanel.vue b/components/desktop/extensions/capital/features/Story/EditRequirement/ui/EditRequirementPanel.vue index a63c03fe58c..b1a877237a5 100644 --- a/components/desktop/extensions/capital/features/Story/EditRequirement/ui/EditRequirementPanel.vue +++ b/components/desktop/extensions/capital/features/Story/EditRequirement/ui/EditRequirementPanel.vue @@ -86,24 +86,6 @@ q-card.column.no-wrap.edit-req-panel( :padded='false' ) - q-card-actions.q-pa-md( - v-if='variant === "dialog" || (canEdit && hasChanges)' - align='right' - ) - q-btn( - v-if='variant === "dialog" && !hasChanges' - flat - label='Закрыть' - @click='handleClose' - ) - template(v-if='canEdit && hasChanges') - q-btn(flat label='Отменить' @click='resetChanges' :disable='isSaving') - q-btn( - label='Сохранить' - color='primary' - @click='handleSave' - :loading='isSaving' - ) diff --git a/components/desktop/extensions/chatcoop/features/Calendar/DeleteEvent/ui/index.ts b/components/desktop/extensions/chatcoop/features/Calendar/DeleteEvent/ui/index.ts new file mode 100644 index 00000000000..7a0f31d3105 --- /dev/null +++ b/components/desktop/extensions/chatcoop/features/Calendar/DeleteEvent/ui/index.ts @@ -0,0 +1 @@ +export { default as DeleteCalendarEventDialog } from './DeleteCalendarEventDialog.vue' diff --git a/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/index.ts b/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/index.ts new file mode 100644 index 00000000000..a258c76fe6f --- /dev/null +++ b/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/index.ts @@ -0,0 +1 @@ +export { SaveCalendarEventDialog } from './ui' diff --git a/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/SaveCalendarEventDialog.vue b/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/SaveCalendarEventDialog.vue new file mode 100644 index 00000000000..386805be3e0 --- /dev/null +++ b/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/SaveCalendarEventDialog.vue @@ -0,0 +1,124 @@ + + + diff --git a/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/index.ts b/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/index.ts new file mode 100644 index 00000000000..fb7a1cbc324 --- /dev/null +++ b/components/desktop/extensions/chatcoop/features/Calendar/SaveEvent/ui/index.ts @@ -0,0 +1 @@ +export { default as SaveCalendarEventDialog } from './SaveCalendarEventDialog.vue' diff --git a/components/desktop/extensions/chatcoop/install.ts b/components/desktop/extensions/chatcoop/install.ts index 83b32d3b1cb..63df995a16e 100644 --- a/components/desktop/extensions/chatcoop/install.ts +++ b/components/desktop/extensions/chatcoop/install.ts @@ -1,5 +1,5 @@ import { markRaw } from 'vue'; -import { ChatCoopPage, MobileClientPage, TranscriptionsPage, TranscriptionDetailPage } from './pages'; +import { CalendarPage, ChatCoopPage, MobileClientPage, TranscriptionsPage, TranscriptionDetailPage } from './pages'; import { agreementsBase } from 'src/shared/lib/consts/workspaces'; import type { IWorkspaceConfig } from 'src/shared/lib/types/workspace'; @@ -34,6 +34,19 @@ export default async function (): Promise { }, children: [], }, + { + path: 'calendar', + name: 'chatcoop-calendar', + component: markRaw(CalendarPage), + meta: { + title: 'Календарь событий', + icon: 'fa-solid fa-calendar-days', + roles: ['chairman', 'member', 'user'], + agreements: agreementsBase, + requiresAuth: true, + }, + children: [], + }, { path: 'mobile', name: 'chatcoop-mobile', diff --git a/components/desktop/extensions/chatcoop/pages/CalendarPage/index.ts b/components/desktop/extensions/chatcoop/pages/CalendarPage/index.ts new file mode 100644 index 00000000000..b0387a08aff --- /dev/null +++ b/components/desktop/extensions/chatcoop/pages/CalendarPage/index.ts @@ -0,0 +1 @@ +export { CalendarPage } from './ui' diff --git a/components/desktop/extensions/chatcoop/pages/CalendarPage/ui/CalendarPage.vue b/components/desktop/extensions/chatcoop/pages/CalendarPage/ui/CalendarPage.vue new file mode 100644 index 00000000000..6de0852fde1 --- /dev/null +++ b/components/desktop/extensions/chatcoop/pages/CalendarPage/ui/CalendarPage.vue @@ -0,0 +1,55 @@ + + + diff --git a/components/desktop/extensions/chatcoop/pages/CalendarPage/ui/index.ts b/components/desktop/extensions/chatcoop/pages/CalendarPage/ui/index.ts new file mode 100644 index 00000000000..c7d05655b12 --- /dev/null +++ b/components/desktop/extensions/chatcoop/pages/CalendarPage/ui/index.ts @@ -0,0 +1 @@ +export { default as CalendarPage } from './CalendarPage.vue' diff --git a/components/desktop/extensions/chatcoop/pages/ChatCoopPage/ui/ChatCoopPage.vue b/components/desktop/extensions/chatcoop/pages/ChatCoopPage/ui/ChatCoopPage.vue index 9736417e1e3..b77b657a8c8 100644 --- a/components/desktop/extensions/chatcoop/pages/ChatCoopPage/ui/ChatCoopPage.vue +++ b/components/desktop/extensions/chatcoop/pages/ChatCoopPage/ui/ChatCoopPage.vue @@ -16,7 +16,7 @@ div // Лоадер пока загружается iframe Matrix клиента WindowLoader( - v-else-if="chatcoopStore.accountStatus?.iframeUrl && isIframeLoading", + v-else-if="iframeSrc && isIframeLoading", text="Загрузка клиента..." ) @@ -33,9 +33,9 @@ div // Iframe с Matrix клиентом после полной загрузки iframe( - v-else-if="chatcoopStore.accountStatus?.iframeUrl", + v-else-if="iframeSrc", v-show="!isIframeLoading", - :src="chatcoopStore.accountStatus.iframeUrl", + :src="iframeSrc", class="matrix-iframe", frameborder="0", width="100%", @@ -46,21 +46,35 @@ div diff --git a/components/desktop/extensions/chatcoop/widgets/CalendarErrorBanner/ui/index.ts b/components/desktop/extensions/chatcoop/widgets/CalendarErrorBanner/ui/index.ts new file mode 100644 index 00000000000..e97ccd49612 --- /dev/null +++ b/components/desktop/extensions/chatcoop/widgets/CalendarErrorBanner/ui/index.ts @@ -0,0 +1 @@ +export { default as CalendarErrorBanner } from './CalendarErrorBanner.vue' diff --git a/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/index.ts b/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/index.ts new file mode 100644 index 00000000000..ed584959d76 --- /dev/null +++ b/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/index.ts @@ -0,0 +1 @@ +export * from './ui' diff --git a/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/CalendarEventsTable.vue b/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/CalendarEventsTable.vue new file mode 100644 index 00000000000..d40646d444e --- /dev/null +++ b/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/CalendarEventsTable.vue @@ -0,0 +1,77 @@ + + + diff --git a/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/index.ts b/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/index.ts new file mode 100644 index 00000000000..205e4f5d540 --- /dev/null +++ b/components/desktop/extensions/chatcoop/widgets/CalendarEventsTable/ui/index.ts @@ -0,0 +1 @@ +export { default as CalendarEventsTable } from './CalendarEventsTable.vue' diff --git a/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/index.ts b/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/index.ts new file mode 100644 index 00000000000..ed584959d76 --- /dev/null +++ b/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/index.ts @@ -0,0 +1 @@ +export * from './ui' diff --git a/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/CalendarPageToolbar.vue b/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/CalendarPageToolbar.vue new file mode 100644 index 00000000000..74257822734 --- /dev/null +++ b/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/CalendarPageToolbar.vue @@ -0,0 +1,67 @@ + + + diff --git a/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/index.ts b/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/index.ts new file mode 100644 index 00000000000..1caa8717a68 --- /dev/null +++ b/components/desktop/extensions/chatcoop/widgets/CalendarPageToolbar/ui/index.ts @@ -0,0 +1 @@ +export { default as CalendarPageToolbar } from './CalendarPageToolbar.vue' diff --git a/components/inter/src/chatcoop-calendar.port.ts b/components/inter/src/chatcoop-calendar.port.ts new file mode 100644 index 00000000000..ae7020a8656 --- /dev/null +++ b/components/inter/src/chatcoop-calendar.port.ts @@ -0,0 +1,34 @@ +/** + * Read-модель события календаря ChatCoop для межмодульного контракта (Capital и др.). + * Без Nest/TypeORM — только plain-типы. + */ +export interface InterCalendarEventWindow { + /** ISO 8601 */ + fromInclusive: string; + /** ISO 8601 */ + toExclusive: string; +} + +export interface InterCoopCalendarEventRead { + id: string; + matrixRoomId: string; + /** Из реестра управляемых комнат; null если комната не в реестре */ + projectHash: string | null; + title: string; + description: string | null; + startsAtIso: string; + endsAtIso: string | null; + createdByUsername: string; + icsSequence: number; +} + +/** + * Порт: чтение событий календаря по комнатам проекта Capital и опционально по окну времени. + * Реализация — расширение chatcoop; регистрация через InterCommunicationBridgeModule. + */ +export interface InterChatCoopCalendarPort { + listEventsByProjectHash(input: { + projectHash: string; + window?: InterCalendarEventWindow; + }): Promise; +} diff --git a/components/inter/src/index.ts b/components/inter/src/index.ts index 883a9762391..aa4e88e60b7 100644 --- a/components/inter/src/index.ts +++ b/components/inter/src/index.ts @@ -1,4 +1,8 @@ -export { INTER_MATRIX_ROOM_MESSAGING, INTER_PROJECT_COMMUNICATION_ARTIFACTS } from './tokens'; +export { + INTER_CHATCOOP_CALENDAR, + INTER_MATRIX_ROOM_MESSAGING, + INTER_PROJECT_COMMUNICATION_ARTIFACTS, +} from './tokens'; export type { InterCompletedCallTranscriptionHead, @@ -14,3 +18,9 @@ export type { InterMatrixSendTextAndPinInput, InterMatrixUnpinAndRedactAnnouncementInput, } from './matrix-room-messaging.port'; + +export type { + InterCalendarEventWindow, + InterChatCoopCalendarPort, + InterCoopCalendarEventRead, +} from './chatcoop-calendar.port'; diff --git a/components/inter/src/tokens.ts b/components/inter/src/tokens.ts index 3001843ae23..b554dcd68e0 100644 --- a/components/inter/src/tokens.ts +++ b/components/inter/src/tokens.ts @@ -7,3 +7,6 @@ export const INTER_PROJECT_COMMUNICATION_ARTIFACTS = Symbol.for( /** Matrix: отправка сообщений и закрепления (ChatCoop / MatrixApiService). */ export const INTER_MATRIX_ROOM_MESSAGING = Symbol.for('InterMatrixRoomMessaging'); + +/** ChatCoop: чтение событий календаря для Capital и др. */ +export const INTER_CHATCOOP_CALENDAR = Symbol.for('InterChatCoopCalendar'); diff --git a/components/sdk/src/mutations/chatcoop/createCalendarEvent.ts b/components/sdk/src/mutations/chatcoop/createCalendarEvent.ts new file mode 100644 index 00000000000..83e67909647 --- /dev/null +++ b/components/sdk/src/mutations/chatcoop/createCalendarEvent.ts @@ -0,0 +1,19 @@ +import { chatCoopCalendarEventSelector } from '../../selectors/chatcoop' +import { $, type GraphQLTypes, type InputType, type ModelTypes, Selector } from '../../zeus/index' + +export const name = 'chatcoopCreateCalendarEvent' + +export const mutation = Selector('Mutation')({ + [name]: [{ data: $('data', 'CreateChatCoopCalendarEventInput!') }, chatCoopCalendarEventSelector], +}) + +export interface IInput { + /** + * @private + */ + [key: string]: unknown + + data: ModelTypes['CreateChatCoopCalendarEventInput'] +} + +export type IOutput = InputType diff --git a/components/sdk/src/mutations/chatcoop/createCalendarIcsSubscription.ts b/components/sdk/src/mutations/chatcoop/createCalendarIcsSubscription.ts new file mode 100644 index 00000000000..1bd5f876199 --- /dev/null +++ b/components/sdk/src/mutations/chatcoop/createCalendarIcsSubscription.ts @@ -0,0 +1,17 @@ +import { chatCoopCalendarIcsUrlSelector } from '../../selectors/chatcoop' +import { type GraphQLTypes, type InputType, Selector } from '../../zeus/index' + +export const name = 'chatcoopCreateCalendarIcsSubscription' + +export const mutation = Selector('Mutation')({ + [name]: chatCoopCalendarIcsUrlSelector, +}) + +export interface IInput { + /** + * @private + */ + [key: string]: unknown +} + +export type IOutput = InputType diff --git a/components/sdk/src/mutations/chatcoop/deleteCalendarEvent.ts b/components/sdk/src/mutations/chatcoop/deleteCalendarEvent.ts new file mode 100644 index 00000000000..2bf4b10d603 --- /dev/null +++ b/components/sdk/src/mutations/chatcoop/deleteCalendarEvent.ts @@ -0,0 +1,18 @@ +import { $, type GraphQLTypes, type InputType, Selector } from '../../zeus/index' + +export const name = 'chatcoopDeleteCalendarEvent' + +export const mutation = Selector('Mutation')({ + [name]: [{ id: $('id', 'String!') }, true], +}) + +export interface IInput { + /** + * @private + */ + [key: string]: unknown + + id: string +} + +export type IOutput = InputType diff --git a/components/sdk/src/mutations/chatcoop/index.ts b/components/sdk/src/mutations/chatcoop/index.ts index 7fde63287dc..700ef2d1dd4 100644 --- a/components/sdk/src/mutations/chatcoop/index.ts +++ b/components/sdk/src/mutations/chatcoop/index.ts @@ -1 +1,5 @@ export * as CreateAccount from './createAccount' +export * as CreateCalendarEvent from './createCalendarEvent' +export * as CreateCalendarIcsSubscription from './createCalendarIcsSubscription' +export * as DeleteCalendarEvent from './deleteCalendarEvent' +export * as UpdateCalendarEvent from './updateCalendarEvent' diff --git a/components/sdk/src/mutations/chatcoop/updateCalendarEvent.ts b/components/sdk/src/mutations/chatcoop/updateCalendarEvent.ts new file mode 100644 index 00000000000..1de190a0e2b --- /dev/null +++ b/components/sdk/src/mutations/chatcoop/updateCalendarEvent.ts @@ -0,0 +1,19 @@ +import { chatCoopCalendarEventSelector } from '../../selectors/chatcoop' +import { $, type GraphQLTypes, type InputType, type ModelTypes, Selector } from '../../zeus/index' + +export const name = 'chatcoopUpdateCalendarEvent' + +export const mutation = Selector('Mutation')({ + [name]: [{ data: $('data', 'UpdateChatCoopCalendarEventInput!') }, chatCoopCalendarEventSelector], +}) + +export interface IInput { + /** + * @private + */ + [key: string]: unknown + + data: ModelTypes['UpdateChatCoopCalendarEventInput'] +} + +export type IOutput = InputType diff --git a/components/sdk/src/queries/chatcoop/index.ts b/components/sdk/src/queries/chatcoop/index.ts index 20bcfe0dfe4..1e262ff10c5 100644 --- a/components/sdk/src/queries/chatcoop/index.ts +++ b/components/sdk/src/queries/chatcoop/index.ts @@ -2,3 +2,5 @@ export * as CheckUsernameAvailability from './checkUsernameAvailability' export * as GetAccountStatus from './getAccountStatus' export * as GetTranscription from './getTranscription' export * as GetTranscriptions from './getTranscriptions' +export * as ListCalendarEvents from './listCalendarEvents' +export * as ListCalendarRooms from './listCalendarRooms' diff --git a/components/sdk/src/queries/chatcoop/listCalendarEvents.ts b/components/sdk/src/queries/chatcoop/listCalendarEvents.ts new file mode 100644 index 00000000000..e0fc6823bf0 --- /dev/null +++ b/components/sdk/src/queries/chatcoop/listCalendarEvents.ts @@ -0,0 +1,17 @@ +import { chatCoopCalendarEventSelector } from '../../selectors/chatcoop' +import { type GraphQLTypes, type InputType, Selector } from '../../zeus/index' + +export const name = 'chatcoopListCalendarEvents' + +export const query = Selector('Query')({ + [name]: chatCoopCalendarEventSelector, +}) + +export interface IInput { + /** + * @private + */ + [key: string]: unknown +} + +export type IOutput = InputType diff --git a/components/sdk/src/queries/chatcoop/listCalendarRooms.ts b/components/sdk/src/queries/chatcoop/listCalendarRooms.ts new file mode 100644 index 00000000000..a35a809872e --- /dev/null +++ b/components/sdk/src/queries/chatcoop/listCalendarRooms.ts @@ -0,0 +1,17 @@ +import { chatCoopCalendarRoomOptionSelector } from '../../selectors/chatcoop' +import { type GraphQLTypes, type InputType, Selector } from '../../zeus/index' + +export const name = 'chatcoopListCalendarRooms' + +export const query = Selector('Query')({ + [name]: chatCoopCalendarRoomOptionSelector, +}) + +export interface IInput { + /** + * @private + */ + [key: string]: unknown +} + +export type IOutput = InputType diff --git a/components/sdk/src/selectors/chatcoop/index.ts b/components/sdk/src/selectors/chatcoop/index.ts index d2fb12ebb0b..6faa4457e64 100644 --- a/components/sdk/src/selectors/chatcoop/index.ts +++ b/components/sdk/src/selectors/chatcoop/index.ts @@ -15,5 +15,50 @@ export type chatcoopAccountStatusModel = ModelTypes['MatrixAccountStatusResponse export const chatcoopAccountStatusSelector = Selector('MatrixAccountStatusResponseDTO')(rawChatCoopAccountStatusSelector) export { rawChatCoopAccountStatusSelector } +const rawChatCoopCalendarRoomOptionSelector = { + matrixRoomId: true, + displayLabel: true, +} + +const _validateCalendarRoom: MakeAllFieldsRequired = + rawChatCoopCalendarRoomOptionSelector + +export type chatCoopCalendarRoomOptionModel = ModelTypes['ChatCoopCalendarRoomOption'] + +export const chatCoopCalendarRoomOptionSelector = Selector('ChatCoopCalendarRoomOption')( + rawChatCoopCalendarRoomOptionSelector, +) + +const rawChatCoopCalendarEventSelector = { + id: true, + matrixRoomId: true, + title: true, + description: true, + startsAt: true, + endsAt: true, + createdByUsername: true, + icsSequence: true, + createdAt: true, + updatedAt: true, +} + +const _validateCalendarEvent: MakeAllFieldsRequired = + rawChatCoopCalendarEventSelector + +export type chatCoopCalendarEventModel = ModelTypes['ChatCoopCalendarEvent'] + +export const chatCoopCalendarEventSelector = Selector('ChatCoopCalendarEvent')(rawChatCoopCalendarEventSelector) + +const rawChatCoopCalendarIcsUrlSelector = { + icsUrl: true, +} + +const _validateCalendarIcs: MakeAllFieldsRequired = + rawChatCoopCalendarIcsUrlSelector + +export const chatCoopCalendarIcsUrlSelector = Selector('ChatCoopCalendarIcsUrlResponse')( + rawChatCoopCalendarIcsUrlSelector, +) + // Экспорт селекторов для транскрипций export * from './transcription' diff --git a/components/sdk/src/zeus/const.ts b/components/sdk/src/zeus/const.ts index c92efe707a8..3de188ac087 100644 --- a/components/sdk/src/zeus/const.ts +++ b/components/sdk/src/zeus/const.ts @@ -171,6 +171,7 @@ export const AllTypesProps: Record = { CapitalSegmentFilter:{ status:"SegmentStatus" }, + CapitalStoryContentFormat: "enum" as const, CapitalStoryFilter:{ status:"StoryStatus" }, @@ -259,6 +260,10 @@ export const AllTypesProps: Record = { }, CreateBranchInput:{ + }, + CreateChatCoopCalendarEventInput:{ + endsAt:"DateTime", + startsAt:"DateTime" }, CreateChildOrderInput:{ document:"ReturnByAssetStatementSignedDocumentInput" @@ -854,6 +859,15 @@ export const AllTypesProps: Record = { chatcoopCreateAccount:{ data:"CreateMatrixAccountInputDTO" }, + chatcoopCreateCalendarEvent:{ + data:"CreateChatCoopCalendarEventInput" + }, + chatcoopDeleteCalendarEvent:{ + + }, + chatcoopUpdateCalendarEvent:{ + data:"UpdateChatCoopCalendarEventInput" + }, completeCapitalOnboardingStep:{ data:"CapitalOnboardingStepInput" }, @@ -1649,7 +1663,6 @@ export const AllTypesProps: Record = { }, StoryStatus: "enum" as const, - CapitalStoryContentFormat: "enum" as const, SubmitVoteInput:{ votes:"VoteDistributionInput" }, @@ -1680,6 +1693,10 @@ export const AllTypesProps: Record = { UpdateBankAccountInput:{ data:"BankAccountInput" }, + UpdateChatCoopCalendarEventInput:{ + endsAt:"DateTime", + startsAt:"DateTime" + }, UpdateEntrepreneurDataInput:{ country:"Country", details:"EntrepreneurDetailsInput" @@ -1707,6 +1724,7 @@ export const AllTypesProps: Record = { }, UpdateStoryInput:{ + content_format:"CapitalStoryContentFormat", status:"StoryStatus" }, UserStatus: "enum" as const, @@ -2142,8 +2160,8 @@ export const ReturnTypes: Record = { generator_offer_hash:"String", hours_per_day:"Float", id:"Int", - is_external_contract:"Boolean", is_external_blagorost_agreement:"Boolean", + is_external_contract:"Boolean", last_energy_update:"String", level:"Int", main_wallet:"ProgramWallet", @@ -2567,8 +2585,8 @@ export const ReturnTypes: Record = { _id:"String", _updated_at:"DateTime", block_num:"Float", - coopname:"String", content_format:"CapitalStoryContentFormat", + coopname:"String", created_by:"String", description:"String", issue_hash:"String", @@ -2656,6 +2674,25 @@ export const ReturnTypes: Record = { name:"String", writeoff:"String" }, + ChatCoopCalendarEvent:{ + createdAt:"DateTime", + createdByUsername:"String", + description:"String", + endsAt:"DateTime", + icsSequence:"Int", + id:"String", + matrixRoomId:"String", + startsAt:"DateTime", + title:"String", + updatedAt:"DateTime" + }, + ChatCoopCalendarIcsUrlResponse:{ + icsUrl:"String" + }, + ChatCoopCalendarRoomOption:{ + displayLabel:"String", + matrixRoomId:"String" + }, ContactsDTO:{ chairman:"PublicChairman", details:"OrganizationDetails", @@ -3168,6 +3205,10 @@ export const ReturnTypes: Record = { chairmanConfirmApprove:"Approval", chairmanDeclineApprove:"Approval", chatcoopCreateAccount:"Boolean", + chatcoopCreateCalendarEvent:"ChatCoopCalendarEvent", + chatcoopCreateCalendarIcsSubscription:"ChatCoopCalendarIcsUrlResponse", + chatcoopDeleteCalendarEvent:"Boolean", + chatcoopUpdateCalendarEvent:"ChatCoopCalendarEvent", completeCapitalOnboardingStep:"CapitalOnboardingState", completeChairmanAgendaStep:"ChairmanOnboardingState", completeChairmanGeneralMeetStep:"ChairmanOnboardingState", @@ -3673,6 +3714,8 @@ export const ReturnTypes: Record = { chatcoopGetAccountStatus:"MatrixAccountStatusResponseDTO", chatcoopGetTranscription:"CallTranscriptionWithSegments", chatcoopGetTranscriptions:"CallTranscription", + chatcoopListCalendarEvents:"ChatCoopCalendarEvent", + chatcoopListCalendarRooms:"ChatCoopCalendarRoomOption", getAccount:"Account", getAccounts:"AccountsPaginationResult", getActions:"PaginatedActionsPaginationResult", diff --git a/components/sdk/src/zeus/index.ts b/components/sdk/src/zeus/index.ts index 88ab82ddc02..143e3ff2397 100644 --- a/components/sdk/src/zeus/index.ts +++ b/components/sdk/src/zeus/index.ts @@ -2031,6 +2031,7 @@ export type ValueTypes = { endedAt?:boolean | `@${string}`, id?:boolean | `@${string}`, matrixRoomId?:boolean | `@${string}`, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants?:boolean | `@${string}`, roomId?:boolean | `@${string}`, roomName?:boolean | `@${string}`, @@ -2264,10 +2265,10 @@ export type ValueTypes = { hours_per_day?:boolean | `@${string}`, /** ID в блокчейне */ id?:boolean | `@${string}`, - /** Является ли внешним контрактом */ - is_external_contract?:boolean | `@${string}`, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?:boolean | `@${string}`, + /** Является ли внешним контрактом */ + is_external_contract?:boolean | `@${string}`, /** Последнее обновление энергии */ last_energy_update?:boolean | `@${string}`, /** Уровень участника */ @@ -3253,10 +3254,10 @@ export type ValueTypes = { _updated_at?:boolean | `@${string}`, /** Номер блока крайней синхронизации с блокчейном */ block_num?:boolean | `@${string}`, - /** Имя аккаунта кооператива */ - coopname?:boolean | `@${string}`, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format?:boolean | `@${string}`, + /** Имя аккаунта кооператива */ + coopname?:boolean | `@${string}`, /** Имя пользователя, создавшего историю */ created_by?:boolean | `@${string}`, /** Описание истории */ @@ -3277,6 +3278,8 @@ export type ValueTypes = { title?:boolean | `@${string}`, __typename?: boolean | `@${string}` }>; + /** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +["CapitalStoryContentFormat"]:CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -3467,6 +3470,29 @@ export type ValueTypes = { /** Списанные средства */ writeoff?:boolean | `@${string}`, __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarEvent"]: AliasType<{ + createdAt?:boolean | `@${string}`, + createdByUsername?:boolean | `@${string}`, + description?:boolean | `@${string}`, + endsAt?:boolean | `@${string}`, + icsSequence?:boolean | `@${string}`, + id?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + startsAt?:boolean | `@${string}`, + title?:boolean | `@${string}`, + updatedAt?:boolean | `@${string}`, + __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarIcsUrlResponse"]: AliasType<{ + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl?:boolean | `@${string}`, + __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarRoomOption"]: AliasType<{ + displayLabel?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + __typename?: boolean | `@${string}` }>; ["CheckMatrixUsernameInput"]: { username: string | Variable @@ -3834,6 +3860,13 @@ export type ValueTypes = { short_name: string | Variable, /** Имя аккаунта уполномоченного (председателя) кооперативного участка */ trustee: string | Variable +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null | Variable, + endsAt?: ValueTypes["DateTime"] | undefined | null | Variable, + matrixRoomId: string | Variable, + startsAt: ValueTypes["DateTime"] | Variable, + title: string | Variable }; ["CreateChildOrderInput"]: { /** Имя кооператива */ @@ -4157,10 +4190,10 @@ export type ValueTypes = { phone: string | Variable }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string | Variable, /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: ValueTypes["CapitalStoryContentFormat"] | undefined | null | Variable, + /** Имя аккаунта кооператива */ + coopname: string | Variable, /** Описание истории */ description?: string | undefined | null | Variable, /** Хеш задачи (если история привязана к задаче) */ @@ -5722,6 +5755,11 @@ capitalUpdateStory?: [{ data: ValueTypes["UpdateStoryInput"] | Variable},ValueTypes["Approval"]], chairmanDeclineApprove?: [{ data: ValueTypes["DeclineApproveInput"] | Variable},ValueTypes["Approval"]], chatcoopCreateAccount?: [{ data: ValueTypes["CreateMatrixAccountInputDTO"] | Variable},boolean | `@${string}`], +chatcoopCreateCalendarEvent?: [{ data: ValueTypes["CreateChatCoopCalendarEventInput"] | Variable},ValueTypes["ChatCoopCalendarEvent"]], + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription?:ValueTypes["ChatCoopCalendarIcsUrlResponse"], +chatcoopDeleteCalendarEvent?: [{ id: string | Variable},boolean | `@${string}`], +chatcoopUpdateCalendarEvent?: [{ data: ValueTypes["UpdateChatCoopCalendarEventInput"] | Variable},ValueTypes["ChatCoopCalendarEvent"]], completeCapitalOnboardingStep?: [{ data: ValueTypes["CapitalOnboardingStepInput"] | Variable},ValueTypes["CapitalOnboardingState"]], completeChairmanAgendaStep?: [{ data: ValueTypes["ChairmanOnboardingAgendaInput"] | Variable},ValueTypes["ChairmanOnboardingState"]], completeChairmanGeneralMeetStep?: [{ data: ValueTypes["ChairmanOnboardingGeneralMeetInput"] | Variable},ValueTypes["ChairmanOnboardingState"]], @@ -6843,6 +6881,10 @@ chatcoopCheckUsernameAvailability?: [{ data: ValueTypes["CheckMatrixUsernameInpu chatcoopGetAccountStatus?:ValueTypes["MatrixAccountStatusResponseDTO"], chatcoopGetTranscription?: [{ data: ValueTypes["GetTranscriptionInput"] | Variable},ValueTypes["CallTranscriptionWithSegments"]], chatcoopGetTranscriptions?: [{ data?: ValueTypes["GetTranscriptionsInput"] | undefined | null | Variable},ValueTypes["CallTranscription"]], + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents?:ValueTypes["ChatCoopCalendarEvent"], + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms?:ValueTypes["ChatCoopCalendarRoomOption"], getAccount?: [{ data: ValueTypes["GetAccountInput"] | Variable},ValueTypes["Account"]], getAccounts?: [{ data?: ValueTypes["GetAccountsInput"] | undefined | null | Variable, options?: ValueTypes["PaginationInput"] | undefined | null | Variable},ValueTypes["AccountsPaginationResult"]], getActions?: [{ filters?: ValueTypes["ActionFiltersInput"] | undefined | null | Variable, pagination?: ValueTypes["PaginationInput"] | undefined | null | Variable},ValueTypes["PaginatedActionsPaginationResult"]], @@ -7785,8 +7827,6 @@ searchPrivateAccounts?: [{ data: ValueTypes["SearchPrivateAccountsInput"] | Vari }; /** Статус истории в системе CAPITAL */ ["StoryStatus"]:StoryStatus; - /** Формат содержимого требования (истории) в CAPITAL */ -["CapitalStoryContentFormat"]:CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string | Variable, @@ -7904,7 +7944,9 @@ searchPrivateAccounts?: [{ data: ValueTypes["SearchPrivateAccountsInput"] | Vari createdAt?:boolean | `@${string}`, endOffset?:boolean | `@${string}`, id?:boolean | `@${string}`, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity?:boolean | `@${string}`, + /** Отображаемое имя из Synapse (displayname) */ speakerName?:boolean | `@${string}`, startOffset?:boolean | `@${string}`, text?:boolean | `@${string}`, @@ -7961,6 +8003,14 @@ searchPrivateAccounts?: [{ data: ValueTypes["SearchPrivateAccountsInput"] | Vari method_id: string | Variable, /** Имя аккаунта пользователя */ username: string | Variable +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null | Variable, + endsAt?: ValueTypes["DateTime"] | undefined | null | Variable, + id: string | Variable, + matrixRoomId: string | Variable, + startsAt: ValueTypes["DateTime"] | Variable, + title: string | Variable }; ["UpdateEntrepreneurDataInput"]: { /** Дата рождения */ @@ -8095,6 +8145,8 @@ searchPrivateAccounts?: [{ data: ValueTypes["SearchPrivateAccountsInput"] | Vari provider_name?: string | undefined | null | Variable }; ["UpdateStoryInput"]: { + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: ValueTypes["CapitalStoryContentFormat"] | undefined | null | Variable, /** Описание истории */ description?: string | undefined | null | Variable, /** Хеш задачи (если история привязана к задаче) */ @@ -9386,6 +9438,7 @@ export type ResolverInputTypes = { endedAt?:boolean | `@${string}`, id?:boolean | `@${string}`, matrixRoomId?:boolean | `@${string}`, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants?:boolean | `@${string}`, roomId?:boolean | `@${string}`, roomName?:boolean | `@${string}`, @@ -9619,10 +9672,10 @@ export type ResolverInputTypes = { hours_per_day?:boolean | `@${string}`, /** ID в блокчейне */ id?:boolean | `@${string}`, - /** Является ли внешним контрактом */ - is_external_contract?:boolean | `@${string}`, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?:boolean | `@${string}`, + /** Является ли внешним контрактом */ + is_external_contract?:boolean | `@${string}`, /** Последнее обновление энергии */ last_energy_update?:boolean | `@${string}`, /** Уровень участника */ @@ -10608,10 +10661,10 @@ export type ResolverInputTypes = { _updated_at?:boolean | `@${string}`, /** Номер блока крайней синхронизации с блокчейном */ block_num?:boolean | `@${string}`, - /** Имя аккаунта кооператива */ - coopname?:boolean | `@${string}`, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format?:boolean | `@${string}`, + /** Имя аккаунта кооператива */ + coopname?:boolean | `@${string}`, /** Имя пользователя, создавшего историю */ created_by?:boolean | `@${string}`, /** Описание истории */ @@ -10632,6 +10685,8 @@ export type ResolverInputTypes = { title?:boolean | `@${string}`, __typename?: boolean | `@${string}` }>; + /** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +["CapitalStoryContentFormat"]:CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -10822,6 +10877,29 @@ export type ResolverInputTypes = { /** Списанные средства */ writeoff?:boolean | `@${string}`, __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarEvent"]: AliasType<{ + createdAt?:boolean | `@${string}`, + createdByUsername?:boolean | `@${string}`, + description?:boolean | `@${string}`, + endsAt?:boolean | `@${string}`, + icsSequence?:boolean | `@${string}`, + id?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + startsAt?:boolean | `@${string}`, + title?:boolean | `@${string}`, + updatedAt?:boolean | `@${string}`, + __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarIcsUrlResponse"]: AliasType<{ + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl?:boolean | `@${string}`, + __typename?: boolean | `@${string}` +}>; + ["ChatCoopCalendarRoomOption"]: AliasType<{ + displayLabel?:boolean | `@${string}`, + matrixRoomId?:boolean | `@${string}`, + __typename?: boolean | `@${string}` }>; ["CheckMatrixUsernameInput"]: { username: string @@ -11189,6 +11267,13 @@ export type ResolverInputTypes = { short_name: string, /** Имя аккаунта уполномоченного (председателя) кооперативного участка */ trustee: string +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ResolverInputTypes["DateTime"] | undefined | null, + matrixRoomId: string, + startsAt: ResolverInputTypes["DateTime"], + title: string }; ["CreateChildOrderInput"]: { /** Имя кооператива */ @@ -11512,10 +11597,10 @@ export type ResolverInputTypes = { phone: string }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: ResolverInputTypes["CapitalStoryContentFormat"] | undefined | null, + /** Имя аккаунта кооператива */ + coopname: string, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -13077,6 +13162,11 @@ capitalUpdateStory?: [{ data: ResolverInputTypes["UpdateStoryInput"]},ResolverIn chairmanConfirmApprove?: [{ data: ResolverInputTypes["ConfirmApproveInput"]},ResolverInputTypes["Approval"]], chairmanDeclineApprove?: [{ data: ResolverInputTypes["DeclineApproveInput"]},ResolverInputTypes["Approval"]], chatcoopCreateAccount?: [{ data: ResolverInputTypes["CreateMatrixAccountInputDTO"]},boolean | `@${string}`], +chatcoopCreateCalendarEvent?: [{ data: ResolverInputTypes["CreateChatCoopCalendarEventInput"]},ResolverInputTypes["ChatCoopCalendarEvent"]], + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription?:ResolverInputTypes["ChatCoopCalendarIcsUrlResponse"], +chatcoopDeleteCalendarEvent?: [{ id: string},boolean | `@${string}`], +chatcoopUpdateCalendarEvent?: [{ data: ResolverInputTypes["UpdateChatCoopCalendarEventInput"]},ResolverInputTypes["ChatCoopCalendarEvent"]], completeCapitalOnboardingStep?: [{ data: ResolverInputTypes["CapitalOnboardingStepInput"]},ResolverInputTypes["CapitalOnboardingState"]], completeChairmanAgendaStep?: [{ data: ResolverInputTypes["ChairmanOnboardingAgendaInput"]},ResolverInputTypes["ChairmanOnboardingState"]], completeChairmanGeneralMeetStep?: [{ data: ResolverInputTypes["ChairmanOnboardingGeneralMeetInput"]},ResolverInputTypes["ChairmanOnboardingState"]], @@ -14200,6 +14290,10 @@ chatcoopCheckUsernameAvailability?: [{ data: ResolverInputTypes["CheckMatrixUser chatcoopGetAccountStatus?:ResolverInputTypes["MatrixAccountStatusResponseDTO"], chatcoopGetTranscription?: [{ data: ResolverInputTypes["GetTranscriptionInput"]},ResolverInputTypes["CallTranscriptionWithSegments"]], chatcoopGetTranscriptions?: [{ data?: ResolverInputTypes["GetTranscriptionsInput"] | undefined | null},ResolverInputTypes["CallTranscription"]], + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents?:ResolverInputTypes["ChatCoopCalendarEvent"], + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms?:ResolverInputTypes["ChatCoopCalendarRoomOption"], getAccount?: [{ data: ResolverInputTypes["GetAccountInput"]},ResolverInputTypes["Account"]], getAccounts?: [{ data?: ResolverInputTypes["GetAccountsInput"] | undefined | null, options?: ResolverInputTypes["PaginationInput"] | undefined | null},ResolverInputTypes["AccountsPaginationResult"]], getActions?: [{ filters?: ResolverInputTypes["ActionFiltersInput"] | undefined | null, pagination?: ResolverInputTypes["PaginationInput"] | undefined | null},ResolverInputTypes["PaginatedActionsPaginationResult"]], @@ -15142,8 +15236,6 @@ searchPrivateAccounts?: [{ data: ResolverInputTypes["SearchPrivateAccountsInput" }; /** Статус истории в системе CAPITAL */ ["StoryStatus"]:StoryStatus; - /** Формат содержимого требования (истории) в CAPITAL */ -["CapitalStoryContentFormat"]:CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string, @@ -15261,7 +15353,9 @@ searchPrivateAccounts?: [{ data: ResolverInputTypes["SearchPrivateAccountsInput" createdAt?:boolean | `@${string}`, endOffset?:boolean | `@${string}`, id?:boolean | `@${string}`, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity?:boolean | `@${string}`, + /** Отображаемое имя из Synapse (displayname) */ speakerName?:boolean | `@${string}`, startOffset?:boolean | `@${string}`, text?:boolean | `@${string}`, @@ -15318,6 +15412,14 @@ searchPrivateAccounts?: [{ data: ResolverInputTypes["SearchPrivateAccountsInput" method_id: string, /** Имя аккаунта пользователя */ username: string +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ResolverInputTypes["DateTime"] | undefined | null, + id: string, + matrixRoomId: string, + startsAt: ResolverInputTypes["DateTime"], + title: string }; ["UpdateEntrepreneurDataInput"]: { /** Дата рождения */ @@ -15452,6 +15554,8 @@ searchPrivateAccounts?: [{ data: ResolverInputTypes["SearchPrivateAccountsInput" provider_name?: string | undefined | null }; ["UpdateStoryInput"]: { + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: ResolverInputTypes["CapitalStoryContentFormat"] | undefined | null, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -16721,6 +16825,7 @@ export type ModelTypes = { endedAt?: ModelTypes["DateTime"] | undefined | null, id: string, matrixRoomId: string, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants: Array, roomId: string, roomName: string, @@ -16947,10 +17052,10 @@ export type ModelTypes = { hours_per_day?: number | undefined | null, /** ID в блокчейне */ id?: number | undefined | null, - /** Является ли внешним контрактом */ - is_external_contract?: boolean | undefined | null, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?: boolean | undefined | null, + /** Является ли внешним контрактом */ + is_external_contract?: boolean | undefined | null, /** Последнее обновление энергии */ last_energy_update?: string | undefined | null, /** Уровень участника */ @@ -17913,10 +18018,10 @@ export type ModelTypes = { _updated_at: ModelTypes["DateTime"], /** Номер блока крайней синхронизации с блокчейном */ block_num?: number | undefined | null, - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format: ModelTypes["CapitalStoryContentFormat"], + /** Имя аккаунта кооператива */ + coopname: string, /** Имя пользователя, создавшего историю */ created_by: string, /** Описание истории */ @@ -17936,6 +18041,7 @@ export type ModelTypes = { /** Название истории */ title: string }; + ["CapitalStoryContentFormat"]:CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -18120,6 +18226,26 @@ export type ModelTypes = { name: string, /** Списанные средства */ writeoff: string +}; + ["ChatCoopCalendarEvent"]: { + createdAt: ModelTypes["DateTime"], + createdByUsername: string, + description?: string | undefined | null, + endsAt?: ModelTypes["DateTime"] | undefined | null, + icsSequence: number, + id: string, + matrixRoomId: string, + startsAt: ModelTypes["DateTime"], + title: string, + updatedAt: ModelTypes["DateTime"] +}; + ["ChatCoopCalendarIcsUrlResponse"]: { + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl: string +}; + ["ChatCoopCalendarRoomOption"]: { + displayLabel: string, + matrixRoomId: string }; ["CheckMatrixUsernameInput"]: { username: string @@ -18481,6 +18607,13 @@ export type ModelTypes = { short_name: string, /** Имя аккаунта уполномоченного (председателя) кооперативного участка */ trustee: string +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ModelTypes["DateTime"] | undefined | null, + matrixRoomId: string, + startsAt: ModelTypes["DateTime"], + title: string }; ["CreateChildOrderInput"]: { /** Имя кооператива */ @@ -18804,10 +18937,10 @@ export type ModelTypes = { phone: string }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: ModelTypes["CapitalStoryContentFormat"] | undefined | null, + /** Имя аккаунта кооператива */ + coopname: string, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -20397,6 +20530,14 @@ export type ModelTypes = { chairmanDeclineApprove: ModelTypes["Approval"], /** Создать Matrix аккаунт с именем пользователя и паролем */ chatcoopCreateAccount: boolean, + /** Создать событие календаря */ + chatcoopCreateCalendarEvent: ModelTypes["ChatCoopCalendarEvent"], + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription: ModelTypes["ChatCoopCalendarIcsUrlResponse"], + /** Удалить событие календаря */ + chatcoopDeleteCalendarEvent: boolean, + /** Обновить событие календаря */ + chatcoopUpdateCalendarEvent: ModelTypes["ChatCoopCalendarEvent"], /** Выполнить шаг онбординга capital (создание предложения повестки) */ completeCapitalOnboardingStep: ModelTypes["CapitalOnboardingState"], /** Выполнить один из шагов онбординга (создание предложения повестки) */ @@ -21582,6 +21723,10 @@ export type ModelTypes = { chatcoopGetTranscription?: ModelTypes["CallTranscriptionWithSegments"] | undefined | null, /** Получить список транскрипций звонков */ chatcoopGetTranscriptions: Array, + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents: Array, + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms: Array, /** Получить сводную информацию о аккаунте */ getAccount: ModelTypes["Account"], /** Получить сводную информацию о аккаунтах системы */ @@ -22528,7 +22673,6 @@ export type ModelTypes = { project_hash: string }; ["StoryStatus"]:StoryStatus; - ["CapitalStoryContentFormat"]:CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string, @@ -22638,7 +22782,9 @@ export type ModelTypes = { createdAt: ModelTypes["DateTime"], endOffset: number, id: string, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity: string, + /** Отображаемое имя из Synapse (displayname) */ speakerName: string, startOffset: number, text: string @@ -22693,6 +22839,14 @@ export type ModelTypes = { method_id: string, /** Имя аккаунта пользователя */ username: string +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: ModelTypes["DateTime"] | undefined | null, + id: string, + matrixRoomId: string, + startsAt: ModelTypes["DateTime"], + title: string }; ["UpdateEntrepreneurDataInput"]: { /** Дата рождения */ @@ -22827,6 +22981,8 @@ export type ModelTypes = { provider_name?: string | undefined | null }; ["UpdateStoryInput"]: { + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: ModelTypes["CapitalStoryContentFormat"] | undefined | null, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -24116,6 +24272,7 @@ export type GraphQLTypes = { endedAt?: GraphQLTypes["DateTime"] | undefined | null, id: string, matrixRoomId: string, + /** Отображаемые имена участников (Synapse displayname); в БД хранятся канонические Matrix user id */ participants: Array, roomId: string, roomName: string, @@ -24349,10 +24506,10 @@ export type GraphQLTypes = { hours_per_day?: number | undefined | null, /** ID в блокчейне */ id?: number | undefined | null, - /** Является ли внешним контрактом */ - is_external_contract?: boolean | undefined | null, /** Соглашение Благорост предоставлено при импорте (внешний документ) */ is_external_blagorost_agreement?: boolean | undefined | null, + /** Является ли внешним контрактом */ + is_external_contract?: boolean | undefined | null, /** Последнее обновление энергии */ last_energy_update?: string | undefined | null, /** Уровень участника */ @@ -25338,10 +25495,10 @@ export type GraphQLTypes = { _updated_at: GraphQLTypes["DateTime"], /** Номер блока крайней синхронизации с блокчейном */ block_num?: number | undefined | null, - /** Имя аккаунта кооператива */ - coopname: string, /** Формат содержимого (markdown-текст или BPMN 2.0 XML в description) */ content_format: GraphQLTypes["CapitalStoryContentFormat"], + /** Имя аккаунта кооператива */ + coopname: string, /** Имя пользователя, создавшего историю */ created_by: string, /** Описание истории */ @@ -25361,6 +25518,8 @@ export type GraphQLTypes = { /** Название истории */ title: string }; + /** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +["CapitalStoryContentFormat"]: CapitalStoryContentFormat; /** Параметры фильтрации для запросов историй CAPITAL */ ["CapitalStoryFilter"]: { /** Фильтр по названию кооператива */ @@ -25551,6 +25710,29 @@ export type GraphQLTypes = { name: string, /** Списанные средства */ writeoff: string +}; + ["ChatCoopCalendarEvent"]: { + __typename: "ChatCoopCalendarEvent", + createdAt: GraphQLTypes["DateTime"], + createdByUsername: string, + description?: string | undefined | null, + endsAt?: GraphQLTypes["DateTime"] | undefined | null, + icsSequence: number, + id: string, + matrixRoomId: string, + startsAt: GraphQLTypes["DateTime"], + title: string, + updatedAt: GraphQLTypes["DateTime"] +}; + ["ChatCoopCalendarIcsUrlResponse"]: { + __typename: "ChatCoopCalendarIcsUrlResponse", + /** Полный URL ленты ICS с секретом в query (без JWT) */ + icsUrl: string +}; + ["ChatCoopCalendarRoomOption"]: { + __typename: "ChatCoopCalendarRoomOption", + displayLabel: string, + matrixRoomId: string }; ["CheckMatrixUsernameInput"]: { username: string @@ -25918,6 +26100,13 @@ export type GraphQLTypes = { short_name: string, /** Имя аккаунта уполномоченного (председателя) кооперативного участка */ trustee: string +}; + ["CreateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: GraphQLTypes["DateTime"] | undefined | null, + matrixRoomId: string, + startsAt: GraphQLTypes["DateTime"], + title: string }; ["CreateChildOrderInput"]: { /** Имя кооператива */ @@ -26241,10 +26430,10 @@ export type GraphQLTypes = { phone: string }; ["CreateStoryInput"]: { - /** Имя аккаунта кооператива */ - coopname: string, - /** Формат содержимого; по умолчанию MARKDOWN */ + /** Формат содержимого; по умолчанию MARKDOWN */ content_format?: GraphQLTypes["CapitalStoryContentFormat"] | undefined | null, + /** Имя аккаунта кооператива */ + coopname: string, /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ @@ -27887,6 +28076,14 @@ export type GraphQLTypes = { chairmanDeclineApprove: GraphQLTypes["Approval"], /** Создать Matrix аккаунт с именем пользователя и паролем */ chatcoopCreateAccount: boolean, + /** Создать событие календаря */ + chatcoopCreateCalendarEvent: GraphQLTypes["ChatCoopCalendarEvent"], + /** Выдать или обновить персональный URL подписки ICS (секрет в query) */ + chatcoopCreateCalendarIcsSubscription: GraphQLTypes["ChatCoopCalendarIcsUrlResponse"], + /** Удалить событие календаря */ + chatcoopDeleteCalendarEvent: boolean, + /** Обновить событие календаря */ + chatcoopUpdateCalendarEvent: GraphQLTypes["ChatCoopCalendarEvent"], /** Выполнить шаг онбординга capital (создание предложения повестки) */ completeCapitalOnboardingStep: GraphQLTypes["CapitalOnboardingState"], /** Выполнить один из шагов онбординга (создание предложения повестки) */ @@ -29140,6 +29337,10 @@ export type GraphQLTypes = { chatcoopGetTranscription?: GraphQLTypes["CallTranscriptionWithSegments"] | undefined | null, /** Получить список транскрипций звонков */ chatcoopGetTranscriptions: Array, + /** Список событий календаря кооператива */ + chatcoopListCalendarEvents: Array, + /** Незашифрованные комнаты из реестра ChatCoop для привязки события календаря */ + chatcoopListCalendarRooms: Array, /** Получить сводную информацию о аккаунте */ getAccount: GraphQLTypes["Account"], /** Получить сводную информацию о аккаунтах системы */ @@ -30106,8 +30307,6 @@ export type GraphQLTypes = { }; /** Статус истории в системе CAPITAL */ ["StoryStatus"]: StoryStatus; - /** Формат содержимого требования (истории) в CAPITAL */ -["CapitalStoryContentFormat"]: CapitalStoryContentFormat; ["SubmitVoteInput"]: { /** Имя аккаунта кооператива */ coopname: string, @@ -30226,7 +30425,9 @@ export type GraphQLTypes = { createdAt: GraphQLTypes["DateTime"], endOffset: number, id: string, + /** Канонический Matrix user id (@localpart:server) */ speakerIdentity: string, + /** Отображаемое имя из Synapse (displayname) */ speakerName: string, startOffset: number, text: string @@ -30282,6 +30483,14 @@ export type GraphQLTypes = { method_id: string, /** Имя аккаунта пользователя */ username: string +}; + ["UpdateChatCoopCalendarEventInput"]: { + description?: string | undefined | null, + endsAt?: GraphQLTypes["DateTime"] | undefined | null, + id: string, + matrixRoomId: string, + startsAt: GraphQLTypes["DateTime"], + title: string }; ["UpdateEntrepreneurDataInput"]: { /** Дата рождения */ @@ -30416,7 +30625,9 @@ export type GraphQLTypes = { provider_name?: string | undefined | null }; ["UpdateStoryInput"]: { - /** Описание истории */ + /** Формат тела требования (MARKDOWN, BPMN, DRAWIO, MERMAID) */ + content_format?: GraphQLTypes["CapitalStoryContentFormat"] | undefined | null, + /** Описание истории */ description?: string | undefined | null, /** Хеш задачи (если история привязана к задаче) */ issue_hash?: string | undefined | null, @@ -30628,6 +30839,13 @@ export enum CapitalOnboardingStep { generator_offer_template = "generator_offer_template", generator_program_template = "generator_program_template" } +/** Формат содержимого требования (истории) в CAPITAL: MARKDOWN, BPMN (XML), DRAWIO (draw.io / diagrams.net XML) или MERMAID (текст диаграммы) */ +export enum CapitalStoryContentFormat { + BPMN = "BPMN", + DRAWIO = "DRAWIO", + MARKDOWN = "MARKDOWN", + MERMAID = "MERMAID" +} export enum ChairmanOnboardingAgendaStep { participant_application = "participant_application", privacy_agreement = "privacy_agreement", @@ -30909,13 +31127,6 @@ export enum StoryStatus { COMPLETED = "COMPLETED", PENDING = "PENDING" } -/** Формат содержимого требования (истории) в CAPITAL */ -export enum CapitalStoryContentFormat { - BPMN = "BPMN", - DRAWIO = "DRAWIO", - MARKDOWN = "MARKDOWN", - MERMAID = "MERMAID" -} /** Состояние контроллера кооператива */ export enum SystemStatus { active = "active", @@ -30995,6 +31206,7 @@ type ZEUS_VARIABLES = { ["CapitalOnboardingStepInput"]: ValueTypes["CapitalOnboardingStepInput"]; ["CapitalProjectFilter"]: ValueTypes["CapitalProjectFilter"]; ["CapitalSegmentFilter"]: ValueTypes["CapitalSegmentFilter"]; + ["CapitalStoryContentFormat"]: ValueTypes["CapitalStoryContentFormat"]; ["CapitalStoryFilter"]: ValueTypes["CapitalStoryFilter"]; ["CapitalTimeEntriesFilter"]: ValueTypes["CapitalTimeEntriesFilter"]; ["CapitalTimeStatsInput"]: ValueTypes["CapitalTimeStatsInput"]; @@ -31025,6 +31237,7 @@ type ZEUS_VARIABLES = { ["Country"]: ValueTypes["Country"]; ["CreateAnnualGeneralMeetInput"]: ValueTypes["CreateAnnualGeneralMeetInput"]; ["CreateBranchInput"]: ValueTypes["CreateBranchInput"]; + ["CreateChatCoopCalendarEventInput"]: ValueTypes["CreateChatCoopCalendarEventInput"]; ["CreateChildOrderInput"]: ValueTypes["CreateChildOrderInput"]; ["CreateCommitInput"]: ValueTypes["CreateCommitInput"]; ["CreateCycleInput"]: ValueTypes["CreateCycleInput"]; @@ -31230,7 +31443,6 @@ type ZEUS_VARIABLES = { ["StartVotingInput"]: ValueTypes["StartVotingInput"]; ["StopProjectInput"]: ValueTypes["StopProjectInput"]; ["StoryStatus"]: ValueTypes["StoryStatus"]; - ["CapitalStoryContentFormat"]: ValueTypes["CapitalStoryContentFormat"]; ["SubmitVoteInput"]: ValueTypes["SubmitVoteInput"]; ["SupplyOnRequestInput"]: ValueTypes["SupplyOnRequestInput"]; ["SystemStatus"]: ValueTypes["SystemStatus"]; @@ -31241,6 +31453,7 @@ type ZEUS_VARIABLES = { ["Update"]: ValueTypes["Update"]; ["UpdateAccountInput"]: ValueTypes["UpdateAccountInput"]; ["UpdateBankAccountInput"]: ValueTypes["UpdateBankAccountInput"]; + ["UpdateChatCoopCalendarEventInput"]: ValueTypes["UpdateChatCoopCalendarEventInput"]; ["UpdateEntrepreneurDataInput"]: ValueTypes["UpdateEntrepreneurDataInput"]; ["UpdateIndividualDataInput"]: ValueTypes["UpdateIndividualDataInput"]; ["UpdateIssueInput"]: ValueTypes["UpdateIssueInput"];