feat: request portal — bespoke forms + approval mechanism
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import styles from './RequestPortal.module.scss';
|
||||
import type { IRequestPortalProps } from './IRequestPortalProps';
|
||||
import { IconSprite } from '../shared/ui/Icon/IconSprite';
|
||||
import { Icon } from '../shared/ui/Icon/Icon';
|
||||
import { CategoryTabs } from './CategoryTabs/CategoryTabs';
|
||||
import { RequestGrid } from './RequestGrid/RequestGrid';
|
||||
import { CATEGORIES, DEFAULT_CATEGORY_ID } from '../data/categories';
|
||||
import { getRequestsByCategory } from '../data/requests';
|
||||
import { getFormComponent } from '../forms/registry';
|
||||
import { IRequestType } from '../models/types';
|
||||
|
||||
const RequestPortal: React.FC<IRequestPortalProps> = ({ hasTeamsContext, context }) => {
|
||||
const [activeCategoryId, setActiveCategoryId] = useState<string>(DEFAULT_CATEGORY_ID);
|
||||
const [selectedRequest, setSelectedRequest] = useState<IRequestType | null>(null);
|
||||
|
||||
const rootClasses = [styles.requestPortal, hasTeamsContext ? styles.teams : '']
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
const requests = getRequestsByCategory(activeCategoryId);
|
||||
|
||||
const handleCategoryChange = (categoryId: string): void => {
|
||||
setActiveCategoryId(categoryId);
|
||||
setSelectedRequest(null); // при смене категории возвращаемся к списку плиток
|
||||
};
|
||||
|
||||
const handleBack = (): void => setSelectedRequest(null);
|
||||
|
||||
const renderSelected = (request: IRequestType): React.ReactNode => {
|
||||
const FormComponent = getFormComponent(request.id);
|
||||
|
||||
// Форма ещё не реализована — показываем заглушку.
|
||||
if (!FormComponent) {
|
||||
return (
|
||||
<div className={styles.formPlaceholder}>
|
||||
<button type="button" className={styles.backButton} onClick={handleBack}>
|
||||
<Icon name="arrow-left" size="sm" />
|
||||
<span>К списку заявок</span>
|
||||
</button>
|
||||
<p className={styles.placeholderText}>Форма «{request.title}» ещё в разработке.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Ленивый рендер формы: её код подтягивается только сейчас, при открытии.
|
||||
return (
|
||||
<React.Suspense fallback={<div className={styles.loading}>Загрузка формы…</div>}>
|
||||
<FormComponent requestType={request} context={context} onCancel={handleBack} />
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={rootClasses}>
|
||||
<IconSprite />
|
||||
<div className={styles.content}>
|
||||
<CategoryTabs
|
||||
categories={CATEGORIES}
|
||||
activeId={activeCategoryId}
|
||||
onChange={handleCategoryChange}
|
||||
/>
|
||||
|
||||
{selectedRequest ? (
|
||||
renderSelected(selectedRequest)
|
||||
) : (
|
||||
<RequestGrid requests={requests} onSelect={setSelectedRequest} />
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestPortal;
|
||||
Reference in New Issue
Block a user