4fccfef7d5
# Conflicts: # src/webparts/requestPortal/RequestPortalWebPart.ts # src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx # src/webparts/requestPortal/forms/Clearance/submit.ts
195 lines
9.5 KiB
TypeScript
195 lines
9.5 KiB
TypeScript
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 { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||
import { IReadonlyTheme } from '@microsoft/sp-component-base';
|
||
|
||
import * as strings from 'RequestPortalWebPartStrings';
|
||
import RequestPortal from './components/RequestPortal';
|
||
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 { initListPartitions } from './shared/approval/listPartitions';
|
||
import { ErrorOverlay } from './shared/ui';
|
||
import { PropertyPaneDebugUser } from './propertyPane/PropertyPaneDebugUser';
|
||
|
||
export interface IRequestPortalWebPartProps {
|
||
/**
|
||
* Отладочная подмена личности — см. shared/impersonation.ts. На бою пусто.
|
||
* Одно поле на оба случая: e-mail, введённый напрямую, хранится как есть;
|
||
* ФИО, найденное поиском по имени (см. propertyPane/DebugUserField), после
|
||
* резолва заменяется собой же на найденный e-mail — отдельное поле под
|
||
* «сырой» ввод не нужно, DebugUserField ведёт его в своём локальном состоянии.
|
||
*/
|
||
debugUserQuery: string;
|
||
debugUserDisplayName: string;
|
||
/** Куда шлём запросы approval-route (маршруты согласования) — прод или локальный backend. */
|
||
approvalApiBaseUrl: string;
|
||
}
|
||
|
||
export default class RequestPortalWebPart extends BaseClientSideWebPart<IRequestPortalWebPartProps> {
|
||
|
||
private _isDarkTheme: boolean = false;
|
||
private _environmentMessage: string = '';
|
||
|
||
public render(): void {
|
||
// Именно здесь, а не в onInit: свойство меняется в панели без перезапуска
|
||
// веб-части, и render вызывается на каждое такое изменение.
|
||
setImpersonatedIdentity(this.properties.debugUserQuery, this.properties.debugUserDisplayName);
|
||
this.properties.approvalApiBaseUrl = this.properties.approvalApiBaseUrl || APPROVAL_API_BASE_URL_PROD;
|
||
setApprovalApiBaseUrl(this.properties.approvalApiBaseUrl);
|
||
|
||
const element: React.ReactElement = React.createElement(
|
||
ErrorOverlay,
|
||
undefined,
|
||
React.createElement<IRequestPortalProps>(RequestPortal, {
|
||
// key на подменённом e-mail: смена подмены в property pane должна
|
||
// пересобрать раздел заново — иначе счётчики в боковом меню и открытые
|
||
// таблицы остались бы от прежнего пользователя (их эффекты завязаны на
|
||
// монтирование/context, а не на identity подмены).
|
||
key: this.properties.debugUserQuery || '__self__',
|
||
isDarkTheme: this._isDarkTheme,
|
||
environmentMessage: this._environmentMessage,
|
||
hasTeamsContext: !!this.context.sdks.microsoftTeams,
|
||
userDisplayName: this.context.pageContext.user.displayName,
|
||
context: this.context
|
||
})
|
||
);
|
||
|
||
ReactDom.render(element, this.domElement);
|
||
}
|
||
|
||
protected onInit(): Promise<void> {
|
||
installErrorLogging(); // до всего остального — чтобы поймать и ранние отклонения
|
||
initSP(this.context); // единый клиент PnPjs для записи в списки
|
||
|
||
// Адрес approval-route должен быть выставлен ДО загрузки партиций — они
|
||
// берутся именно оттуда. В render() он ставится снова (свойство меняется в
|
||
// панели без перезапуска), но render случается позже onInit.
|
||
this.properties.approvalApiBaseUrl = this.properties.approvalApiBaseUrl || APPROVAL_API_BASE_URL_PROD;
|
||
setApprovalApiBaseUrl(this.properties.approvalApiBaseUrl);
|
||
|
||
// Партиции грузим здесь, а не по требованию из форм: имена списков нужны
|
||
// формам синхронно (см. shared/listTitles.ts), а формы — лениво подгружаемые
|
||
// чанки, монтируются заведомо позже onInit. initListPartitions не бросает:
|
||
// при недоступном approval-route портал работает по конвенции имён.
|
||
return Promise.all([this._getEnvironmentMessage(), initListPartitions(this.context)]).then(([message]) => {
|
||
this._environmentMessage = message;
|
||
});
|
||
}
|
||
|
||
|
||
|
||
private _getEnvironmentMessage(): Promise<string> {
|
||
if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook
|
||
return this.context.sdks.microsoftTeams.teamsJs.app.getContext()
|
||
.then(context => {
|
||
let environmentMessage: string;
|
||
switch (context.app.host.name) {
|
||
case 'Office': // running in Office
|
||
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
|
||
break;
|
||
case 'Outlook': // running in Outlook
|
||
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
|
||
break;
|
||
case 'Teams': // running in Teams
|
||
case 'TeamsModern':
|
||
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
|
||
break;
|
||
default:
|
||
environmentMessage = strings.UnknownEnvironment;
|
||
}
|
||
|
||
return environmentMessage;
|
||
});
|
||
}
|
||
|
||
return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
|
||
}
|
||
|
||
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
|
||
if (!currentTheme) {
|
||
return;
|
||
}
|
||
|
||
this._isDarkTheme = !!currentTheme.isInverted;
|
||
const {
|
||
semanticColors
|
||
} = currentTheme;
|
||
|
||
if (semanticColors) {
|
||
this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null);
|
||
this.domElement.style.setProperty('--link', semanticColors.link || null);
|
||
this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null);
|
||
}
|
||
|
||
}
|
||
|
||
protected onDispose(): void {
|
||
ReactDom.unmountComponentAtNode(this.domElement);
|
||
}
|
||
|
||
protected get dataVersion(): Version {
|
||
return Version.parse('1.0');
|
||
}
|
||
|
||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||
return {
|
||
pages: [
|
||
{
|
||
header: {
|
||
description: strings.PropertyPaneDescription
|
||
},
|
||
groups: [
|
||
{
|
||
groupName: strings.BasicGroupName,
|
||
groupFields: [
|
||
PropertyPaneDebugUser('debugUserQuery', {
|
||
label: strings.DebugUserFieldLabel,
|
||
description: strings.DebugUserFieldDescription,
|
||
context: this.context,
|
||
initialQuery: this.properties.debugUserQuery,
|
||
onResolved: (query, email, displayName) => {
|
||
// Сохраняем ТОЛЬКО e-mail (введённый напрямую или найденный по
|
||
// ФИО) — свойство одно и то же используется как e-mail для
|
||
// подмены (см. render() выше), так что писать в него сырое ФИО
|
||
// нельзя. Пустой query — поле очистили, подмену выключаем.
|
||
// А вот «email пуст, но query не пуст» — ФИО ещё не нашлось
|
||
// (недописано/не существует): ничего не трогаем, иначе неудачный
|
||
// промежуточный поиск молча выключил бы уже работающую подмену.
|
||
if (email) {
|
||
this.properties.debugUserQuery = email;
|
||
this.properties.debugUserDisplayName = displayName;
|
||
this.render();
|
||
} else if (!query) {
|
||
this.properties.debugUserQuery = '';
|
||
this.properties.debugUserDisplayName = '';
|
||
this.render();
|
||
}
|
||
}
|
||
})
|
||
]
|
||
},
|
||
{
|
||
groupName: 'Approval Route API',
|
||
groupFields: [
|
||
PropertyPaneDropdown('approvalApiBaseUrl', {
|
||
label: 'Api Base Url',
|
||
selectedKey: this.properties.approvalApiBaseUrl,
|
||
options: [
|
||
{ key: APPROVAL_API_BASE_URL_PROD, text: `Прод (${APPROVAL_API_BASE_URL_PROD})` },
|
||
{ key: APPROVAL_API_BASE_URL_LOCAL, text: `Локально (${APPROVAL_API_BASE_URL_LOCAL})` }
|
||
]
|
||
})
|
||
]
|
||
}
|
||
]
|
||
}
|
||
]
|
||
};
|
||
}
|
||
}
|