From 6172321710ff053a3c0e344bc563bf92240829ac 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: Thu, 27 Aug 2026 10:09:05 +0500 Subject: [PATCH] feat: added split print button --- package-lock.json | 8 +-- package.json | 2 +- .../forms/Clearance/ClearanceView.tsx | 7 +-- .../requestPortal/forms/Clearance/model.ts | 2 +- .../forms/Referral/ReferralView.tsx | 9 +-- .../requestPortal/forms/Referral/model.ts | 2 +- .../forms/TmcTransfer/TmcTransferView.tsx | 8 +-- .../requestPortal/forms/TmcTransfer/model.ts | 2 +- .../forms/UnpaidLeave/UnpaidLeaveView.tsx | 7 +-- .../requestPortal/forms/UnpaidLeave/model.ts | 2 +- .../forms/Vacation/VacationView.tsx | 7 +-- .../requestPortal/forms/Vacation/model.ts | 2 +- .../WorkplaceSetup/WorkplaceSetupView.tsx | 8 +-- .../forms/WorkplaceSetup/model.ts | 2 +- .../shared/ui/OrgLetterhead/OrgLetterhead.tsx | 2 +- .../OrganizationField/OrganizationField.tsx | 10 ++-- .../PrintOrgSelect/PrintOrgSelect.module.scss | 16 ------ .../ui/PrintOrgSelect/PrintOrgSelect.tsx | 31 ---------- .../ui/PrintSplitButton/PrintSplitButton.tsx | 56 +++++++++++++++++++ src/webparts/requestPortal/shared/ui/index.ts | 2 +- 20 files changed, 92 insertions(+), 93 deletions(-) delete mode 100644 src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.module.scss delete mode 100644 src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.tsx create mode 100644 src/webparts/requestPortal/shared/ui/PrintSplitButton/PrintSplitButton.tsx diff --git a/package-lock.json b/package-lock.json index 1543f7d..f67ed01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/ui-kit": "^0.9.0", + "@tnsu/ui-kit": "^0.12.0", "react": "17.0.1", "react-dom": "17.0.1", "tslib": "2.3.1" @@ -9870,9 +9870,9 @@ "license": "0BSD" }, "node_modules/@tnsu/ui-kit": { - "version": "0.9.0", - "resolved": "http://git.tnsu.kz/api/packages/timur.gorodetskiy/npm/%40tnsu%2Fui-kit/-/0.9.0/ui-kit-0.9.0.tgz", - "integrity": "sha512-q4eLdsAd0FW7Jem1sVazztuFrUQxdP72d6KwBa7L/6YKqJzB2PzdaZqUWaFkp7+qgtxhK9Zotx18PHjG2i5Zmw==", + "version": "0.12.0", + "resolved": "http://git.tnsu.kz/api/packages/timur.gorodetskiy/npm/%40tnsu%2Fui-kit/-/0.12.0/ui-kit-0.12.0.tgz", + "integrity": "sha512-lJYf2DoYaydrktbSotRNY0umeJlQw8Ekhh6fu1FGB0p+/TXTozpBfAs+TxK/mvOXlS9yYenEuGBi85fd/dRF2A==", "license": "ISC", "peerDependencies": { "@fluentui/react": "^8.106.4", diff --git a/package.json b/package.json index 7f3dd08..f6a56db 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/ui-kit": "^0.9.0", + "@tnsu/ui-kit": "^0.12.0", "react": "17.0.1", "react-dom": "17.0.1", "tslib": "2.3.1" diff --git a/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx b/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx index 633a964..22a8470 100644 --- a/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx +++ b/src/webparts/requestPortal/forms/Clearance/ClearanceView.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useState, useEffect, useMemo, useCallback } from 'react'; import type { WebPartContext } from '@microsoft/sp-webpart-base'; import styles from './ClearanceView.module.scss'; -import { Alert, Button, FormCard, InitiatorLine, FormModeProvider, PrintButton, PrintOrgSelect, OrgLetterhead } from '../../shared/ui'; +import { Alert, Button, FormCard, InitiatorLine, FormModeProvider, PrintSplitButton, OrgLetterhead } from '../../shared/ui'; import { ApproverService, DecisionPanel, @@ -65,7 +65,7 @@ const ClearanceView: React.FC = ({ context, itemId, formKey const [lockedWriteOffs, setLockedWriteOffs] = useState>(new Set()); const [saveState, setSaveState] = useState('idle'); // Какая из организаций сотрудника (см. OrganizationField) идёт в печать — - // выбор в тулбаре через PrintOrgSelect, по умолчанию первая. + // выбор в тулбаре через PrintSplitButton, по умолчанию первая. const [printOrgIndex, setPrintOrgIndex] = useState(0); const [saveErrorMessage, setSaveErrorMessage] = useState(''); @@ -263,8 +263,7 @@ const ClearanceView: React.FC = ({ context, itemId, formKey return (
- - +
{canEditAnything && ( diff --git a/src/webparts/requestPortal/forms/Clearance/model.ts b/src/webparts/requestPortal/forms/Clearance/model.ts index d8d554f..d348bc0 100644 --- a/src/webparts/requestPortal/forms/Clearance/model.ts +++ b/src/webparts/requestPortal/forms/Clearance/model.ts @@ -124,7 +124,7 @@ export interface ISectionProps { * отдельный обработчик. Если не передан (режим чтения) — правка через set. */ onEmployeeChange?: (login: string) => void; - /** Индекс организации для печати (см. shared/ui/PrintOrgSelect и OrganizationField.printIndex). */ + /** Индекс организации для печати (см. shared/ui/PrintSplitButton и OrganizationField.printIndex). */ printOrgIndex?: number; } diff --git a/src/webparts/requestPortal/forms/Referral/ReferralView.tsx b/src/webparts/requestPortal/forms/Referral/ReferralView.tsx index 1ad6173..15cda2e 100644 --- a/src/webparts/requestPortal/forms/Referral/ReferralView.tsx +++ b/src/webparts/requestPortal/forms/Referral/ReferralView.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useState, useEffect, useMemo, useCallback } from 'react'; import type { WebPartContext } from '@microsoft/sp-webpart-base'; import styles from './ReferralView.module.scss'; -import { Alert, FormCard, InitiatorLine, FormModeProvider, PrintButton, PrintOrgSelect, OrgLetterhead } from '../../shared/ui'; +import { Alert, FormCard, InitiatorLine, FormModeProvider, PrintSplitButton, OrgLetterhead } from '../../shared/ui'; import { ApproverService, DecisionPanel, @@ -54,7 +54,7 @@ const ReferralView: React.FC = ({ context, itemId, formKey, const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Какая из организаций рекомендателя (см. OrganizationField) идёт в печать — - // выбор в тулбаре через PrintOrgSelect, по умолчанию первая. + // выбор в тулбаре через PrintSplitButton, по умолчанию первая. const [printOrgIndex, setPrintOrgIndex] = useState(0); // listId уже проверен в ApproveFeature (не смонтировались бы сюда иначе) — @@ -146,10 +146,7 @@ const ReferralView: React.FC = ({ context, itemId, formKey,
{isApprovedStatus(status) && ( - <> - - - + )}
diff --git a/src/webparts/requestPortal/forms/Referral/model.ts b/src/webparts/requestPortal/forms/Referral/model.ts index 6477630..fc422b2 100644 --- a/src/webparts/requestPortal/forms/Referral/model.ts +++ b/src/webparts/requestPortal/forms/Referral/model.ts @@ -97,7 +97,7 @@ export interface ISectionProps { * обработчик. Если не передан (режим чтения) — правка через set. */ onReferrerChange?: (login: string) => void; - /** Индекс организации для печати (см. shared/ui/PrintOrgSelect и OrganizationField.printIndex). */ + /** Индекс организации для печати (см. shared/ui/PrintSplitButton и OrganizationField.printIndex). */ printOrgIndex?: number; } diff --git a/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx b/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx index d10e9e3..42e3726 100644 --- a/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx +++ b/src/webparts/requestPortal/forms/TmcTransfer/TmcTransferView.tsx @@ -11,8 +11,7 @@ import { CollapseSection, FormField, AttachmentList, - PrintButton, - PrintOrgSelect, + PrintSplitButton, OrgLetterhead, Icon } from '../../shared/ui'; @@ -62,7 +61,7 @@ const TmcTransferView: React.FC = ({ context, itemId, for const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Какая из организаций заявителя (см. OrganizationField) идёт в печать — - // выбор в тулбаре через PrintOrgSelect, по умолчанию первая. + // выбор в тулбаре через PrintSplitButton, по умолчанию первая. const [printOrgIndex, setPrintOrgIndex] = useState(0); const { id: requestTypeId } = useRequestTypeId(context, formKey); @@ -159,8 +158,7 @@ const TmcTransferView: React.FC = ({ context, itemId, for Редактировать )} - - +
diff --git a/src/webparts/requestPortal/forms/TmcTransfer/model.ts b/src/webparts/requestPortal/forms/TmcTransfer/model.ts index 7e3f73b..9461feb 100644 --- a/src/webparts/requestPortal/forms/TmcTransfer/model.ts +++ b/src/webparts/requestPortal/forms/TmcTransfer/model.ts @@ -66,7 +66,7 @@ export interface ISectionProps { data: IFormData; set: SetField; context: WebPartContext; - /** Индекс организации для печати (см. shared/ui/PrintOrgSelect и OrganizationField.printIndex). */ + /** Индекс организации для печати (см. shared/ui/PrintSplitButton и OrganizationField.printIndex). */ printOrgIndex?: number; /** * Выбор заявителя подтягивает его ФИО и организации — поэтому у поля отдельный diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx index e392b14..4539760 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx +++ b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveView.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useState, useEffect, useMemo, useCallback } from 'react'; import type { WebPartContext } from '@microsoft/sp-webpart-base'; import styles from './UnpaidLeaveView.module.scss'; -import { Alert, FormCard, InitiatorLine, FormModeProvider, PrintButton, PrintOrgSelect, OrgLetterhead } from '../../shared/ui'; +import { Alert, FormCard, InitiatorLine, FormModeProvider, PrintSplitButton, OrgLetterhead } from '../../shared/ui'; import { ApproverService, DecisionPanel, @@ -48,7 +48,7 @@ const UnpaidLeaveView: React.FC = ({ context, itemId, for const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Какая из организаций сотрудника (см. OrganizationField) идёт в печать — - // выбор в тулбаре через PrintOrgSelect, по умолчанию первая. + // выбор в тулбаре через PrintSplitButton, по умолчанию первая. const [printOrgIndex, setPrintOrgIndex] = useState(0); // listId уже проверен в ApproveFeature (не смонтировались бы сюда иначе) — @@ -121,8 +121,7 @@ const UnpaidLeaveView: React.FC = ({ context, itemId, for return (
- - +
diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/model.ts b/src/webparts/requestPortal/forms/UnpaidLeave/model.ts index 6045c7e..4259f10 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/model.ts +++ b/src/webparts/requestPortal/forms/UnpaidLeave/model.ts @@ -60,7 +60,7 @@ export interface ISectionProps { * отдельный обработчик. Если не передан (режим чтения) — правка через set. */ onEmployeeChange?: (login: string) => void; - /** Индекс организации для печати (см. shared/ui/PrintOrgSelect и OrganizationField.printIndex). */ + /** Индекс организации для печати (см. shared/ui/PrintSplitButton и OrganizationField.printIndex). */ printOrgIndex?: number; } diff --git a/src/webparts/requestPortal/forms/Vacation/VacationView.tsx b/src/webparts/requestPortal/forms/Vacation/VacationView.tsx index c1654a0..248e7ed 100644 --- a/src/webparts/requestPortal/forms/Vacation/VacationView.tsx +++ b/src/webparts/requestPortal/forms/Vacation/VacationView.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useState, useEffect, useMemo, useCallback } from 'react'; import type { WebPartContext } from '@microsoft/sp-webpart-base'; import styles from './VacationView.module.scss'; -import { Alert, FormCard, InitiatorLine, FormModeProvider, PrintButton, PrintOrgSelect, OrgLetterhead } from '../../shared/ui'; +import { Alert, FormCard, InitiatorLine, FormModeProvider, PrintSplitButton, OrgLetterhead } from '../../shared/ui'; import { ApproverService, DecisionPanel, @@ -48,7 +48,7 @@ const VacationView: React.FC = ({ context, itemId, formKey, const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Какая из организаций сотрудника (см. OrganizationField) идёт в печать — - // выбор в тулбаре через PrintOrgSelect, по умолчанию первая. + // выбор в тулбаре через PrintSplitButton, по умолчанию первая. const [printOrgIndex, setPrintOrgIndex] = useState(0); // listId уже проверен в ApproveFeature (не смонтировались бы сюда иначе) — @@ -121,8 +121,7 @@ const VacationView: React.FC = ({ context, itemId, formKey, return (
- - +
diff --git a/src/webparts/requestPortal/forms/Vacation/model.ts b/src/webparts/requestPortal/forms/Vacation/model.ts index 0a81a56..5b99877 100644 --- a/src/webparts/requestPortal/forms/Vacation/model.ts +++ b/src/webparts/requestPortal/forms/Vacation/model.ts @@ -59,7 +59,7 @@ export interface ISectionProps { * отдельный обработчик. Если не передан (режим чтения) — правка через set. */ onEmployeeChange?: (login: string) => void; - /** Индекс организации для печати (см. shared/ui/PrintOrgSelect и OrganizationField.printIndex). */ + /** Индекс организации для печати (см. shared/ui/PrintSplitButton и OrganizationField.printIndex). */ printOrgIndex?: number; } diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx index d071477..cef8bde 100644 --- a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupView.tsx @@ -11,8 +11,7 @@ import { CollapseSection, FormField, AttachmentList, - PrintButton, - PrintOrgSelect, + PrintSplitButton, OrgLetterhead, Icon } from '../../shared/ui'; @@ -68,7 +67,7 @@ const WorkplaceSetupView: React.FC = ({ context, itemI const [canEditFields, setCanEditFields] = useState(false); const [saveState, setSaveState] = useState('idle'); // Какая из организаций заявителя (см. OrganizationField) идёт в печать — - // выбор в тулбаре через PrintOrgSelect, по умолчанию первая. + // выбор в тулбаре через PrintSplitButton, по умолчанию первая. const [printOrgIndex, setPrintOrgIndex] = useState(0); const { id: requestTypeId } = useRequestTypeId(context, formKey); @@ -213,8 +212,7 @@ const WorkplaceSetupView: React.FC = ({ context, itemI Редактировать )} - - +
{canEditFields && ( diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/model.ts b/src/webparts/requestPortal/forms/WorkplaceSetup/model.ts index 83998e0..b7810f8 100644 --- a/src/webparts/requestPortal/forms/WorkplaceSetup/model.ts +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/model.ts @@ -53,7 +53,7 @@ export interface ISectionProps { data: IFormData; set: SetField; context: WebPartContext; - /** Индекс организации для печати (см. shared/ui/PrintOrgSelect и OrganizationField.printIndex). */ + /** Индекс организации для печати (см. shared/ui/PrintSplitButton и OrganizationField.printIndex). */ printOrgIndex?: number; } diff --git a/src/webparts/requestPortal/shared/ui/OrgLetterhead/OrgLetterhead.tsx b/src/webparts/requestPortal/shared/ui/OrgLetterhead/OrgLetterhead.tsx index 33a9e82..8a81385 100644 --- a/src/webparts/requestPortal/shared/ui/OrgLetterhead/OrgLetterhead.tsx +++ b/src/webparts/requestPortal/shared/ui/OrgLetterhead/OrgLetterhead.tsx @@ -10,7 +10,7 @@ interface IOrgLetterheadProps { /** * Брендированная плашка над FormCard — логотип организации, выбранной в - * PrintOrgSelect (видна и на экране, и в печати). Название компании — часть + * PrintSplitButton (видна и на экране, и в печати). Название компании — часть * строки до « — должность» (см. formatEmployeeOrganizations). Если логотипа * для этой компании нет (shared/orgLogos.ts) — ничего не рендерим. */ diff --git a/src/webparts/requestPortal/shared/ui/OrganizationField/OrganizationField.tsx b/src/webparts/requestPortal/shared/ui/OrganizationField/OrganizationField.tsx index ab5fc6c..3742a79 100644 --- a/src/webparts/requestPortal/shared/ui/OrganizationField/OrganizationField.tsx +++ b/src/webparts/requestPortal/shared/ui/OrganizationField/OrganizationField.tsx @@ -12,11 +12,11 @@ interface IOrganizationFieldProps { */ label?: string; /** - * Индекс строки, которая остаётся в печатной версии (см. PrintOrgSelect в - * тулбаре *View.tsx) — остальные строки помечаются `rp-no-print` и PrintButton - * вырезает их из клона перед печатью (shared/ui/PrintButton живёт в - * @tnsu/ui-kit). На экране видны ВСЕ строки независимо от выбора — это только - * для печати. Не передан → печатаются все организации как раньше. + * Индекс строки, которая остаётся в печатной версии (см. shared/ui/PrintSplitButton + * в тулбаре *View.tsx) — остальные строки помечаются `rp-no-print` и printRoot + * (@tnsu/ui-kit) вырезает их из клона перед печатью. На экране видны ВСЕ строки + * независимо от выбора — это только для печати. Не передан → печатаются все + * организации как раньше. */ printIndex?: number; } diff --git a/src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.module.scss b/src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.module.scss deleted file mode 100644 index 163461f..0000000 --- a/src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.module.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import '../../../../../styles/design-tokens'; - -.wrap { - display: flex; - align-items: center; - gap: $sp-2; - min-width: 0; -} - -.label { - flex: none; - font-family: $font-sans; - font-size: $fs-sm; - color: $gray-500; - white-space: nowrap; -} diff --git a/src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.tsx b/src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.tsx deleted file mode 100644 index 1fa9777..0000000 --- a/src/webparts/requestPortal/shared/ui/PrintOrgSelect/PrintOrgSelect.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from 'react'; -import styles from './PrintOrgSelect.module.scss'; -import { Select, ISelectOption } from '@tnsu/ui-kit'; - -interface IPrintOrgSelectProps { - /** `\n`-joined «Компания — должность», как в data.Organization. */ - value: string; - selectedIndex: number; - onChange: (index: number) => void; -} - -/** - * Выбор организации для печати — сотрудник может состоять сразу в нескольких - * (см. shared/ui/OrganizationField), а печатный документ должен ссылаться на - * одну конкретную. Живёт в тулбаре рядом с PrintButton, помечен `rp-no-print` - * самим тулбаром — сам в печать не идёт. Рендерится, только если организаций - * больше одной, иначе выбирать не из чего. - */ -export const PrintOrgSelect: React.FC = ({ value, selectedIndex, onChange }) => { - const lines = value ? value.split('\n') : []; - if (lines.length <= 1) return null; - - const options: ISelectOption[] = lines.map((line, i) => ({ value: String(i), label: line })); - - return ( -
- Организация для печати -