F: Improve stats UI for OSS and RSForm

This commit is contained in:
Ivan 2024-08-23 21:29:07 +03:00
parent 05920b1b6a
commit b4d8ff74b2
11 changed files with 329 additions and 186 deletions

View File

@ -2,33 +2,39 @@
For more specific TODOs see comments in code
[Functionality - PROGRESS]
- Design first user experience
- Private projects. Consider cooperative editing
- OSS change propagation: Advanced features
[Functionality - PENDING]
- Search functionality for manuals
- User notifications on edit - consider spam prevention and change aggregation
- Static analyzer for RSForm as a whole: check term duplication and empty conventions
- Content based search in Library
- User profile: Settings + settings persistency
- Landing page
- Home page (user specific)
- Design first user experience
- Demo sandbox for anonymous users
- User profile: Settings + settings persistency
- Custom LibraryItem lists
- Custom user filters and sharing filters
- Static analyzer for RSForm as a whole: check term duplication and empty conventions
- OSS clone and versioning
- Focus on codemirror editor when label is clicked (need React 19 ref for clean code solution)
- Draggable rows in constituents table
- replace reagraph with react-flow in TermGraph and FormulaGraph
- Search functionality for Help Manuals
- Export PDF (Items list, Graph)
- ARIA (accessibility considerations) - for now machine reading not supported
- Internationalization - at least english version. Consider react.intl
- Focus on codemirror editor when label is clicked (need React 19 ref for clean code solution)
- Sitemap for better SEO and crawler optimization
[Functionality - CANCELED]
- User notifications on edit - consider spam prevention and change aggregation
- Content based search in Library
- Home page (user specific)
- Private projects. Consider cooperative editing
[Tech]
- duplicate syntax parsing and type info calculations to client. Consider moving backend to Nodejs or embedding c++ lib
- add debounce to some search fields
- add debounce to some search fields. Consider pagination and dynamic loading
- DataTable: fixed percentage columns, especially for SubstituteTable. Rework column sizing mechanics
- move autopep8 and isort settings from vscode settings to pyproject.toml
- Test UI for #enable-force-dark Chrome setting

View File

@ -69,6 +69,16 @@ export { IoLibrary as IconLibrary2 } from 'react-icons/io5';
export { BiDiamond as IconTemplates } from 'react-icons/bi';
export { TbHexagons as IconOSS } from 'react-icons/tb';
export { TbHexagon as IconRSForm } from 'react-icons/tb';
export { TbAssembly as IconRSFormOwned } from 'react-icons/tb';
export { TbBallFootball as IconRSFormImported } from 'react-icons/tb';
export { TbHexagonLetterX as IconCstBaseSet } from 'react-icons/tb';
export { TbHexagonLetterC as IconCstConstSet } from 'react-icons/tb';
export { TbHexagonLetterS as IconCstStructured } from 'react-icons/tb';
export { TbHexagonLetterA as IconCstAxiom } from 'react-icons/tb';
export { TbHexagonLetterD as IconCstTerm } from 'react-icons/tb';
export { TbHexagonLetterF as IconCstFunction } from 'react-icons/tb';
export { TbHexagonLetterP as IconCstPredicate } from 'react-icons/tb';
export { TbHexagonLetterT as IconCstTheorem } from 'react-icons/tb';
export { LuNewspaper as IconDefinition } from 'react-icons/lu';
export { LuDna as IconTerminology } from 'react-icons/lu';
export { FaRegHandshake as IconConvention } from 'react-icons/fa6';

View File

@ -90,7 +90,8 @@ export class OssLoader {
count_operations: items.length,
count_inputs: items.filter(item => item.operation_type === OperationType.INPUT).length,
count_synthesis: items.filter(item => item.operation_type === OperationType.SYNTHESIS).length,
count_schemas: this.schemaIDs.length
count_schemas: this.schemaIDs.length,
count_owned: items.filter(item => !!item.result && item.is_owned).length
};
}
}

View File

