теперь хранится id списка а не его название
Tests / test (push) Successful in 1m33s

This commit is contained in:
gorbenko.pavel
2026-08-03 16:14:31 +05:00
parent c84c2a6cb4
commit 9d9f390548
17 changed files with 421 additions and 92 deletions
@@ -6,6 +6,7 @@ import { texts } from '../../texts';
import { useRequestTypes } from '../../hooks/useRequestTypes';
import { useSharePointLists } from '../../hooks/useSharePointLists';
import { useSharePointSiteTitle } from '../../hooks/useSharePointSiteTitle';
import { useSharePointListTitle } from '../../hooks/useSharePointListTitle';
import { useAsyncSubmit } from '../../hooks/useAsyncSubmit';
import { useDeleteTarget } from '../../hooks/useDeleteTarget';
import {
@@ -36,10 +37,10 @@ interface IFormState {
pageUrl: string;
section: string;
siteUrl: string;
listName: string;
listId: string;
}
const EMPTY_FORM: IFormState = { name: '', pageUrl: '', section: '', siteUrl: '', listName: '' };
const EMPTY_FORM: IFormState = { name: '', pageUrl: '', section: '', siteUrl: '', listId: '' };
/** Ячейка «Сайт SharePoint» — подтягивает заголовок сайта по url, хранящемуся на бэкенде. */
const SiteUrlCell: React.FC<{ client: SharePointListsClient; siteUrl: string | null }> = ({ client, siteUrl }) => {
@@ -47,6 +48,12 @@ const SiteUrlCell: React.FC<{ client: SharePointListsClient; siteUrl: string | n
return <>{siteUrl ? formatSiteLabel(title, siteUrl) : texts.common.empty}</>;
};
/** Ячейка «Список SharePoint» — на бэкенде хранится id списка (устойчив к переименованию), тут подтягиваем Title для читаемости. */
const ListIdCell: React.FC<{ client: SharePointListsClient; siteUrl: string | null; listId: string | null }> = ({ client, siteUrl, listId }) => {
const { title } = useSharePointListTitle(client, siteUrl, listId);
return <>{listId ? (title ?? listId) : texts.common.empty}</>;
};
export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sharePoint }) => {
const list = useRequestTypes(api);
@@ -76,7 +83,7 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
pageUrl: item.pageUrl,
section: item.section,
siteUrl: item.siteUrl ?? '',
listName: item.listName ?? ''
listId: item.listId ?? ''
});
setShowForm(true);
};
@@ -87,8 +94,8 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
return;
}
const siteUrl = form.siteUrl.trim();
const listName = form.listName.trim();
if (Boolean(siteUrl) !== Boolean(listName)) {
const listId = form.listId.trim();
if (Boolean(siteUrl) !== Boolean(listId)) {
submit.fail(texts.requestTypes.validationListReference);
return;
}
@@ -98,7 +105,7 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
pageUrl: form.pageUrl.trim(),
section: form.section.trim(),
siteUrl: siteUrl || null,
listName: listName || null
listId: listId || null
};
const request = editing ? api.requestTypes.update(editing.id, input) : api.requestTypes.create(input);
@@ -110,7 +117,8 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
);
};
const listNameOptions = sharePointLists.items.map(l => ({ value: l.title, label: l.title }));
// value — server-relative url списка (устойчив к переименованию Title в SharePoint), label — Title для читаемости.
const listIdOptions = sharePointLists.items.map(l => ({ value: l.url, label: l.title }));
return (
<div>
@@ -130,7 +138,7 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
{ key: 'name', header: texts.requestTypes.columnName, render: r => <PrimaryCell>{r.name}</PrimaryCell> },
{ key: 'url', header: texts.requestTypes.columnPageUrl, render: r => r.pageUrl },
{ key: 'section', header: texts.requestTypes.columnSection, render: r => r.section },
{ key: 'listName', header: texts.requestTypes.columnListName, render: r => r.listName ?? texts.common.empty },
{ key: 'listId', header: texts.requestTypes.columnListId, render: r => <ListIdCell client={sharePoint} siteUrl={r.siteUrl} listId={r.listId} /> },
{ key: 'siteUrl', header: texts.requestTypes.columnSiteUrl, render: r => <SiteUrlCell client={sharePoint} siteUrl={r.siteUrl} /> },
{
key: 'actions',
@@ -196,29 +204,29 @@ export const RequestTypesScreen: React.FC<IRequestTypesScreenProps> = ({ api, sh
getSublabel={s => s.url}
onChange={(siteUrl, item) => {
if (siteUrl && item) sharePoint.cacheSiteTitle(siteUrl, item.title);
setForm({ ...form, siteUrl, listName: '' });
setForm({ ...form, siteUrl, listId: '' });
}}
/>
</Field>
<Field
label={texts.requestTypes.listNameLabel}
label={texts.requestTypes.listIdLabel}
full
hint={sharePointLists.error ? texts.requestTypes.listNameError(sharePointLists.error) : texts.requestTypes.listNameHint}
hint={sharePointLists.error ? texts.requestTypes.listIdError(sharePointLists.error) : texts.requestTypes.listIdHint}
>
<Select
options={listNameOptions}
value={form.listName}
options={listIdOptions}
value={form.listId}
disabled={!form.siteUrl.trim() || sharePointLists.isLoading}
placeholder={
!form.siteUrl.trim()
? texts.requestTypes.listNamePlaceholder
? texts.requestTypes.listIdPlaceholder
: sharePointLists.isLoading
? texts.requestTypes.listNameLoading
: listNameOptions.length === 0
? texts.requestTypes.listNameEmpty
? texts.requestTypes.listIdLoading
: listIdOptions.length === 0
? texts.requestTypes.listIdEmpty
: texts.select.placeholder
}
onChange={e => setForm({ ...form, listName: e.target.value })}
onChange={e => setForm({ ...form, listId: e.target.value })}
/>
</Field>
</FormGrid>
@@ -69,8 +69,8 @@ export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary,
const { requestTypeId, userEmail, projectId, projectLabel, status, fieldValues = [] } = form;
const requestType = requestTypes.items.find(rt => rt.id === Number(requestTypeId));
const hasListName = Boolean(requestType?.listName);
const sharePointFields = useSharePointFields(sharePoint, requestType?.siteUrl, requestType?.listName);
const hasListId = Boolean(requestType?.listId);
const sharePointFields = useSharePointFields(sharePoint, requestType?.siteUrl, requestType?.listId);
const fieldNameOptions = sharePointFields.items.map(f => ({ value: f.internalName, label: f.title }));
const resolved = useResolveRoute(api);
@@ -180,12 +180,12 @@ export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary,
<div className={styles.conditions}>
<div className={styles.conditionsHeader}>
<span className={styles.conditionsTitle}>{texts.resolve.conditionsLabel}</span>
{requestTypeId && !hasListName && (
{requestTypeId && !hasListId && (
<span className={styles.conditionsHint}>{texts.resolve.conditionsDisabledHint}</span>
)}
</div>
{hasListName && (
{hasListId && (
<>
{fieldValues.map((fv, index) => {
const field = sharePointFields.items.find(f => f.internalName === fv.fieldName);
@@ -112,8 +112,8 @@ export const RouteStepFormModal: React.FC<IRouteStepFormModalProps> = ({
}) => {
const [form, setForm] = React.useState<IFormState>(() => toFormState(editing, nextOrderNo));
const submit = useAsyncSubmit();
const hasListName = Boolean(requestType?.listName);
const sharePointFields = useSharePointFields(sharePoint, requestType?.siteUrl, requestType?.listName);
const hasListId = Boolean(requestType?.listId);
const sharePointFields = useSharePointFields(sharePoint, requestType?.siteUrl, requestType?.listId);
const fieldNameOptions = React.useMemo(() => {
const options = sharePointFields.items.map(f => ({ value: f.internalName, label: f.title }));
@@ -283,16 +283,16 @@ export const RouteStepFormModal: React.FC<IRouteStepFormModalProps> = ({
/>
</Field>
<Field label={texts.routeSteps.conditionLabel} full hint={!hasListName ? texts.routeSteps.conditionDisabledHint : undefined}>
<Field label={texts.routeSteps.conditionLabel} full hint={!hasListId ? texts.routeSteps.conditionDisabledHint : undefined}>
<Checkbox
label={texts.routeSteps.conditionCheckbox}
checked={form.hasCondition}
disabled={!hasListName}
disabled={!hasListId}
onChange={v => setForm({ ...form, hasCondition: v })}
/>
</Field>
{form.hasCondition && hasListName && (
{form.hasCondition && hasListId && (
<>
<Field
label={texts.routeSteps.conditionFieldNameLabel}