Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Austenem/CAT-754 Refactor "My Lists" #3671

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a3d8db0
add ukv endpoint
austenem Jan 6, 2025
c175b6c
pull authenticated lists into my lists page
austenem Jan 9, 2025
f053c9e
implement add functionality
austenem Jan 9, 2025
79e358b
refactor hooks
austenem Jan 9, 2025
474e6ff
update deletion functionality
austenem Jan 10, 2025
535979c
update lists functionality
austenem Jan 13, 2025
72d9e54
fix typing
austenem Jan 13, 2025
b2721d8
add entity to list functionality
austenem Jan 13, 2025
f9a2919
edit and deletion functionality
austenem Jan 13, 2025
8a4b318
liststobedeleted functionality
austenem Jan 14, 2025
5ac2d16
remove savedlistscontent
austenem Jan 14, 2025
e0a60e1
update stores
austenem Jan 14, 2025
b0c654f
move logic to hooks
austenem Jan 16, 2025
2ad59ef
typescript conversions and list page update
austenem Jan 16, 2025
56476b0
adjust useeffect and language
austenem Jan 16, 2025
5ab11ea
add documentation, minor updates
austenem Jan 16, 2025
c3a2e27
fix re-rendering issue
austenem Jan 16, 2025
7a1b127
copy over local lists and entities
austenem Jan 16, 2025
62be1cd
update local to remote copy logic
austenem Jan 17, 2025
d45d82d
update save entity logic
austenem Jan 17, 2025
4f8b5e2
update alerts
austenem Jan 17, 2025
5a86baa
continue adding alerts
austenem Jan 17, 2025
2714c12
add save entities button to search page
austenem Jan 21, 2025
65dd5ab
update saved items table, add button to table
austenem Jan 21, 2025
6738147
adjust button tooltips
austenem Jan 21, 2025
19f6c9b
add barrel files
austenem Jan 21, 2025
8b6ca64
convert to ts
austenem Jan 21, 2025
b6f1209
continue conversions
austenem Jan 21, 2025
972cba2
separate out api and store
austenem Jan 21, 2025
c994ddc
update tests
austenem Jan 21, 2025
c4126bc
fix minor uuid bug, adjust menu button
austenem Jan 21, 2025
da6a178
sort items
austenem Jan 22, 2025
6d017c6
continue to add sorting
austenem Jan 22, 2025
76511e4
clean up and add changelog
austenem Jan 22, 2025
135fbf3
update list page
austenem Jan 22, 2025
7be52bb
update isLoading bool to pass test
austenem Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG-refactor-my-lists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Update "My Lists" feature to persist across devices for logged-in users.
- Update "My Lists" UI and messaging.
1 change: 1 addition & 0 deletions context/app/default_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class DefaultConfig(object):
USER_TEMPLATES_ENDPOINT = 'should-be-overriden'
UBKG_ENDPOINT = 'should-be-overridden'
SOFT_ASSAY_ENDPOINT = 'should-be-overridden'
UKV_ENDPOINT = 'should-be-overridden'

