UI improvements

This commit is contained in:
IRBorisov 2024-06-18 15:07:41 +03:00
parent 63f51e026f
commit 2d2a605991
9 changed files with 68 additions and 23 deletions

View File

@ -2,25 +2,26 @@
For more specific TODOs see comments in code
[Functionality - PROGRESS]
- Landing page
- Home page (user specific)
- Operational synthesis schema as LibraryItem ?
- Draggable rows in constituents table
- Clickable IDs in RSEditor tooltips
- Library organization, search and exploration. Consider new user experience
- Private projects and permissions. Consider cooperative editing
- Rework access setup: project-based, user-based, enable sharing. Prevent enumerating access to private schemas by default
[Functionality - PENDING]
- Search functionality for manuals
- User notifications on edit - consider spam prevention and change aggregation
- Static analyzer for RSForm
- Content based search in Library
- User profile: Settings + settings persistency
- Landing page
- Home page (user specific)
- Draggable rows in constituents table
- Export PDF (Items list, Graph)
- ARIA (accessibility considerations) - for now machine reading not supported
- Internationalization - at least english version. Consider react.intl
@ -42,6 +43,8 @@ For more specific TODOs see comments in code
[Security]
- password-reset leaks info of email being used
- improve nginx config. Consider DDOS and other types of attacks on infrastructure
- recaptcha for create user and rest password
https://yandex.cloud/ru/docs/smartcaptcha
[Research]

View File

@ -24,6 +24,7 @@ function NavigationButton({ icon, title, titleHtml, hideTitle, onClick, text }:
'flex items-center gap-1',
'clr-btn-nav',
'rounded-xl',
'transition duration-500',
'font-controls whitespace-nowrap',
{
'px-2': text,

View File

@ -18,7 +18,8 @@ export { BiCog as IconSettings } from 'react-icons/bi';
export { TbEye as IconShow } from 'react-icons/tb';
export { TbEyeX as IconHide } from 'react-icons/tb';
export { BiShareAlt as IconShare } from 'react-icons/bi';
export { BiFilterAlt as IconFilter } from 'react-icons/bi';
export { LuFilter as IconFilter } from 'react-icons/lu';
export { LuFilterX as IconFilterReset } from 'react-icons/lu';
export {BiDownArrowCircle as IconOpenList } from 'react-icons/bi';
export { LuAlertTriangle as IconAlert } from 'react-icons/lu';

View File

@ -48,7 +48,12 @@ function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstPr
schema={schema}
prefix={prefixes.cst_dependant_list}
/>
<Checkbox label='Удалить зависимые конституенты' value={expandOut} setValue={value => setExpandOut(value)} />
<Checkbox
label='Удалить зависимые конституенты'
className='my-2'
value={expandOut}
setValue={value => setExpandOut(value)}
/>
</Modal>
);
}

View File

