Portal/rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx

230 lines
7.3 KiB
TypeScript
Raw Normal View History

2024-06-07 20:17:03 +03:00
'use client';
2024-06-19 22:09:31 +03:00
import { AnimatePresence } from 'framer-motion';
2024-08-22 22:41:29 +03:00
import fileDownload from 'js-file-download';
2024-06-07 20:17:03 +03:00
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
2024-06-07 20:17:03 +03:00
2024-08-22 22:41:29 +03:00
import { IconCSV } from '@/components/Icons';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
2024-06-07 20:17:03 +03:00
import DataLoader from '@/components/wrap/DataLoader';
2024-06-14 21:43:05 +03:00
import { useAuth } from '@/context/AuthContext';
2024-09-15 21:18:32 +03:00
import { useConceptOptions } from '@/context/ConceptOptionsContext';
2024-06-07 20:17:03 +03:00
import { useLibrary } from '@/context/LibraryContext';
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
2024-06-07 20:17:03 +03:00
import useLocalStorage from '@/hooks/useLocalStorage';
import { ILibraryItem, IRenameLocationData, LocationHead } from '@/models/library';
2024-06-07 20:17:03 +03:00
import { ILibraryFilter } from '@/models/miscellaneous';
2024-09-27 12:03:13 +03:00
import { UserID } from '@/models/user';
2024-06-07 20:17:03 +03:00
import { storage } from '@/utils/constants';
import { information } from '@/utils/labels';
2024-08-22 22:41:29 +03:00
import { convertToCSV, toggleTristateFlag } from '@/utils/utils';
2024-06-07 20:17:03 +03:00
import TableLibraryItems from './TableLibraryItems';
import ToolbarSearch from './ToolbarSearch';
2024-06-27 11:34:52 +03:00
import ViewSideLocation from './ViewSideLocation';
2024-06-07 20:17:03 +03:00
function LibraryPage() {
const library = useLibrary();
2024-06-14 21:43:05 +03:00
const { user } = useAuth();
2024-06-07 20:17:03 +03:00
const [items, setItems] = useState<ILibraryItem[]>([]);
2024-09-15 21:18:32 +03:00
const options = useConceptOptions();
2024-06-07 20:17:03 +03:00
const [query, setQuery] = useState('');
const [path, setPath] = useState('');
const [head, setHead] = useLocalStorage<LocationHead | undefined>(storage.librarySearchHead, undefined);
const [subfolders, setSubfolders] = useLocalStorage<boolean>(storage.librarySearchSubfolders, false);
2024-06-07 20:17:03 +03:00
const [isVisible, setIsVisible] = useLocalStorage<boolean | undefined>(storage.librarySearchVisible, true);
2024-09-27 12:03:13 +03:00
const [isOwned, setIsOwned] = useLocalStorage<boolean | undefined>(storage.librarySearchOwned, undefined);
2024-06-07 20:17:03 +03:00
const [isEditor, setIsEditor] = useLocalStorage<boolean | undefined>(storage.librarySearchEditor, undefined);
2024-09-27 12:03:13 +03:00
const [filterUser, setFilterUser] = useLocalStorage<UserID | undefined>(storage.librarySearchUser, undefined);
const [showRenameLocation, setShowRenameLocation] = useState(false);
2024-06-07 20:17:03 +03:00
const filter: ILibraryFilter = useMemo(
() => ({
head: head,
path: path,
query: query,
2024-06-14 21:43:05 +03:00
isEditor: user ? isEditor : undefined,
isOwned: user ? isOwned : undefined,
2024-06-19 22:09:31 +03:00
isVisible: user ? isVisible : true,
2024-09-15 21:18:32 +03:00
folderMode: options.folderMode,
subfolders: subfolders,
2024-09-27 12:03:13 +03:00
location: options.location,
filterUser: filterUser
2024-06-07 20:17:03 +03:00
}),
2024-09-27 12:03:13 +03:00
[
head,
path,
query,
isEditor,
isOwned,
isVisible,
user,
options.folderMode,
options.location,
subfolders,
filterUser
]
2024-06-07 20:17:03 +03:00
);
2024-06-18 15:06:52 +03:00
const hasCustomFilter = useMemo(
() =>
!!filter.path ||
!!filter.query ||
filter.head !== undefined ||
filter.isEditor !== undefined ||
filter.isOwned !== undefined ||
2024-06-19 22:09:31 +03:00
filter.isVisible !== true ||
2024-09-27 12:03:13 +03:00
filter.filterUser !== undefined ||
2024-06-27 11:34:52 +03:00
!!filter.location,
2024-06-18 15:06:52 +03:00
[filter]
);
2024-06-07 20:17:03 +03:00
useLayoutEffect(() => {
setItems(library.applyFilter(filter));
}, [library, library.items.length, filter]);
2024-06-07 20:17:03 +03:00
const toggleVisible = useCallback(() => setIsVisible(prev => toggleTristateFlag(prev)), [setIsVisible]);
const toggleOwned = useCallback(() => setIsOwned(prev => toggleTristateFlag(prev)), [setIsOwned]);
const toggleEditor = useCallback(() => setIsEditor(prev => toggleTristateFlag(prev)), [setIsEditor]);
const toggleFolderMode = useCallback(() => options.setFolderMode(prev => !prev), [options]);
const toggleSubfolders = useCallback(() => setSubfolders(prev => !prev), [setSubfolders]);
2024-06-07 20:17:03 +03:00
const resetFilter = useCallback(() => {
setQuery('');
setPath('');
setHead(undefined);
setIsVisible(true);
setIsOwned(undefined);
setIsEditor(undefined);
2024-09-27 12:03:13 +03:00
setFilterUser(undefined);
2024-09-15 21:18:32 +03:00
options.setLocation('');
}, [setHead, setIsVisible, setIsOwned, setIsEditor, setFilterUser, options]);
const promptRenameLocation = useCallback(() => {
setShowRenameLocation(true);
}, []);
const handleRenameLocation = useCallback(
(newLocation: string) => {
const data: IRenameLocationData = {
2024-09-15 21:18:32 +03:00
target: options.location,
new_location: newLocation
};
library.renameLocation(data, () => {
2024-09-15 21:18:32 +03:00
options.setLocation(newLocation);
toast.success(information.locationRenamed);
});
},
[options, library]
);
2024-06-07 20:17:03 +03:00
2024-08-22 22:41:29 +03:00
const handleDownloadCSV = useCallback(() => {
if (items.length === 0) {
toast.error(information.noDataToExport);
return;
}
const blob = convertToCSV(items);
try {
fileDownload(blob, 'library.csv', 'text/csv;charset=utf-8;');
} catch (error) {
console.error(error);
}
}, [items]);
2024-06-27 11:34:52 +03:00
const viewLibrary = useMemo(
2024-06-07 20:17:03 +03:00
() => (
<TableLibraryItems
2024-06-27 11:34:52 +03:00
resetQuery={resetFilter}
2024-06-07 20:17:03 +03:00
items={items}
2024-09-15 21:18:32 +03:00
folderMode={options.folderMode}
2024-06-19 22:09:31 +03:00
toggleFolderMode={toggleFolderMode}
2024-06-07 20:17:03 +03:00
/>
),
2024-09-15 21:18:32 +03:00
[resetFilter, items, options.folderMode, toggleFolderMode]
2024-06-07 20:17:03 +03:00
);
2024-06-27 11:34:52 +03:00
const viewLocations = useMemo(
() => (
<ViewSideLocation
activeLocation={options.location}
onChangeActiveLocation={options.setLocation}
subfolders={subfolders}
2024-06-27 11:34:52 +03:00
folderTree={library.folders}
toggleFolderMode={toggleFolderMode}
toggleSubfolders={toggleSubfolders}
onRenameLocation={promptRenameLocation}
2024-06-27 11:34:52 +03:00
/>
),
[
options.location,
library.folders,
options.setLocation,
toggleFolderMode,
promptRenameLocation,
toggleSubfolders,
subfolders
]
2024-06-27 11:34:52 +03:00
);
2024-06-07 20:17:03 +03:00
return (
<DataLoader
id='library-page' // prettier: split lines
isLoading={library.loading}
2024-06-19 22:09:31 +03:00
error={library.loadingError}
2024-06-07 20:17:03 +03:00
hasNoData={library.items.length === 0}
>
{showRenameLocation ? (
<DlgChangeLocation
2024-09-15 21:18:32 +03:00
initial={options.location}
onChangeLocation={handleRenameLocation}
hideWindow={() => setShowRenameLocation(false)}
/>
) : null}
2024-09-15 21:18:32 +03:00
<Overlay
position={options.noNavigation ? 'top-[0.25rem] right-[3rem]' : 'top-[0.25rem] right-0'}
layer='z-tooltip'
className='transition-all'
>
2024-08-22 22:41:29 +03:00
<MiniButton
title='Выгрузить в формате CSV'
icon={<IconCSV size='1.25rem' className='icon-green' />}
onClick={handleDownloadCSV}
/>
</Overlay>
<ToolbarSearch
2024-06-18 15:06:52 +03:00
total={library.items.length ?? 0}
filtered={items.length}
hasCustomFilter={hasCustomFilter}
2024-06-07 20:17:03 +03:00
query={query}
onChangeQuery={setQuery}
2024-06-07 20:17:03 +03:00
path={path}
onChangePath={setPath}
2024-06-07 20:17:03 +03:00
head={head}
onChangeHead={setHead}
2024-06-07 20:17:03 +03:00
isVisible={isVisible}
isOwned={isOwned}
toggleOwned={toggleOwned}
toggleVisible={toggleVisible}
isEditor={isEditor}
toggleEditor={toggleEditor}
2024-09-27 12:03:13 +03:00
filterUser={filterUser}
onChangeFilterUser={setFilterUser}
2024-06-18 15:06:52 +03:00
resetFilter={resetFilter}
2024-09-15 21:18:32 +03:00
folderMode={options.folderMode}
2024-06-19 22:09:31 +03:00
toggleFolderMode={toggleFolderMode}
2024-06-07 20:17:03 +03:00
/>
2024-06-19 22:09:31 +03:00
<div className='flex'>
2024-09-15 21:18:32 +03:00
<AnimatePresence initial={false}>{options.folderMode ? viewLocations : null}</AnimatePresence>
2024-06-27 11:34:52 +03:00
{viewLibrary}
2024-06-19 22:09:31 +03:00
</div>
2024-06-07 20:17:03 +03:00
</DataLoader>
);
}
export default LibraryPage;