mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 12:56:05 +00:00
fix field date error in modal, adjust notification spacing, added entity quick switcher
This commit is contained in:
@@ -45,7 +45,7 @@ function AdminInternal() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MantineProvider {...createMantineTheme(theme as any)}>
|
<MantineProvider {...createMantineTheme(theme as any)}>
|
||||||
<Notifications />
|
<Notifications position="top-right" />
|
||||||
<FlashMessage />
|
<FlashMessage />
|
||||||
<BkndModalsProvider>
|
<BkndModalsProvider>
|
||||||
<Routes />
|
<Routes />
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
|
|||||||
autoClose: 3000,
|
autoClose: 3000,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (res.success === true) {
|
if (res.success) {
|
||||||
console.log("update config", action, module, path, res.body);
|
console.log("update config", action, module, path, res.body);
|
||||||
if (res.body.success) {
|
if (res.body.success) {
|
||||||
setSchema((prev) => {
|
setSchema((prev) => {
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ const useLocationFromRouter = (router) => {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function isLinkActive(href: string, strict?: boolean) {
|
||||||
|
const path = window.location.pathname;
|
||||||
|
return strict ? path === href : path.includes(href);
|
||||||
|
}
|
||||||
|
|
||||||
export function Link({
|
export function Link({
|
||||||
className,
|
className,
|
||||||
native,
|
native,
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ export const SidebarLink = <E extends React.ElementType = "a">({
|
|||||||
<Tag
|
<Tag
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"flex flex-row px-4 py-2.5 items-center gap-2",
|
"flex flex-row px-4 items-center gap-2 h-12",
|
||||||
!disabled &&
|
!disabled &&
|
||||||
"cursor-pointer rounded-md [&.active]:bg-primary/10 [&.active]:hover:bg-primary/15 [&.active]:font-medium hover:bg-primary/5 focus:bg-primary/5 link",
|
"cursor-pointer rounded-md [&.active]:bg-primary/10 [&.active]:hover:bg-primary/15 [&.active]:font-medium hover:bg-primary/5 focus:bg-primary/5 link",
|
||||||
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
|
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
Textarea,
|
Textarea,
|
||||||
createTheme,
|
createTheme,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { Notifications } from "@mantine/notifications";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
// default: https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/core/MantineProvider/default-theme.ts
|
// default: https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/core/MantineProvider/default-theme.ts
|
||||||
@@ -126,6 +127,13 @@ export function createMantineTheme(scheme: "light" | "dark"): {
|
|||||||
indicator: light ? "bg-background" : "bg-primary/15",
|
indicator: light ? "bg-background" : "bg-primary/15",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
Notifications: Notifications.extend({
|
||||||
|
classNames: (theme, props) => {
|
||||||
|
return {
|
||||||
|
notification: "-top-4 -right-4",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
primaryColor: "dark",
|
primaryColor: "dark",
|
||||||
primaryShade: 9,
|
primaryShade: 9,
|
||||||
|
|||||||
@@ -53,6 +53,14 @@ export function withAbsolute(url: string) {
|
|||||||
return `~/${basepath}/${url}`.replace(/\/+/g, "/");
|
return `~/${basepath}/${url}`.replace(/\/+/g, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useRouteNavigate() {
|
||||||
|
const [navigate] = useNavigate();
|
||||||
|
|
||||||
|
return (fn: (r: typeof routes) => string, options?: Parameters<typeof navigate>[1]) => {
|
||||||
|
navigate(fn(routes), options);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function useNavigate() {
|
export function useNavigate() {
|
||||||
const [location, navigate] = useLocation();
|
const [location, navigate] = useLocation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
@@ -63,9 +63,10 @@ export function StepEntityFields() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("valid");
|
||||||
nextStep("create")();
|
nextStep("create")();
|
||||||
} else {
|
} else {
|
||||||
console.warn("not valid");
|
console.warn("not valid", ref.current?.getErrors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,18 +7,20 @@ import {
|
|||||||
IconPhoto,
|
IconPhoto,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
|
IconSwitchHorizontal,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import type { Entity, TEntityType } from "data";
|
import type { Entity, TEntityType } from "data";
|
||||||
import { TbDatabasePlus } from "react-icons/tb";
|
import { TbDatabasePlus } from "react-icons/tb";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
|
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
|
||||||
|
import { Button } from "ui/components/buttons/Button";
|
||||||
import { IconButton } from "ui/components/buttons/IconButton";
|
import { IconButton } from "ui/components/buttons/IconButton";
|
||||||
import { Empty } from "ui/components/display/Empty";
|
import { Empty } from "ui/components/display/Empty";
|
||||||
import { Dropdown, type DropdownClickableChild } from "ui/components/overlay/Dropdown";
|
import { Dropdown, type DropdownClickableChild } from "ui/components/overlay/Dropdown";
|
||||||
import { Link } from "ui/components/wouter/Link";
|
import { Link, isLinkActive } from "ui/components/wouter/Link";
|
||||||
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
||||||
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
import * as AppShell from "ui/layouts/AppShell/AppShell";
|
||||||
import { routes, useNavigate } from "ui/lib/routes";
|
import { routes, useNavigate, useRouteNavigate } from "ui/lib/routes";
|
||||||
|
|
||||||
export function DataRoot({ children }) {
|
export function DataRoot({ children }) {
|
||||||
// @todo: settings routes should be centralized
|
// @todo: settings routes should be centralized
|
||||||
@@ -106,6 +108,7 @@ const EntityLinkList = ({
|
|||||||
suggestCreate = false,
|
suggestCreate = false,
|
||||||
}: { entities: Entity[]; title?: string; context: "data" | "schema"; suggestCreate?: boolean }) => {
|
}: { entities: Entity[]; title?: string; context: "data" | "schema"; suggestCreate?: boolean }) => {
|
||||||
const { $data } = useBkndData();
|
const { $data } = useBkndData();
|
||||||
|
const navigate = useRouteNavigate();
|
||||||
if (entities.length === 0) {
|
if (entities.length === 0) {
|
||||||
return suggestCreate ? (
|
return suggestCreate ? (
|
||||||
<Empty
|
<Empty
|
||||||
@@ -119,6 +122,22 @@ const EntityLinkList = ({
|
|||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleClick(entity: Entity) {
|
||||||
|
return (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
switch (context) {
|
||||||
|
case "schema":
|
||||||
|
navigate((r) => r.data.entity.list(entity.name));
|
||||||
|
break;
|
||||||
|
case "data":
|
||||||
|
navigate((r) => r.data.schema.entity(entity.name));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
@@ -135,8 +154,22 @@ const EntityLinkList = ({
|
|||||||
: routes.data.schema.entity(entity.name);
|
: routes.data.schema.entity(entity.name);
|
||||||
return (
|
return (
|
||||||
<EntityContextMenu key={entity.name} entity={entity}>
|
<EntityContextMenu key={entity.name} entity={entity}>
|
||||||
<AppShell.SidebarLink as={Link} href={href}>
|
<AppShell.SidebarLink
|
||||||
|
as={Link}
|
||||||
|
href={href}
|
||||||
|
className="justify-between items-center"
|
||||||
|
>
|
||||||
{entity.label}
|
{entity.label}
|
||||||
|
|
||||||
|
{isLinkActive(href) && (
|
||||||
|
<Button
|
||||||
|
IconLeft={IconSwitchHorizontal}
|
||||||
|
size="small"
|
||||||
|
onClick={handleClick(entity)}
|
||||||
|
>
|
||||||
|
{context === "schema" ? "Data" : "Fields"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</AppShell.SidebarLink>
|
</AppShell.SidebarLink>
|
||||||
</EntityContextMenu>
|
</EntityContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -225,6 +225,9 @@ const Fields = ({
|
|||||||
<Button size="small" onClick={() => setRes(ref.current?.getData())}>
|
<Button size="small" onClick={() => setRes(ref.current?.getData())}>
|
||||||
data
|
data
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button size="small" onClick={() => setRes(ref.current?.getErrors())}>
|
||||||
|
errors
|
||||||
|
</Button>
|
||||||
<Button size="small" onClick={handleUpdate}>
|
<Button size="small" onClick={handleUpdate}>
|
||||||
update
|
update
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
import { typeboxResolver } from "@hookform/resolvers/typebox";
|
||||||
import { Tabs, TextInput, Textarea, Tooltip } from "@mantine/core";
|
import { Tabs, TextInput, Textarea, Tooltip } from "@mantine/core";
|
||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import { Type } from "@sinclair/typebox";
|
|
||||||
import {
|
import {
|
||||||
|
Default,
|
||||||
type Static,
|
type Static,
|
||||||
StringIdentifier,
|
StringIdentifier,
|
||||||
|
Type,
|
||||||
objectCleanEmpty,
|
objectCleanEmpty,
|
||||||
ucFirstAllSnakeToPascalWithSpaces,
|
ucFirstAllSnakeToPascalWithSpaces,
|
||||||
} from "core/utils";
|
} from "core/utils";
|
||||||
import { Entity } from "data";
|
|
||||||
import {
|
import {
|
||||||
type TAppDataEntityFields,
|
type TAppDataEntityFields,
|
||||||
fieldsSchemaObject as originalFieldsSchemaObject,
|
fieldsSchemaObject as originalFieldsSchemaObject,
|
||||||
@@ -31,11 +31,16 @@ import { dataFieldsUiSchema } from "../../settings/routes/data.settings";
|
|||||||
const fieldsSchemaObject = originalFieldsSchemaObject;
|
const fieldsSchemaObject = originalFieldsSchemaObject;
|
||||||
const fieldsSchema = Type.Union(Object.values(fieldsSchemaObject));
|
const fieldsSchema = Type.Union(Object.values(fieldsSchemaObject));
|
||||||
|
|
||||||
const fieldSchema = Type.Object({
|
const fieldSchema = Type.Object(
|
||||||
name: StringIdentifier,
|
{
|
||||||
new: Type.Optional(Type.Boolean({ const: true })),
|
name: StringIdentifier,
|
||||||
field: fieldsSchema,
|
new: Type.Optional(Type.Boolean({ const: true })),
|
||||||
});
|
field: fieldsSchema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
additionalProperties: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
type TFieldSchema = Static<typeof fieldSchema>;
|
type TFieldSchema = Static<typeof fieldSchema>;
|
||||||
|
|
||||||
const schema = Type.Object({
|
const schema = Type.Object({
|
||||||
@@ -63,6 +68,7 @@ export type EntityFieldsFormRef = {
|
|||||||
getValues: () => TFieldsFormSchema;
|
getValues: () => TFieldsFormSchema;
|
||||||
getData: () => TAppDataEntityFields;
|
getData: () => TAppDataEntityFields;
|
||||||
isValid: () => boolean;
|
isValid: () => boolean;
|
||||||
|
getErrors: () => any;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,6 +123,7 @@ export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsForm
|
|||||||
return toCleanValues(getValues());
|
return toCleanValues(getValues());
|
||||||
},
|
},
|
||||||
isValid: () => isValid,
|
isValid: () => isValid,
|
||||||
|
getErrors: () => errors,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function handleAppend(_type: keyof typeof fieldsSchemaObject) {
|
function handleAppend(_type: keyof typeof fieldsSchemaObject) {
|
||||||
@@ -125,7 +132,7 @@ export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsForm
|
|||||||
new: true,
|
new: true,
|
||||||
field: {
|
field: {
|
||||||
type: _type,
|
type: _type,
|
||||||
config: {},
|
config: Default(fieldsSchemaObject[_type]?.properties.config, {}) as any,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
append(newField);
|
append(newField);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Button, Modal, Switch, Tooltip, useMantineColorScheme } from "@mantine/core";
|
import { Button, Modal, Switch, Tooltip, useMantineColorScheme } from "@mantine/core";
|
||||||
import { useColorScheme, useDisclosure } from "@mantine/hooks";
|
import { useColorScheme, useDisclosure } from "@mantine/hooks";
|
||||||
|
import { notifications } from "@mantine/notifications";
|
||||||
import { Button as AppButton } from "../../../components/buttons/Button";
|
import { Button as AppButton } from "../../../components/buttons/Button";
|
||||||
|
|
||||||
export default function MantineTest() {
|
export default function MantineTest() {
|
||||||
@@ -10,6 +11,19 @@ export default function MantineTest() {
|
|||||||
<Button color="blue">Mantine</Button>
|
<Button color="blue">Mantine</Button>
|
||||||
<AppButton>Button</AppButton>
|
<AppButton>Button</AppButton>
|
||||||
<AppButton variant="primary">Button</AppButton>
|
<AppButton variant="primary">Button</AppButton>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
notifications.show({
|
||||||
|
title: "Notification",
|
||||||
|
message: "This is a notification",
|
||||||
|
autoClose: false,
|
||||||
|
color: "blue",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Notification
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<MantineModal />
|
<MantineModal />
|
||||||
<MantineTooltip />
|
<MantineTooltip />
|
||||||
|
|||||||
Reference in New Issue
Block a user