mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
admin: fix useSearch
This commit is contained in:
@@ -104,15 +104,17 @@ export function DataTable<Data extends Record<string, any> = Record<string, any>
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"link hover:bg-primary/5 py-1.5 rounded-md inline-flex flex-row justify-start items-center gap-1",
|
"py-1.5 rounded-md inline-flex flex-row justify-start items-center gap-1",
|
||||||
onClickSort ? "pl-2.5 pr-1" : "px-2.5",
|
onClickSort
|
||||||
|
? "link hover:bg-primary/5 pl-2.5 pr-1"
|
||||||
|
: "px-2.5",
|
||||||
)}
|
)}
|
||||||
onClick={() => onClickSort?.(property)}
|
onClick={() => onClickSort?.(property)}
|
||||||
>
|
>
|
||||||
<span className="text-left text-nowrap whitespace-nowrap">
|
<span className="text-left text-nowrap whitespace-nowrap">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
{onClickSort && (
|
{(onClickSort || (sort && sort.by === property)) && (
|
||||||
<SortIndicator sort={sort} field={property} />
|
<SortIndicator sort={sort} field={property} />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,32 +1,41 @@
|
|||||||
import { decodeSearch, encodeSearch, mergeObject, parseDecode } from "core/utils";
|
import { decodeSearch, encodeSearch, mergeObject } from "core/utils";
|
||||||
import { isEqual, transform } from "lodash-es";
|
import { isEqual, transform } from "lodash-es";
|
||||||
import { useLocation, useSearch as useWouterSearch } from "wouter";
|
import { useLocation, useSearch as useWouterSearch } from "wouter";
|
||||||
import { type s, parse, cloneSchema } from "core/object/schema";
|
import { type s, parse } from "core/object/schema";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export type UseSearchOptions<Schema extends s.TAnySchema = s.TAnySchema> = {
|
||||||
|
defaultValue?: Partial<s.StaticCoerced<Schema>>;
|
||||||
|
beforeEncode?: (search: Partial<s.StaticCoerced<Schema>>) => object;
|
||||||
|
};
|
||||||
|
|
||||||
// @todo: migrate to Typebox
|
|
||||||
export function useSearch<Schema extends s.TAnySchema = s.TAnySchema>(
|
export function useSearch<Schema extends s.TAnySchema = s.TAnySchema>(
|
||||||
_schema: Schema,
|
schema: Schema,
|
||||||
defaultValue?: Partial<s.StaticCoerced<Schema>>,
|
options?: UseSearchOptions<Schema>,
|
||||||
) {
|
) {
|
||||||
const schema = cloneSchema(_schema as any) as s.TSchema;
|
|
||||||
const searchString = useWouterSearch();
|
const searchString = useWouterSearch();
|
||||||
const [location, navigate] = useLocation();
|
const [location, navigate] = useLocation();
|
||||||
const initial = searchString.length > 0 ? decodeSearch(searchString) : (defaultValue ?? {});
|
const [value, setValue] = useState<s.StaticCoerced<Schema>>(
|
||||||
const value = parse(schema, initial, {
|
options?.defaultValue ?? ({} as any),
|
||||||
withDefaults: true,
|
);
|
||||||
clone: true,
|
const _defaults = mergeObject(
|
||||||
}) as s.StaticCoerced<Schema>;
|
// @ts-ignore
|
||||||
|
schema.template({ withOptional: true }),
|
||||||
|
options?.defaultValue ?? {},
|
||||||
|
);
|
||||||
|
|
||||||
// @ts-ignore
|
useEffect(() => {
|
||||||
const _defaults = mergeObject(schema.template({ withOptional: true }), defaultValue ?? {});
|
const initial =
|
||||||
|
searchString.length > 0 ? decodeSearch(searchString) : (options?.defaultValue ?? {});
|
||||||
|
const v = parse(schema, Object.assign({}, _defaults, initial)) as any;
|
||||||
|
setValue(v);
|
||||||
|
}, [searchString, JSON.stringify(options?.defaultValue), location]);
|
||||||
|
|
||||||
function set<Update extends Partial<s.StaticCoerced<Schema>>>(update: Update): void {
|
function set<Update extends Partial<s.StaticCoerced<Schema>>>(update: Update): void {
|
||||||
// @ts-ignore
|
const search = getWithoutDefaults(Object.assign({}, value, update), _defaults);
|
||||||
if (schema.validate(update).valid) {
|
const prepared = options?.beforeEncode?.(search) ?? search;
|
||||||
const search = getWithoutDefaults(mergeObject(value, update), _defaults);
|
const encoded = encodeSearch(prepared, { encode: false });
|
||||||
const encoded = encodeSearch(search, { encode: false });
|
navigate(location + (encoded.length > 0 ? "?" + encoded : ""));
|
||||||
navigate(location + (encoded.length > 0 ? "?" + encoded : ""));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -257,15 +257,20 @@ function EntityDetailInner({
|
|||||||
}) {
|
}) {
|
||||||
const other = relation.other(entity);
|
const other = relation.other(entity);
|
||||||
const [navigate] = useNavigate();
|
const [navigate] = useNavigate();
|
||||||
|
const [search, setSearch] = useState({
|
||||||
const search = {
|
|
||||||
select: other.entity.getSelect(undefined, "table"),
|
select: other.entity.getSelect(undefined, "table"),
|
||||||
|
sort: other.entity.getDefaultSort(),
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
};
|
});
|
||||||
|
|
||||||
// @todo: add custom key for invalidation
|
// @todo: add custom key for invalidation
|
||||||
const $q = useApiQuery((api) =>
|
const $q = useApiQuery(
|
||||||
api.data.readManyByReference(entity.name, id, other.reference, search),
|
(api) => api.data.readManyByReference(entity.name, id, other.reference, search),
|
||||||
|
{
|
||||||
|
keepPreviousData: true,
|
||||||
|
revalidateOnFocus: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleClickRow(row: Record<string, any>) {
|
function handleClickRow(row: Record<string, any>) {
|
||||||
@@ -300,11 +305,17 @@ function EntityDetailInner({
|
|||||||
select={search.select}
|
select={search.select}
|
||||||
data={$q.data ?? null}
|
data={$q.data ?? null}
|
||||||
entity={other.entity}
|
entity={other.entity}
|
||||||
|
sort={search.sort}
|
||||||
onClickRow={handleClickRow}
|
onClickRow={handleClickRow}
|
||||||
onClickNew={handleClickNew}
|
onClickNew={handleClickNew}
|
||||||
page={1}
|
page={Math.floor(search.offset / search.limit) + 1}
|
||||||
total={$q.data?.body?.meta?.count ?? 1}
|
total={$q.data?.body?.meta?.count ?? 1}
|
||||||
/*onClickPage={handleClickPage}*/
|
onClickPage={(page) => {
|
||||||
|
setSearch((s) => ({
|
||||||
|
...s,
|
||||||
|
offset: (page - 1) * s.limit,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -35,8 +35,19 @@ export function DataEntityList({ params }) {
|
|||||||
useBrowserTitle(["Data", entity?.label ?? params.entity]);
|
useBrowserTitle(["Data", entity?.label ?? params.entity]);
|
||||||
const [navigate] = useNavigate();
|
const [navigate] = useNavigate();
|
||||||
const search = useSearch(searchSchema, {
|
const search = useSearch(searchSchema, {
|
||||||
select: entity.getSelect(undefined, "table"),
|
defaultValue: {
|
||||||
sort: entity.getDefaultSort(),
|
select: entity.getSelect(undefined, "table"),
|
||||||
|
sort: entity.getDefaultSort(),
|
||||||
|
},
|
||||||
|
beforeEncode: (v) => {
|
||||||
|
if ("sort" in v && v.sort) {
|
||||||
|
return {
|
||||||
|
...v,
|
||||||
|
sort: `${v.sort.dir === "asc" ? "" : "-"}${v.sort.by}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const $q = useApiQuery(
|
const $q = useApiQuery(
|
||||||
|
|||||||
Reference in New Issue
Block a user