feat: чат по заявке через комментарии к строке списка
Tests / frontend-test (pull_request) Successful in 1m16s

Обсуждение заявки поверх штатных комментариев SharePoint (PnPjs
item.comments) — без новых колонок и провижининга, права на переписку
раздаёт сам SharePoint по правам на элемент.

Встроено в ApproveFeature и EditFeature (единственные входы в просмотр
заявки для всех типов), поэтому доступно автору, всем согласующим
(включая уже решивших и ещё не дошедших до своего шага) и исполнителям,
а не только текущему шагу маршрута, как DecisionPanel.

Автора комментария всегда ставит реальная учётка — отладочная подмена
личности на запись в SharePoint не влияет, в UI есть предупреждение.
This commit is contained in:
gorbenko.pavel
2026-08-24 10:10:18 +05:00
parent fd19ddc41a
commit 2c5935b72d
13 changed files with 728 additions and 5 deletions
@@ -0,0 +1,64 @@
import '@pnp/sp/items';
import '@pnp/sp/comments';
import type { ICommentInfo, IComments } from '@pnp/sp/comments';
import { resolveDataList } from '../dataList';
import { RequestTypeId } from '../../models/types';
import { IChatMessage } from './models/IChatMessage';
import { toChatMessages } from './chatMessages';
/**
* Чат по заявке ПОВЕРХ штатных комментариев к строке списка.
*
* Почему комментарии, а не своя колонка/список переписки: комментарии к
* элементу — готовый механизм SharePoint. Он сам хранит автора и время, сам
* раздаёт права (кто видит заявку — видит переписку), не требует ни новой
* колонки в девяти списках, ни провижининга, и переписка не мешает
* согласованию (комментарий решения — отдельное поле строки маршрута).
*
* Заявка живёт в КОНКРЕТНОЙ партиции (списки нумерованные, см.
* shared/approval/listPartitions.ts), поэтому список резолвим общим
* resolveDataList(formKey, listId) — тем же, которым формы пишут саму заявку.
* Иначе переписка архивной заявки читалась бы из текущей партиции, где под
* тем же Id лежит другая заявка.
*/
/** Комментарии элемента списка данных заявки. */
function commentsOf(formKey: RequestTypeId, itemId: number, listId?: string): IComments {
return resolveDataList(formKey, listId).items.getById(itemId).comments;
}
/**
* Прочитать переписку по заявке.
*
* currentUserEmail нужен, чтобы отметить свои сообщения. Передаём РЕАЛЬНОГО
* пользователя (context.pageContext.user.email), а не подменённого: комментарий
* SharePoint пишет под настоящей учёткой, отладочная подмена личности
* (shared/impersonation.ts) на автора не влияет.
*
* SharePoint отдаёт комментарии страницей. Дочитывания страниц здесь нет:
* переписка по одной заявке — это единицы сообщений, и усложнять сервис под
* гипотетический случай смысла нет. Если объём вырастет, дочитывание добавится
* здесь, интерфейс менять не придётся.
*/
export async function getChatMessages(
formKey: RequestTypeId,
itemId: number,
listId: string | undefined,
currentUserEmail: string
): Promise<IChatMessage[]> {
const comments: ICommentInfo[] = await commentsOf(formKey, itemId, listId)();
return toChatMessages(comments, currentUserEmail);
}
/**
* Отправить сообщение — добавить комментарий к строке заявки. Автора и время
* ставит сам SharePoint, поэтому передаём только текст.
*/
export async function addChatMessage(
formKey: RequestTypeId,
itemId: number,
listId: string | undefined,
text: string
): Promise<void> {
await commentsOf(formKey, itemId, listId).add(text);
}