diff --git a/app/src/components/layout/header/BaseHeader.tsx b/app/src/components/layout/header/BaseHeader.tsx
index 177762a4..6add9e96 100644
--- a/app/src/components/layout/header/BaseHeader.tsx
+++ b/app/src/components/layout/header/BaseHeader.tsx
@@ -44,8 +44,8 @@ const BaseHeader = (props: IBaseHeader) => {
= () => {
-
- BioHub
+
+ BioHub BC
= () => {
}}>
-
+
);
@@ -223,7 +223,7 @@ const Header: React.FC = () => {
{
py: 4,
pb: 0
}}>
-
+
Submissions
diff --git a/app/src/features/admin/dashboard/components/ReviewedSubmissionsTable.tsx b/app/src/features/admin/dashboard/components/ReviewedSubmissionsTable.tsx
index abb6bd62..d3ca0bb3 100644
--- a/app/src/features/admin/dashboard/components/ReviewedSubmissionsTable.tsx
+++ b/app/src/features/admin/dashboard/components/ReviewedSubmissionsTable.tsx
@@ -90,22 +90,25 @@ const ReviewedSubmissionsTable = () => {
return (
<>
-
+
{`${submissionRecords.length} ${p(
submissionRecords.length,
'record'
)} found`}
-
-
+
+
+
+
{submissionRecords.map((submissionRecord) => {
return (
diff --git a/app/src/features/admin/dashboard/components/UnreviewedSubmissionsTable.tsx b/app/src/features/admin/dashboard/components/UnreviewedSubmissionsTable.tsx
index 8bec5304..d3281869 100644
--- a/app/src/features/admin/dashboard/components/UnreviewedSubmissionsTable.tsx
+++ b/app/src/features/admin/dashboard/components/UnreviewedSubmissionsTable.tsx
@@ -83,17 +83,20 @@ const UnreviewedSubmissionsTable = () => {
return (
<>
-
+
{`${submissionRecords.length} ${p(
submissionRecords.length,
'record'
)} found`}
-
-
+
+
+
+
{submissionRecords.map((submissionRecord) => {
return (
diff --git a/app/src/features/submissions/components/SubmissionHeaderSecurityStatus.tsx b/app/src/features/submissions/components/SubmissionHeaderSecurityStatus.tsx
index 23105a4d..f02c697b 100644
--- a/app/src/features/submissions/components/SubmissionHeaderSecurityStatus.tsx
+++ b/app/src/features/submissions/components/SubmissionHeaderSecurityStatus.tsx
@@ -20,34 +20,67 @@ const SubmissionHeaderSecurityStatus = (props: ISubmissionHeaderSecurityStatusPr
switch (submission.security) {
case SECURITY_APPLIED_STATUS.SECURED: {
securityStatus = (
- <>
+
-
+
Secured
- >
+
);
break;
}
case SECURITY_APPLIED_STATUS.PARTIALLY_SECURED: {
securityStatus = (
- <>
+
-
+
Partially Secured
- >
+
);
break;
}
default: {
securityStatus = (
- <>
+
-
+
Unsecured
- >
+
);
break;
}
@@ -57,56 +90,48 @@ const SubmissionHeaderSecurityStatus = (props: ISubmissionHeaderSecurityStatusPr
}
sx={{
textTransform: 'uppercase'
}}
title="Open access to all records">
-
+
{securityStatus}
{submission.publish_timestamp ? (
-
-
- Published:
+
+
+ Published
- {getFormattedDate(DATE_FORMAT.ShortDateFormat, submission.publish_timestamp as string)}
+ ({getFormattedDate(DATE_FORMAT.ShortDateFormat, submission.publish_timestamp as string)})
) : submission.security_review_timestamp ? (
-
-
- Completed Review:
+
+
+ Review Complete
- {getFormattedDate(DATE_FORMAT.ShortDateFormat, submission.security_review_timestamp as string)}
+ ({getFormattedDate(DATE_FORMAT.ShortDateFormat, submission.security_review_timestamp as string)})
) : (
-
-
- Pending Security Review
+
+
+ Pending Review
)}
-
-
- Submitted:
+
+
+ Date Submitted
- {getFormattedDate(DATE_FORMAT.ShortDateFormat, submission.create_date as string)}
+ ({getFormattedDate(DATE_FORMAT.ShortDateFormat, submission.create_date as string)})
diff --git a/app/src/features/submissions/list/SortMenuItem.tsx b/app/src/features/submissions/list/SortMenuItem.tsx
deleted file mode 100644
index a5ab699f..00000000
--- a/app/src/features/submissions/list/SortMenuItem.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { mdiSortAlphabeticalAscending, mdiSortAlphabeticalDescending } from '@mdi/js';
-import Icon from '@mdi/react';
-import MenuItem from '@mui/material/MenuItem';
-import useTheme from '@mui/system/useTheme';
-import { FuseResult } from 'fuse.js';
-import { SubmissionRecord } from 'interfaces/useSubmissionsApi.interface';
-import sortBy from 'lodash-es/sortBy';
-import { useState } from 'react';
-import { SortSubmission } from './SubmissionsListSortMenu';
-
-interface ISortMenuItemProps {
- submissions: TSubmission[];
- handleSort: (data: TSubmission[]) => void;
- sortKey: keyof SubmissionRecord;
- name: string;
-}
-
-const SortMenuItem = (props: ISortMenuItemProps) => {
- const { submissions, handleSort, sortKey, name } = props;
-
- const theme = useTheme();
- const [sortAscending, setSortAscending] = useState(true);
-
- /**
- * sorts by property
- *
- * @param {keyof SubmissionRecord} sortKey - property to sort datasets by
- */
- const sort = (sortKey: keyof SubmissionRecord, ascending: boolean) => {
- const getSortKey = (submission: SortSubmission) =>
- // uncertain why this needs to be cast to FuseResult when checking for item property?
- 'item' in submission ? (submission as FuseResult).item[sortKey] : submission[sortKey];
- const sortedData: TSubmission[] = ascending
- ? sortBy(submissions, getSortKey)
- : sortBy(submissions, getSortKey).reverse();
- handleSort(sortedData);
- };
- return (
-
- );
-};
-
-export default SortMenuItem;
diff --git a/app/src/features/submissions/list/SubmissionsListPage.tsx b/app/src/features/submissions/list/SubmissionsListPage.tsx
index 2fd86613..0ca7dbc5 100644
--- a/app/src/features/submissions/list/SubmissionsListPage.tsx
+++ b/app/src/features/submissions/list/SubmissionsListPage.tsx
@@ -86,13 +86,16 @@ const SubmissionsListPage = () => {
fuzzyData.length,
'record'
)} found`}
- {
- handleFuzzyData(data);
- }}
- />
+
+ {
+ handleFuzzyData(data);
+ }}
+ apiSortSync={{ key: 'publish_timestamp', sort: 'asc' }}
+ />
+
submissions={mockSubmissions}
handleSubmissions={mockHandleSubmissions}
sortMenuItems={menuItems}
+ apiSortSync={{ key: 'name', sort: 'asc' }}
/>
);
diff --git a/app/src/features/submissions/list/SubmissionsListSortMenu.tsx b/app/src/features/submissions/list/SubmissionsListSortMenu.tsx
index 467e8eaf..a4763586 100644
--- a/app/src/features/submissions/list/SubmissionsListSortMenu.tsx
+++ b/app/src/features/submissions/list/SubmissionsListSortMenu.tsx
@@ -1,19 +1,57 @@
-import { mdiChevronDown } from '@mdi/js';
+import { mdiChevronDown, mdiSortAscending, mdiSortDescending, mdiSortReverseVariant } from '@mdi/js';
import Icon from '@mdi/react';
+import { MenuItem, useTheme } from '@mui/material';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
-import Stack from '@mui/material/Stack';
import { FuseResult } from 'fuse.js';
import { SubmissionRecord } from 'interfaces/useSubmissionsApi.interface';
+import sortBy from 'lodash-es/sortBy';
import React, { useState } from 'react';
-import SortMenuItem from './SortMenuItem';
+import { objectKeys } from 'utils/Utils';
export type SortSubmission = FuseResult | SubmissionRecord;
+type SortProp = { key: keyof SubmissionRecord; sort: 'asc' | 'desc' };
+
export interface ISubmissionsListSortMenuProps {
+ /**
+ * Submissions to sort
+ *
+ * @type {TSubmission[]}
+ * @memberof ISubmissionsListSortMenuProps
+ */
submissions: TSubmission[];
+
+ /**
+ * Callback fired after submissions are sorted
+ *
+ * @param {TSubmission[]} - data
+ * @returns {void}
+ * @memberof ISubmissionsListSortMenuProps
+ */
handleSubmissions: (data: TSubmission[]) => void;
+
+ /**
+ * Sort menu items
+ * The object values will be the labels for the menu items
+ *
+ * @type {Partial>}
+ * @example: { name: 'Name', publish_timestamp: 'Publish Date' }
+ * @memberof ISubmissionsListSortMenuProps
+ */
sortMenuItems: Partial>;
+
+ /**
+ * Manually syncs the default sort with what api responds with
+ *
+ * Note: This does not sort the submissions, only sets the default selected sort of the menu.
+ * Open to suggestions on a better way to implement this
+ *
+ * @type {SortProp}
+ * @example: { key: 'publish_timetamp', sort: 'desc' }
+ * @memberof ISubmissionsListSortMenuProps
+ */
+ apiSortSync?: SortProp;
}
/**
@@ -26,9 +64,12 @@ export interface ISubmissionsListSortMenuProps {
const SubmissionsListSortMenu = (
props: ISubmissionsListSortMenuProps
) => {
- const { submissions, sortMenuItems, handleSubmissions } = props;
+ const { submissions, sortMenuItems, handleSubmissions, apiSortSync } = props;
+
+ const theme = useTheme();
const [anchorEl, setAnchorEl] = useState(null);
+ const [sortProp, setSortProp] = useState(apiSortSync);
const open = Boolean(anchorEl);
@@ -40,13 +81,50 @@ const SubmissionsListSortMenu = (
setAnchorEl(null);
};
- const handleSort = (submissions: TSubmission[]) => {
+ /**
+ * sorts submissions by property
+ *
+ * @param {SortProp} _sortProp - property and direction of sort
+ */
+ const sortSubmissions = (_sortProp: SortProp) => {
+ const getSortKey = (submission: SortSubmission) =>
+ // uncertain why this needs to be cast to FuseResult when checking for item property?
+ 'item' in submission
+ ? (submission as FuseResult).item[_sortProp.key]
+ : submission[_sortProp.key];
+
+ const sortedData: TSubmission[] =
+ _sortProp.sort === 'asc' ? sortBy(submissions, getSortKey) : sortBy(submissions, getSortKey).reverse();
+
+ return sortedData;
+ };
+
+ const handleMenuItemClick = (sortKey: keyof SubmissionRecord) => {
+ const sortDirection = !sortProp || sortProp.sort === 'desc' ? 'asc' : 'desc';
+ const newSortProp: SortProp = {
+ key: sortKey,
+ sort: sortDirection
+ };
+
+ const sortedSubmissions = sortSubmissions(newSortProp);
+
+ handleSubmissions(sortedSubmissions);
+ setSortProp(newSortProp);
handleClose();
- handleSubmissions(submissions);
+ };
+
+ const getSortIcon = (sortKey: keyof SubmissionRecord) => {
+ if (!sortProp || sortKey !== sortProp.key) {
+ return mdiSortReverseVariant;
+ }
+ if (sortProp.sort === 'asc') {
+ return mdiSortAscending;
+ }
+ return mdiSortDescending;
};
return (
-
+ <>
-
+ >
);
};
diff --git a/app/src/utils/Utils.ts b/app/src/utils/Utils.ts
index 4cd6f521..a887189a 100644
--- a/app/src/utils/Utils.ts
+++ b/app/src/utils/Utils.ts
@@ -380,3 +380,14 @@ export const getFormattedIdentitySource = (identitySource: SYSTEM_IDENTITY_SOURC
return null;
}
};
+
+/**
+ * same implementation as Object.keys but with correct typings for interable
+ *
+ * @template Obj
+ * @param {Obj} obj - object to iterate through
+ * @returns {(keyof Obj)[]} array of object keys with correct typings ie: not string[]
+ */
+export const objectKeys = (obj: Obj): (keyof Obj)[] => {
+ return Object.keys(obj) as (keyof Obj)[];
+};