From 1625a0c7c007d97a30d86089160eeb66825a10ed Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 11:24:24 +0100 Subject: [PATCH] made the creation of an entity more accessible and obvious --- .../ui/client/schema/data/use-bknd-data.ts | 13 ++- app/src/ui/components/display/Empty.tsx | 26 ++--- app/src/ui/components/steps/Steps.tsx | 8 +- app/src/ui/modals/index.tsx | 12 +-- .../schema/create-modal/CreateModal.tsx | 99 ++++++++++--------- .../schema/create-modal/step.select.tsx | 3 +- app/src/ui/routes/data/_data.root.tsx | 66 +++++++++---- .../ui/routes/data/data.schema.$entity.tsx | 54 +++++----- app/src/ui/routes/data/data.schema.index.tsx | 21 ++-- app/src/ui/routes/media/_media.root.tsx | 6 +- .../ui/routes/settings/components/Setting.tsx | 5 +- biome.json | 1 + 12 files changed, 176 insertions(+), 138 deletions(-) diff --git a/app/src/ui/client/schema/data/use-bknd-data.ts b/app/src/ui/client/schema/data/use-bknd-data.ts index 36db148..21030a9 100644 --- a/app/src/ui/client/schema/data/use-bknd-data.ts +++ b/app/src/ui/client/schema/data/use-bknd-data.ts @@ -12,6 +12,7 @@ import { } from "data/data-schema"; import { useBknd } from "ui/client/bknd"; import type { TSchemaActions } from "ui/client/schema/actions"; +import { bkndModals } from "ui/modals"; export function useBkndData() { const { config, app, schema, actions: bkndActions } = useBknd(); @@ -62,7 +63,8 @@ export function useBkndData() { } }; const $data = { - entity: (name: string) => entities[name] + entity: (name: string) => entities[name], + modals }; return { @@ -75,6 +77,15 @@ export function useBkndData() { }; } +const modals = { + createAny: () => bkndModals.open(bkndModals.ids.dataCreate, {}), + createEntity: () => + bkndModals.open(bkndModals.ids.dataCreate, { + initialPath: ["entities", "entity"], + initialState: { action: "entity" } + }) +}; + function entityFieldActions(bkndActions: TSchemaActions, entityName: string) { return { add: async (name: string, field: TAppDataField) => { diff --git a/app/src/ui/components/display/Empty.tsx b/app/src/ui/components/display/Empty.tsx index 717b781..9f1291c 100644 --- a/app/src/ui/components/display/Empty.tsx +++ b/app/src/ui/components/display/Empty.tsx @@ -1,33 +1,33 @@ -import { Button } from "../buttons/Button"; +import { twMerge } from "tailwind-merge"; +import { Button, type ButtonProps } from "../buttons/Button"; export type EmptyProps = { Icon?: any; title?: string; description?: string; - buttonText?: string; - buttonOnClick?: () => void; + primary?: ButtonProps; + secondary?: ButtonProps; + className?: string; }; export const Empty: React.FC = ({ Icon = undefined, title = undefined, description = "Check back later my friend.", - buttonText, - buttonOnClick + primary, + secondary, + className }) => ( -
+
{Icon && }
{title &&

{title}

}

{description}

- {buttonText && ( -
- -
- )} +
+ {secondary &&
); diff --git a/app/src/ui/components/steps/Steps.tsx b/app/src/ui/components/steps/Steps.tsx index 0dc02f2..d0ccc47 100644 --- a/app/src/ui/components/steps/Steps.tsx +++ b/app/src/ui/components/steps/Steps.tsx @@ -10,6 +10,7 @@ import { export type TStepsProps = { children: any; initialPath?: string[]; + initialState?: any; lastBack?: () => void; [key: string]: any; }; @@ -19,13 +20,14 @@ type TStepContext = { stepBack: () => void; close: () => void; state: T; + path: string[]; setState: Dispatch>; }; const StepContext = createContext(undefined as any); -export function Steps({ children, initialPath = [], lastBack }: TStepsProps) { - const [state, setState] = useState({}); +export function Steps({ children, initialPath = [], initialState = {}, lastBack }: TStepsProps) { + const [state, setState] = useState(initialState); const [path, setPath] = useState(initialPath); const steps: any[] = Children.toArray(children).filter( (child: any) => child.props.disabled !== true @@ -46,7 +48,7 @@ export function Steps({ children, initialPath = [], lastBack }: TStepsProps) { const current = steps.find((step) => step.props.id === path[path.length - 1]) || steps[0]; return ( - + {current} ); diff --git a/app/src/ui/modals/index.tsx b/app/src/ui/modals/index.tsx index 3ea2143..9869158 100644 --- a/app/src/ui/modals/index.tsx +++ b/app/src/ui/modals/index.tsx @@ -1,8 +1,8 @@ import type { ModalProps } from "@mantine/core"; import { modals as $modals, ModalsProvider, closeModal, openContextModal } from "@mantine/modals"; -import { transformObject } from "core/utils"; import type { ComponentProps } from "react"; import { OverlayModal } from "ui/modals/debug/OverlayModal"; +import { CreateModal } from "ui/modules/data/components/schema/create-modal/CreateModal"; import { DebugModal } from "./debug/DebugModal"; import { SchemaFormModal } from "./debug/SchemaFormModal"; import { TestModal } from "./debug/TestModal"; @@ -11,7 +11,8 @@ const modals = { test: TestModal, debug: DebugModal, form: SchemaFormModal, - overlay: OverlayModal + overlay: OverlayModal, + dataCreate: CreateModal }; declare module "@mantine/modals" { @@ -54,10 +55,9 @@ function close(modal: Modal) { } export const bkndModals = { - ids: transformObject(modals, (key) => key) as unknown as Record< - keyof typeof modals, - keyof typeof modals - >, + ids: Object.fromEntries(Object.keys(modals).map((key) => [key, key])) as { + [K in keyof typeof modals]: K; + }, open, close, closeAll: $modals.closeAll diff --git a/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx b/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx index 1863d83..791edec 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx @@ -1,15 +1,9 @@ -import { type Static, StringEnum, StringIdentifier, Type, transformObject } from "core/utils"; -import { FieldClassMap } from "data"; +import type { ModalProps } from "@mantine/core"; +import type { ContextModalProps } from "@mantine/modals"; +import { type Static, StringEnum, StringIdentifier, Type } from "core/utils"; import { entitiesSchema, fieldsSchema, relationsSchema } from "data/data-schema"; -import { omit } from "lodash-es"; -import { forwardRef, useState } from "react"; -import { - Modal2, - type Modal2Ref, - ModalBody, - ModalFooter, - ModalTitle -} from "ui/components/modal/Modal2"; +import { useState } from "react"; +import { type Modal2Ref, ModalBody, ModalFooter, ModalTitle } from "ui/components/modal/Modal2"; import { Step, Steps, useStepContext } from "ui/components/steps/Steps"; import { StepCreate } from "ui/modules/data/components/schema/create-modal/step.create"; import { StepEntity } from "./step.entity"; @@ -67,48 +61,59 @@ const createModalSchema = Type.Object( ); export type TCreateModalSchema = Static; -export const CreateModal = forwardRef(function CreateModal(props, ref) { - const [path, setPath] = useState([]); +export function CreateModal({ + context, + id, + innerProps: { initialPath = [], initialState } +}: ContextModalProps<{ initialPath?: string[]; initialState?: TCreateModalSchema }>) { + const [path, setPath] = useState(initialPath); + console.log("...", initialPath, initialState); function close() { - // @ts-ignore - ref?.current?.close(); + context.closeModal(id); } return ( - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - {/* Templates */} - {Templates.map(([Component, meta]) => ( - - - - - ))} - - + {/* Templates */} + {Templates.map(([Component, meta]) => ( + + + + + ))} + ); -}); +} +CreateModal.defaultTitle = undefined; +CreateModal.modalProps = { + withCloseButton: false, + size: "xl", + padding: 0, + classNames: { + root: "bknd-admin" + } +} satisfies Partial; export { ModalBody, ModalFooter, ModalTitle, useStepContext, relationsSchema }; diff --git a/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx b/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx index 30bb3a2..a26c62d 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx @@ -12,7 +12,7 @@ import { import Templates from "./templates/register"; export function StepSelect() { - const { nextStep, stepBack, state, setState } = useStepContext(); + const { nextStep, stepBack, state, path, setState } = useStepContext(); const selected = state.action ?? null; function handleSelect(action: TSchemaAction) { @@ -74,6 +74,7 @@ export function StepSelect() { }} prev={{ onClick: stepBack }} prevLabel="Cancel" + debug={{ state, path }} /> ); diff --git a/app/src/ui/routes/data/_data.root.tsx b/app/src/ui/routes/data/_data.root.tsx index d021c54..1ad24fa 100644 --- a/app/src/ui/routes/data/_data.root.tsx +++ b/app/src/ui/routes/data/_data.root.tsx @@ -1,8 +1,10 @@ -import { SegmentedControl } from "@mantine/core"; +import { SegmentedControl, Tooltip } from "@mantine/core"; import { IconDatabase } from "@tabler/icons-react"; import type { Entity, TEntityType } from "data"; +import { TbDatabasePlus } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; -import { useBknd } from "ui/client/bknd"; +import { useBkndData } from "ui/client/schema/data/use-bknd-data"; +import { IconButton } from "ui/components/buttons/IconButton"; import { Empty } from "ui/components/display/Empty"; import { Link } from "ui/components/wouter/Link"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; @@ -11,9 +13,7 @@ import { routes, useNavigate } from "ui/lib/routes"; export function DataRoot({ children }) { // @todo: settings routes should be centralized - const { - app: { entities } - } = useBknd(); + const { entities, $data } = useBkndData(); const entityList: Record = { regular: [], generated: [], @@ -22,7 +22,7 @@ export function DataRoot({ children }) { const [navigate] = useNavigate(); const context = window.location.href.match(/\/schema/) ? "schema" : "data"; - for (const entity of entities) { + for (const entity of Object.values(entities)) { entityList[entity.getType()].push(entity); } @@ -52,14 +52,19 @@ export function DataRoot({ children }) { + <> + + + + + } > Entities @@ -70,7 +75,7 @@ export function DataRoot({ children }) {
*/} - + { - if (entities.length === 0) return null; + context, + suggestCreate = false +}: { entities: Entity[]; title?: string; context: "data" | "schema"; suggestCreate?: boolean }) => { + const { $data } = useBkndData(); + if (entities.length === 0) { + return suggestCreate ? ( + $data.modals.createEntity() + }} + /> + ) : null; + } return (