diff --git a/src/webparts/requestPortal/features/myRequests/MyRequestsPanel.tsx b/src/webparts/requestPortal/features/myRequests/MyRequestsPanel.tsx index a70a910..0d278d2 100644 --- a/src/webparts/requestPortal/features/myRequests/MyRequestsPanel.tsx +++ b/src/webparts/requestPortal/features/myRequests/MyRequestsPanel.tsx @@ -4,6 +4,7 @@ import { getMyRequests } from './myRequests.service'; import { IMyRequest } from './models/IMyRequest'; import { REQUEST_STATUS } from '../../shared/approval'; import { navigate, routeUrl } from '../../shared/routing'; +import { onRequestsChanged } from '../../shared/requestEvents'; import styles from './MyRequestsPanel.module.scss'; export interface IMyRequestsPanelProps { @@ -53,7 +54,10 @@ function statusClass(status: string): string { } } -const RequestItem: React.FC<{ r: IMyRequest }> = ({ r }) => ( +// «Ждёt вашего решения»: автор здесь — не вы, поэтому важны email + имя, +// а статус не нужен (в этой секции все заявки по определению «на согласовании»). +// «Созданы вами»: автор — всегда вы, строку с ним не показываем, зато нужен статус. +const RequestItem: React.FC<{ r: IMyRequest; variant: 'own' | 'approval' }> = ({ r, variant }) => (
  • = ({ r }) => ( >
    {r.title || `#${r.id}`} - - {r.authorEmail || r.authorName || '— автор не определён —'} - + {variant === 'approval' && ( + + {[r.authorName, r.authorEmail].filter(Boolean).join(' · ') || '— автор не определён —'} + + )} {r.created && {formatDate(r.created)}}
    - {r.status || '—'} + {variant === 'own' && ( + {r.status || '—'} + )}
  • ); -const RequestGroups: React.FC<{ rows: IMyRequest[] }> = ({ rows }) => ( +const RequestGroups: React.FC<{ rows: IMyRequest[]; variant: 'own' | 'approval' }> = ({ + rows, + variant, +}) => ( <> {groupByType(rows).map((group) => (
    {group.requestTypeName}
    @@ -102,6 +113,11 @@ const RequestGroups: React.FC<{ rows: IMyRequest[] }> = ({ rows }) => ( // Владелец: второй разработчик. Вся логика/данные — внутри этой папки. // Общий UI-кит (shared/ui), pnpjs и config можно импортировать, но НЕ менять. // +// Перечитывает данные по событию rp-requests-changed (shared/requestEvents) — +// оно шлётся после сохранения формы (SubmitFeature) и после решения +// согласующего (ApproverService.decide), так что панель обновляется сама, +// без перезагрузки страницы. +// // Диагностика по типам заявок (listId/siteUrl/rowCount/error) в UI не выводится — // см. console.table в devtools при загрузке панели. const MyRequestsPanel: React.FC = ({ context }) => { @@ -110,17 +126,25 @@ const MyRequestsPanel: React.FC = ({ context }) => { React.useEffect(() => { let cancelled = false; - getMyRequests(context) - .then((result) => { - if (cancelled) return; - setRows(result.rows); - console.table(result.debug); - }) - .catch((e) => { - if (!cancelled) setError((e as Error).message); - }); + + const load = (): void => { + getMyRequests(context) + .then((result) => { + if (cancelled) return; + setRows(result.rows); + setError(''); + console.table(result.debug); + }) + .catch((e) => { + if (!cancelled) setError((e as Error).message); + }); + }; + + load(); + const unsubscribe = onRequestsChanged(load); return () => { cancelled = true; + unsubscribe(); }; }, [context]); @@ -142,14 +166,14 @@ const MyRequestsPanel: React.FC = ({ context }) => { {pendingApproval.length > 0 && (
    Ждёт вашего решения
    - +
    )} {own.length > 0 && (
    Созданы вами
    - +
    )} diff --git a/src/webparts/requestPortal/features/submit/SubmitFeature.tsx b/src/webparts/requestPortal/features/submit/SubmitFeature.tsx index 62ae41c..a43881e 100644 --- a/src/webparts/requestPortal/features/submit/SubmitFeature.tsx +++ b/src/webparts/requestPortal/features/submit/SubmitFeature.tsx @@ -7,6 +7,7 @@ import { RequestGrid } from '../../components/RequestGrid/RequestGrid'; import { getRequestsByCategory, REQUEST_TYPES } from '../../data/requests'; import { getFormComponent } from '../../forms/registry'; import { IRequestType } from '../../models/types'; +import { emitRequestsChanged } from '../../shared/requestEvents'; export interface ISubmitFeatureProps { context: WebPartContext; @@ -60,7 +61,13 @@ const SubmitFeature: React.FC = ({ context, activeCategoryI // Ленивый рендер формы: её код подтягивается только сейчас, при открытии. return ( Загрузка формы…}> - + ); }; diff --git a/src/webparts/requestPortal/shared/approval/approverDecision.service.ts b/src/webparts/requestPortal/shared/approval/approverDecision.service.ts index 2b5f91e..2f0f22b 100644 --- a/src/webparts/requestPortal/shared/approval/approverDecision.service.ts +++ b/src/webparts/requestPortal/shared/approval/approverDecision.service.ts @@ -6,6 +6,7 @@ import { ApprovalListService } from './approvalList.service'; import { REQUEST_STATUS, APPROVAL_PHASE } from './models/statuses'; import { IApprovalRow } from './models/IApprovalRow'; import { ISiteUser } from './models/ISiteUser'; +import { emitRequestsChanged } from '../requestEvents'; export type ApproverDecision = 'approve' | 'return' | 'reject'; @@ -74,6 +75,7 @@ export class ApproverService { const rows = await this.approval.getForRequest(requestId); const requestStatus = this.computeRequestStatus(rows); await this.updateRequestStatus(requestId, requestStatus); + emitRequestsChanged(); return requestStatus; } diff --git a/src/webparts/requestPortal/shared/requestEvents.ts b/src/webparts/requestPortal/shared/requestEvents.ts new file mode 100644 index 0000000..80ee54a --- /dev/null +++ b/src/webparts/requestPortal/shared/requestEvents.ts @@ -0,0 +1,15 @@ +// Общее событие «список заявок изменился»: создана новая заявка или принято +// решение согласующего. Панель «Текущие заявки» (features/myRequests) подписана +// и перечитывает данные. Через window.dispatchEvent — проще шины состояния, +// достаточно для нечастых событий (сохранение формы, решение согласующего). +const EVENT_NAME = 'rp-requests-changed'; + +export function emitRequestsChanged(): void { + window.dispatchEvent(new Event(EVENT_NAME)); +} + +/** Подписка на изменения. Возвращает функцию отписки (вызвать в cleanup эффекта). */ +export function onRequestsChanged(handler: () => void): () => void { + window.addEventListener(EVENT_NAME, handler); + return () => window.removeEventListener(EVENT_NAME, handler); +}