Skip to content

Commit

Permalink
Enhance Sort functionality on multi item table
Browse files Browse the repository at this point in the history
  • Loading branch information
agalin920 authored and shrunyan committed Jul 15, 2024
1 parent 57845da commit de05964
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 69 deletions.
79 changes: 76 additions & 3 deletions src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Menu, MenuItem, Button } from "@mui/material";
import { Box, Menu, MenuItem, Button, Typography } from "@mui/material";
import {
DateFilter,
FilterButton,
Expand All @@ -7,9 +7,13 @@ import {
import { useEffect, useMemo, useState } from "react";
import { useParams } from "../../../../../../shell/hooks/useParams";
import { ArrowDropDownOutlined } from "@mui/icons-material";
import { useGetLangsQuery } from "../../../../../../shell/services/instance";
import {
useGetContentModelFieldsQuery,
useGetLangsQuery,
} from "../../../../../../shell/services/instance";
import { useDateFilterParams } from "../../../../../../shell/hooks/useDateFilterParams";
import { useGetUsersQuery } from "../../../../../../shell/services/accounts";
import { useParams as useRouterParams } from "react-router";

const SORT_ORDER = {
dateSaved: "Date Saved",
Expand All @@ -24,6 +28,29 @@ const STATUS_FILTER = {
notPublished: "Not Published",
} as const;

const FILTERABLE_DATA_TYPES = [
"text",
"wysiwyg_basic",
"wysiwyg_advanced",
"article_writer",
"markdown",
"textarea",
"number",
"images",
"date",
"datetime",
"one_to_many",
"one_to_one",
"uuid",
"number",
"currency",
"date",
"datetime",
"link",
"internal_link",
"sort",
] as const;

const getCountryCode = (langCode: string) => {
if (!langCode) return "";
const splitTag = langCode.split("-");
Expand All @@ -48,6 +75,7 @@ const getFlagEmojiFromIETFTag = (langCode: string) => {
};

export const ItemListFilters = () => {
const { modelZUID } = useRouterParams<{ modelZUID: string }>();
const [anchorEl, setAnchorEl] = useState({
currentTarget: null,
id: "",
Expand All @@ -57,6 +85,8 @@ export const ItemListFilters = () => {
const { data: languages } = useGetLangsQuery({});
const activeLanguageCode = params.get("lang");
const { data: users } = useGetUsersQuery();
const { data: fields, isFetching: isFieldsFetching } =
useGetContentModelFieldsQuery(modelZUID);

const userOptions = useMemo(() => {
return users?.map((user) => ({
Expand All @@ -73,7 +103,9 @@ export const ItemListFilters = () => {
filterId="sortByFilter"
isFilterActive={false}
buttonText={`Sort: ${
SORT_ORDER[params.get("sort") as keyof typeof SORT_ORDER] ??
(SORT_ORDER[params.get("sort") as keyof typeof SORT_ORDER] ||
fields?.find((field) => field.name === params.get("sort"))
?.label) ??
SORT_ORDER.dateSaved
}`}
onOpenMenu={(event: React.MouseEvent<HTMLButtonElement>) => {
Expand All @@ -92,6 +124,13 @@ export const ItemListFilters = () => {
vertical: -8,
horizontal: "left",
}}
// add set width to the menu
PaperProps={{
sx: {
width: "240px",
maxHeight: "420px",
},
}}
>
{Object.entries(SORT_ORDER).map(([key, value]) => (
<MenuItem
Expand All @@ -112,6 +151,40 @@ export const ItemListFilters = () => {
{value}
</MenuItem>
))}
<Typography
variant="body3"
color="text.secondary"
fontWeight={600}
sx={{
display: "block",
pt: 1,
pl: 2,
borderTop: (theme) => `1px solid ${theme.palette.border}`,
}}
>
FIELDS
</Typography>
{fields
?.filter((field) =>
FILTERABLE_DATA_TYPES.includes(field.datatype as any)
)
?.map((field) => (
<MenuItem
key={field.ZUID}
onClick={() => {
setParams(field.name, "sort");
setAnchorEl({
currentTarget: null,
id: "",
});
}}
selected={params.get("sort") === field.name}
>
<Typography variant="inherit" noWrap>
{field.label}
</Typography>
</MenuItem>
))}
</Menu>
<FilterButton
filterId="statusFilter"
Expand Down
151 changes: 88 additions & 63 deletions src/apps/content-editor/src/app/views/ItemList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,73 +217,98 @@ export const ItemList = () => {
const sortedAndFilteredItems = useMemo(() => {
let clonedItems = [...processedItems];
clonedItems?.sort((a: any, b: any) => {
if (sort === "datePublished") {
// Handle undefined publishAt by setting a default far-future date for sorting purposes
if (sort) {
if (sort === "datePublished") {
// Handle undefined publishAt by setting a default far-future date for sorting purposes

let dateA = a?.publishing?.publishAt || a?.priorPublishing?.publishAt;
dateA = dateA ? new Date(dateA).getTime() : Number.NEGATIVE_INFINITY;
let dateA = a?.publishing?.publishAt || a?.priorPublishing?.publishAt;
dateA = dateA ? new Date(dateA).getTime() : Number.NEGATIVE_INFINITY;

let dateB = b?.publishing?.publishAt || b?.priorPublishing?.publishAt;
dateB = dateB ? new Date(dateB).getTime() : Number.NEGATIVE_INFINITY;
let dateB = b?.publishing?.publishAt || b?.priorPublishing?.publishAt;
dateB = dateB ? new Date(dateB).getTime() : Number.NEGATIVE_INFINITY;

return dateB - dateA;
} else if (sort === "dateCreated") {
return (
new Date(b.meta.createdAt).getTime() -
new Date(a.meta.createdAt).getTime()
);
} else if (sort === "status") {
const aPublishing = a?.publishing || a?.priorPublishing;
const bPublishing = b?.publishing || b?.priorPublishing;

const aDate = aPublishing?.publishAt
? new Date(aPublishing.publishAt)
: null;
const bDate = bPublishing?.publishAt
? new Date(bPublishing.publishAt)
: null;

// Determine the status of each item
const aIsPublished = aDate && aDate <= now;
const bIsPublished = bDate && bDate <= now;

const aIsScheduled = aDate && aDate > now;
const bIsScheduled = bDate && bDate > now;

// Check if meta.version exists
const aHasVersion = a?.meta?.version != null;
const bHasVersion = b?.meta?.version != null;

// Place items without meta.version at the bottom
if (!aHasVersion && bHasVersion) {
return 1;
} else if (aHasVersion && !bHasVersion) {
return -1;
}

// Continue with the original sorting logic
if (aIsPublished && !bIsPublished) {
return -1; // A is published, B is not
} else if (!aIsPublished && bIsPublished) {
return 1; // B is published, A is not
} else if (aIsPublished && bIsPublished) {
return bDate.getTime() - aDate.getTime(); // Both are published, sort by publish date descending
}

if (aIsScheduled && !bIsScheduled) {
return -1; // A is scheduled, B is not
} else if (!aIsScheduled && bIsScheduled) {
return 1; // B is scheduled, A is not
} else if (aIsScheduled && bIsScheduled) {
return aDate.getTime() - bDate.getTime(); // Both are scheduled, sort by publish date ascending
return dateB - dateA;
} else if (sort === "dateCreated") {
return (
new Date(b.meta.createdAt).getTime() -
new Date(a.meta.createdAt).getTime()
);
} else if (sort === "status") {
const aPublishing = a?.publishing || a?.priorPublishing;
const bPublishing = b?.publishing || b?.priorPublishing;

const aDate = aPublishing?.publishAt
? new Date(aPublishing.publishAt)
: null;
const bDate = bPublishing?.publishAt
? new Date(bPublishing.publishAt)
: null;

// Determine the status of each item
const aIsPublished = aDate && aDate <= now;
const bIsPublished = bDate && bDate <= now;

const aIsScheduled = aDate && aDate > now;
const bIsScheduled = bDate && bDate > now;

// Check if meta.version exists
const aHasVersion = a?.meta?.version != null;
const bHasVersion = b?.meta?.version != null;

// Place items without meta.version at the bottom
if (!aHasVersion && bHasVersion) {
return 1;
} else if (aHasVersion && !bHasVersion) {
return -1;
}

// Continue with the original sorting logic
if (aIsPublished && !bIsPublished) {
return -1; // A is published, B is not
} else if (!aIsPublished && bIsPublished) {
return 1; // B is published, A is not
} else if (aIsPublished && bIsPublished) {
return bDate.getTime() - aDate.getTime(); // Both are published, sort by publish date descending
}

if (aIsScheduled && !bIsScheduled) {
return -1; // A is scheduled, B is not
} else if (!aIsScheduled && bIsScheduled) {
return 1; // B is scheduled, A is not
} else if (aIsScheduled && bIsScheduled) {
return aDate.getTime() - bDate.getTime(); // Both are scheduled, sort by publish date ascending
}

return 0; // Neither is published nor scheduled, consider them equal
} else if (fields?.find((field) => field.name === sort)) {
const dataType = fields?.find(
(field) => field.name === sort
)?.datatype;
if (typeof a.data[sort] === "number") {
if (a.data[sort] == null) return 1;
if (b.data[sort] == null) return -1;

return dataType === "sort"
? a.data[sort] - b.data[sort]
: b.data[sort] - a.data[sort];
}
if (dataType === "date" || dataType === "datetime") {
return (
new Date(b.data[sort]).getTime() -
new Date(a.data[sort]).getTime()
);
}
const aValue =
dataType === "images" ? a.data[sort]?.filename : a.data[sort];
const bValue =
dataType === "images" ? b.data[sort]?.filename : b.data[sort];
return aValue?.trim()?.localeCompare(bValue?.trim());
} else {
return (
new Date(b.meta.updatedAt).getTime() -
new Date(a.meta.updatedAt).getTime()
);
}

return 0; // Neither is published nor scheduled, consider them equal
} else {
return (
new Date(b.meta.updatedAt).getTime() -
new Date(a.meta.updatedAt).getTime()
);
}
});
if (search) {
Expand Down
7 changes: 5 additions & 2 deletions src/shell/components/Filters/FilterButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FC } from "react";
import { Button, ButtonGroup } from "@mui/material";
import { Button, ButtonGroup, Typography } from "@mui/material";
import ArrowDropDownOutlinedIcon from "@mui/icons-material/ArrowDropDownOutlined";
import CheckIcon from "@mui/icons-material/Check";
import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
Expand Down Expand Up @@ -59,9 +59,12 @@ export const FilterButton: FC<FilterButton> = ({
sx={{
backgroundColor: "common.white",
height: "28px",
maxWidth: "320px",
}}
>
{buttonText}
<Typography variant="inherit" noWrap>
{buttonText}
</Typography>
</Button>
{children}
</>
Expand Down
3 changes: 2 additions & 1 deletion src/shell/services/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ export type ContentModelFieldDataType =
| "link"
| "internal_link"
| "yes_no"
| "color";
| "color"
| "sort";

export interface ContentModelField {
ZUID: string;
Expand Down

0 comments on commit de05964

Please sign in to comment.