F: Add block stats to side panel
This commit is contained in:
parent
d08d3432bc
commit
ae22e9b9f7
|
@ -41,7 +41,7 @@ export function HelpOssGraph() {
|
||||||
<IconFitImage className='inline-icon' /> Вписать в экран
|
<IconFitImage className='inline-icon' /> Вписать в экран
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconLeftOpen className='inline-icon' /> Панель связанной КС
|
<IconLeftOpen className='inline-icon' /> Панель содержания
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconSettings className='inline-icon' /> Диалог настроек
|
<IconSettings className='inline-icon' /> Диалог настроек
|
||||||
|
|
|
@ -236,7 +236,6 @@ export function OssFlow() {
|
||||||
showPanel ? 'translate-x-0' : 'opacity-0 translate-x-full pointer-events-none'
|
showPanel ? 'translate-x-0' : 'opacity-0 translate-x-full pointer-events-none'
|
||||||
)}
|
)}
|
||||||
isMounted={showPanel}
|
isMounted={showPanel}
|
||||||
selectedItems={selectedItems}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { OperationType } from '../../../../backend/types';
|
||||||
|
import { OssStats } from '../../../../components/oss-stats';
|
||||||
|
import { type IBlock, type IOperationSchema, NodeType } from '../../../../models/oss';
|
||||||
|
|
||||||
|
interface BlockStatsProps {
|
||||||
|
target: IBlock;
|
||||||
|
oss: IOperationSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BlockStats({ target, oss }: BlockStatsProps) {
|
||||||
|
const contents = oss.hierarchy.expandAllOutputs([target.nodeID]);
|
||||||
|
const items = contents.map(item => oss.itemByNodeID.get(item)).filter(item => !!item);
|
||||||
|
const operations = items.filter(item => item.nodeType === NodeType.OPERATION);
|
||||||
|
const blockStats = {
|
||||||
|
count_all: contents.length,
|
||||||
|
count_inputs: operations.filter(item => item.operation_type === OperationType.INPUT).length,
|
||||||
|
count_synthesis: operations.filter(item => item.operation_type === OperationType.SYNTHESIS).length,
|
||||||
|
count_schemas: operations.filter(item => !!item.result).length,
|
||||||
|
count_owned: operations.filter(item => !!item.result && item.is_owned).length,
|
||||||
|
count_block: contents.length - operations.length
|
||||||
|
};
|
||||||
|
|
||||||
|
return <OssStats stats={blockStats} className='pr-3' />;
|
||||||
|
}
|
|
@ -10,19 +10,23 @@ import { useMainHeight } from '@/stores/app-layout';
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { type IOssItem, NodeType } from '../../../../models/oss';
|
import { NodeType } from '../../../../models/oss';
|
||||||
|
import { useOssEdit } from '../../oss-edit-context';
|
||||||
|
|
||||||
|
import { BlockStats } from './block-stats';
|
||||||
import { ViewSchema } from './view-schema';
|
import { ViewSchema } from './view-schema';
|
||||||
|
|
||||||
interface SidePanelProps {
|
interface SidePanelProps {
|
||||||
selectedItems: IOssItem[];
|
|
||||||
className?: string;
|
className?: string;
|
||||||
isMounted: boolean;
|
isMounted: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SidePanel({ selectedItems, isMounted, className }: SidePanelProps) {
|
export function SidePanel({ isMounted, className }: SidePanelProps) {
|
||||||
|
const { schema, isMutable, selectedItems } = useOssEdit();
|
||||||
const selectedOperation =
|
const selectedOperation =
|
||||||
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.OPERATION ? selectedItems[0] : null;
|
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.OPERATION ? selectedItems[0] : null;
|
||||||
|
const selectedBlock =
|
||||||
|
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK ? selectedItems[0] : null;
|
||||||
const selectedSchema = selectedOperation?.result ?? null;
|
const selectedSchema = selectedOperation?.result ?? null;
|
||||||
|
|
||||||
const debouncedMounted = useDebounce(isMounted, PARAMETER.moveDuration);
|
const debouncedMounted = useDebounce(isMounted, PARAMETER.moveDuration);
|
||||||
|
@ -52,21 +56,23 @@ export function SidePanel({ selectedItems, isMounted, className }: SidePanelProp
|
||||||
'mt-0 mb-1',
|
'mt-0 mb-1',
|
||||||
'font-medium text-sm select-none self-center',
|
'font-medium text-sm select-none self-center',
|
||||||
'transition-transform',
|
'transition-transform',
|
||||||
selectedSchema && 'translate-x-16'
|
selectedSchema && 'translate-x-20'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Содержание КС
|
Содержание
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!selectedOperation ? (
|
{!selectedOperation && !selectedBlock ? (
|
||||||
<div className='text-center text-sm cc-fade-in'>Выделите операцию для просмотра</div>
|
<div className='text-center text-sm cc-fade-in'>Выделите операцию или блок для просмотра</div>
|
||||||
) : !selectedSchema ? (
|
) : null}
|
||||||
|
{selectedOperation && !selectedSchema ? (
|
||||||
<div className='text-center text-sm cc-fade-in'>Отсутствует концептуальная схема для выбранной операции</div>
|
<div className='text-center text-sm cc-fade-in'>Отсутствует концептуальная схема для выбранной операции</div>
|
||||||
) : debouncedMounted ? (
|
) : selectedOperation && selectedSchema && debouncedMounted ? (
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<ViewSchema schemaID={selectedSchema} />
|
<ViewSchema schemaID={selectedSchema} isMutable={isMutable && selectedOperation.is_owned} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
) : null}
|
) : null}
|
||||||
|
{selectedBlock ? <BlockStats target={selectedBlock} oss={schema} /> : null}
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { type RO } from '@/utils/meta';
|
||||||
|
|
||||||
interface ToolbarConstituentsProps {
|
interface ToolbarConstituentsProps {
|
||||||
schema: IRSForm;
|
schema: IRSForm;
|
||||||
|
isMutable: boolean;
|
||||||
activeCst: IConstituenta | null;
|
activeCst: IConstituenta | null;
|
||||||
setActive: (cstID: number) => void;
|
setActive: (cstID: number) => void;
|
||||||
resetActive: () => void;
|
resetActive: () => void;
|
||||||
|
@ -27,6 +28,7 @@ export function ToolbarConstituents({
|
||||||
activeCst,
|
activeCst,
|
||||||
setActive,
|
setActive,
|
||||||
resetActive,
|
resetActive,
|
||||||
|
isMutable,
|
||||||
className
|
className
|
||||||
}: ToolbarConstituentsProps) {
|
}: ToolbarConstituentsProps) {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
@ -162,33 +164,33 @@ export function ToolbarConstituents({
|
||||||
title='Создать конституенту'
|
title='Создать конституенту'
|
||||||
icon={<IconNewItem size='1rem' className='icon-green' />}
|
icon={<IconNewItem size='1rem' className='icon-green' />}
|
||||||
onClick={createCst}
|
onClick={createCst}
|
||||||
disabled={isProcessing}
|
disabled={!isMutable || isProcessing}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Клонировать конституенту'
|
title='Клонировать конституенту'
|
||||||
icon={<IconClone size='1rem' className='icon-green' />}
|
icon={<IconClone size='1rem' className='icon-green' />}
|
||||||
onClick={cloneCst}
|
onClick={cloneCst}
|
||||||
disabled={!activeCst || isProcessing}
|
disabled={!isMutable || !activeCst || isProcessing}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Удалить выделенную конституенту'
|
title='Удалить выделенную конституенту'
|
||||||
onClick={promptDeleteCst}
|
onClick={promptDeleteCst}
|
||||||
icon={<IconDestroy size='1rem' className='icon-red' />}
|
icon={<IconDestroy size='1rem' className='icon-red' />}
|
||||||
disabled={!activeCst || isProcessing || activeCst?.is_inherited}
|
disabled={!isMutable || !activeCst || isProcessing || activeCst?.is_inherited}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Переместить вверх'
|
title='Переместить вверх'
|
||||||
icon={<IconMoveUp size='1rem' className='icon-primary' />}
|
icon={<IconMoveUp size='1rem' className='icon-primary' />}
|
||||||
onClick={moveUp}
|
onClick={moveUp}
|
||||||
disabled={!activeCst || isProcessing || schema.items.length < 2 || hasSearch}
|
disabled={!isMutable || !activeCst || isProcessing || schema.items.length < 2 || hasSearch}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Переместить вниз'
|
title='Переместить вниз'
|
||||||
icon={<IconMoveDown size='1rem' className='icon-primary' />}
|
icon={<IconMoveDown size='1rem' className='icon-primary' />}
|
||||||
onClick={moveDown}
|
onClick={moveDown}
|
||||||
disabled={!activeCst || isProcessing || schema.items.length < 2 || hasSearch}
|
disabled={!isMutable || !activeCst || isProcessing || schema.items.length < 2 || hasSearch}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,9 +10,10 @@ import { ToolbarConstituents } from './toolbar-constituents';
|
||||||
|
|
||||||
interface ViewSchemaProps {
|
interface ViewSchemaProps {
|
||||||
schemaID: number;
|
schemaID: number;
|
||||||
|
isMutable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ViewSchema({ schemaID }: ViewSchemaProps) {
|
export function ViewSchema({ schemaID, isMutable }: ViewSchemaProps) {
|
||||||
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
const [activeID, setActiveID] = useState<number | null>(null);
|
const [activeID, setActiveID] = useState<number | null>(null);
|
||||||
const activeCst = activeID ? schema.cstByID.get(activeID) ?? null : null;
|
const activeCst = activeID ? schema.cstByID.get(activeID) ?? null : null;
|
||||||
|
@ -25,6 +26,7 @@ export function ViewSchema({ schemaID }: ViewSchemaProps) {
|
||||||
className='absolute -top-7 left-1'
|
className='absolute -top-7 left-1'
|
||||||
schema={schema}
|
schema={schema}
|
||||||
activeCst={activeCst}
|
activeCst={activeCst}
|
||||||
|
isMutable={isMutable}
|
||||||
setActive={setActiveID}
|
setActive={setActiveID}
|
||||||
resetActive={() => setActiveID(null)}
|
resetActive={() => setActiveID(null)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -115,7 +115,7 @@ export function ToolbarOssGraph({
|
||||||
onClick={resetView}
|
onClick={resetView}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Панель содержания КС'
|
title='Панель содержания'
|
||||||
icon={<IconShowSidebar value={showSidePanel} isBottom={false} size='1.25rem' />}
|
icon={<IconShowSidebar value={showSidePanel} isBottom={false} size='1.25rem' />}
|
||||||
onClick={toggleShowSidePanel}
|
onClick={toggleShowSidePanel}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -98,7 +98,7 @@ export function TableSideConstituents({
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<NoData className='min-h-20'>
|
<NoData className='min-h-20'>
|
||||||
<p>Список конституент пуст</p>
|
<p>Список конституент пуст</p>
|
||||||
<p>Измените параметры фильтра</p>
|
<p>Измените параметры фильтра или создайте конституенту</p>
|
||||||
</NoData>
|
</NoData>
|
||||||
}
|
}
|
||||||
onRowClicked={onActivate ? cst => onActivate(cst) : undefined}
|
onRowClicked={onActivate ? cst => onActivate(cst) : undefined}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user