improv: use statusFilter instead of includeArchive,includeDrafts for document search (#6537)
* improv: use statusFilter instead of includeArchive,includeDrafts for document search * improv: update FilterComponent to add support for multiple selected items * feat: update document type search ui * fix test * Restore support for old parameters to avoid breaking change --------- Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@@ -16,7 +16,7 @@ type TFilterOption = {
|
||||
|
||||
type Props = {
|
||||
options: TFilterOption[];
|
||||
activeKey: string | null | undefined;
|
||||
selectedKeys: (string | null | undefined)[];
|
||||
defaultLabel?: string;
|
||||
selectedPrefix?: string;
|
||||
className?: string;
|
||||
@@ -25,7 +25,7 @@ type Props = {
|
||||
|
||||
const FilterOptions = ({
|
||||
options,
|
||||
activeKey = "",
|
||||
selectedKeys = [],
|
||||
defaultLabel = "Filter options",
|
||||
selectedPrefix = "",
|
||||
className,
|
||||
@@ -34,17 +34,22 @@ const FilterOptions = ({
|
||||
const menu = useMenuState({
|
||||
modal: true,
|
||||
});
|
||||
const selected =
|
||||
options.find((option) => option.key === activeKey) || options[0];
|
||||
const selectedItems = options.filter((option) =>
|
||||
selectedKeys.includes(option.key)
|
||||
);
|
||||
|
||||
const selectedLabel = selected ? `${selectedPrefix} ${selected.label}` : "";
|
||||
const selectedLabel = selectedItems.length
|
||||
? selectedItems
|
||||
.map((selected) => `${selectedPrefix} ${selected.label}`)
|
||||
.join(", ")
|
||||
: "";
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<MenuButton {...menu}>
|
||||
{(props) => (
|
||||
<StyledButton {...props} className={className} neutral disclosure>
|
||||
{activeKey ? selectedLabel : defaultLabel}
|
||||
{selectedItems.length ? selectedLabel : defaultLabel}
|
||||
</StyledButton>
|
||||
)}
|
||||
</MenuButton>
|
||||
@@ -56,7 +61,7 @@ const FilterOptions = ({
|
||||
onSelect(option.key);
|
||||
menu.hide();
|
||||
}}
|
||||
selected={option.key === activeKey}
|
||||
selected={selectedKeys.includes(option.key)}
|
||||
{...menu}
|
||||
>
|
||||
{option.icon && <Icon>{option.icon}</Icon>}
|
||||
|
||||
@@ -9,7 +9,10 @@ import breakpoint from "styled-components-breakpoint";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { Pagination } from "@shared/constants";
|
||||
import { hideScrollbars } from "@shared/styles";
|
||||
import { DateFilter as TDateFilter } from "@shared/types";
|
||||
import {
|
||||
DateFilter as TDateFilter,
|
||||
StatusFilter as TStatusFilter,
|
||||
} from "@shared/types";
|
||||
import ArrowKeyNavigation from "~/components/ArrowKeyNavigation";
|
||||
import DocumentListItem from "~/components/DocumentListItem";
|
||||
import Empty from "~/components/Empty";
|
||||
@@ -54,18 +57,18 @@ function Search(props: Props) {
|
||||
|
||||
// filters
|
||||
const query = decodeURIComponentSafe(routeMatch.params.term ?? "");
|
||||
const includeArchived = params.get("includeArchived") === "true";
|
||||
const includeDrafts = params.get("includeDrafts") !== "false";
|
||||
const collectionId = params.get("collectionId") ?? undefined;
|
||||
const userId = params.get("userId") ?? undefined;
|
||||
const dateFilter = (params.get("dateFilter") as TDateFilter) ?? undefined;
|
||||
const statusFilter = params.getAll("statusFilter")?.length
|
||||
? (params.getAll("statusFilter") as TStatusFilter[])
|
||||
: [TStatusFilter.Published, TStatusFilter.Draft];
|
||||
const titleFilter = params.get("titleFilter") === "true";
|
||||
|
||||
const filters = React.useMemo(
|
||||
() => ({
|
||||
query,
|
||||
includeArchived,
|
||||
includeDrafts,
|
||||
statusFilter,
|
||||
collectionId,
|
||||
userId,
|
||||
dateFilter,
|
||||
@@ -73,8 +76,7 @@ function Search(props: Props) {
|
||||
}),
|
||||
[
|
||||
query,
|
||||
includeArchived,
|
||||
includeDrafts,
|
||||
JSON.stringify(statusFilter),
|
||||
collectionId,
|
||||
userId,
|
||||
dateFilter,
|
||||
@@ -118,8 +120,7 @@ function Search(props: Props) {
|
||||
collectionId?: string | undefined;
|
||||
userId?: string | undefined;
|
||||
dateFilter?: TDateFilter;
|
||||
includeArchived?: boolean | undefined;
|
||||
includeDrafts?: boolean | undefined;
|
||||
statusFilter?: TStatusFilter[];
|
||||
titleFilter?: boolean | undefined;
|
||||
}) => {
|
||||
history.replace({
|
||||
@@ -214,10 +215,9 @@ function Search(props: Props) {
|
||||
<>
|
||||
<Filters>
|
||||
<DocumentTypeFilter
|
||||
includeArchived={includeArchived}
|
||||
includeDrafts={includeDrafts}
|
||||
onSelect={({ includeArchived, includeDrafts }) =>
|
||||
handleFilterChange({ includeArchived, includeDrafts })
|
||||
statusFilter={statusFilter}
|
||||
onSelect={({ statusFilter }) =>
|
||||
handleFilterChange({ statusFilter })
|
||||
}
|
||||
/>
|
||||
<CollectionFilter
|
||||
|
||||
@@ -29,7 +29,7 @@ function CollectionFilter(props: Props) {
|
||||
return (
|
||||
<FilterOptions
|
||||
options={options}
|
||||
activeKey={collectionId}
|
||||
selectedKeys={[collectionId]}
|
||||
onSelect={onSelect}
|
||||
defaultLabel={t("Any collection")}
|
||||
selectedPrefix={`${t("Collection")}:`}
|
||||
|
||||
@@ -39,7 +39,7 @@ const DateFilter = ({ dateFilter, onSelect }: Props) => {
|
||||
return (
|
||||
<FilterOptions
|
||||
options={options}
|
||||
activeKey={dateFilter}
|
||||
selectedKeys={[dateFilter]}
|
||||
onSelect={onSelect}
|
||||
defaultLabel={t("Any time")}
|
||||
/>
|
||||
|
||||
@@ -1,80 +1,50 @@
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { StatusFilter as TStatusFilter } from "@shared/types";
|
||||
import FilterOptions from "~/components/FilterOptions";
|
||||
|
||||
type Props = {
|
||||
includeArchived?: boolean;
|
||||
includeDrafts?: boolean;
|
||||
onSelect: (option: {
|
||||
includeArchived?: boolean;
|
||||
includeDrafts?: boolean;
|
||||
}) => void;
|
||||
statusFilter: TStatusFilter[];
|
||||
onSelect: (option: { statusFilter: TStatusFilter[] }) => void;
|
||||
};
|
||||
|
||||
enum DocumentType {
|
||||
Published = "published",
|
||||
Active = "active",
|
||||
All = "all",
|
||||
}
|
||||
|
||||
const DocumentTypeFilter = ({
|
||||
includeArchived,
|
||||
includeDrafts,
|
||||
onSelect,
|
||||
}: Props) => {
|
||||
const DocumentTypeFilter = ({ statusFilter, onSelect }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const options = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
key: DocumentType.Published,
|
||||
key: TStatusFilter.Published,
|
||||
label: t("Published documents"),
|
||||
note: t("Documents you have access to, excluding drafts"),
|
||||
},
|
||||
{
|
||||
key: DocumentType.Active,
|
||||
label: t("Active documents"),
|
||||
note: t("Documents you have access to, including drafts"),
|
||||
key: TStatusFilter.Archived,
|
||||
label: t("Archived documents"),
|
||||
},
|
||||
{
|
||||
key: DocumentType.All,
|
||||
label: t("All documents"),
|
||||
note: t("Documents you have access to, including drafts and archived"),
|
||||
key: TStatusFilter.Draft,
|
||||
label: t("Draft documents"),
|
||||
},
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const getActiveKey = () => {
|
||||
if (includeArchived && includeDrafts) {
|
||||
return DocumentType.All;
|
||||
const handleSelect = (key: TStatusFilter) => {
|
||||
let modifiedStatusFilter;
|
||||
if (statusFilter.includes(key)) {
|
||||
modifiedStatusFilter = statusFilter.filter((status) => status !== key);
|
||||
} else {
|
||||
modifiedStatusFilter = [...statusFilter, key];
|
||||
}
|
||||
|
||||
if (includeDrafts) {
|
||||
return DocumentType.Active;
|
||||
}
|
||||
|
||||
return DocumentType.Published;
|
||||
};
|
||||
|
||||
const handleSelect = (key: DocumentType) => {
|
||||
switch (key) {
|
||||
case DocumentType.Published:
|
||||
return onSelect({ includeArchived: false, includeDrafts: false });
|
||||
case DocumentType.Active:
|
||||
return onSelect({ includeArchived: false, includeDrafts: true });
|
||||
case DocumentType.All:
|
||||
return onSelect({ includeArchived: true, includeDrafts: true });
|
||||
default:
|
||||
onSelect({ includeArchived: false, includeDrafts: false });
|
||||
}
|
||||
onSelect({ statusFilter: modifiedStatusFilter });
|
||||
};
|
||||
|
||||
return (
|
||||
<FilterOptions
|
||||
options={options}
|
||||
activeKey={getActiveKey()}
|
||||
selectedKeys={statusFilter}
|
||||
onSelect={handleSelect}
|
||||
defaultLabel={t("Document type")}
|
||||
defaultLabel={t("Any status")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ function UserFilter(props: Props) {
|
||||
return (
|
||||
<FilterOptions
|
||||
options={options}
|
||||
activeKey={userId}
|
||||
selectedKeys={[userId]}
|
||||
onSelect={onSelect}
|
||||
defaultLabel={t("Any author")}
|
||||
selectedPrefix={`${t("Author")}:`}
|
||||
|
||||
@@ -56,7 +56,7 @@ const UserStatusFilter = ({ activeKey, onSelect, ...rest }: Props) => {
|
||||
return (
|
||||
<FilterOptions
|
||||
options={options}
|
||||
activeKey={activeKey}
|
||||
selectedKeys={[activeKey]}
|
||||
onSelect={onSelect}
|
||||
defaultLabel={t("Active")}
|
||||
{...rest}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
JSONObject,
|
||||
NavigationNode,
|
||||
PublicTeam,
|
||||
StatusFilter,
|
||||
} from "@shared/types";
|
||||
import { subtractDate } from "@shared/utils/date";
|
||||
import { bytesToHumanReadable } from "@shared/utils/files";
|
||||
@@ -36,8 +37,7 @@ export type SearchParams = {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
dateFilter?: DateFilter;
|
||||
includeArchived?: boolean;
|
||||
includeDrafts?: boolean;
|
||||
statusFilter?: StatusFilter[];
|
||||
collectionId?: string;
|
||||
userId?: string;
|
||||
shareId?: string;
|
||||
|
||||
Reference in New Issue
Block a user