SECRET_KEY = 'should-be-overridden'
APP_CLIENT_ID = 'should-be-overridden'
Expand Down
1 change: 1 addition & 0 deletions context/app/static/js/components/Contexts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface AppContextType {
workspacesEndpoint: string;
userTemplatesEndpoint: string;
ubkgEndpoint: string;
ukvEndpoint: string;
protocolsClientToken: string;
isAuthenticated: boolean;
isWorkspacesUser: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import Stack from '@mui/material/Stack';

import type { Entity } from 'js/components/types';
import { CollapsibleDetailPageSection } from 'js/components/detailPage/DetailPageSection';
import RelatedEntitiesTable from 'js/components/detailPage/related-entities/RelatedEntitiesTable';
import BulkDownloadButton from 'js/components/bulkDownload/buttons/BulkDownloadButton';
import SaveEntitiesButton from 'js/components/savedLists/SaveEntitiesButton';
import { SpacedSectionButtonRow } from 'js/shared-styles/sections/SectionButtonRow';
import { sectionIconMap } from 'js/shared-styles/icons/sectionIconMap';

Expand All @@ -33,7 +35,12 @@ function CollectionDatasetsTable({ datasets }: CollectionDatasetsTableProps) {
{datasets.length} Datasets
</Typography>
}
buttons={<BulkDownloadButton uuids={uuids} tooltip="Bulk download files for datasets in this table." />}
buttons={
<Stack direction="row" spacing={1}>
<SaveEntitiesButton uuids={uuids} entity_type="Dataset" />
<BulkDownloadButton uuids={uuids} tooltip="Bulk download files for datasets in this table." />
</Stack>
}
/>
<Paper>
<RelatedEntitiesTable columns={columns} entities={data} entityType="dataset" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,11 @@ import React, { PropsWithChildren } from 'react';
import { createPortal } from 'react-dom';
import Stack from '@mui/material/Stack';

import useEntityStore, { savedAlertStatus, editedAlertStatus, EntityStore } from 'js/stores/useEntityStore';
import TableOfContents from 'js/shared-styles/sections/TableOfContents';
import { TableOfContentsItems } from 'js/shared-styles/sections/TableOfContents/types';
import { leftRouteBoundaryID, rightRouteBoundaryID } from 'js/components/Routes/Route/Route';
import { SectionOrder, getSections } from 'js/shared-styles/sections/TableOfContents/utils';
import { StyledAlert } from './style';

const entityStoreSelector = (state: EntityStore) => ({
shouldDisplaySavedOrEditedAlert: state.shouldDisplaySavedOrEditedAlert,
setShouldDisplaySavedOrEditedAlert: state.setShouldDisplaySavedOrEditedAlert,
});
import SavedListsSuccessAlert from 'js/components/savedLists/SavedListsSuccessAlert';

interface DetailLayoutProps extends PropsWithChildren {
sections: SectionOrder;
Expand Down Expand Up @@ -46,34 +40,12 @@ export function HelperPanelPortal({ children }: PropsWithChildren) {
);
}

function DetailAlert() {
const { shouldDisplaySavedOrEditedAlert, setShouldDisplaySavedOrEditedAlert } = useEntityStore(entityStoreSelector);

if (shouldDisplaySavedOrEditedAlert === savedAlertStatus) {
return (
<StyledAlert severity="success" onClose={() => setShouldDisplaySavedOrEditedAlert(false)}>
Successfully added to My Saves List. All lists are currently stored on local storage and are not transferable
between devices.
</StyledAlert>
);
}

if (shouldDisplaySavedOrEditedAlert === editedAlertStatus) {
return (
<StyledAlert severity="success" onClose={() => setShouldDisplaySavedOrEditedAlert(false)}>
Successfully updated save status. All lists are currently stored on local storage and are not transferable
between devices.
</StyledAlert>
);
}
}

function DetailLayout({ sections, children, isLoading = false }: DetailLayoutProps) {
const items = getSections(sections);

return (
<>
<DetailAlert />
<SavedListsSuccessAlert />
<TableOfContentsPortal items={items} isLoading={isLoading} />
{children}
</>
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import UnfoldLessRoundedIcon from '@mui/icons-material/UnfoldLessRounded';

import { TooltipButtonProps, TooltipIconButton } from 'js/shared-styles/buttons/TooltipButton';
import { CheckIcon, EditSavedEntityIcon, FileIcon, SaveEntityIcon } from 'js/shared-styles/icons';
import useEntityStore, { savedAlertStatus, SummaryViewsType } from 'js/stores/useEntityStore';
import { SummaryViewsType } from 'js/stores/useEntityStore';
import { useSavedListsAlertsStore, savedAlertStatus } from 'js/stores/useSavedListsAlertsStore';
import { useTrackEntityPageEvent } from 'js/components/detailPage/useTrackEntityPageEvent';
import EditSavedStatusDialog from 'js/components/savedLists/EditSavedStatusDialog';
import useSavedEntitiesStore, { SavedEntitiesStore } from 'js/stores/useSavedEntitiesStore';
import { Entity } from 'js/components/types';
import { AllEntityTypes } from 'js/shared-styles/icons/entityIconMap';
import { useFlaskDataContext } from 'js/components/Contexts';
import { sectionIconMap } from 'js/shared-styles/icons/sectionIconMap';
import { useIsLargeDesktop } from 'js/hooks/media-queries';
import ProcessedDataWorkspaceMenu from 'js/components/detailPage/ProcessedData/ProcessedDataWorkspaceMenu';
import { useSavedLists } from 'js/components/savedLists/hooks';
import WorkspacesIcon from 'assets/svg/workspaces.svg';

function ActionButton<E extends ElementType = IconButtonTypeMap['defaultComponent']>({
Expand Down Expand Up @@ -44,25 +45,24 @@ function JSONButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { entity_type:
}

function SaveEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const saveEntity = useSavedEntitiesStore((state) => state.saveEntity);
const setShouldDisplaySavedOrEditedAlert = useEntityStore((state) => state.setShouldDisplaySavedOrEditedAlert);

const { saveEntity } = useSavedLists();
const setSavedOrEditedAlert = useSavedListsAlertsStore((state) => state.setSavedOrEditedList);
const trackSave = useTrackEntityPageEvent();

return (
<ActionButton
onClick={() => {
saveEntity(uuid);
trackSave({ action: 'Save To List', label: uuid });
setShouldDisplaySavedOrEditedAlert(savedAlertStatus);
setSavedOrEditedAlert(savedAlertStatus);
}}
icon={SaveEntityIcon}
tooltip="Save to list"
/>
);
}

function EditSavedEntityButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { entity_type: AllEntityTypes }) {
function EditSavedEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const [dialogIsOpen, setDialogIsOpen] = useState(false);

return (
Expand All @@ -74,12 +74,7 @@ function EditSavedEntityButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { e
icon={EditSavedEntityIcon}
tooltip="Edit saved status"
/>
<EditSavedStatusDialog
dialogIsOpen={dialogIsOpen}
setDialogIsOpen={setDialogIsOpen}
uuid={uuid}
entity_type={entity_type}
/>
<EditSavedStatusDialog dialogIsOpen={dialogIsOpen} setDialogIsOpen={setDialogIsOpen} uuid={uuid} />
</>
);
}
Expand All @@ -88,16 +83,10 @@ function WorkspaceSVGIcon({ color = 'primary', ...props }: SvgIconProps) {
return <SvgIcon component={WorkspacesIcon} color={color} {...props} />;
}

const useSavedEntitiesSelector = (state: SavedEntitiesStore) => state.savedEntities;
function SaveEditEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const { savedEntities } = useSavedLists();

function SaveEditEntityButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { entity_type: AllEntityTypes }) {
const savedEntities = useSavedEntitiesStore(useSavedEntitiesSelector);

return uuid in savedEntities ? (
<EditSavedEntityButton uuid={uuid} entity_type={entity_type} />
) : (
<SaveEntityButton uuid={uuid} />
);
return uuid in savedEntities ? <EditSavedEntityButton uuid={uuid} /> : <SaveEntityButton uuid={uuid} />;
}

function ViewSelectChip({
Expand Down Expand Up @@ -197,7 +186,7 @@ function EntityHeaderActionButtons({
return (
<Stack direction="row" spacing={1} alignItems="center">
{isLargeDesktop && <ViewSelectChips selectedView={view} setView={setView} entity_type={entity_type} />}
<SaveEditEntityButton uuid={uuid} entity_type={entity_type} />
<SaveEditEntityButton uuid={uuid} />
<JSONButton entity_type={entity_type} uuid={uuid} />
{isDataset && (
<ProcessedDataWorkspaceMenu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Stack from '@mui/material/Stack';
import { useFlaskDataContext } from 'js/components/Contexts';
import { useTrackEntityPageEvent } from 'js/components/detailPage/useTrackEntityPageEvent';
import BulkDownloadButton from 'js/components/bulkDownload/buttons/BulkDownloadButton';
import SaveEntitiesButton from 'js/components/savedLists/SaveEntitiesButton';

interface RelatedEntitiesSectionHeaderProps {
searchPageHref: string;
Expand All @@ -19,6 +20,7 @@ export function RelatedEntitiesSectionActions({ searchPageHref, uuids }: Related

return (
<Stack direction="row" spacing={1}>
<SaveEntitiesButton uuids={uuids} entity_type="Dataset" />
<BulkDownloadButton uuids={uuids} tooltip="Bulk download files for datasets in this table." />
<Button
variant="contained"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import LabelledSectionText from 'js/shared-styles/sections/LabelledSectionText';
import OutboundIconLink from 'js/shared-styles/Links/iconLinks/OutboundIconLink';
import Citation from 'js/components/detailPage/Citation';
import { isCollection, isDataset, isPublication } from 'js/components/types';
import { SavedEntitiesList } from 'js/components/savedLists/types';
import { useFlaskDataContext } from 'js/components/Contexts';
import { getCollectionDOI } from 'js/pages/Collection/utils';
import { getEntityCreationInfo } from 'js/helpers/functions';
Expand Down Expand Up @@ -117,11 +118,24 @@ function CollectionCitation() {
function SummaryBodyContent({
isEntityHeader = false,
direction = 'column',
description: propDescription,
creationLabel: propCreationLabel,
creationDate: propCreationDate,
...stackProps
}: { isEntityHeader?: boolean } & Partial<StackProps>) {
}: {
isEntityHeader?: boolean;
description?: string;
creationLabel?: string;
creationDate?: string;
} & Partial<StackProps> &
Partial<SavedEntitiesList>) {
const { entity } = useFlaskDataContext();
const { description } = entity;
const { creationLabel, creationDate } = getEntityCreationInfo(entity);

const description = propDescription ?? entity.description;
const { creationLabel, creationDate } =
propCreationLabel && propCreationDate
? { creationLabel: propCreationLabel, creationDate: propCreationDate }
: getEntityCreationInfo(entity);

if (isPublication(entity)) {
return (
Expand Down
2 changes: 1 addition & 1 deletion context/app/static/js/components/organ/Samples/Samples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function Samples({ organTerms, id }: OrganSamplesProps) {
View Data on Search Page
</Button>
<AddItemsToListDialog
itemsToAddUUIDS={selectedRows}
itemsToAddUUIDS={Array.from(selectedRows)}
onSaveCallback={deselectHeaderAndRows}
disabled={selectedRows.size === 0}
/>
Expand Down
12 changes: 6 additions & 6 deletions context/app/static/js/components/profile/MyLists.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import React from 'react';
import Typography from '@mui/material/Typography';
import SectionPaper from 'js/shared-styles/sections/SectionPaper';
import { InternalLink } from 'js/shared-styles/Links';
import useSavedEntitiesStore from 'js/stores/useSavedEntitiesStore';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import SectionPaper from 'js/shared-styles/sections/SectionPaper';
import { CollapsibleDetailPageSection } from 'js/components/detailPage/DetailPageSection';
import { useSavedLists } from 'js/components/savedLists/hooks';

export function MyLists() {
const savedListCount = useSavedEntitiesStore((s) => Object.keys(s.savedLists).length);
const { savedLists } = useSavedLists();
const savedListCount = Object.keys(savedLists).length;

const buttonText = savedListCount === 0 ? 'Create List' : `Manage Lists (${savedListCount})`;
return (
<CollapsibleDetailPageSection id="my-lists" title="My Lists" component="h2" variant="h2">
<SectionPaper>
<Stack spacing={1} alignItems="start">
<Typography variant="body1">
Your lists are currently stored on local storage and are not transferable between devices. To manage your
lists, navigate to your <InternalLink href="/my-lists">lists</InternalLink>.
Lists saved here are stored to your profile and accessible across devices.
</Typography>
<Button variant="contained" color="primary" href="/my-lists">
{buttonText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { StyledListsIcon } from './style';
const prompt = 'Add To List';

interface AddItemsToListDialogProps extends React.ComponentProps<typeof LeftMarginButton> {
itemsToAddUUIDS: Set<string>;
itemsToAddUUIDS: string[];
onSaveCallback: () => void;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import React from 'react';

import useSavedEntitiesStore, { SavedEntitiesStore } from 'js/stores/useSavedEntitiesStore';
import AddToListItem from 'js/components/savedLists/AddToListItem';
import { SavedEntitiesList } from 'js/components/savedLists/types';
import { MaxHeightList } from './style';

const usedSavedEntitiesStoreSelector = (state: SavedEntitiesStore) => state.savedLists;

interface AddToListProps {
selectedLists: Set<string>;
allLists: Record<string, SavedEntitiesList>;
addToSelectedLists: (listUUID: string) => void;
removeFromSelectedLists: (listUUID: string) => void;
}

function AddToList({ selectedLists, addToSelectedLists, removeFromSelectedLists }: AddToListProps) {
const savedLists = useSavedEntitiesStore(usedSavedEntitiesStoreSelector);

function AddToList({ selectedLists, allLists, addToSelectedLists, removeFromSelectedLists }: AddToListProps) {
return (
<MaxHeightList>
{Object.entries(savedLists).map(([listUUID, value]) => (
{Object.entries(allLists).map(([listUUID, value]) => (
<AddToListItem
key={listUUID}
title={value.title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@ import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Checkbox from '@mui/material/Checkbox';

function AddToListItem({ isSelected, addToSelectedLists, removeFromSelectedLists, title, listUUID }) {
interface AddToListItemProps {
isSelected: boolean;
addToSelectedLists: (listUUID: string) => void;
removeFromSelectedLists: (listUUID: string) => void;
title: string;
listUUID: string;
}

function AddToListItem({
isSelected,
addToSelectedLists,
removeFromSelectedLists,
title,
listUUID,
}: AddToListItemProps) {
const labelId = `checkbox-list-${title}`;

const handleClick = isSelected ? removeFromSelectedLists : addToSelectedLists;
Expand Down
Loading
Loading