feat: feat: брендированная печать, поле «Подразделение» и подача ТМЦ за другого
- Печать доступна только после согласования (Отдел рекрутинга); выбор организации и логотип-шапка для форм с несколькими юрлицами - «СЗ на перемещение ТМЦ»: заявителя можно выбрать (подача за другого), маршрут резолвится на выбранного - «Реферальная рекомендация»: организация вакансии (мультивыбор) и подразделение/проект вакансии - Поле «Подразделение» (справочник) — Vacation, UnpaidLeave, Clearance, WorkplaceSetup, RecruitingHire, RecruitingSelection, TmcTransfer - Поле «Проект» — Vacation, UnpaidLeave, Clearance
This commit is contained in:
@@ -5,15 +5,22 @@ import { getCreatedBy, IInitiator } from '../../createdBy';
|
||||
export interface IInitiatorLineProps {
|
||||
/** Заголовок списка данных заявки. */
|
||||
listTitle: string;
|
||||
/** Id элемента заявки. undefined — заявка ещё не сохранена (ничего не показываем). */
|
||||
/** Id элемента заявки. undefined — заявка ещё не сохранена (см. fallback). */
|
||||
itemId?: number;
|
||||
/**
|
||||
* Показать вместо Created-By-lookup, пока заявка ещё не сохранена (itemId
|
||||
* не задан) — напр. в формах без поля «Сотрудник», где заявитель нигде
|
||||
* больше не виден до первого сохранения (см. TmcTransferForm.tsx).
|
||||
*/
|
||||
fallback?: { name?: string; email?: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* Строка «Инициатор: Ф.И.О. · email» под номером заявки. Сама подгружает
|
||||
* создателя (Created By) по listTitle+itemId. Пока не сохранено / нет данных — не рендерится.
|
||||
* создателя (Created By) по listTitle+itemId. Пока не сохранено — либо не
|
||||
* рендерится, либо (если передан fallback) показывает его вместо lookup'а.
|
||||
*/
|
||||
export const InitiatorLine: React.FC<IInitiatorLineProps> = ({ listTitle, itemId }) => {
|
||||
export const InitiatorLine: React.FC<IInitiatorLineProps> = ({ listTitle, itemId, fallback }) => {
|
||||
const [initiator, setInitiator] = React.useState<IInitiator | undefined>(undefined);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -32,18 +39,20 @@ export const InitiatorLine: React.FC<IInitiatorLineProps> = ({ listTitle, itemId
|
||||
};
|
||||
}, [listTitle, itemId]);
|
||||
|
||||
if (!initiator || (!initiator.name && !initiator.email)) {
|
||||
const shown = itemId ? initiator : fallback;
|
||||
|
||||
if (!shown || (!shown.name && !shown.email)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.line}>
|
||||
<span className={styles.label}>Инициатор:</span>
|
||||
{initiator.name && <span className={styles.name}>{initiator.name}</span>}
|
||||
{initiator.name && initiator.email && <span className={styles.sep}>·</span>}
|
||||
{initiator.email && (
|
||||
<a className={styles.email} href={`mailto:${initiator.email}`}>
|
||||
{initiator.email}
|
||||
{shown.name && <span className={styles.name}>{shown.name}</span>}
|
||||
{shown.name && shown.email && <span className={styles.sep}>·</span>}
|
||||
{shown.email && (
|
||||
<a className={styles.email} href={`mailto:${shown.email}`}>
|
||||
{shown.email}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
@import '../../../../../styles/design-tokens';
|
||||
|
||||
.letterhead {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding-left: $sp-3;
|
||||
margin-bottom: $sp-4;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 96px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import * as React from 'react';
|
||||
import styles from './OrgLetterhead.module.scss';
|
||||
import { ORG_LOGOS } from '../../orgLogos';
|
||||
|
||||
interface IOrgLetterheadProps {
|
||||
/** `\n`-joined «Компания — должность», как в data.Organization. */
|
||||
value: string;
|
||||
selectedIndex: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Брендированная плашка над FormCard — логотип организации, выбранной в
|
||||
* PrintOrgSelect (видна и на экране, и в печати). Название компании — часть
|
||||
* строки до « — должность» (см. formatEmployeeOrganizations). Если логотипа
|
||||
* для этой компании нет (shared/orgLogos.ts) — ничего не рендерим.
|
||||
*/
|
||||
export const OrgLetterhead: React.FC<IOrgLetterheadProps> = ({ value, selectedIndex }) => {
|
||||
const lines = value ? value.split('\n') : [];
|
||||
const line = lines[selectedIndex];
|
||||
if (!line) return null;
|
||||
|
||||
const company = line.split(' — ')[0].trim();
|
||||
const logo = ORG_LOGOS[company];
|
||||
if (!logo) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.letterhead}>
|
||||
<img src={logo} alt={company} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,6 +11,14 @@ interface IOrganizationFieldProps {
|
||||
* читалось как «организация того, о ком заявка».
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* Индекс строки, которая остаётся в печатной версии (см. PrintOrgSelect в
|
||||
* тулбаре *View.tsx) — остальные строки помечаются `rp-no-print` и PrintButton
|
||||
* вырезает их из клона перед печатью (shared/ui/PrintButton живёт в
|
||||
* @tnsu/ui-kit). На экране видны ВСЕ строки независимо от выбора — это только
|
||||
* для печати. Не передан → печатаются все организации как раньше.
|
||||
*/
|
||||
printIndex?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,15 +28,18 @@ interface IOrganizationFieldProps {
|
||||
* тогда строк несколько. Пока не резолвилось (сотрудник не выбран/не найден) —
|
||||
* ничего не рендерим, а не пустой блок.
|
||||
*/
|
||||
export const OrganizationField: React.FC<IOrganizationFieldProps> = ({ value, label = 'Организация' }) => {
|
||||
export const OrganizationField: React.FC<IOrganizationFieldProps> = ({ value, label = 'Организация', printIndex }) => {
|
||||
const lines = value ? value.split('\n') : [];
|
||||
if (lines.length === 0) return null;
|
||||
|
||||
return (
|
||||
<FormField label={label} full>
|
||||
<div className={styles.list}>
|
||||
{lines.map((line) => (
|
||||
<div key={line} className={styles.line}>
|
||||
{lines.map((line, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={printIndex !== undefined && i !== printIndex ? `${styles.line} rp-no-print` : styles.line}
|
||||
>
|
||||
{line}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
@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;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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<IPrintOrgSelectProps> = ({ 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 (
|
||||
<div className={styles.wrap}>
|
||||
<span className={styles.label}>Организация для печати</span>
|
||||
<Select value={String(selectedIndex)} onChange={(v) => onChange(Number(v))} options={options} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -39,6 +39,8 @@ export { parseExcelRows } from './ItemsTable/parseExcelRows';
|
||||
export { PeopleField } from './PeopleField/PeopleField';
|
||||
export { PeopleFieldMulti } from './PeopleField/PeopleFieldMulti';
|
||||
export { OrganizationField } from './OrganizationField/OrganizationField';
|
||||
export { PrintOrgSelect } from './PrintOrgSelect/PrintOrgSelect';
|
||||
export { OrgLetterhead } from './OrgLetterhead/OrgLetterhead';
|
||||
export { ErrorOverlay } from './ErrorOverlay/ErrorOverlay';
|
||||
export { SubmitActions } from './SubmitActions/SubmitActions';
|
||||
export { InitiatorLine } from './InitiatorLine/InitiatorLine';
|
||||
|
||||
Reference in New Issue
Block a user