Skip to content

Commit

Permalink
feat(os): sorting + hash state (#98)
Browse files Browse the repository at this point in the history
* feat(os): sorting

* feat(os): sorting
  • Loading branch information
pkim-gswell authored Aug 28, 2023
1 parent 8f958e9 commit f7b5cab
Show file tree
Hide file tree
Showing 28 changed files with 995 additions and 360 deletions.
10 changes: 6 additions & 4 deletions src/packages/shared-types/opensearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@ export type OsField =

export type OsFilterable = {
type: OsFilterType;
field: OsField;
field: OsField | "";
value: OsFilterValue;
prefix: "must" | "must_not" | "should" | "filter";
};

export type OsQueryState<T = any> = {
sort: { field: string; order: "asc" | "desc" };
sort: { field: OsField; order: "asc" | "desc" };
pagination: { number: number; size: number };
buckets: Record<string, { label: string; value: string }[]>;
data: T[];
filters: OsFilterable[];
search?: string;
// buckets: Record<string, { label: string; value: string }[]>;
// data: T[];
};

export type OsAggregateQuery = Record<
Expand Down
3 changes: 3 additions & 0 deletions src/services/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"@mui/system": "^5.14.1",
"@mui/x-data-grid": "^6.10.0",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-slot": "^1.0.2",
"@tanstack/react-query": "^4.29.1",
"@tanstack/react-query-devtools": "^4.29.5",
Expand All @@ -40,6 +42,7 @@
"file-saver": "^2.0.5",
"framer-motion": "^10.16.1",
"jszip": "^3.10.1",
"lz-string": "^1.5.0",
"lucide-react": "^0.268.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
6 changes: 3 additions & 3 deletions src/services/ui/src/api/useSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
import { useMutation, UseMutationOptions } from "@tanstack/react-query";
import { API } from "aws-amplify";
import { ReactQueryApiError, SearchData } from "shared-types";
import type { OsFilterable, OsQueryState } from "shared-types";
import type { OsQueryState, OsFilterable } from "shared-types";

type QueryProps = {
filters: OsFilterable[];
filters: OsQueryState["filters"];
sort?: OsQueryState["sort"];
pagination: OsQueryState["pagination"];
};
Expand Down Expand Up @@ -57,7 +57,7 @@ export const getAllSearchData = async (
return allHits.flat();
};

export const useSearch = (
export const useOsSearch = (
options?: UseMutationOptions<SearchData, ReactQueryApiError, QueryProps>
) => {
return useMutation<SearchData, ReactQueryApiError, QueryProps>(
Expand Down
13 changes: 7 additions & 6 deletions src/services/ui/src/components/ExportButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import { Download, Loader } from "lucide-react";
import { useState } from "react";
import { motion } from "framer-motion";
import { format } from "date-fns";
import { useOsParams } from "../Opensearch";

type Props = {
type: "waiver" | "spa";
};

export const ExportButton = ({ type }: Props) => {
export const OsExportButton = () => {
const [loading, setLoading] = useState(false);
const params = useOsParams();

const handleExport = async () => {
const csvExporter = new ExportToCsv({
useKeysAsHeaders: true,
filename: `${type}-export-${format(new Date(), "MM/dd/yyyy")}`,
filename: `${params.state.tab}-export-${format(
new Date(),
"MM/dd/yyyy"
)}`,
});
setLoading(true);

Expand Down
44 changes: 44 additions & 0 deletions src/services/ui/src/components/Opensearch/Filtering/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Sheet,
SheetContent,
SheetHeader,
SheetTrigger,
} from "@/components/Sheet";
import { SearchForm } from "@/components";
import { FC } from "react";
import { Icon, Typography } from "@enterprise-cmcs/macpro-ux-lib";
import { useOsParams } from "../useOpensearch";
import { OsExportButton } from "@/components/ExportButton";

export const OsFiltering: FC<{ disabled?: boolean }> = (props) => {
const params = useOsParams();

return (
<div className="flex flex-row gap-2 border-[1px] border-slate-200">
<SearchForm
handleSearch={(search) =>
params.onSet((s) => ({
...s,
pagination: { ...s.pagination, number: 0 },
search,
}))
}
disabled={!!props.disabled}
/>
<OsExportButton />
<Sheet>
<SheetTrigger>
<div className="flex flex-row item-center border-slate-100 px-4">
<Icon name="filter_list" />
<Typography size="md">Filters</Typography>
</div>
</SheetTrigger>
<SheetContent className="bg-white">
<SheetHeader>
<Typography size="lg">Filters</Typography>
</SheetHeader>
</SheetContent>
</Sheet>
</div>
);
};
22 changes: 22 additions & 0 deletions src/services/ui/src/components/Opensearch/Provider/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ReactNode } from "react";
import { createContextProvider } from "@/utils";
import { ReactQueryApiError, SearchData } from "shared-types";

type ContextState = {
data: SearchData | undefined;
isLoading: boolean;
error: ReactQueryApiError | null;
};

export const [OsContextProvider, useOsContext] =
createContextProvider<ContextState>({
name: "OsSearch Context",
errorMessage: "forgot to wrap with OsProvider",
});

export const OsProvider = (props: {
children: ReactNode;
value: ContextState;
}) => {
return <OsContextProvider {...props} />;
};
64 changes: 64 additions & 0 deletions src/services/ui/src/components/Opensearch/Settings/Visibility.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { cn } from "@/lib/utils";
import { Icon, Typography } from "@enterprise-cmcs/macpro-ux-lib";
import * as UI from "@/components/Popover";

type Item = { label: string; field: string; hidden: boolean };

type Props<T extends Item> = {
list: T[];
onItemClick: (field: string) => void;
};

export const VisibilityPopover = <T extends Item>(props: Props<T>) => {
return (
<UI.Popover>
<UI.PopoverTrigger>
<Icon name="visibility" />
</UI.PopoverTrigger>
<UI.PopoverContent className="bg-white">
<div className="flex flex-col gap-2">
<VisibilityMenu {...props} />
</div>
</UI.PopoverContent>
</UI.Popover>
);
};

export const VisiblityItem = <T extends Item>(
props: T & { onClick: () => void }
) => {
return (
<div
className={cn("flex flex-row gap-2 cursor-pointer", {
"text-gray-800": !props.hidden,
"text-gray-400": props.hidden,
})}
onClick={props.onClick}
>
<Icon
name={!props.hidden ? "visibility" : "visibility_off"}
className={cn({
"text-gray-800": !props.hidden,
"text-gray-400": props.hidden,
})}
/>
<Typography size="md" className="mt-[-1px]">
{props.label}
</Typography>
</div>
);
};

export const VisibilityMenu = <T extends Item>(props: Props<T>) => {
return (
<div className="flex flex-col gap-2">
{props.list.map((IT) => (
<VisiblityItem
key={`vis-${IT.field}`}
onClick={() => props.onItemClick(IT.field)}
{...IT}
/>
))}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Visibility";
78 changes: 78 additions & 0 deletions src/services/ui/src/components/Opensearch/Table/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import * as UI from "@/components/Table";
import { FC, useState } from "react";
import { OsTableColumn } from "./types";
import { useOsContext } from "../Provider";
import { useOsParams } from "../useOpensearch";
import { VisibilityPopover } from "../Settings";

export const OsTable: FC<{
columns: OsTableColumn[];
}> = (props) => {
const context = useOsContext();
const params = useOsParams();
const [osColumns, setOsColumns] = useState(
props.columns.map((COL) => ({ ...COL, hidden: false }))
);

const onToggle = (field: string) => {
setOsColumns((state) => {
return state?.map((S) => {
if (S.field !== field) return S;
return { ...S, hidden: !S.hidden };
});
});
};

return (
<UI.Table className="flex-1 border-[1px]">
<UI.TableHeader className="sticky top-0 bg-white">
<UI.TableRow>
{osColumns.map((TH) => {
if (TH.hidden) return null;
return (
<UI.TableHead
{...(!!TH.props && TH.props)}
key={`TH-${TH.field}`}
isActive={params.state.sort.field === TH.field}
desc={params.state.sort.order === "desc"}
onClick={() =>
params.onSet((s) => ({
...s,
sort: {
field: TH.field,
order: s.sort.order === "desc" ? "asc" : "desc",
},
}))
}
>
{TH.label}
</UI.TableHead>
);
})}

<UI.TableHead
className="w-[10px]"
icon={<VisibilityPopover list={osColumns} onItemClick={onToggle} />}
/>
</UI.TableRow>
</UI.TableHeader>
<UI.TableBody>
{context.data?.hits.map((DAT) => (
<UI.TableRow key={DAT._source.id}>
{osColumns.map((COL) => {
if (COL.hidden) return null;
return (
<UI.TableCell
key={`${COL.field}-${DAT._source.id}`}
className="font-medium"
>
{COL.cell(DAT._source)}
</UI.TableCell>
);
})}
</UI.TableRow>
))}
</UI.TableBody>
</UI.Table>
);
};
9 changes: 9 additions & 0 deletions src/services/ui/src/components/Opensearch/Table/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { OsField, OsHit, OsMainSourceItem } from "shared-types";
import type { ReactNode } from "react";

export type OsTableColumn = {
field: OsField;
label: string;
props?: any;
cell: (data: OsHit<OsMainSourceItem>["_source"]) => ReactNode;
};
4 changes: 4 additions & 0 deletions src/services/ui/src/components/Opensearch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./useOpensearch";
export * from "./Table";
export * from "./Filtering";
export * from "./Provider";
Loading

0 comments on commit f7b5cab

Please sign in to comment.