diff --git a/app/__test__/ui/json-form.spec.ts b/app/__test__/ui/json-form.spec.ts index 16998ca..3a2df82 100644 --- a/app/__test__/ui/json-form.spec.ts +++ b/app/__test__/ui/json-form.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from "bun:test"; import { Draft2019 } from "json-schema-library"; import type { JSONSchema } from "json-schema-to-ts"; -import * as utils from "../../src/ui/components/form/json-schema-form2/utils"; +import * as utils from "../../src/ui/components/form/json-schema-form/utils"; describe("json form", () => { test("normalize path", () => { @@ -122,7 +122,7 @@ describe("json form", () => { } }); - test.only("...", () => { + test("...", () => { const schema = { type: "object", properties: { diff --git a/app/package.json b/app/package.json index 1e9f061..4860cea 100644 --- a/app/package.json +++ b/app/package.json @@ -47,7 +47,7 @@ "dayjs": "^1.11.13", "fast-xml-parser": "^4.4.0", "hono": "^4.6.12", - "json-schema-form-react": "link:json-schema-form-react", + "json-schema-form-react": "^0.0.2", "json-schema-library": "^10.0.0-rc7", "kysely": "^0.27.4", "liquidjs": "^10.15.0", diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index 1209c0e..ba5b8a8 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -14,6 +14,7 @@ type BkndContext = { config: ModuleConfigs; permissions: string[]; hasSecrets: boolean; + fetched: boolean; requireSecrets: () => Promise; actions: ReturnType; app: AppReduced; @@ -103,7 +104,7 @@ export function BkndProvider({ return ( {/*{error && ( diff --git a/app/src/ui/client/schema/media/use-bknd-media.ts b/app/src/ui/client/schema/media/use-bknd-media.ts index 4d8dbd1..426bfb8 100644 --- a/app/src/ui/client/schema/media/use-bknd-media.ts +++ b/app/src/ui/client/schema/media/use-bknd-media.ts @@ -1,26 +1,15 @@ +import type { TAppMediaConfig } from "media/media-schema"; import { useBknd } from "ui/client/BkndProvider"; export function useBkndMedia() { const { config, schema, actions: bkndActions } = useBknd(); const actions = { - /*roles: { - add: async (name: string, data: any = {}) => { - console.log("add role", name, data); - return await bkndActions.add("auth", `roles.${name}`, data); - }, - patch: async (name: string, data: any) => { - console.log("patch role", name, data); - return await bkndActions.patch("auth", `roles.${name}`, data); - }, - delete: async (name: string) => { - console.log("delete role", name); - if (window.confirm(`Are you sure you want to delete the role "${name}"?`)) { - return await bkndActions.remove("auth", `roles.${name}`); - } - return false; + config: { + patch: async (data: Partial) => { + return await bkndActions.set("media", data, true); } - }*/ + } }; const $media = {}; diff --git a/app/src/ui/components/buttons/Button.tsx b/app/src/ui/components/buttons/Button.tsx index eb5ad35..89ee795 100644 --- a/app/src/ui/components/buttons/Button.tsx +++ b/app/src/ui/components/buttons/Button.tsx @@ -20,7 +20,7 @@ const styles = { default: "bg-primary/5 hover:bg-primary/10 link text-primary/70", primary: "bg-primary hover:bg-primary/80 link text-background", ghost: "bg-transparent hover:bg-primary/5 link text-primary/70", - outline: "border border-primary/50 bg-transparent hover:bg-primary/5 link text-primary/80", + outline: "border border-primary/20 bg-transparent hover:bg-primary/5 link text-primary/80", red: "dark:bg-red-950 dark:hover:bg-red-900 bg-red-100 hover:bg-red-200 link text-primary/70", subtlered: "dark:text-red-950 text-red-700 dark:hover:bg-red-900 bg-transparent hover:bg-red-50 link" @@ -51,7 +51,7 @@ const Base = ({ }: BaseProps) => ({ ...props, className: twMerge( - "flex flex-row flex-nowrap items-center font-semibold disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed", + "flex flex-row flex-nowrap items-center font-semibold disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed transition-[opacity,background-color,color,border-color]", sizes[size ?? "default"], styles[variant ?? "default"], props.className diff --git a/app/src/ui/components/form/json-schema-form/Field.tsx b/app/src/ui/components/form/json-schema-form/Field.tsx index 904a1a5..8d4acd3 100644 --- a/app/src/ui/components/form/json-schema-form/Field.tsx +++ b/app/src/ui/components/form/json-schema-form/Field.tsx @@ -38,10 +38,11 @@ export const Field = ({ name, schema: _schema, onChange, label: _label, hidden } setValue(pointer, value as any);*/ const value = coerce(e.target.value, schema as any, { required }); - //console.log("handleChange", pointer, e.target.value, { value }); - if (typeof value === "undefined" && !required) { + //console.log("handleChange", pointer, e.target.value, { value }, ctx.options); + if (typeof value === "undefined" && !required && ctx.options?.keepEmpty !== true) { ctx.deleteValue(pointer); } else { + //console.log("setValue", pointer, value); setValue(pointer, value); } } diff --git a/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx b/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx index a6ba1c7..4e555ff 100644 --- a/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx +++ b/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx @@ -2,7 +2,7 @@ import { Popover } from "@mantine/core"; import { IconBug } from "@tabler/icons-react"; import type { JsonError } from "json-schema-library"; import type { JSONSchema } from "json-schema-to-ts"; -import { Children, type ReactElement, type ReactNode, cloneElement } from "react"; +import { Children, type ReactElement, type ReactNode, cloneElement, isValidElement } from "react"; import { IconButton } from "ui/components/buttons/IconButton"; import { JsonViewer } from "ui/components/code/JsonViewer"; import * as Formy from "ui/components/form/Formy"; @@ -14,7 +14,7 @@ export type FieldwrapperProps = { required?: boolean; errors?: JsonError[]; schema?: Exclude; - debug?: object; + debug?: object | boolean; wrapper?: "group" | "fieldset"; hidden?: boolean; children: ReactElement | ReactNode; @@ -26,7 +26,7 @@ export function FieldWrapper({ required, errors = [], schema, - debug = {}, + debug, wrapper, hidden, children @@ -43,20 +43,28 @@ export function FieldWrapper({ as={wrapper === "fieldset" ? "fieldset" : "div"} className={hidden ? "hidden" : "relative"} > - {/*
- - - - - - - - -
*/} + {debug && ( +
+ + + + + + + + +
+ )} {label && ( @@ -65,9 +73,9 @@ export function FieldWrapper({ )}
- {children} - {/*{Children.count(children) === 1 + {Children.count(children) === 1 && isValidElement(children) ? cloneElement(children, { + // @ts-ignore list: examples.length > 0 ? examplesId : undefined }) : children} @@ -77,7 +85,7 @@ export function FieldWrapper({
{description && {description}} diff --git a/app/src/ui/components/form/json-schema-form/Form.tsx b/app/src/ui/components/form/json-schema-form/Form.tsx index ff4a7cc..8e8ae35 100644 --- a/app/src/ui/components/form/json-schema-form/Form.tsx +++ b/app/src/ui/components/form/json-schema-form/Form.tsx @@ -14,6 +14,7 @@ import { useRef, useState } from "react"; +import { JsonViewer } from "ui/components/code/JsonViewer"; import { Field } from "./Field"; import { isRequired, normalizePath, omitSchema, prefixPointer } from "./utils"; @@ -32,6 +33,10 @@ export type FormProps< onSubmit?: (data: Partial) => void | Promise; onInvalidSubmit?: (errors: JsonError[], data: Partial) => void; hiddenSubmit?: boolean; + options?: { + debug?: boolean; + keepEmpty?: boolean; + }; }; export type FormContext = { @@ -44,6 +49,7 @@ export type FormContext = { submitting: boolean; schema: JSONSchema; lib: Draft2019; + options: FormProps["options"]; }; const FormContext = createContext>(undefined!); @@ -62,6 +68,7 @@ export function Form< validateOn = "submit", hiddenSubmit = true, ignoreKeys = [], + options = {}, ...props }: FormProps) { const [schema, initial] = omitSchema(_schema, ignoreKeys, _initialValues); @@ -146,7 +153,8 @@ export function Form< deleteValue, errors, schema, - lib + lib, + options } as any; //console.log("context", context); @@ -229,3 +237,10 @@ export function Subscribe({ children }: { children: (ctx: FormContext) => R const ctx = useFormContext(); return children(ctx); } + +export function FormDebug() { + const { options, data, dirty, errors, submitting } = useFormContext(); + if (options?.debug !== true) return null; + + return ; +} diff --git a/app/src/ui/routes/media/_media.root.tsx b/app/src/ui/routes/media/_media.root.tsx index 891f6b5..a14e6e0 100644 --- a/app/src/ui/routes/media/_media.root.tsx +++ b/app/src/ui/routes/media/_media.root.tsx @@ -1,33 +1,17 @@ -import { IconPhoto } from "@tabler/icons-react"; +import { IconAlertHexagon } from "@tabler/icons-react"; import { TbSettings } from "react-icons/tb"; import { useBknd } from "ui/client/BkndProvider"; import { IconButton } from "ui/components/buttons/IconButton"; -import { Empty } from "ui/components/display/Empty"; import { Link } from "ui/components/wouter/Link"; import { Media } from "ui/elements"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import * as AppShell from "ui/layouts/AppShell/AppShell"; -import { useLocation } from "wouter"; export function MediaRoot({ children }) { const { app, config } = useBknd(); - const [, navigate] = useLocation(); + const mediaDisabled = !config.media.enabled; useBrowserTitle(["Media"]); - if (!config.media.enabled) { - return ( - navigate(app.getSettingsPath(["media"])) - }} - /> - ); - } - return ( <> @@ -42,12 +26,13 @@ export function MediaRoot({ children }) {
- {/*
- -
*/}