added bknd admin config override to make it easier to include <Admin />

This commit is contained in:
dswbx
2024-11-28 11:15:07 +01:00
parent 54b38401d8
commit bdc6eb55bf
14 changed files with 103 additions and 59 deletions

View File

@@ -1,4 +1,5 @@
import { withApi } from "bknd/adapter/nextjs"; import { withApi } from "bknd/adapter/nextjs";
import type { BkndAdminProps } from "bknd/ui";
import type { InferGetServerSidePropsType } from "next"; import type { InferGetServerSidePropsType } from "next";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
@@ -10,15 +11,10 @@ export const getServerSideProps = withApi(async (context) => {
}; };
}); });
export function adminPage() { export function adminPage(adminProps?: BkndAdminProps) {
const Admin = dynamic(() => import("bknd/ui").then((mod) => mod.Admin), { ssr: false }); const Admin = dynamic(() => import("bknd/ui").then((mod) => mod.Admin), { ssr: false });
const ClientProvider = dynamic(() => import("bknd/ui").then((mod) => mod.ClientProvider));
return (props: InferGetServerSidePropsType<typeof getServerSideProps>) => { return (props: InferGetServerSidePropsType<typeof getServerSideProps>) => {
if (typeof document === "undefined") return null; if (typeof document === "undefined") return null;
return ( return <Admin withProvider={{ user: props.user }} {...adminProps} />;
<ClientProvider user={props.user}>
<Admin />
</ClientProvider>
);
}; };
} }

View File

