различные правки

This commit is contained in:
gorbenko.pavel
2026-08-04 14:36:47 +05:00
parent 687ba5a753
commit 3a68f2a1c3
4 changed files with 67 additions and 19 deletions
@@ -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 }) => (
<li>
<a
className={styles.item}
@@ -71,24 +75,31 @@ const RequestItem: React.FC<{ r: IMyRequest }> = ({ r }) => (
>
<div className={styles.itemMain}>
<span className={styles.itemTitle}>{r.title || `#${r.id}`}</span>
<span className={styles.itemAuthor}>
{r.authorEmail || r.authorName || '— автор не определён —'}
</span>
{variant === 'approval' && (
<span className={styles.itemAuthor}>
{[r.authorName, r.authorEmail].filter(Boolean).join(' · ') || '— автор не определён —'}
</span>
)}
{r.created && <span className={styles.itemDate}>{formatDate(r.created)}</span>}
</div>
<span className={`${styles.status} ${statusClass(r.status)}`}>{r.status || '—'}</span>
{variant === 'own' && (
<span className={`${styles.status} ${statusClass(r.status)}`}>{r.status || '—'}</span>
)}
</a>
</li>
);
const RequestGroups: React.FC<{ rows: IMyRequest[] }> = ({ rows }) => (
const RequestGroups: React.FC<{ rows: IMyRequest[]; variant: 'own' | 'approval' }> = ({
rows,
variant,
}) => (
<>
{groupByType(rows).map((group) => (
<div className={styles.group} key={group.requestTypeName}>
<div className={styles.groupTitle}>{group.requestTypeName}</div>
<ul className={styles.list}>
{group.rows.map((r) => (
<RequestItem r={r} key={`${r.requestTypeId}-${r.id}`} />
<RequestItem r={r} variant={variant} key={`${r.requestTypeId}-${r.id}`} />
))}
</ul>
</div>
@@ -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<IMyRequestsPanelProps> = ({ context }) => {
@@ -110,17 +126,25 @@ const MyRequestsPanel: React.FC<IMyRequestsPanelProps> = ({ 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<IMyRequestsPanelProps> = ({ context }) => {
{pendingApproval.length > 0 && (
<div className={styles.section}>
<div className={styles.sectionTitle}>Ждёт вашего решения</div>
<RequestGroups rows={pendingApproval} />
<RequestGroups rows={pendingApproval} variant="approval" />
</div>
)}
{own.length > 0 && (
<div className={styles.section}>
<div className={styles.sectionTitle}>Созданы вами</div>
<RequestGroups rows={own} />
<RequestGroups rows={own} variant="own" />
</div>
)}
</div>
@@ -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<ISubmitFeatureProps> = ({ context, activeCategoryI
// Ленивый рендер формы: её код подтягивается только сейчас, при открытии.
return (
<React.Suspense fallback={<div className={styles.loading}>Загрузка формы…</div>}>
<FormComponent requestType={request} context={context} onCancel={handleBack} sourceId={sourceId} />
<FormComponent
requestType={request}
context={context}
onCancel={handleBack}
onSaved={emitRequestsChanged}
sourceId={sourceId}
/>
</React.Suspense>
);
};
@@ -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;
}
@@ -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);
}