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

[OPIK-372] [FE] Add sorting for projects table #593

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion apps/opik-frontend/src/api/projects/useProjectsList.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { QueryFunctionContext, useQuery } from "@tanstack/react-query";
import api, { PROJECTS_REST_ENDPOINT, QueryConfig } from "@/api/api";
import { Project } from "@/types/projects";
import { processSorting } from "@/lib/sorting";
import { Sorting } from "@/types/sorting";

type UseProjectsListParams = {
workspaceName: string;
search?: string;
sorting?: Sorting;
page: number;
size: number;
};
Expand All @@ -16,12 +19,13 @@ type UseProjectsListResponse = {

const getProjectsList = async (
{ signal }: QueryFunctionContext,
{ workspaceName, search, size, page }: UseProjectsListParams,
{ workspaceName, search, sorting, size, page }: UseProjectsListParams,
) => {
const { data } = await api.get(PROJECTS_REST_ENDPOINT, {
signal,
params: {
workspace_name: workspaceName,
...processSorting(sorting),
...(search && { name: search }),
size,
page,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ const DatasetItemsPage = () => {
cell: DatasetItemRowActionsCell,
size: 48,
enableResizing: false,
enableSorting: false,
});

return retVal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const DatasetsPage: React.FunctionComponent = () => {
cell: DatasetRowActionsCell,
size: 48,
enableResizing: false,
enableSorting: false,
});

return retVal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const ExperimentsPage: React.FunctionComponent = () => {
cell: ExperimentRowActionsCell,
size: 48,
enableResizing: false,
enableSorting: false,
});

return retVal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const FeedbackDefinitionsPage: React.FunctionComponent = () => {
cell: FeedbackDefinitionsRowActionsCell,
size: 48,
enableResizing: false,
enableSorting: false,
});

return retVal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,26 @@ import ColumnsButton from "@/components/shared/ColumnsButton/ColumnsButton";
import { COLUMN_TYPE, ColumnData } from "@/types/shared";
import { convertColumnDataToColumn } from "@/lib/table";
import useLocalStorageState from "use-local-storage-state";
import { ColumnSort } from "@tanstack/react-table";

const SELECTED_COLUMNS_KEY = "projects-selected-columns";
const COLUMNS_WIDTH_KEY = "projects-columns-width";
const COLUMNS_ORDER_KEY = "projects-columns-order";
const COLUMNS_SORT_KEY = "projects-columns-sort";

export const DEFAULT_COLUMNS: ColumnData<Project>[] = [
{
id: "id",
label: "ID",
type: COLUMN_TYPE.string,
cell: IdCell as never,
sortable: true,
},
{
id: "name",
label: "Name",
type: COLUMN_TYPE.string,
sortable: true,
},
{
id: "last_updated_at",
Expand All @@ -56,6 +60,13 @@ export const DEFAULT_SELECTED_COLUMNS: string[] = [
"created_at",
];

export const DEFAULT_SORTING_COLUMNS: ColumnSort[] = [
{
id: "id",
desc: true,
},
];

const ProjectsPage: React.FunctionComponent = () => {
const navigate = useNavigate();
const workspaceName = useAppStore((state) => state.activeWorkspaceName);
Expand All @@ -66,10 +77,19 @@ const ProjectsPage: React.FunctionComponent = () => {
const [search, setSearch] = useState("");
const [page, setPage] = useState(1);
const [size, setSize] = useState(10);

const [sortedColumns, setSortedColumns] = useLocalStorageState<ColumnSort[]>(
COLUMNS_SORT_KEY,
{
defaultValue: DEFAULT_SORTING_COLUMNS,
},
);

const { data, isPending } = useProjectsList(
{
workspaceName,
search,
sorting: sortedColumns,
page,
size,
},
Expand Down Expand Up @@ -119,6 +139,7 @@ const ProjectsPage: React.FunctionComponent = () => {
cell: ProjectRowActionsCell,
size: 48,
enableResizing: false,
enableSorting: false,
});

return retVal;
Expand Down Expand Up @@ -183,6 +204,11 @@ const ProjectsPage: React.FunctionComponent = () => {
columns={columns}
data={projects}
onRowClick={handleRowClick}
sortConfig={{
enabled: true,
sorting: sortedColumns,
setSorting: setSortedColumns,
}}
resizeConfig={resizeConfig}
noData={
<DataTableNoData title={noDataText}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const PromptsPage: React.FunctionComponent = () => {
cell: PromptRowActionsCell,
size: 48,
enableResizing: false,
enableSorting: false,
});

return retVal;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ReactNode, useEffect, useMemo } from "react";
import {
ColumnDef,
ColumnSort,
flexRender,
getCoreRowModel,
RowData,
Expand Down Expand Up @@ -45,6 +46,12 @@ declare module "@tanstack/react-table" {
}
}

interface SortConfig {
enabled: boolean;
sorting: ColumnSort[];
setSorting: OnChangeFn<ColumnSort[]>;
}

interface ResizeConfig {
enabled: boolean;
onColumnResize?: (data: Record<string, number>) => void;
Expand All @@ -55,6 +62,7 @@ interface DataTableProps<TData, TValue> {
data: TData[];
onRowClick?: (row: TData) => void;
activeRowId?: string;
sortConfig?: SortConfig;
resizeConfig?: ResizeConfig;
getRowId?: (row: TData) => string;
getRowHeightClass?: (height: ROW_HEIGHT) => string;
Expand All @@ -70,6 +78,7 @@ const DataTable = <TData, TValue>({
data,
onRowClick,
activeRowId,
sortConfig,
resizeConfig,
getRowId,
getRowHeightClass = calculateHeightClass,
Expand All @@ -87,9 +96,13 @@ const DataTable = <TData, TValue>({
columns,
getRowId,
columnResizeMode: "onChange",
enableSorting: sortConfig?.enabled ?? false,
enableSortingRemoval: false,
getCoreRowModel: getCoreRowModel(),
onRowSelectionChange: setRowSelection ? setRowSelection : undefined,
onSortingChange: sortConfig?.setSorting,
state: {
...(sortConfig?.sorting && { sorting: sortConfig.sorting }),
...(rowSelection && { rowSelection }),
},
meta: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
Clock,
Braces,
PenLine,
ArrowDown,
ArrowUp,
} from "lucide-react";
import { cn } from "@/lib/utils";

Expand All @@ -32,17 +34,47 @@ export const TypeHeader = <TData,>({
const { header, type: columnType, iconType } = column.columnDef.meta ?? {};
const type = iconType ?? columnType;
const Icon = type ? COLUMN_TYPE_MAP[type] : "span";
const isSortable = column.getCanSort();
const direction = column.getIsSorted();

const renderSort = () => {
const nextDirection = column.getNextSortingOrder();

if (!nextDirection || !isSortable) return null;

const Icon = direction === "asc" ? ArrowDown : ArrowUp;
const NextIcon = nextDirection === "asc" ? ArrowDown : ArrowUp;
return (
<>
<Icon
className={cn(
"size-3.5 group-hover:hidden shrink-0",
!direction && "hidden",
)}
/>
<NextIcon
className={cn("hidden size-3.5 group-hover:inline shrink-0")}
/>
</>
);
};

return (
<div
className={cn(
"flex size-full items-center gap-2 px-2",
"flex size-full items-center gap-1 px-2",
type === COLUMN_TYPE.number && "justify-end",
isSortable && "cursor-pointer group",
)}
onClick={(e) => e.stopPropagation()}
onClick={
isSortable
? column.getToggleSortingHandler()
: (e) => e.stopPropagation()
}
>
{Boolean(Icon) && <Icon className="size-4 shrink-0" />}
{Boolean(Icon) && <Icon className="mr-1 size-4 shrink-0" />}
<span className="truncate">{header}</span>
{renderSort()}
</div>
);
};
24 changes: 24 additions & 0 deletions apps/opik-frontend/src/lib/sorting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ColumnSort } from "@tanstack/react-table";
import { SORT_DIRECTION, SortingField } from "@/types/sorting";

export const processSorting = (sorting?: ColumnSort[]) => {
const retVal: {
sorting?: string;
} = {};
const sortingFields: SortingField[] = [];

if (sorting && sorting.length > 0) {
sorting.forEach(({ id, desc }) => {
sortingFields.push({
field: id,
direction: desc ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC,
});
});
}

if (sortingFields.length > 0) {
retVal.sorting = JSON.stringify(sortingFields);
}

return retVal;
};
1 change: 1 addition & 0 deletions apps/opik-frontend/src/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const convertColumnDataToColumn = <TColumnData, TData>(
},
...(size && { size }),
cell: (column.cell ?? TextCell) as never,
enableSorting: column.sortable || false,
});
}
});
Expand Down
1 change: 1 addition & 0 deletions apps/opik-frontend/src/types/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type ColumnData<T> = {
iconType?: COLUMN_TYPE;
cell?: Cell<T, unknown>;
verticalAlignment?: CELL_VERTICAL_ALIGNMENT;
sortable?: boolean;
};

export type DynamicColumn = {
Expand Down
13 changes: 13 additions & 0 deletions apps/opik-frontend/src/types/sorting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ColumnSort } from "@tanstack/react-table";

export enum SORT_DIRECTION {
ASC = "ASC",
DESC = "DESC",
}

export type SortingField = {
field: string;
direction: SORT_DIRECTION;
};

export type Sorting = ColumnSort[];
Loading