@@ -1,6 +1,7 @@
import type { BkndAdminProps } from "bknd/ui";
import { Suspense, lazy, useEffect, useState } from "react"; import { Suspense, lazy, useEffect, useState } from "react";
export function adminPage() { export function adminPage(props?: BkndAdminProps) {
const Admin = lazy(() => import("bknd/ui").then((mod) => ({ default: mod.Admin }))); const Admin = lazy(() => import("bknd/ui").then((mod) => ({ default: mod.Admin })));
return () => { return () => {
const [loaded, setLoaded] = useState(false); const [loaded, setLoaded] = useState(false);
@@ -12,7 +13,7 @@ export function adminPage() {
return ( return (
<Suspense> <Suspense>
<Admin /> <Admin {...props} />
</Suspense> </Suspense>
); );
}; };

View File

@@ -1,5 +1,6 @@
import { MantineProvider } from "@mantine/core"; import { MantineProvider } from "@mantine/core";
import { Notifications } from "@mantine/notifications"; import { Notifications } from "@mantine/notifications";
import type { ModuleConfigs } from "modules";
import React from "react"; import React from "react";
import { FlashMessage } from "ui/modules/server/FlashMessage"; import { FlashMessage } from "ui/modules/server/FlashMessage";
import { BkndProvider, ClientProvider, type ClientProviderProps, useBknd } from "./client"; import { BkndProvider, ClientProvider, type ClientProviderProps, useBknd } from "./client";
@@ -10,12 +11,16 @@ import { Routes } from "./routes";
export type BkndAdminProps = { export type BkndAdminProps = {
baseUrl?: string; baseUrl?: string;
withProvider?: boolean | ClientProviderProps; withProvider?: boolean | ClientProviderProps;
// @todo: add admin config override config?: ModuleConfigs["server"]["admin"];
}; };
export default function Admin({ baseUrl: baseUrlOverride, withProvider = false }: BkndAdminProps) { export default function Admin({
baseUrl: baseUrlOverride,
withProvider = false,
config
}: BkndAdminProps) {
const Component = ( const Component = (
<BkndProvider> <BkndProvider adminOverride={config}>
<AdminInternal /> <AdminInternal />
</BkndProvider> </BkndProvider>
); );

View File

@@ -14,6 +14,7 @@ type BkndContext = {
requireSecrets: () => Promise<void>; requireSecrets: () => Promise<void>;
actions: ReturnType<typeof getSchemaActions>; actions: ReturnType<typeof getSchemaActions>;
app: AppReduced; app: AppReduced;
adminOverride?: ModuleConfigs["server"]["admin"];
}; };
const BkndContext = createContext<BkndContext>(undefined!); const BkndContext = createContext<BkndContext>(undefined!);
@@ -21,8 +22,9 @@ export type { TSchemaActions };
export function BkndProvider({ export function BkndProvider({
includeSecrets = false, includeSecrets = false,
adminOverride,
children children
}: { includeSecrets?: boolean; children: any }) { }: { includeSecrets?: boolean; children: any } & Pick<BkndContext, "adminOverride">) {
const [withSecrets, setWithSecrets] = useState<boolean>(includeSecrets); const [withSecrets, setWithSecrets] = useState<boolean>(includeSecrets);
const [schema, setSchema] = const [schema, setSchema] =
useState<Pick<BkndContext, "version" | "schema" | "config" | "permissions">>(); useState<Pick<BkndContext, "version" | "schema" | "config" | "permissions">>();
@@ -64,6 +66,13 @@ export function BkndProvider({
permissions: [] permissions: []
} as any); } as any);
if (adminOverride) {
schema.config.server.admin = {
...schema.config.server.admin,
...adminOverride
};
}
startTransition(() => { startTransition(() => {
setSchema(schema); setSchema(schema);
setWithSecrets(_includeSecrets); setWithSecrets(_includeSecrets);
@@ -86,7 +95,7 @@ export function BkndProvider({
const actions = getSchemaActions({ client, setSchema, reloadSchema }); const actions = getSchemaActions({ client, setSchema, reloadSchema });
return ( return (
<BkndContext.Provider value={{ ...schema, actions, requireSecrets, app }}> <BkndContext.Provider value={{ ...schema, actions, requireSecrets, app, adminOverride }}>
{children} {children}
</BkndContext.Provider> </BkndContext.Provider>
); );

View File

@@ -1,11 +1,11 @@
import type { ComponentPropsWithoutRef } from "react"; import type { ComponentPropsWithoutRef, ReactNode } from "react";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
export type AlertProps = ComponentPropsWithoutRef<"div"> & { export type AlertProps = ComponentPropsWithoutRef<"div"> & {
className?: string; className?: string;
visible?: boolean; visible?: boolean;
title?: string; title?: string;
message?: string; message?: ReactNode | string;
}; };
const Base: React.FC<AlertProps> = ({ visible = true, title, message, className, ...props }) => const Base: React.FC<AlertProps> = ({ visible = true, title, message, className, ...props }) =>

View File

@@ -1,4 +1,4 @@
export { default as Admin } from "./Admin"; export { default as Admin, type BkndAdminProps } from "./Admin";
export { Button } from "./components/buttons/Button"; export { Button } from "./components/buttons/Button";
export { Context } from "./components/Context"; export { Context } from "./components/Context";
export { export {

View File

@@ -234,7 +234,7 @@ export function Setting<Schema extends TObject = any>({
<Breadcrumbs path={path} /> <Breadcrumbs path={path} />
</AppShell.SectionHeader> </AppShell.SectionHeader>
<AppShell.Scrollable key={path.join("-")}> <AppShell.Scrollable key={path.join("-")}>
{typeof showAlert === "string" && <Alert.Warning message={showAlert} />} {typeof showAlert !== "undefined" && <Alert.Warning message={showAlert} />}
<div className="flex flex-col flex-grow p-3 gap-3"> <div className="flex flex-col flex-grow p-3 gap-3">
<JsonSchemaForm <JsonSchemaForm

View File

@@ -12,6 +12,7 @@ import { Setting } from "./components/Setting";
import { AuthSettings } from "./routes/auth.settings"; import { AuthSettings } from "./routes/auth.settings";
import { DataSettings } from "./routes/data.settings"; import { DataSettings } from "./routes/data.settings";
import { FlowsSettings } from "./routes/flows.settings"; import { FlowsSettings } from "./routes/flows.settings";
import { ServerSettings } from "./routes/server.settings";
function SettingsSidebar() { function SettingsSidebar() {
const { version, schema } = useBknd(); const { version, schema } = useBknd();
@@ -39,36 +40,6 @@ function SettingsSidebar() {
))} ))}
</nav> </nav>
</div> </div>
{/*<button
onClick={() =>
modals.openContextModal({
modal: "test",
title: "Test Modal",
innerProps: { modalBody: "This is a test modal" }
})
}
>
modal
</button>
<button
onClick={() =>
bkndModals.open(bkndModals.ids.test, { modalBody: "test" }, { title: "what" })
}
>
modal2
</button>
<button onClick={() => bkndModals.open("test", { modalBody: "test" })}>modal</button>
<button
onClick={() =>
bkndModals.open("debug", {
data: {
one: { what: 1 }
}
})
}
>
debug
</button>*/}
</AppShell.Scrollable> </AppShell.Scrollable>
</AppShell.Sidebar> </AppShell.Sidebar>
); );
@@ -145,12 +116,7 @@ const SettingRoutesRoutes = () => {
return ( return (
<> <>
<FallbackRoutes <ServerSettings schema={schema.server} config={config.server} />
module="server"
schema={schema}
config={config}
uiSchema={uiSchema.server}
/>
<DataSettings schema={schema.data} config={config.data} /> <DataSettings schema={schema.data} config={config.data} />
<AuthSettings schema={schema.auth} config={config.auth} /> <AuthSettings schema={schema.auth} config={config.auth} />
<FallbackRoutes module="media" schema={schema} config={config} uiSchema={uiSchema.media} /> <FallbackRoutes module="media" schema={schema} config={config} uiSchema={uiSchema.media} />

View File

@@ -0,0 +1,55 @@
import { cloneDeep } from "lodash-es";
import { useBknd } from "ui";
import { Setting } from "ui/routes/settings/components/Setting";
import { Route } from "wouter";
const uiSchema = {
cors: {
allow_methods: {
"ui:widget": "checkboxes"
},
allow_headers: {
"ui:options": {
orderable: false
}
}
}
};
export const ServerSettings = ({ schema: _unsafe_copy, config }) => {
const { app, adminOverride } = useBknd();
const { basepath } = app.getAdminConfig();
const _schema = cloneDeep(_unsafe_copy);
const prefix = `~/${basepath}/settings`.replace(/\/+/g, "/");
const schema = _schema;
if (adminOverride) {
schema.properties.admin.readOnly = true;
}
return (
<Route path="/server" nest>
<Route
path="/"
component={() => (
<Setting
options={{
showAlert: () => {
if (adminOverride) {
return "The admin settings are read-only as they are overriden. Remaining server configuration can be edited.";
}
return;
}
}}
schema={schema}
uiSchema={uiSchema}
config={config}
prefix={`${prefix}/server`}
path={["server"]}
/>
)}
nest
/>
</Route>
);
};

View File

@@ -38,7 +38,9 @@ import { adminPage, getServerSideProps } from "bknd/adapter/nextjs";
import "bknd/dist/styles.css"; import "bknd/dist/styles.css";
export { getServerSideProps }; export { getServerSideProps };
export default adminPage(); export default adminPage({
config: { basepath: "/admin" }
});
``` ```
## Example usage of the API in pages dir ## Example usage of the API in pages dir

View File

@@ -81,7 +81,9 @@ Create a new splat route file at `app/routes/admin.$.tsx`:
import { adminPage } from "bknd/adapter/remix"; import { adminPage } from "bknd/adapter/remix";
import "bknd/dist/styles.css"; import "bknd/dist/styles.css";
export default adminPage(); export default adminPage({
config: { basepath: "/admin" }
});
``` ```
## Example usage of the API ## Example usage of the API

View File

@@ -13,6 +13,6 @@ const user = api.getUser();
<html> <html>
<body> <body>
<Admin withProvider={{ user }} client:load /> <Admin withProvider={{ user }} config={{ basepath: "/admin" }} client:load />
</body> </body>
</html> </html>

View File

@@ -2,4 +2,8 @@ import { adminPage, getServerSideProps } from "bknd/adapter/nextjs";
import "bknd/dist/styles.css"; import "bknd/dist/styles.css";
export { getServerSideProps }; export { getServerSideProps };
export default adminPage(); export default adminPage({
config: {
basepath: "/admin"
}
});

View File

@@ -1,4 +1,8 @@
import { adminPage } from "bknd/adapter/remix"; import { adminPage } from "bknd/adapter/remix";
import "bknd/dist/styles.css"; import "bknd/dist/styles.css";
export default adminPage(); export default adminPage({
config: {
basepath: "/admin"
}
});