feat: port stroy-bot-projects-binder as binder page

Add the "binder" section (?page=binder&projectId=): links a SharePoint
project to the TNSU API project catalogue by writing UniqueId0/UniqueName
to the "Проекты" list.

The class component is rewritten as function components with hooks while
keeping the legacy behaviour and texts: the same project GET with
odata=nometadata and empty odata-version, MERGE with IF-MATCH * for
bind/unbind (no confirmation), catalogue search with 400 ms debounce and
20-per-page paging, card select/deselect, status colours, ru-RU dates and
HTML-entity decoding of the address. Fluent UI is replaced with ui-kit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-10-16 08:44:57 +05:00
parent 55ce772eaa
commit e7527081b4
18 changed files with 1390 additions and 0 deletions
@@ -0,0 +1,21 @@
import * as React from 'react';
import { Alert, IconButton } from '@tnsu/ui-kit';
import styles from './BinderPage.module.scss';
interface Props {
variant: 'warn' | 'ok' | 'err' | 'info';
dismissLabel: string;
onDismiss: () => void;
children: React.ReactNode;
}
// Alert из ui-kit не закрывается, а MessageBar в binder закрывался крестиком — добавляем его рядом.
// Такой же компонент есть в pages/resources — кандидат на перенос в shared/components.
export const DismissibleAlert: React.FC<Props> = ({ variant, dismissLabel, onDismiss, children }) => (
<div className={styles.dismissibleAlert}>
<div className={styles.dismissibleAlertBody}>
<Alert variant={variant}>{children}</Alert>
</div>
<IconButton icon="close" title={dismissLabel} size="sm" onClick={onDismiss} />
</div>
);