Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/8
This commit was merged in pull request #8.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import * as React from 'react';
|
||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import { resolveDebugUser } from '../shared/impersonation';
|
||||
|
||||
export interface IDebugUserFieldProps {
|
||||
label: string;
|
||||
description: string;
|
||||
context: WebPartContext;
|
||||
initialQuery: string;
|
||||
/** Зовётся при каждом изменении: query — как введено, email/displayName — итог поиска ('' если не найдено). */
|
||||
onResolved: (query: string, email: string, displayName: string) => void;
|
||||
}
|
||||
|
||||
type Status = 'idle' | 'loading' | 'found' | 'notfound' | 'email';
|
||||
|
||||
const DEBOUNCE_MS = 400;
|
||||
|
||||
/**
|
||||
* Поле свойств веб-части: ввод «Фамилия Имя [Отчество]» (в любом порядке) или
|
||||
* e-mail напрямую. Сам резолвит e-mail через SharePoint people search — см.
|
||||
* shared/impersonation.ts (resolveDebugUser) — и показывает найденное тут же,
|
||||
* чтобы не гадать, кого именно подставили.
|
||||
*/
|
||||
export const DebugUserField: React.FC<IDebugUserFieldProps> = ({ label, description, context, initialQuery, onResolved }) => {
|
||||
const [query, setQuery] = React.useState(initialQuery || '');
|
||||
const [status, setStatus] = React.useState<Status>('idle');
|
||||
const [found, setFound] = React.useState<{ email: string; displayName: string } | undefined>(undefined);
|
||||
|
||||
React.useEffect(() => {
|
||||
const text = query.trim();
|
||||
|
||||
if (!text) {
|
||||
setStatus('idle');
|
||||
setFound(undefined);
|
||||
onResolved('', '', '');
|
||||
return;
|
||||
}
|
||||
|
||||
// E-mail введён напрямую — используем его как есть, но всё равно ищем ФИО
|
||||
// (для красной полосы в интерфейсе и для проверки, что такой сотрудник есть).
|
||||
const isEmail = text.indexOf('@') >= 0;
|
||||
|
||||
let cancelled = false;
|
||||
setStatus(isEmail ? 'email' : 'loading');
|
||||
if (isEmail) setFound(undefined);
|
||||
const timer = window.setTimeout(() => {
|
||||
resolveDebugUser(context, text)
|
||||
.then((hit) => {
|
||||
if (cancelled) return;
|
||||
if (isEmail) {
|
||||
setFound(hit);
|
||||
onResolved(text, text, hit ? hit.displayName : '');
|
||||
} else if (hit) {
|
||||
setStatus('found');
|
||||
setFound(hit);
|
||||
onResolved(text, hit.email, hit.displayName);
|
||||
} else {
|
||||
setStatus('notfound');
|
||||
setFound(undefined);
|
||||
onResolved(text, '', '');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (cancelled) return;
|
||||
if (isEmail) {
|
||||
onResolved(text, text, '');
|
||||
} else {
|
||||
setStatus('notfound');
|
||||
setFound(undefined);
|
||||
onResolved(text, '', '');
|
||||
}
|
||||
});
|
||||
}, DEBOUNCE_MS);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
window.clearTimeout(timer);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ fontSize: 14, fontWeight: 600, marginBottom: 4 }}>{label}</div>
|
||||
<input
|
||||
type="text"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="Иванов Иван Иванович, Иван Иванов или e-mail"
|
||||
style={{ width: '100%', boxSizing: 'border-box', padding: '5px 8px', fontSize: 14 }}
|
||||
/>
|
||||
<div style={{ marginTop: 4, fontSize: 12, lineHeight: 1.4, color: '#a4262c', minHeight: 16 }}>
|
||||
{status === 'loading' && 'Ищем сотрудника…'}
|
||||
{status === 'found' && found && `Найдено: ${found.displayName} — ${found.email}`}
|
||||
{status === 'notfound' && 'Сотрудник не найден — подмена работать не будет'}
|
||||
{status === 'email' &&
|
||||
(found
|
||||
? `Используется как e-mail напрямую: ${query.trim()} (${found.displayName})`
|
||||
: `Используется как e-mail напрямую: ${query.trim()}`)}
|
||||
</div>
|
||||
<div style={{ marginTop: 6, fontSize: 12, lineHeight: 1.4, color: '#605e5c' }}>{description}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import {
|
||||
IPropertyPaneField,
|
||||
PropertyPaneFieldType,
|
||||
IPropertyPaneCustomFieldProps
|
||||
} from '@microsoft/sp-property-pane';
|
||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import { DebugUserField } from './DebugUserField';
|
||||
|
||||
export interface IPropertyPaneDebugUserProps {
|
||||
label: string;
|
||||
description: string;
|
||||
context: WebPartContext;
|
||||
initialQuery: string;
|
||||
onResolved: (query: string, email: string, displayName: string) => void;
|
||||
}
|
||||
|
||||
class PropertyPaneDebugUserBuilder
|
||||
implements IPropertyPaneField<IPropertyPaneCustomFieldProps & IPropertyPaneDebugUserProps> {
|
||||
public type: PropertyPaneFieldType = PropertyPaneFieldType.Custom;
|
||||
public targetProperty: string;
|
||||
public properties: IPropertyPaneCustomFieldProps & IPropertyPaneDebugUserProps;
|
||||
|
||||
constructor(targetProperty: string, properties: IPropertyPaneDebugUserProps) {
|
||||
this.targetProperty = targetProperty;
|
||||
this.properties = {
|
||||
key: targetProperty,
|
||||
onRender: this.onRender.bind(this),
|
||||
onDispose: this.onDispose.bind(this),
|
||||
...properties
|
||||
};
|
||||
}
|
||||
|
||||
private onRender(elem: HTMLElement): void {
|
||||
ReactDom.render(
|
||||
<DebugUserField
|
||||
label={this.properties.label}
|
||||
description={this.properties.description}
|
||||
context={this.properties.context}
|
||||
initialQuery={this.properties.initialQuery}
|
||||
onResolved={this.properties.onResolved}
|
||||
/>,
|
||||
elem
|
||||
);
|
||||
}
|
||||
|
||||
private onDispose(elem: HTMLElement): void {
|
||||
ReactDom.unmountComponentAtNode(elem);
|
||||
}
|
||||
}
|
||||
|
||||
/** Кастомное поле property pane: ФИО/e-mail сотрудника с резолвом в e-mail на лету. */
|
||||
export function PropertyPaneDebugUser(
|
||||
targetProperty: string,
|
||||
properties: IPropertyPaneDebugUserProps
|
||||
): IPropertyPaneField<IPropertyPaneCustomFieldProps & IPropertyPaneDebugUserProps> {
|
||||
return new PropertyPaneDebugUserBuilder(targetProperty, properties);
|
||||
}
|
||||
Reference in New Issue
Block a user