@ -44,6 +44,18 @@ function LibraryPage() {
[head, path, query, isEditor, isOwned, isSubscribed, isVisible, user]
);
const hasCustomFilter = useMemo(
() =>
!!filter.path ||
!!filter.query ||
filter.head !== undefined ||
filter.isEditor !== undefined ||
filter.isOwned !== undefined ||
filter.isSubscribed !== undefined ||
filter.isVisible !== true,
[filter]
);
useLayoutEffect(() => {
setItems(library.applyFilter(filter));
}, [library, filter, filter.query]);
@ -81,14 +93,15 @@ function LibraryPage() {
hasNoData={library.items.length === 0}
>
<SearchPanel
total={library.items.length ?? 0}
filtered={items.length}
hasCustomFilter={hasCustomFilter}
query={query}
setQuery={setQuery}
path={path}
setPath={setPath}
head={head}
setHead={setHead}
total={library.items.length ?? 0}
filtered={items.length}
isVisible={isVisible}
isOwned={isOwned}
toggleOwned={toggleOwned}
@ -97,6 +110,7 @@ function LibraryPage() {
toggleSubscribed={toggleSubscribed}
isEditor={isEditor}
toggleEditor={toggleEditor}
resetFilter={resetFilter}
/>
{view}
</DataLoader>

View File

@ -4,11 +4,12 @@ import clsx from 'clsx';
import { useCallback } from 'react';
import { LocationIcon, SubscribeIcon, VisibilityIcon } from '@/components/DomainIcons';
import { IconEditor, IconFolder, IconOwner } from '@/components/Icons';
import { IconEditor, IconFilterReset, IconFolder, IconOwner } from '@/components/Icons';
import BadgeHelp from '@/components/info/BadgeHelp';
import Dropdown from '@/components/ui/Dropdown';
import DropdownButton from '@/components/ui/DropdownButton';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import SearchBar from '@/components/ui/SearchBar';
import SelectorButton from '@/components/ui/SelectorButton';
import { useAuth } from '@/context/AuthContext';
@ -22,6 +23,7 @@ import { tripleToggleColor } from '@/utils/utils';
interface SearchPanelProps {
total: number;
filtered: number;
hasCustomFilter: boolean;
query: string;
setQuery: React.Dispatch<React.SetStateAction<string>>;
@ -38,11 +40,14 @@ interface SearchPanelProps {
toggleSubscribed: () => void;
isEditor: boolean | undefined;
toggleEditor: () => void;
resetFilter: () => void;
}
function SearchPanel({
total,
filtered,
hasCustomFilter,
query,
setQuery,
path,
@ -57,7 +62,8 @@ function SearchPanel({
isSubscribed,
toggleSubscribed,
isEditor,
toggleEditor
toggleEditor,
resetFilter
}: SearchPanelProps) {
const { user } = useAuth();
const headMenu = useDropdown();
@ -109,6 +115,13 @@ function SearchPanel({
icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />}
onClick={toggleEditor}
/>
<MiniButton
title='Сбросить фильтры'
icon={<IconFilterReset size='1.25rem' className='icon-primary' />}
onClick={resetFilter}
disabled={!hasCustomFilter}
/>
</div>
) : null}
@ -169,18 +182,19 @@ function SearchPanel({
placeholder='Путь'
noIcon
noBorder
className='min-w-[5rem]'
className='min-w-[4.5rem] sm:min-w-[5rem]'
value={path}
onChange={setPath}
/>
</div>
<Overlay position='top-[-0.75rem] right-0'>
<BadgeHelp
topic={HelpTopic.UI_LIBRARY}
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'text-sm')}
offset={5}
place='right-start'
/>
</Overlay>
</div>
);
}

View File

@ -8,10 +8,11 @@ import BadgeHelp from '@/components/info/BadgeHelp';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import { useAccessMode } from '@/context/AccessModeContext';
import { AccessPolicy } from '@/models/library';
import { HelpTopic } from '@/models/miscellaneous';
import { UserLevel } from '@/models/user';
import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/labels';
import { prepareTooltip, tooltips } from '@/utils/labels';
import { useRSEdit } from '../RSEditContext';
@ -38,9 +39,10 @@ function RSFormToolbar({ modified, anonymous, subscribed, onSubmit, onDestroy }:
/>
) : null}
<MiniButton
title='Поделиться схемой'
titleHtml={tooltips.shareItem(controller.schema?.access_policy)}
icon={<IconShare size='1.25rem' className='icon-primary' />}
onClick={controller.share}
disabled={controller.schema?.access_policy !== AccessPolicy.PUBLIC}
/>
{!anonymous ? (
<MiniButton

View File

@ -32,8 +32,9 @@ import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import { useRSForm } from '@/context/RSFormContext';
import useDropdown from '@/hooks/useDropdown';
import { AccessPolicy } from '@/models/library';
import { UserLevel } from '@/models/user';
import { describeAccessMode, labelAccessMode } from '@/utils/labels';
import { describeAccessMode, labelAccessMode, tooltips } from '@/utils/labels';
import { useRSEdit } from './RSEditContext';
@ -138,8 +139,10 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
<Dropdown isOpen={schemaMenu.isOpen}>
<DropdownButton
text='Поделиться'
titleHtml={tooltips.shareItem(controller.schema?.access_policy)}
icon={<IconShare size='1rem' className='icon-primary' />}
onClick={handleShare}
disabled={controller.schema?.access_policy !== AccessPolicy.PUBLIC}
/>
{user ? (
<DropdownButton

View File

@ -909,7 +909,9 @@ export const errors = {
* UI tooltip descriptors.
*/
export const tooltips = {
unsaved: 'Сохраните или отмените изменения'
unsaved: 'Сохраните или отмените изменения',
shareItem: (policy?: AccessPolicy) =>
policy === AccessPolicy.PUBLIC ? 'Поделиться схемой' : 'Поделиться можно только <br/>открытой схемой'
};
/**