различные правки
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user