fix(ct): своя строка «Дополнительной информации» по ссылке — без HTML-обёртки SharePoint
Значение своей строки хранится в comment, а это в «Критерии» ОМТС многострочное поле с форматированием: SharePoint отдаёт его как <div class="ExternalClass…">да</div>, и по ссылке показывалась разметка. При загрузке теперь остаётся чистый текст (plainTextFromSp).
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { buildCriteriaRecords, isPaymentTypeOption, restoreCriteria } from './criteria';
|
||||
import { buildCriteriaRecords, isPaymentTypeOption, plainTextFromSp, restoreCriteria } from './criteria';
|
||||
import { Supplier } from '../types/domain';
|
||||
|
||||
const ARLAN_KZT: Supplier = { id: '263_277', spId: 263, kpFileSpId: 277, name: 'ТОО «Арлан Офис Снаб»', hasOffer: true };
|
||||
@@ -96,3 +96,19 @@ describe('isPaymentTypeOption', () => {
|
||||
expect(isPaymentTypeOption('40% предоплата, 60% по аккредитиву')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('plainTextFromSp', () => {
|
||||
// comment в SP «Критерии» — многострочное поле с форматированием: SharePoint заворачивает текст в HTML,
|
||||
// и по ссылке своя строка показывалась как «<div class="ExternalClass…">да</div>».
|
||||
it('убирает обёртку ExternalClass и теги', () => {
|
||||
expect(plainTextFromSp('<div class="ExternalClass11A142948BDD4FEB93E8B98BB14AE465">да</div>')).toBe('да');
|
||||
});
|
||||
|
||||
it('переносы строк сохраняет, HTML-сущности раскодирует', () => {
|
||||
expect(plainTextFromSp('<div>12 мес<br>от "поставки" & монтажа</div>')).toBe('12 мес\nот "поставки" & монтажа');
|
||||
});
|
||||
|
||||
it('обычный текст не трогает', () => {
|
||||
expect(plainTextFromSp('12 мес')).toBe('12 мес');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,26 @@ const EMPTY_ENTRY: CriteriaEntry = { paymentType: '', deliveryType: '', deadline
|
||||
const hasAnyValue = (e: CriteriaEntry): boolean =>
|
||||
[e.paymentType, e.deliveryType, e.deadline, e.sertificates].some(v => (v ?? '').trim() !== '');
|
||||
|
||||
const HTML_ENTITIES: Record<string, string> = {
|
||||
' ': ' ',
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
''': "'",
|
||||
};
|
||||
|
||||
// Текст из многострочного поля SP с форматированием (comment в «Критерии»): SharePoint хранит его как HTML
|
||||
// в обёртке <div class="ExternalClass…">. Оставляем чистый текст, переносы строк — как есть.
|
||||
export function plainTextFromSp(value: string): string {
|
||||
if (!value.includes('<')) return value;
|
||||
return value
|
||||
.replace(/<br\s*\/?>/gi, '\n')
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.replace(/&(nbsp|amp|lt|gt|quot|#39);/g, entity => HTML_ENTITIES[entity] ?? entity)
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function isPaymentTypeOption(value: string): boolean {
|
||||
return PAYMENT_TYPE_OPTIONS.includes(value.trim());
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
keepLatestOfferRecords,
|
||||
parseOfferItemIndex,
|
||||
} from "../../../shared/offers/offerRecords";
|
||||
import { CriteriaRecord } from "../components/utils/criteria";
|
||||
import { CriteriaRecord, plainTextFromSp } from "../components/utils/criteria";
|
||||
import {
|
||||
NoOfferSupplierEntry,
|
||||
NoOfferSupplierRecord,
|
||||
@@ -680,7 +680,8 @@ export class CtService {
|
||||
return ((data.value ?? []) as Array<Record<string, unknown>>).map((item): CriteriaRecord => {
|
||||
const base = { id: Number(item.Id), supplierSpId: Number(item.supplierId) };
|
||||
const label = String(item.criteriaLabel ?? "").trim();
|
||||
if (label) return { ...base, criteriaLabel: label, value: String(item.comment ?? "") };
|
||||
// comment — многострочное поле с форматированием: SharePoint отдаёт его в HTML-обёртке.
|
||||
if (label) return { ...base, criteriaLabel: label, value: plainTextFromSp(String(item.comment ?? "")) };
|
||||
return {
|
||||
...base,
|
||||
paymentType: String(item.paymentType ?? ""),
|
||||
|
||||
Reference in New Issue
Block a user