diff --git a/app/src/adapter/nextjs/AdminPage.tsx b/app/src/adapter/nextjs/AdminPage.tsx index 222e40f..bd0a81e 100644 --- a/app/src/adapter/nextjs/AdminPage.tsx +++ b/app/src/adapter/nextjs/AdminPage.tsx @@ -1,4 +1,5 @@ import { withApi } from "bknd/adapter/nextjs"; +import type { BkndAdminProps } from "bknd/ui"; import type { InferGetServerSidePropsType } from "next"; 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 ClientProvider = dynamic(() => import("bknd/ui").then((mod) => mod.ClientProvider)); return (props: InferGetServerSidePropsType) => { if (typeof document === "undefined") return null; - return ( - - - - ); + return ; }; } diff --git a/app/src/adapter/remix/AdminPage.tsx b/app/src/adapter/remix/AdminPage.tsx index 6e40032..9361cc2 100644 --- a/app/src/adapter/remix/AdminPage.tsx +++ b/app/src/adapter/remix/AdminPage.tsx @@ -1,6 +1,7 @@ +import type { BkndAdminProps } from "bknd/ui"; 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 }))); return () => { const [loaded, setLoaded] = useState(false); @@ -12,7 +13,7 @@ export function adminPage() { return ( - + ); }; diff --git a/app/src/ui/Admin.tsx b/app/src/ui/Admin.tsx index d3faea1..2a5aeeb 100644 --- a/app/src/ui/Admin.tsx +++ b/app/src/ui/Admin.tsx @@ -1,5 +1,6 @@ import { MantineProvider } from "@mantine/core"; import { Notifications } from "@mantine/notifications"; +import type { ModuleConfigs } from "modules"; import React from "react"; import { FlashMessage } from "ui/modules/server/FlashMessage"; import { BkndProvider, ClientProvider, type ClientProviderProps, useBknd } from "./client"; @@ -10,12 +11,16 @@ import { Routes } from "./routes"; export type BkndAdminProps = { baseUrl?: string; 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 = ( - + ); diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index 14e704a..b0991e7 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -14,6 +14,7 @@ type BkndContext = { requireSecrets: () => Promise; actions: ReturnType; app: AppReduced; + adminOverride?: ModuleConfigs["server"]["admin"]; }; const BkndContext = createContext(undefined!); @@ -21,8 +22,9 @@ export type { TSchemaActions }; export function BkndProvider({ includeSecrets = false, + adminOverride, children -}: { includeSecrets?: boolean; children: any }) { +}: { includeSecrets?: boolean; children: any } & Pick) { const [withSecrets, setWithSecrets] = useState(includeSecrets); const [schema, setSchema] = useState>(); @@ -64,6 +66,13 @@ export function BkndProvider({ permissions: [] } as any); + if (adminOverride) { + schema.config.server.admin = { + ...schema.config.server.admin, + ...adminOverride + }; + } + startTransition(() => { setSchema(schema); setWithSecrets(_includeSecrets); @@ -86,7 +95,7 @@ export function BkndProvider({ const actions = getSchemaActions({ client, setSchema, reloadSchema }); return ( - + {children} ); diff --git a/app/src/ui/components/display/Alert.tsx b/app/src/ui/components/display/Alert.tsx index e3b51df..ba3c4cd 100644 --- a/app/src/ui/components/display/Alert.tsx +++ b/app/src/ui/components/display/Alert.tsx @@ -1,11 +1,11 @@ -import type { ComponentPropsWithoutRef } from "react"; +import type { ComponentPropsWithoutRef, ReactNode } from "react"; import { twMerge } from "tailwind-merge"; export type AlertProps = ComponentPropsWithoutRef<"div"> & { className?: string; visible?: boolean; title?: string; - message?: string; + message?: ReactNode | string; }; const Base: React.FC = ({ visible = true, title, message, className, ...props }) => diff --git a/app/src/ui/index.ts b/app/src/ui/index.ts index 1cedbf3..ed820ab 100644 --- a/app/src/ui/index.ts +++ b/app/src/ui/index.ts @@ -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 { Context } from "./components/Context"; export { diff --git a/app/src/ui/routes/settings/components/Setting.tsx b/app/src/ui/routes/settings/components/Setting.tsx index 8ed58f2..1c12543 100644 --- a/app/src/ui/routes/settings/components/Setting.tsx +++ b/app/src/ui/routes/settings/components/Setting.tsx @@ -234,7 +234,7 @@ export function Setting({ - {typeof showAlert === "string" && } + {typeof showAlert !== "undefined" && }
- {/* - - - */}
); @@ -145,12 +116,7 @@ const SettingRoutesRoutes = () => { return ( <> - + diff --git a/app/src/ui/routes/settings/routes/server.settings.tsx b/app/src/ui/routes/settings/routes/server.settings.tsx new file mode 100644 index 0000000..9a1e867 --- /dev/null +++ b/app/src/ui/routes/settings/routes/server.settings.tsx @@ -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 ( + + ( + { + 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 + /> + + ); +}; diff --git a/docs/integration/nextjs.mdx b/docs/integration/nextjs.mdx index 898568b..99b5735 100644 --- a/docs/integration/nextjs.mdx +++ b/docs/integration/nextjs.mdx @@ -38,7 +38,9 @@ import { adminPage, getServerSideProps } from "bknd/adapter/nextjs"; import "bknd/dist/styles.css"; export { getServerSideProps }; -export default adminPage(); +export default adminPage({ + config: { basepath: "/admin" } +}); ``` ## Example usage of the API in pages dir diff --git a/docs/integration/remix.mdx b/docs/integration/remix.mdx index 5e5761f..faf1bfb 100644 --- a/docs/integration/remix.mdx +++ b/docs/integration/remix.mdx @@ -81,7 +81,9 @@ Create a new splat route file at `app/routes/admin.$.tsx`: import { adminPage } from "bknd/adapter/remix"; import "bknd/dist/styles.css"; -export default adminPage(); +export default adminPage({ + config: { basepath: "/admin" } +}); ``` ## Example usage of the API diff --git a/examples/astro/src/pages/admin/[...admin].astro b/examples/astro/src/pages/admin/[...admin].astro index dccde9d..25f1aa7 100644 --- a/examples/astro/src/pages/admin/[...admin].astro +++ b/examples/astro/src/pages/admin/[...admin].astro @@ -13,6 +13,6 @@ const user = api.getUser(); - + \ No newline at end of file diff --git a/examples/nextjs/src/pages/admin/[[...admin]].tsx b/examples/nextjs/src/pages/admin/[[...admin]].tsx index 58b4991..bd95826 100644 --- a/examples/nextjs/src/pages/admin/[[...admin]].tsx +++ b/examples/nextjs/src/pages/admin/[[...admin]].tsx @@ -2,4 +2,8 @@ import { adminPage, getServerSideProps } from "bknd/adapter/nextjs"; import "bknd/dist/styles.css"; export { getServerSideProps }; -export default adminPage(); +export default adminPage({ + config: { + basepath: "/admin" + } +}); diff --git a/examples/remix/app/routes/admin.$.tsx b/examples/remix/app/routes/admin.$.tsx index 0207428..c9d725d 100644 --- a/examples/remix/app/routes/admin.$.tsx +++ b/examples/remix/app/routes/admin.$.tsx @@ -1,4 +1,8 @@ import { adminPage } from "bknd/adapter/remix"; import "bknd/dist/styles.css"; -export default adminPage(); +export default adminPage({ + config: { + basepath: "/admin" + } +});