feat/custom-json-schema (#172)

* init

* update

* finished new repo query, removed old implementation

* remove debug folder
This commit is contained in:
dswbx
2025-05-22 08:52:25 +02:00
committed by GitHub
parent 6694c63990
commit 773df544dd
31 changed files with 614 additions and 424 deletions

View File

@@ -1,46 +1,43 @@
import {
type Static,
type StaticDecode,
type TSchema,
decodeSearch,
encodeSearch,
parseDecode,
} from "core/utils";
import { decodeSearch, encodeSearch, parseDecode } from "core/utils";
import { isEqual, transform } from "lodash-es";
import { useLocation, useSearch as useWouterSearch } from "wouter";
import { type s, parse } from "core/object/schema";
// @todo: migrate to Typebox
export function useSearch<Schema extends TSchema = TSchema>(
export function useSearch<Schema extends s.TAnySchema = s.TAnySchema>(
schema: Schema,
defaultValue?: Partial<StaticDecode<Schema>>,
defaultValue?: Partial<s.StaticCoerced<Schema>>,
) {
const searchString = useWouterSearch();
const [location, navigate] = useLocation();
let value: StaticDecode<Schema> = defaultValue ? parseDecode(schema, defaultValue as any) : {};
let value = (defaultValue ? parse(schema, defaultValue as any) : {}) as s.StaticCoerced<Schema>;
if (searchString.length > 0) {
value = parseDecode(schema, decodeSearch(searchString));
value = parse(schema, decodeSearch(searchString));
//console.log("search:decode", value);
}
// @todo: add option to set multiple keys at once
function set<Key extends keyof Static<Schema>>(key: Key, value: Static<Schema>[Key]): void {
function set<Key extends keyof s.StaticCoerced<Schema>>(
key: Key,
value: s.StaticCoerced<Schema>[Key],
): void {
//console.log("set", key, value);
const update = parseDecode(schema, { ...decodeSearch(searchString), [key]: value });
const update = parse(schema, { ...decodeSearch(searchString), [key]: value });
const search = transform(
update as any,
(result, value, key) => {
if (defaultValue && isEqual(value, defaultValue[key])) return;
result[key] = value;
},
{} as Static<Schema>,
{} as s.StaticCoerced<Schema>,
);
const encoded = encodeSearch(search, { encode: false });
navigate(location + (encoded.length > 0 ? "?" + encoded : ""));
}
return {
value: value as Required<StaticDecode<Schema>>,
value: value as Required<s.StaticCoerced<Schema>>,
set,
};
}

View File

@@ -11,8 +11,7 @@ import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2";
import { routes } from "ui/lib/routes";
import { EntityForm } from "ui/modules/data/components/EntityForm";
import { useEntityForm } from "ui/modules/data/hooks/useEntityForm";
import * as tbbox from "@sinclair/typebox";
const { Type } = tbbox;
import { s } from "core/object/schema";
export function DataEntityCreate({ params }) {
const { $data } = useBkndData();
@@ -29,7 +28,7 @@ export function DataEntityCreate({ params }) {
const $q = useEntityMutate(entity.name);
// @todo: use entity schema for prefilling
const search = useSearch(Type.Object({}), {});
const search = useSearch(s.object({}), {});
function goBack() {
window.history.go(-1);

View File

@@ -1,4 +1,4 @@
import { type Entity, querySchema } from "data";
import { type Entity, repoQuery } from "data";
import { Fragment } from "react";
import { TbDots } from "react-icons/tb";
import { useApiQuery } from "ui/client";
@@ -14,20 +14,14 @@ import * as AppShell from "ui/layouts/AppShell/AppShell";
import { routes, useNavigate } from "ui/lib/routes";
import { useCreateUserModal } from "ui/modules/auth/hooks/use-create-user-modal";
import { EntityTable2 } from "ui/modules/data/components/EntityTable2";
import * as tbbox from "@sinclair/typebox";
const { Type } = tbbox;
import { s } from "core/object/schema";
import { pick } from "core/utils/objects";
// @todo: migrate to Typebox
const searchSchema = Type.Composite(
[
Type.Pick(querySchema, ["select", "where", "sort"]),
Type.Object({
page: Type.Optional(Type.Number({ default: 1 })),
perPage: Type.Optional(Type.Number({ default: 10 })),
}),
],
{ additionalProperties: false },
);
const searchSchema = s.partialObject({
...pick(repoQuery.properties, ["select", "where", "sort"]),
page: s.number({ default: 1 }).optional(),
perPage: s.number({ default: 10 }).optional(),
});
const PER_PAGE_OPTIONS = [5, 10, 25];
@@ -74,8 +68,6 @@ export function DataEntityList({ params }) {
const sort = search.value.sort!;
const newSort = { by: name, dir: sort.by === name && sort.dir === "asc" ? "desc" : "asc" };
// // @ts-expect-error - somehow all search keys are optional
console.log("new sort", newSort);
search.set("sort", newSort as any);
}