@ -154,6 +154,7 @@ export interface IOperationSchemaStats {
count_inputs: number;
count_synthesis: number;
count_schemas: number;
count_owned: number;
}
/**

View File

@ -37,13 +37,14 @@ function HelpMain() {
<h2>Разделы Справки</h2>
{[
HelpTopic.INFO,
HelpTopic.THESAURUS,
HelpTopic.INTERFACE,
HelpTopic.CONCEPTUAL,
HelpTopic.RSLANG,
HelpTopic.TERM_CONTROL,
HelpTopic.ACCESS,
HelpTopic.VERSIONS,
HelpTopic.INFO,
HelpTopic.EXTEOR
].map(topic => (
<TopicItem key={`${prefixes.topic_item}${topic}`} topic={topic} />
@ -72,7 +73,7 @@ function HelpMain() {
версию браузера в случае возникновения визуальных ошибок или проблем с производительностью.
</p>
<p>
Ваши пожелания по доработке, найденные ошибки и иные предложения можно направлять по email:{' '}
Ваши пожелания по доработке, найденные ошибки и иные предложения можно направлять на email:{' '}
<TextURL href={external_urls.mail_portal} text='portal@acconcept.ru' />
</p>
</div>

View File

@ -1,5 +1,14 @@
import {
IconChild,
IconConsolidation,
IconCstAxiom,
IconCstBaseSet,
IconCstConstSet,
IconCstFunction,
IconCstPredicate,
IconCstStructured,
IconCstTerm,
IconCstTheorem,
IconDownload,
IconGraphCollapse,
IconGraphExpand,
@ -8,6 +17,8 @@ import {
IconOSS,
IconPredecessor,
IconRSForm,
IconRSFormImported,
IconRSFormOwned,
IconStatusError,
IconStatusIncalculable,
IconStatusOK,
@ -31,7 +42,8 @@ function HelpThesaurus() {
<h2>Концептуальная схема</h2>
<p>
<IconRSForm size='1rem' className='inline-icon' />{' '}
<IconRSForm size='1rem' className='inline-icon' />
{'\u2009'}
<LinkTopic text='Концептуальная схема' topic={HelpTopic.CC_SYSTEM} /> (<i>система определений, КС</i>)
совокупность отдельных понятий и утверждений, а также связей между ними, задаваемых определениями.
</p>
@ -53,6 +65,24 @@ function HelpThesaurus() {
необходимых для формирования выражений аксиом. Остальные конституенты относят к Телу концептуальной схемы.
</p>
<ul>
По <b>отношению к операциям ОСС</b> выделены:
<li>
<IconRSForm size='1rem' className='inline-icon' />
{'\u2009'}свободная КС это КС не прикрепленная ни к одной операции в ОСС;
</li>
<li>
<IconRSFormOwned size='1rem' className='inline-icon' />
{'\u2009'}собственная КС данной ОСС это КС, прикрепленная к операции в ОСС, чьи владелец и расположение
совпадают с соответствующими атрибутами ОСС.
</li>
<li>
<IconRSFormImported size='1rem' className='inline-icon' />
{'\u2009'}внешняя КС данной ОСС это КС, прикрепленная к операции в ОСС, чьи владелец или расположение не
совпадают с соответствующими атрибутами ОСС;
</li>
</ul>
<h2>Конституента</h2>
<p>
Конституента это выделенная часть КС, являющаяся отдельным понятием, схемой построения понятия, либо
@ -77,35 +107,45 @@ function HelpThesaurus() {
<ul>
По <b>назначению</b> выделены типы конституент:
<li>
базисное множество (X#) представляет неопределяемое понятие, представленное структурой множества, чьи элементы
различимы и не сравнимы с элементами других базисных множеств;
<IconCstBaseSet size='1rem' className='inline-icon' />
{'\u2009'}базисное множество (X#) представляет неопределяемое понятие, представленное структурой множества,
чьи элементы различимы и не сравнимы с элементами других базисных множеств;
</li>
<li>
константное множество (C#) представляет неопределяемое понятие, моделируемое термом теории множеств, который
поддерживает ряд формальных операций над его элементами;
<IconCstConstSet size='1rem' className='inline-icon' />
{'\u2009'}константное множество (C#) представляет неопределяемое понятие, моделируемое термом теории множеств,
который поддерживает ряд формальных операций над его элементами;
</li>
<li>
родовая структура (S#) представляет неопределяемое понятие, имеющее определенную структуру, построенную на
базисных множествах и константных множеств. Содержание родовой структуры формируется{' '}
<IconCstStructured size='1rem' className='inline-icon' />
{'\u2009'}родовая структура (S#) представляет неопределяемое понятие, имеющее определенную структуру,
построенную на базисных множествах и константных множеств. Содержание родовой структуры формируется{' '}
<LinkTopic text='отношением типизации' topic={HelpTopic.RSL_TYPES} />, аксиомами и конвенцией;
</li>
<li>терм (D#) представляет выводимое понятие через формальное определение;</li>
<li>
аксиома (A#) представляет утверждение, ограничивающее неопределяемые понятия и выводимые термы. Интерпретация
аксиомы должна быть истинна и является критерием корректности интерпретации КС в целом;
<IconCstAxiom size='1rem' className='inline-icon' />
{'\u2009'}аксиома (A#) представляет утверждение, ограничивающее неопределяемые понятия и выводимые термы.
Интерпретация аксиомы должна быть истинна и является критерием корректности интерпретации КС в целом;
</li>
<li>
теорема (T#) представляет ценное для предметной утверждение, значение которого может быть как истинным так и
ложным;
<IconCstTerm size='1rem' className='inline-icon' />
{'\u2009'}терм (D#) представляет выводимое понятие через формальное определение;
</li>
<li>
терм-функция (F#) представляет выводимое понятие (возможно параметризованное), имеющее характер
<IconCstFunction size='1rem' className='inline-icon' />
{'\u2009'}терм-функция (F#) представляет выводимое понятие (возможно параметризованное), имеющее характер
функционального отношения между набором аргументов и результатом;
</li>
<li>
предикат-функция (P#) представляет выводимое понятие (возможно параметризованное), имеющее характер
<IconCstPredicate size='1rem' className='inline-icon' />
{'\u2009'}предикат-функция (P#) представляет выводимое понятие (возможно параметризованное), имеющее характер
логического выражения, проверяющее заданные аргументы на соответствие некоторому условию;
</li>
<li>
<IconCstTheorem size='1rem' className='inline-icon' />
{'\u2009'}теорема (T#) представляет ценное для предметной утверждение, значение которого может быть как
истинным так и ложным;
</li>
</ul>
<br />
@ -114,19 +154,20 @@ function HelpThesaurus() {
По <b>графу термов</b> выделены:
<li>
<IconGraphOutputs size='1rem' className='inline-icon' />
потребители данной конституенты конституенты, определения которых используют данную конституенту
{'\u2009'}потребители данной конституенты конституенты, определения которых используют данную конституенту
</li>
<li>
<IconGraphInputs size='1rem' className='inline-icon' />
поставщики данной конституенты конституенты, имена которых используются в определении данной конституенты
{'\u2009'}поставщики данной конституенты конституенты, имена которых используются в определении данной
конституенты
</li>
<li>
<IconGraphExpand size='1rem' className='inline-icon' />
зависимые от данной конституенты потребители данной конституенты напрямую или по цепочке
{'\u2009'}зависимые от данной конституенты потребители данной конституенты напрямую или по цепочке
</li>
<li>
<IconGraphCollapse size='1rem' className='inline-icon' />
влияющие на данную конституенту поставщики данной конституенты напрямую или по цепочке
{'\u2009'}влияющие на данную конституенту поставщики данной конституенты напрямую или по цепочке
</li>
</ul>
@ -150,22 +191,25 @@ function HelpThesaurus() {
<ul>
Для характеристики <b>корректности определения</b> введены статусы конституент:
<li>
<IconStatusUnknown size='1rem' className='inline-icon' /> не проверено требуется проверка формального
определения (промежуточный статус);
<IconStatusUnknown size='1rem' className='inline-icon' />
{'\u2009'}не проверено требуется проверка формального определения (промежуточный статус);
</li>
<li>
<IconStatusOK size='1rem' className='inline-icon' /> корректно формальное определение корректно;
<IconStatusOK size='1rem' className='inline-icon' />
{'\u2009'}корректно формальное определение корректно;
</li>
<li>
<IconStatusError size='1rem' className='inline-icon' /> ошибочно ошибка в формальном определении;
<IconStatusError size='1rem' className='inline-icon' />
{'\u2009'}ошибочно ошибка в формальном определении;
</li>
<li>
<IconStatusProperty size='1rem' className='inline-icon' /> неразмерное формальное определение задает
невычислимое множество, для которого возможно вычислить предикат проверки принадлежности;
<IconStatusProperty size='1rem' className='inline-icon' />
{'\u2009'}неразмерное формальное определение задает невычислимое множество, для которого возможно вычислить
предикат проверки принадлежности;
</li>
<li>
<IconStatusIncalculable size='1rem' className='inline-icon' /> невычислимо формальное определение невозможно
интерпретировать напрямую;
<IconStatusIncalculable size='1rem' className='inline-icon' />
{'\u2009'}невычислимо формальное определение невозможно интерпретировать напрямую;
</li>
</ul>
@ -186,22 +230,24 @@ function HelpThesaurus() {
<ul>
Для описания <b>наследования</b> конституент в рамках ОСС введены:
<li>
<IconChild size='1rem' className='inline-icon' /> наследованная конституента конституента, перенесенная из
другой КС в рамках операции синтеза;
<IconChild size='1rem' className='inline-icon' />
{'\u2009'}наследованная конституента конституента, перенесенная из другой КС в рамках операции синтеза;
</li>
<li>
<IconPredecessor size='1rem' className='inline-icon' /> собственная конституента конституента, не являющаяся
наследником других конституент;
<IconPredecessor size='1rem' className='inline-icon' />
{'\u2009'}собственная конституента конституента, не являющаяся наследником других конституент;
</li>
<li>
<IconPredecessor size='1rem' className='inline-icon' /> исходная конституента для данной конституенты
собственная конституента, прямым или опосредованным наследником которой является данная конституента.
<IconPredecessor size='1rem' className='inline-icon' />
{'\u2009'}исходная конституента для данной конституенты собственная конституента, прямым или опосредованным
наследником которой является данная конституента.
</li>
</ul>
<h2>Операционная схема синтеза</h2>
<p>
<IconOSS size='1rem' className='inline-icon' />{' '}
<IconOSS size='1rem' className='inline-icon' />
{'\u2009'}
<LinkTopic text='Операционная схема синтеза' topic={HelpTopic.CC_OSS} /> (ОСС) система концептуальных схем,
связанных операциями синтеза.
</p>
@ -216,12 +262,21 @@ function HelpThesaurus() {
<ul>
По <b>способу получения КС выделены</b>:
<li>
<IconDownload size='1rem' className='inline-icon' /> загрузка КС из библиотеки;
<IconDownload size='1rem' className='inline-icon' />
{'\u2009'}загрузка КС из библиотеки;
</li>
<li>
<IconSynthesis size='1rem' className='inline-icon' /> синтез концептуальных схем.ыф
<IconSynthesis size='1rem' className='inline-icon' />
{'\u2009'}синтез концептуальных схем.ыф
</li>
</ul>
<br />
<p>
<IconConsolidation className='inline-icon' />
{'\u2009'}Ромбовидный синтез операция, где используются КС, имеющие общих предков.
</p>
</div>
);
}

View File

@ -1,4 +1,12 @@
import { IconConsolidation, IconExecute, IconOSS } from '@/components/Icons';
import {
IconConsolidation,
IconDownload,
IconExecute,
IconOSS,
IconRSFormImported,
IconRSFormOwned,
IconSynthesis
} from '@/components/Icons';
import LinkTopic from '@/components/ui/LinkTopic';
import { HelpTopic } from '@/models/miscellaneous';
@ -16,9 +24,24 @@ function HelpConceptOSS() {
и отображается в форме <LinkTopic text='Графа синтеза' topic={HelpTopic.UI_OSS_GRAPH} />.
</p>
<p>
Базовыми операциями ОСС являются загрузка и синтез. Схема может быть загружена из другой локации (
<b>внешняя КС</b>) или создана в ОСС (<b>собственная КС</b>). Загрузка схем, полученных синтезом в других ОСС не
допускается. Также запрещена повторная загрузка той же КС в рамках одной ОСС.
Базовыми операциями ОСС являются <IconDownload size='1rem' className='inline-icon' /> загрузка и{' '}
<IconSynthesis size='1rem' className='inline-icon' /> синтез. Схема может быть загружена из другой локации{' '}
<span className='text-nowrap'>
(<IconRSFormImported size='1rem' className='inline-icon' />
<b>внешняя КС</b>)
</span>{' '}
или создана в ОСС{' '}
<span className='text-nowrap'>
(<IconRSFormOwned size='1rem' className='inline-icon' />
<b>собственная КС</b>)
</span>
. Загрузка схем, полученных синтезом в других ОСС не допускается. Также запрещена повторная загрузка той же КС в
рамках одной ОСС.
</p>
<p>
При изменении расположения или владельца ОСС соответствующие атрибуты изменяются у собственных КС. Также при
удалении ОСС удаляются и все собственные КС. При удалении операции, собственная КС отвязывается от ОСС и
становится свободной КС.
</p>
<p>
Операция синтеза в рамках ОСС задаются набором операций-аргументов и <b>таблицей отождествлений</b> понятий из

View File

@ -47,13 +47,16 @@ function EditorOssCard({ isModified, onDestroy, setIsModified }: EditorOssCardPr
onDestroy={onDestroy}
controller={controller}
/>
<AnimateFade onKeyDown={handleInput} className={clsx('sm:w-fit mx-auto', 'flex flex-col sm:flex-row px-6')}>
<AnimateFade
onKeyDown={handleInput}
className={clsx('md:w-fit md:max-w-fit max-w-[32rem]', 'mx-auto ', 'flex flex-col md:flex-row px-6')}
>
<FlexColumn className='px-3'>
<FormOSS id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />
<EditorLibraryItem item={schema} isModified={isModified} controller={controller} />
</FlexColumn>
<OssStats stats={schema?.stats} />
{schema ? <OssStats stats={schema.stats} /> : null}
</AnimateFade>
</>
);

View File

@ -1,26 +1,56 @@
import Divider from '@/components/ui/Divider';
import ValueLabeled from '@/components/ui/ValueLabeled';
import clsx from 'clsx';
import { IconDownload, IconRSForm, IconRSFormImported, IconRSFormOwned, IconSynthesis } from '@/components/Icons';
import ValueStats from '@/components/ui/ValueStats';
import { IOperationSchemaStats } from '@/models/oss';
interface OssStatsProps {
stats?: IOperationSchemaStats;
stats: IOperationSchemaStats;
}
function OssStats({ stats }: OssStatsProps) {
if (!stats) {
return null;
}
return (
<div className='flex flex-col sm:gap-1 sm:ml-6 sm:mt-8 sm:w-[16rem]'>
<Divider margins='my-2' className='sm:hidden' />
<div
className={clsx(
'mt-3 md:ml-5 md:mt-8 md:w-[15rem] w-[20rem] h-min mx-auto', // prettier: split-lines
'grid grid-cols-3 gap-1 justify-items-end'
)}
>
<div id='count_operations' className='w-fit flex gap-3 hover:cursor-default '>
<span>Всего</span>
<span>{stats.count_operations}</span>
</div>
<ValueStats
id='count_inputs'
icon={<IconDownload size='1.25rem' className='clr-text-primary' />}
value={stats.count_inputs}
title='Загрузка'
/>
<ValueStats
id='count_synthesis'
icon={<IconSynthesis size='1.25rem' className='clr-text-primary' />}
value={stats.count_synthesis}
title='Синтез'
/>
<ValueLabeled id='count_all' label='Всего операций' text={stats.count_operations} />
<ValueLabeled id='count_inputs' label='Загрузка' text={stats.count_inputs} />
<ValueLabeled id='count_synthesis' label='Синтез' text={stats.count_synthesis} />
<Divider margins='my-2' />
<ValueLabeled id='count_schemas' label='Прикрепленные схемы' text={stats.count_schemas} />
<ValueStats
id='count_schemas'
icon={<IconRSForm size='1.25rem' className='clr-text-primary' />}
value={stats.count_schemas}
title='Прикрепленные схемы'
/>
<ValueStats
id='count_owned'
icon={<IconRSFormOwned size='1.25rem' className='clr-text-primary' />}
value={stats.count_owned}
title='Собственные'
/>
<ValueStats
id='count_imported'
icon={<IconRSFormImported size='1.25rem' className='clr-text-primary' />}
value={stats.count_schemas - stats.count_owned}
title='Внешние'
/>
</div>
);
}

View File

@ -49,7 +49,7 @@ function EditorRSFormCard({ isModified, onDestroy, setIsModified }: EditorRSForm
/>
<AnimateFade
onKeyDown={handleInput}
className={clsx('md:w-fit md:max-w-fit max-w-[32rem]', 'mx-auto ', 'flex flex-col md:flex-row px-6')}
className={clsx('md:w-fit md:max-w-fit max-w-[32rem] mx-auto', 'flex flex-col md:flex-row px-6')}
>
<FlexColumn className='flex-shrink'>
<FormRSForm id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />

View File

@ -1,6 +1,16 @@
import clsx from 'clsx';
import {
IconChild,
IconConvention,
IconCstAxiom,
IconCstBaseSet,
IconCstConstSet,
IconCstFunction,
IconCstPredicate,
IconCstStructured,
IconCstTerm,
IconCstTheorem,
IconDefinition,
IconPredecessor,
IconStatusError,
@ -19,9 +29,13 @@ interface RSFormStatsProps {
function RSFormStats({ stats, isArchive }: RSFormStatsProps) {
return (
<div className='flex flex-col mt-3 md:gap-1 md:ml-5 md:mt-8 md:w-[18rem] w-[25rem]'>
<div className='grid grid-cols-4 gap-1 mb-3 justify-items-end'>
<div className='col-span-2 w-fit flex gap-3 hover:cursor-default'>
<div
className={clsx(
'mt-3 md:ml-5 md:mt-8 md:w-[18rem] w-[25rem] h-min mx-auto', // prettier: split-lines
'grid grid-cols-4 gap-1 justify-items-end'
)}
>
<div id='count_all' className='col-span-2 w-fit flex gap-3 hover:cursor-default '>
<span>Всего</span>
<span>{stats.count_all}</span>
</div>
@ -66,50 +80,50 @@ function RSFormStats({ stats, isArchive }: RSFormStatsProps) {
<ValueStats
id='count_base'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>X</span>}
icon={<IconCstBaseSet size='1.25rem' className='clr-text-controls' />}
value={stats.count_base}
title='Базисные множества'
/>
<ValueStats
id='count_constant'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>C</span>}
icon={<IconCstConstSet size='1.25rem' className='clr-text-controls' />}
value={stats.count_constant}
title='Константные множества'
/>
<ValueStats
id='count_structured'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>S</span>}
icon={<IconCstStructured size='1.25rem' className='clr-text-controls' />}
value={stats.count_structured}
title='Родовые структуры'
/>
<ValueStats
id='count_axiom'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>A</span>}
icon={<IconCstAxiom size='1.25rem' className='clr-text-controls' />}
value={stats.count_axiom}
title='Аксиомы'
/>
<ValueStats
id='count_term'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>D</span>}
icon={<IconCstTerm size='1.25rem' className='clr-text-controls' />}
value={stats.count_term}
title='Термы'
/>
<ValueStats
id='count_function'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>F</span>}
icon={<IconCstFunction size='1.25rem' className='clr-text-controls' />}
value={stats.count_function}
title='Терм-функции'
/>
<ValueStats
id='count_predicate'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>P</span>}
icon={<IconCstPredicate size='1.25rem' className='clr-text-controls' />}
value={stats.count_predicate}
title='Предикат-функции'
/>
<ValueStats
id='count_theorem'
icon={<span className='font-math clr-text-default md:pr-1 pl-1 md:pl-0'>T</span>}
icon={<IconCstTheorem size='1.25rem' className='clr-text-controls' />}
value={stats.count_theorem}
title='Теоремы'
/>
@ -133,7 +147,6 @@ function RSFormStats({ stats, isArchive }: RSFormStatsProps) {
title='Конвенции'
/>
</div>
</div>
);
}