Skip to content

Commit

Permalink
Merge pull request #123 from premieroctet/feature/search-list
Browse files Browse the repository at this point in the history
Hide search fields on empty search options
  • Loading branch information
cregourd authored Jan 23, 2024
2 parents 9c41f2e + 6285e36 commit d542bc6
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 64 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-games-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@premieroctet/next-admin": patch
---

Possibility to not display search field on list if search option is an empty array. In case search options are not defined, all scalar fields are concerned
120 changes: 61 additions & 59 deletions packages/next-admin/src/components/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { ColumnDef, RowSelectionState } from "@tanstack/react-table";
import debounce from "lodash/debounce";
import {
ChangeEvent,
useContext,
useEffect,
useState,
useTransition,
useTransition
} from "react";
import { ITEMS_PER_PAGE } from "../config";
import { useConfig } from "../context/ConfigContext";
import { useDeleteAction } from "../hooks/useDeleteAction";
import { useRouterInternal } from "../hooks/useRouterInternal";
import {
ListData,
ListDataFieldValue,
Expand All @@ -31,9 +33,6 @@ import {
SelectTrigger,
SelectValue,
} from "./radix/Select";
import { useRouterInternal } from "../hooks/useRouterInternal";
import { useConfig } from "../context/ConfigContext";
import { useDeleteAction } from "../hooks/useDeleteAction";

export type ListProps = {
resource: ModelName;
Expand Down Expand Up @@ -66,67 +65,70 @@ function List({
const sortDirection = query.sortDirection as "asc" | "desc";
const { deleteItems } = useDeleteAction(resource, deleteAction);

const onSearchChange = debounce((e: ChangeEvent<HTMLInputElement>) => {
startTransition(() => {
router?.push({
pathname: location.pathname,
query: { ...query, search: e.target.value },
let onSearchChange;
if (!(options?.list?.search && options?.list?.search?.length === 0)) {
onSearchChange = debounce((e: ChangeEvent<HTMLInputElement>) => {
startTransition(() => {
router?.push({
pathname: location.pathname,
query: { ...query, search: e.target.value },
});
});
});
}, 300);
}, 300);
}

const columns: ColumnDef<ListDataItem<ModelName>>[] =
data && data?.length > 0
? Object.keys(data[0]).map((property) => {
return {
accessorKey: property,
header: () => {
return (
<TableHead
sortDirection={sortDirection}
sortColumn={sortColumn}
property={property}
onClick={() => {
router?.push({
pathname: location.pathname,
query: {
...query,
sortColumn: property,
sortDirection:
query.sortDirection === "asc" ? "desc" : "asc",
},
});
}}
/>
);
},
cell: ({ row }) => {
const modelData = row.original;
const cellData = modelData[
property as keyof ListFieldsOptions<ModelName>
] as unknown as ListDataFieldValue;
return {
accessorKey: property,
header: () => {
return (
<TableHead
sortDirection={sortDirection}
sortColumn={sortColumn}
property={property}
onClick={() => {
router?.push({
pathname: location.pathname,
query: {
...query,
sortColumn: property,
sortDirection:
query.sortDirection === "asc" ? "desc" : "asc",
},
});
}}
/>
);
},
cell: ({ row }) => {
const modelData = row.original;
const cellData = modelData[
property as keyof ListFieldsOptions<ModelName>
] as unknown as ListDataFieldValue;

const dataFormatter =
options?.list?.fields?.[
property as keyof ListFieldsOptions<ModelName>
]?.formatter ||
((cell: any) => {
if (typeof cell === "object") {
return cell[resourcesIdProperty[property as ModelName]];
} else {
return cell;
}
});
const dataFormatter =
options?.list?.fields?.[
property as keyof ListFieldsOptions<ModelName>
]?.formatter ||
((cell: any) => {
if (typeof cell === "object") {
return cell[resourcesIdProperty[property as ModelName]];
} else {
return cell;
}
});

return (
<Cell
cell={cellData}
formatter={!isAppDir ? dataFormatter : undefined}
/>
);
},
};
})
return (
<Cell
cell={cellData}
formatter={!isAppDir ? dataFormatter : undefined}
/>
);
},
};
})
: [];

useEffect(() => {
Expand Down
8 changes: 4 additions & 4 deletions packages/next-admin/src/components/ListHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import Link from "next/link";
import { ChangeEvent, useMemo } from "react";
import Loader from "../assets/icons/Loader";
import { useConfig } from "../context/ConfigContext";
import { useI18n } from "../context/I18nContext";
import { ModelAction, ModelName } from "../types";
import ActionsDropdown from "./ActionsDropdown";
import { buttonVariants } from "./radix/Button";
import { useI18n } from "../context/I18nContext";

type Props = {
resource: ModelName;
isPending: boolean;
onSearchChange: (e: ChangeEvent<HTMLInputElement>) => void;
onSearchChange?: (e: ChangeEvent<HTMLInputElement>) => void;
search: string;
actions?: ModelAction[];
selectedRows: RowSelectionState;
Expand Down Expand Up @@ -53,7 +53,7 @@ export default function ListHeader({
return (
<div className="flex justify-between items-end">
<div>
<div className="mt-4 flex justify-end items-center gap-2">
{onSearchChange && <div className="mt-4 flex justify-end items-center gap-2">
{isPending ? (
<Loader className="h-6 w-6 stroke-gray-400 animate-spin" />
) : (
Expand All @@ -70,7 +70,7 @@ export default function ListHeader({
className="px-3 py-1.5 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm focus-visible:outline focus-visible:outline-indigo-500 focus-visible:ring focus-visible:ring-indigo-500"
placeholder={t("list.header.search.placeholder")}
/>
</div>
</div>}
</div>
<div className="flex items-center gap-x-4">
{!!selectedRowsCount && (
Expand Down
15 changes: 14 additions & 1 deletion packages/next-admin/src/utils/props.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use server";
import { Prisma, PrismaClient } from "@prisma/client";
import { cloneDeep } from "lodash";
import qs from "querystring";
import {
ActionParams,
Expand All @@ -24,7 +25,6 @@ import {
transformData,
transformSchema,
} from "./server";
import { cloneDeep } from "lodash";

export type GetPropsFromParamsParams = {
params?: string[];
Expand Down Expand Up @@ -99,6 +99,18 @@ export async function getPropsFromParams({
);
}

const clientOptions: NextAdminOptions = {
basePath: options.basePath,
model: {
[resource as ModelName]: {
list: {
display: options.model?.[resource as ModelName]?.list?.display,
search: options.model?.[resource as ModelName]?.list?.search,
},
}
},
}

let defaultProps: AdminComponentProps = {
resources,
basePath,
Expand All @@ -112,6 +124,7 @@ export async function getPropsFromParams({
resourcesTitles,
resourcesIdProperty,
deleteAction,
options: clientOptions,
};

if (!params) return defaultProps;
Expand Down

0 comments on commit d542bc6

Please sign in to comment.