diff --git a/src/webparts/requestPortal/RequestPortalWebPart.manifest.json b/src/webparts/requestPortal/RequestPortalWebPart.manifest.json index 33195e2..4c47d65 100644 --- a/src/webparts/requestPortal/RequestPortalWebPart.manifest.json +++ b/src/webparts/requestPortal/RequestPortalWebPart.manifest.json @@ -16,6 +16,6 @@ "title": { "default": "request-portal" }, "description": { "default": "request-portal description" }, "officeFabricIconFontName": "Page", - "properties": {} + "properties": { "notificationsEnabled": true } }] } diff --git a/src/webparts/requestPortal/RequestPortalWebPart.ts b/src/webparts/requestPortal/RequestPortalWebPart.ts index e36543c..7de97bf 100644 --- a/src/webparts/requestPortal/RequestPortalWebPart.ts +++ b/src/webparts/requestPortal/RequestPortalWebPart.ts @@ -1,7 +1,12 @@ import * as React from 'react'; import * as ReactDom from 'react-dom'; import { Version } from '@microsoft/sp-core-library'; -import { type IPropertyPaneConfiguration, PropertyPaneDropdown } from '@microsoft/sp-property-pane'; +import { + type IPropertyPaneConfiguration, + PropertyPaneDropdown, + PropertyPaneTextField, + PropertyPaneToggle +} from '@microsoft/sp-property-pane'; import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; import { IReadonlyTheme } from '@microsoft/sp-component-base'; @@ -11,7 +16,12 @@ import { IRequestPortalProps } from './components/IRequestPortalProps'; import { initSP } from './shared/pnp/pnpjs'; import { installErrorLogging } from './shared/diagnostics'; import { setImpersonatedIdentity } from './shared/impersonation'; -import { APPROVAL_API_BASE_URL_LOCAL, APPROVAL_API_BASE_URL_PROD, setApprovalApiBaseUrl } from './shared/config'; +import { + APPROVAL_API_BASE_URL_LOCAL, + APPROVAL_API_BASE_URL_PROD, + setApprovalApiBaseUrl, + setNotificationSettings +} from './shared/config'; import { initListPartitions } from './shared/approval'; import { ErrorOverlay } from './shared/ui'; import { PropertyPaneDebugUser } from './propertyPane/PropertyPaneDebugUser'; @@ -28,6 +38,13 @@ export interface IRequestPortalWebPartProps { debugUserDisplayName: string; /** Куда шлём запросы approval-route (маршруты согласования) — прод или локальный backend. */ approvalApiBaseUrl: string; + /** + * Уведомления согласующим (поток Power Automate). По умолчанию включены: + * у веб-частей, добавленных до появления свойства, оно undefined — это тоже «вкл». + */ + notificationsEnabled?: boolean; + /** Непустой адрес — письма только если активный шаг назначен на него (обкатка). Пусто — всем. */ + notifyOnlyTo?: string; } export default class RequestPortalWebPart extends BaseClientSideWebPart { @@ -41,6 +58,7 @@ export default class RequestPortalWebPart extends BaseClientSideWebPart { if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook @@ -196,6 +217,23 @@ export default class RequestPortalWebPart extends BaseClientSideWebPart } } -// Разрешена ли отправка на согласующего активной строки (см. NOTIFY_ONLY_TO). +// Разрешена ли отправка на согласующего активной строки — по свойствам веб-части +// notificationsEnabled / notifyOnlyTo (см. setNotificationSettings в config.ts). // -// Неизвестный адрес (пользователь не найден, поле пустое, запрос упал) — -// НЕ отправляем: пока заглушка включена, сомнение трактуется в пользу -// «письмо живому человеку лучше не слать», а не наоборот. +// Ограничение стоит ЗДЕСЬ, на стороне портала, а не в потоке Power Automate, +// потому что адресата поток определяет САМ по RowID — тело запроса на выбор +// получателя не влияет. Значит, единственный способ не отправить письмо, не +// трогая поток, — не дёргать его. +// +// При заданном notifyOnlyTo неизвестный адрес (пользователь не найден, поле +// пустое, запрос упал) — НЕ отправляем: сомнение трактуется в пользу «письмо +// живому человеку лучше не слать». async function isNotifyAllowed(approval: ApprovalListService, approverId: number): Promise { - if (!NOTIFY_ONLY_TO) return true; + if (!getNotificationsEnabled()) return false; + + const onlyTo = getNotifyOnlyTo(); + if (!onlyTo) return true; const email = await approval.resolveUserEmail(approverId); - if (email.toLowerCase() === NOTIFY_ONLY_TO.toLowerCase()) return true; + if (email.toLowerCase() === onlyTo.toLowerCase()) return true; console.warn( - `[Оповещение] Включена заглушка рассылки: письмо для «${email || 'адрес неизвестен'}» не отправлено ` + - `(разрешён только ${NOTIFY_ONLY_TO})` + `[Оповещение] Рассылка ограничена одним адресом: письмо для «${email || 'адрес неизвестен'}» не отправлено ` + + `(разрешён только ${onlyTo})` ); return false; } diff --git a/src/webparts/requestPortal/shared/config.ts b/src/webparts/requestPortal/shared/config.ts index 707784f..2f8c957 100644 --- a/src/webparts/requestPortal/shared/config.ts +++ b/src/webparts/requestPortal/shared/config.ts @@ -45,6 +45,28 @@ export function getApprovalApiBaseUrl(): string { return _approvalApiBaseUrl; } +// Уведомления согласующим (поток Power Automate, см. approval/notify.service.ts). +// Настраивается свойствами веб-части notificationsEnabled / notifyOnlyTo — тот +// же модульный синглтон, что и у approvalApiBaseUrl выше. +// enabled — false выключает рассылку целиком; +// onlyTo — непустой адрес: письма уходят ТОЛЬКО если активный шаг назначен +// на него (обкатка рассылки); пусто — всем. +let _notificationsEnabled = true; +let _notifyOnlyTo = ''; + +export function setNotificationSettings(enabled: boolean, onlyTo: string): void { + _notificationsEnabled = enabled; + _notifyOnlyTo = (onlyTo || '').trim(); +} + +export function getNotificationsEnabled(): boolean { + return _notificationsEnabled; +} + +export function getNotifyOnlyTo(): string { + return _notifyOnlyTo; +} + /** * Справочники (Dictionary API) — тот же gateway api.tnsu.kz и тот же AAD-app * (APPROVAL_API_CLIENT_ID), но другой путь/сервис. Используется, например, для