From 488d3b20daa9e6cf261f3092bba8432ada3ec1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=93=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Wed, 26 Aug 2026 16:38:10 +0500 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B2=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B5=20=D0=B7=D0=B0=D1=8F=D0=B2=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=20=D0=BD=D0=B0=20=D1=87=D1=82=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=BC=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/RequestPortal.tsx | 23 ++++++++++++++++--- .../features/approve/ApproveFeature.tsx | 12 ++++++++-- .../features/edit/EditFeature.tsx | 5 +++- .../forms/Clearance/ClearanceForm.tsx | 4 +++- .../forms/Clearance/ClearanceView.tsx | 5 +++- .../forms/Dismissal/DismissalForm.tsx | 4 +++- .../forms/Dismissal/DismissalView.tsx | 5 +++- .../RecruitingHire/RecruitingHireForm.tsx | 4 +++- .../RecruitingHire/RecruitingHireView.tsx | 5 +++- .../forms/RecruitingHire/model.ts | 1 - .../forms/RecruitingHire/provisioning.json | 2 +- .../RecruitingSelectionForm.tsx | 4 +++- .../RecruitingSelectionView.tsx | 5 +++- .../forms/Referral/ReferralForm.tsx | 4 +++- .../forms/Referral/ReferralView.tsx | 5 +++- .../forms/Ticket1C/Ticket1CForm.tsx | 4 +++- .../forms/Ticket1C/Ticket1CView.tsx | 5 +++- .../forms/TmcTransfer/TmcTransferForm.tsx | 4 +++- .../forms/TmcTransfer/TmcTransferView.tsx | 5 +++- .../forms/UnpaidLeave/UnpaidLeaveForm.tsx | 4 +++- .../forms/UnpaidLeave/UnpaidLeaveView.tsx | 5 +++- .../forms/Vacation/VacationForm.tsx | 4 +++- .../forms/Vacation/VacationView.tsx | 5 +++- .../WorkplaceSetup/WorkplaceSetupForm.tsx | 4 +++- .../WorkplaceSetup/WorkplaceSetupView.tsx | 5 +++- .../requestPortal/forms/viewRegistry.ts | 6 +++++ src/webparts/requestPortal/models/types.ts | 7 ++++++ 27 files changed, 118 insertions(+), 28 deletions(-) diff --git a/src/webparts/requestPortal/components/RequestPortal.tsx b/src/webparts/requestPortal/components/RequestPortal.tsx index 212b451..97ecd09 100644 --- a/src/webparts/requestPortal/components/RequestPortal.tsx +++ b/src/webparts/requestPortal/components/RequestPortal.tsx @@ -70,6 +70,16 @@ const RequestPortal: React.FC = ({ hasTeamsContext, context const [mobileNavOpen, setMobileNavOpen] = React.useState(false); const [navCounts, setNavCounts] = React.useState(undefined); + // Уникальный код заявки для заголовка Drawer (approve/edit) — сама форма + // узнаёт его только после загрузки (см. onCodeLoaded в ApproveFeature/ + // EditFeature), поэтому не берём route.itemId (это RowID в SharePoint, а не + // видимый пользователю код). Сбрасываем при переходе на другую заявку — + // иначе при быстрой смене итемов в заголовке на миг мелькнёт код ПРЕДЫДУЩЕЙ. + const [drawerCode, setDrawerCode] = React.useState(''); + React.useEffect(() => { + setDrawerCode(''); + }, [route.itemId, route.mode]); + const [activeCategoryId, setActiveCategoryId] = React.useState(() => { if (route.mode === 'create' && route.formKey) { const found = REQUEST_TYPES.filter((r) => r.id === route.formKey)[0]; @@ -386,15 +396,21 @@ const RequestPortal: React.FC = ({ hasTeamsContext, context {route.mode === 'approve' && route.formKey && route.itemId && ( - - + + )} {/* Своя незавершённая заявка — та же панель, но с формой. Ввод здесь тоже помечает состояние изменённым, поэтому обёртка ловит input/change. */} {route.mode === 'edit' && route.formKey && route.itemId && ( - +
= ({ hasTeamsContext, context itemId={route.itemId} listId={route.listId} onClose={closeDrawer} + onCodeLoaded={setDrawerCode} />
diff --git a/src/webparts/requestPortal/features/approve/ApproveFeature.tsx b/src/webparts/requestPortal/features/approve/ApproveFeature.tsx index 88591ab..dbfbd08 100644 --- a/src/webparts/requestPortal/features/approve/ApproveFeature.tsx +++ b/src/webparts/requestPortal/features/approve/ApproveFeature.tsx @@ -13,6 +13,8 @@ export interface IApproveFeatureProps { itemId: number; /** Партиция, где лежит заявка (см. IRoute.listId) — не задана у ссылок старого формата. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — транзитом в ViewComponent (см. заголовок Drawer в RequestPortal). */ + onCodeLoaded?: (code: string) => void; } // Фича «Согласование»: открывается по ссылке из потока (mode=approve&type=&id=) @@ -23,7 +25,7 @@ export interface IApproveFeatureProps { // знает о полях конкретной формы, а эта фича — единственный вход в просмотр // заявки, общий для всех типов. Стоит вне .rp-print-root формы, поэтому в // печатную версию заявки переписка не попадает. -const ApproveFeature: React.FC = ({ context, formKey, itemId, listId }) => { +const ApproveFeature: React.FC = ({ context, formKey, itemId, listId, onCodeLoaded }) => { const ViewComponent = getViewComponent(formKey); // Партиция указана и реестр ТОЧНО знает, что её там нет — см. тот же @@ -40,7 +42,13 @@ const ApproveFeature: React.FC = ({ context, formKey, item ) : ViewComponent ? ( <> Загрузка…}> - + {/* Доступно всем, кто может открыть заявку: автору, всем согласующим (и уже решившим, и ещё ждущим своей очереди) и исполнителям — diff --git a/src/webparts/requestPortal/features/edit/EditFeature.tsx b/src/webparts/requestPortal/features/edit/EditFeature.tsx index 401d502..f2b20a9 100644 --- a/src/webparts/requestPortal/features/edit/EditFeature.tsx +++ b/src/webparts/requestPortal/features/edit/EditFeature.tsx @@ -17,6 +17,8 @@ export interface IEditFeatureProps { listId?: string; /** Закрыть панель (после сохранения). */ onClose: () => void; + /** Уведомление о загруженном уникальном коде заявки — транзитом в FormComponent (см. заголовок Drawer в RequestPortal). */ + onCodeLoaded?: (code: string) => void; } // Фича «Редактирование»: открывает СВОЮ незавершённую заявку (черновик, @@ -26,7 +28,7 @@ export interface IEditFeatureProps { // // Отдельно от features/submit намеренно: там форма живёт в потоке «плитки → // новая заявка», а тут — точечное открытие конкретной записи по ссылке. -const EditFeature: React.FC = ({ context, formKey, itemId, listId, onClose }) => { +const EditFeature: React.FC = ({ context, formKey, itemId, listId, onClose, onCodeLoaded }) => { const FormComponent = getFormComponent(formKey); const requestType = REQUEST_TYPES.filter((r) => r.id === formKey)[0]; @@ -65,6 +67,7 @@ const EditFeature: React.FC = ({ context, formKey, itemId, li emitRequestsChanged(); onClose(); }} + onCodeLoaded={onCodeLoaded} /> {/* Обсуждение — то же самое, что видят согласующие (комментарии к строке diff --git a/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx b/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx index ecd5371..f7a61c0 100644 --- a/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx +++ b/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx @@ -45,7 +45,8 @@ const ClearanceForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [requestId, setRequestId] = useState(editId); @@ -77,6 +78,7 @@ const ClearanceForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); } }) .catch((e) => { diff --git a/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx b/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx index 066210d..b786f95 100644 --- a/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx +++ b/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx @@ -39,6 +39,8 @@ interface IClearanceViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } type SaveState = 'idle' | 'saving' | 'saved' | 'error'; @@ -46,7 +48,7 @@ type SaveState = 'idle' | 'saving' | 'saved' | 'error'; // Обходной лист в режиме согласующего. Форма read-only, КРОМЕ отметок, которые // заполняет профильный отдел на своём шаге: КДП — карта СКУД и сумма обучения, // отдел подбора — парковочная карта (см. approverRights.ts). -const ClearanceView: React.FC = ({ context, itemId, formKey, listId }) => { +const ClearanceView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -113,6 +115,7 @@ const ClearanceView: React.FC = ({ context, itemId, formKey setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setError(null); setLoading(false); setLockedWriteOffs( diff --git a/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx b/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx index 3748226..0503374 100644 --- a/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx +++ b/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx @@ -45,7 +45,8 @@ const DismissalForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [requestId, setRequestId] = useState(editId); @@ -81,6 +82,7 @@ const DismissalForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); } }) .catch((e) => { diff --git a/src/webparts/requestPortal/forms/Dismissal/DismissalView.tsx b/src/webparts/requestPortal/forms/Dismissal/DismissalView.tsx index 748eea9..f8cdaba 100644 --- a/src/webparts/requestPortal/forms/Dismissal/DismissalView.tsx +++ b/src/webparts/requestPortal/forms/Dismissal/DismissalView.tsx @@ -48,12 +48,14 @@ interface IDismissalViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } // Заявление на увольнение только для просмотра (режим согласующего). // Переиспользует ту же секцию, что и форма подачи; редактирование выключено через // — поля сами становятся неизменяемыми. -const DismissalView: React.FC = ({ context, itemId, formKey, listId }) => { +const DismissalView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -92,6 +94,7 @@ const DismissalView: React.FC = ({ context, itemId, formKey setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setError(null); setLoading(false); } diff --git a/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx b/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx index e09489c..b061296 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx +++ b/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx @@ -67,7 +67,8 @@ const RecruitingHireForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); // Вложения — три зоны: удостоверение, резюме и прочие (см. attachmentCategories.ts). @@ -100,6 +101,7 @@ const RecruitingHireForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); setExistingAttachments(atts); } }) diff --git a/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireView.tsx b/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireView.tsx index 2d01ff2..93f0267 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireView.tsx +++ b/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireView.tsx @@ -41,6 +41,8 @@ interface IRecruitingHireViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } // Ключ формы «Организация рабочего места» в реестре (data/requests) — цель @@ -53,7 +55,7 @@ const NOOP_SET: SetField = () => undefined; // Заявка «на прием» только для просмотра (режим согласующего). // Переиспользует те же секции, что и форма подачи; редактирование выключено // через — поля сами становятся неизменяемыми. -const RecruitingHireView: React.FC = ({ context, itemId, formKey, listId }) => { +const RecruitingHireView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -114,6 +116,7 @@ const RecruitingHireView: React.FC = ({ context, itemI setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setAttachments(files); setError(null); setLoading(false); diff --git a/src/webparts/requestPortal/forms/RecruitingHire/model.ts b/src/webparts/requestPortal/forms/RecruitingHire/model.ts index 37b1a37..f6f1575 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/model.ts +++ b/src/webparts/requestPortal/forms/RecruitingHire/model.ts @@ -113,7 +113,6 @@ export const HIRE_TYPE_OPTIONS: ISelectOption[] = [ /** Варианты «Испытательный срок» — самые частые сроки (совпадают с choices колонки ProbationPeriod). */ export const PROBATION_PERIOD_OPTIONS: ISelectOption[] = [ - { value: 'Без испытательного срока', label: 'Без испытательного срока' }, { value: '1 месяц', label: '1 месяц' }, { value: '2 месяца', label: '2 месяца' }, { value: '3 месяца', label: '3 месяца' }, diff --git a/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json b/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json index 4935d2f..90d0063 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json +++ b/src/webparts/requestPortal/forms/RecruitingHire/provisioning.json @@ -31,7 +31,7 @@ { "internalName": "PpeClothingSize", "title": "СИЗ: размер одежды", "type": "Text" }, { "internalName": "PpeShoeSize", "title": "СИЗ: размер обуви", "type": "Number" }, - { "internalName": "ProbationPeriod", "title": "Испытательный срок", "type": "Choice", "choices": ["Без испытательного срока", "1 месяц", "2 месяца", "3 месяца", "6 месяцев"] }, + { "internalName": "ProbationPeriod", "title": "Испытательный срок", "type": "Choice", "choices": ["1 месяц", "2 месяца", "3 месяца", "6 месяцев"] }, { "internalName": "SalaryProbation", "title": "Оклад при испытательном сроке", "type": "Number", "decimals": 2 }, { "internalName": "SalaryAfterProbation", "title": "Оклад по завершении ИС", "type": "Number", "decimals": 2 }, { "internalName": "WorkSchedule", "title": "График работы", "type": "Text" }, diff --git a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx index 2b36172..ef1a087 100644 --- a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx +++ b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx @@ -59,7 +59,8 @@ const RecruitingSelectionForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [files, setFiles] = useState([]); @@ -85,6 +86,7 @@ const RecruitingSelectionForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); setExistingAttachments(atts); } }) diff --git a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionView.tsx b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionView.tsx index face6b1..d0d8e61 100644 --- a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionView.tsx +++ b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionView.tsx @@ -37,6 +37,8 @@ interface IRecruitingSelectionViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } // В режиме чтения поля не редактируются, поэтому обновление — пустышка. @@ -44,7 +46,7 @@ const NOOP_SET: SetField = () => undefined; // Заявка «на подбор» только для просмотра (режим согласующего). Переиспользует те же // секции, что и форма подачи; редактирование выключено через . -const RecruitingSelectionView: React.FC = ({ context, itemId, formKey, listId }) => { +const RecruitingSelectionView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -78,6 +80,7 @@ const RecruitingSelectionView: React.FC = ({ cont setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setAttachments(files); setError(null); setLoading(false); diff --git a/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx b/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx index b281a19..14b6032 100644 --- a/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx +++ b/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx @@ -50,7 +50,8 @@ const ReferralForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [requestId, setRequestId] = useState(editId); @@ -82,6 +83,7 @@ const ReferralForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); } }) .catch((e) => { diff --git a/src/webparts/requestPortal/forms/Referral/ReferralView.tsx b/src/webparts/requestPortal/forms/Referral/ReferralView.tsx index 38a8ee0..1ad6173 100644 --- a/src/webparts/requestPortal/forms/Referral/ReferralView.tsx +++ b/src/webparts/requestPortal/forms/Referral/ReferralView.tsx @@ -37,6 +37,8 @@ interface IReferralViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } // В режиме чтения поля не редактируются, поэтому обновление — пустышка. @@ -45,7 +47,7 @@ const NOOP_SET: SetField = () => undefined; // Реферальная рекомендация только для просмотра (режим согласующего). Переиспользует // те же секции, что и форма подачи; редактирование выключено через // — поля сами становятся неизменяемыми. -const ReferralView: React.FC = ({ context, itemId, formKey, listId }) => { +const ReferralView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -99,6 +101,7 @@ const ReferralView: React.FC = ({ context, itemId, formKey, setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setError(null); setLoading(false); } diff --git a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx index 1ae6766..d1e0fc4 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx +++ b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx @@ -76,7 +76,8 @@ const Ticket1CForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [files, setFiles] = useState([]); @@ -101,6 +102,7 @@ const Ticket1CForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); setExistingAttachments(atts); } }) diff --git a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx index 814f561..628623e 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx +++ b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CView.tsx @@ -49,6 +49,8 @@ interface ITicket1CViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } /** Текст карточки Planner из данных заявки; rejection — автор и причина отклонения, если есть. */ @@ -80,7 +82,7 @@ function buildPlannerNotes( } // Заявка в отдел 1С только для просмотра (режим согласующего). -const Ticket1CView: React.FC = ({ context, itemId, formKey, listId }) => { +const Ticket1CView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -116,6 +118,7 @@ const Ticket1CView: React.FC = ({ context, itemId, formKey, setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setAttachments(files); setError(null); setLoading(false); diff --git a/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferForm.tsx b/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferForm.tsx index bde34e2..038df5d 100644 --- a/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferForm.tsx +++ b/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferForm.tsx @@ -57,7 +57,8 @@ const TmcTransferForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [files, setFiles] = useState([]); @@ -134,6 +135,7 @@ const TmcTransferForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); setExistingAttachments(atts); } }) diff --git a/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx b/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx index 5de5ee4..d10e9e3 100644 --- a/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx +++ b/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx @@ -49,10 +49,12 @@ interface ITmcTransferViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } // Заявка «СЗ на перемещение ТМЦ» только для просмотра (режим согласующего). -const TmcTransferView: React.FC = ({ context, itemId, formKey, listId }) => { +const TmcTransferView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -106,6 +108,7 @@ const TmcTransferView: React.FC = ({ context, itemId, for setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setAttachments(files); setError(null); setLoading(false); diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx index 7e13f6c..b493f1f 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx +++ b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx @@ -45,7 +45,8 @@ const UnpaidLeaveForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [requestId, setRequestId] = useState(editId); @@ -77,6 +78,7 @@ const UnpaidLeaveForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); } }) .catch((e) => { diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx index eb0cd5d..e392b14 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx +++ b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx @@ -31,6 +31,8 @@ interface IUnpaidLeaveViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } // В режиме чтения поля не редактируются, поэтому обновление — пустышка. @@ -39,7 +41,7 @@ const NOOP_SET: SetField = () => undefined; // Заявка без содержания только для просмотра (режим согласующего). Переиспользует // ту же секцию, что и форма подачи; редактирование выключено через // — поля сами становятся неизменяемыми. -const UnpaidLeaveView: React.FC = ({ context, itemId, formKey, listId }) => { +const UnpaidLeaveView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -75,6 +77,7 @@ const UnpaidLeaveView: React.FC = ({ context, itemId, for setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setError(null); setLoading(false); } diff --git a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx index 65fb69e..718d129 100644 --- a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx +++ b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx @@ -45,7 +45,8 @@ const VacationForm: React.FC = ({ onCancel, onSaved, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [requestId, setRequestId] = useState(editId); @@ -77,6 +78,7 @@ const VacationForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); } }) .catch((e) => { diff --git a/src/webparts/requestPortal/forms/Vacation/VacationView.tsx b/src/webparts/requestPortal/forms/Vacation/VacationView.tsx index 86cc191..c1654a0 100644 --- a/src/webparts/requestPortal/forms/Vacation/VacationView.tsx +++ b/src/webparts/requestPortal/forms/Vacation/VacationView.tsx @@ -31,6 +31,8 @@ interface IVacationViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } // В режиме чтения поля не редактируются, поэтому обновление — пустышка. @@ -39,7 +41,7 @@ const NOOP_SET: SetField = () => undefined; // Заявка на отпуск только для просмотра (режим согласующего). Переиспользует ту // же секцию, что и форма подачи; редактирование выключено через // — поля сами становятся неизменяемыми. -const VacationView: React.FC = ({ context, itemId, formKey, listId }) => { +const VacationView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -75,6 +77,7 @@ const VacationView: React.FC = ({ context, itemId, formKey, setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setError(null); setLoading(false); } diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx index 03f00f1..82459ad 100644 --- a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx @@ -59,7 +59,8 @@ const WorkplaceSetupForm: React.FC = ({ onSaved, sourceId, editId, - editListId + editListId, + onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [files, setFiles] = useState([]); @@ -90,6 +91,7 @@ const WorkplaceSetupForm: React.FC = ({ setData(loaded.data); setRequestStatus(loaded.status); setRequestCode(loaded.code || undefined); + if (loaded.code) onCodeLoaded?.(loaded.code); setExistingAttachments(atts); } }) diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx index 0a6bd20..d071477 100644 --- a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx @@ -49,6 +49,8 @@ interface IWorkplaceSetupViewProps { formKey: string; /** Партиция, где лежит ЭТА заявка — см. IApprovalViewProps.listId. */ listId?: string; + /** Уведомление о загруженном уникальном коде заявки — см. IApprovalViewProps.onCodeLoaded. */ + onCodeLoaded?: (code: string) => void; } type SaveState = 'idle' | 'saving' | 'saved' | 'error'; @@ -56,7 +58,7 @@ type SaveState = 'idle' | 'saving' | 'saved' | 'error'; // Заявка «орг. рабочего места» только для просмотра (режим согласующего). // Исключение: первый согласующий шаг (по маршруту — непосредственный руководитель) // на своём этапе может заполнить «Необходимое ПО» и «Доступы» (инициатор их не знает). -const WorkplaceSetupView: React.FC = ({ context, itemId, formKey, listId }) => { +const WorkplaceSetupView: React.FC = ({ context, itemId, formKey, listId, onCodeLoaded }) => { const [data, setData] = useState(EMPTY_DATA); const [status, setStatus] = useState(''); const [code, setCode] = useState(''); @@ -121,6 +123,7 @@ const WorkplaceSetupView: React.FC = ({ context, itemI setData(loaded.data); setStatus(loaded.status); setCode(loaded.code); + onCodeLoaded?.(loaded.code); setAttachments(files); setError(null); setLoading(false); diff --git a/src/webparts/requestPortal/forms/viewRegistry.ts b/src/webparts/requestPortal/forms/viewRegistry.ts index 6f933be..965f365 100644 --- a/src/webparts/requestPortal/forms/viewRegistry.ts +++ b/src/webparts/requestPortal/forms/viewRegistry.ts @@ -17,6 +17,12 @@ export interface IApprovalViewProps { * текущую партицию типа заявки (старые ссылки без параметра). */ listId?: string; + /** + * Уведомление о загруженном уникальном коде заявки — нужен родителю + * (RequestPortal) для заголовка Drawer, который рендерится ДО того, как + * View успевает загрузить саму заявку и узнать её код. + */ + onCodeLoaded?: (code: string) => void; } type ViewComponent = React.ComponentType; diff --git a/src/webparts/requestPortal/models/types.ts b/src/webparts/requestPortal/models/types.ts index 6fa63f3..6fbc22b 100644 --- a/src/webparts/requestPortal/models/types.ts +++ b/src/webparts/requestPortal/models/types.ts @@ -53,4 +53,11 @@ export interface IRequestFormProps { * форма откатывается на текущую партицию. */ editListId?: string; + /** + * Уведомление о загруженном уникальном коде заявки (только режим editId — + * при загрузке существующей записи на редактирование). Нужен родителю + * (RequestPortal) для заголовка Drawer — там до загрузки формы неизвестен + * даже сам факт существования кода, только RowID из URL. + */ + onCodeLoaded?: (code: string) => void; }