mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
Refactor JsonEditor and Permission components for improved state management and performance
- Implemented debounced input handling in `JsonEditor` to enhance user experience and reduce unnecessary updates. - Updated `Permission` component to streamline permission state management and improve clarity in policy handling. - Refactored `Policies` component to utilize derived field context for better data handling and rendering efficiency.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { Suspense, lazy, useState } from "react";
|
import { Suspense, lazy, useState } from "react";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import type { CodeEditorProps } from "./CodeEditor";
|
import type { CodeEditorProps } from "./CodeEditor";
|
||||||
|
import { useDebouncedCallback } from "@mantine/hooks";
|
||||||
const CodeEditor = lazy(() => import("./CodeEditor"));
|
const CodeEditor = lazy(() => import("./CodeEditor"));
|
||||||
|
|
||||||
export type JsonEditorProps = Omit<CodeEditorProps, "value" | "onChange"> & {
|
export type JsonEditorProps = Omit<CodeEditorProps, "value" | "onChange"> & {
|
||||||
@@ -21,13 +22,13 @@ export function JsonEditor({
|
|||||||
const [editorValue, setEditorValue] = useState<string | null | undefined>(
|
const [editorValue, setEditorValue] = useState<string | null | undefined>(
|
||||||
JSON.stringify(value, null, 2),
|
JSON.stringify(value, null, 2),
|
||||||
);
|
);
|
||||||
const handleChange = (given: string) => {
|
const handleChange = useDebouncedCallback((given: string) => {
|
||||||
const value = given === "" ? (emptyAs === "null" ? null : undefined) : given;
|
const value = given === "" ? (emptyAs === "null" ? null : undefined) : given;
|
||||||
try {
|
try {
|
||||||
setEditorValue(value);
|
setEditorValue(value);
|
||||||
onChange?.(value ? JSON.parse(value) : value);
|
onChange?.(value ? JSON.parse(value) : value);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
};
|
}, 500);
|
||||||
const handleBlur = (e) => {
|
const handleBlur = (e) => {
|
||||||
setEditorValue(JSON.stringify(value, null, 2));
|
setEditorValue(JSON.stringify(value, null, 2));
|
||||||
onBlur?.(e);
|
onBlur?.(e);
|
||||||
|
|||||||
@@ -233,15 +233,19 @@ const Permission = ({ permission, index }: { permission: TPermission; index?: nu
|
|||||||
const { value } = useDerivedFieldContext("permissions", (ctx) => {
|
const { value } = useDerivedFieldContext("permissions", (ctx) => {
|
||||||
const v = ctx.value;
|
const v = ctx.value;
|
||||||
if (!Array.isArray(v)) return undefined;
|
if (!Array.isArray(v)) return undefined;
|
||||||
return v.find((v) => v && v.permission === permission.name);
|
const v2 = v.find((v) => v && v.permission === permission.name);
|
||||||
|
return {
|
||||||
|
set: !!v2,
|
||||||
|
policies: (v2?.policies?.length ?? 0) as number,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
const { setValue } = useFormContext();
|
const { setValue } = useFormContext();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const data = value as PermissionData | undefined;
|
const policiesCount = value?.policies ?? 0;
|
||||||
const policiesCount = data?.policies?.length ?? 0;
|
const isSet = value?.set ?? false;
|
||||||
|
|
||||||
async function handleSwitch() {
|
async function handleSwitch() {
|
||||||
if (data) {
|
if (isSet) {
|
||||||
setValue(path, undefined);
|
setValue(path, undefined);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -253,6 +257,10 @@ const Permission = ({ permission, index }: { permission: TPermission; index?: nu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleOpen() {
|
||||||
|
setOpen((o) => !o);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@@ -279,13 +287,13 @@ const Permission = ({ permission, index }: { permission: TPermission; index?: nu
|
|||||||
<IconButton
|
<IconButton
|
||||||
size="md"
|
size="md"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
disabled={!data}
|
disabled={!isSet}
|
||||||
Icon={TbAdjustments}
|
Icon={TbAdjustments}
|
||||||
className={cn("disabled:opacity-20")}
|
className={cn("disabled:opacity-20")}
|
||||||
onClick={() => setOpen((o) => !o)}
|
onClick={toggleOpen}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Formy.Switch size="sm" checked={!!data} onChange={handleSwitch} />
|
<Formy.Switch size="sm" checked={isSet} onChange={handleSwitch} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{open && (
|
{open && (
|
||||||
@@ -299,13 +307,22 @@ const Permission = ({ permission, index }: { permission: TPermission; index?: nu
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Policies = ({ path, permission }: { path: string; permission: TPermission }) => {
|
const Policies = ({ path, permission }: { path: string; permission: TPermission }) => {
|
||||||
const { value: _value } = useFormValue(path);
|
const {
|
||||||
const { setValue, schema: policySchema, lib, deleteValue } = useDerivedFieldContext(path);
|
setValue,
|
||||||
const value = _value ?? [];
|
schema: policySchema,
|
||||||
|
lib,
|
||||||
|
deleteValue,
|
||||||
|
value,
|
||||||
|
} = useDerivedFieldContext(path, ({ value }) => {
|
||||||
|
return {
|
||||||
|
policies: (value?.length ?? 0) as number,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const policiesCount = value?.policies ?? 0;
|
||||||
|
|
||||||
function handleAdd() {
|
function handleAdd() {
|
||||||
setValue(
|
setValue(
|
||||||
`${path}.${value.length}`,
|
`${path}.${policiesCount}`,
|
||||||
lib.getTemplate(undefined, policySchema!.items, {
|
lib.getTemplate(undefined, policySchema!.items, {
|
||||||
addOptionalProps: true,
|
addOptionalProps: true,
|
||||||
}),
|
}),
|
||||||
@@ -317,19 +334,20 @@ const Policies = ({ path, permission }: { path: string; permission: TPermission
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex flex-col", value.length > 0 && "gap-8")}>
|
<div className={cn("flex flex-col", policiesCount > 0 && "gap-8")}>
|
||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5">
|
||||||
{value.map((policy, i) => (
|
{policiesCount > 0 &&
|
||||||
<FormContextOverride key={i} prefix={`${path}.${i}`} schema={policySchema.items!}>
|
Array.from({ length: policiesCount }).map((_, i) => (
|
||||||
{i > 0 && <div className="h-px bg-muted" />}
|
<FormContextOverride key={i} prefix={`${path}.${i}`} schema={policySchema.items!}>
|
||||||
<div className="flex flex-row gap-2 items-start">
|
{i > 0 && <div className="h-px bg-muted" />}
|
||||||
<div className="flex flex-col flex-grow w-full">
|
<div className="flex flex-row gap-2 items-start">
|
||||||
<Policy permission={permission} />
|
<div className="flex flex-col flex-grow w-full">
|
||||||
|
<Policy permission={permission} />
|
||||||
|
</div>
|
||||||
|
<IconButton Icon={TbTrash} onClick={() => handleDelete(i)} size="sm" />
|
||||||
</div>
|
</div>
|
||||||
<IconButton Icon={TbTrash} onClick={() => handleDelete(i)} size="sm" />
|
</FormContextOverride>
|
||||||
</div>
|
))}
|
||||||
</FormContextOverride>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row justify-center">
|
<div className="flex flex-row justify-center">
|
||||||
<Button onClick={handleAdd}>Add Policy</Button>
|
<Button onClick={handleAdd}>Add Policy</Button>
|
||||||
@@ -366,7 +384,9 @@ const Policy = ({
|
|||||||
}: {
|
}: {
|
||||||
permission: TPermission;
|
permission: TPermission;
|
||||||
}) => {
|
}) => {
|
||||||
const { value } = useFormValue("");
|
const { value } = useDerivedFieldContext("", ({ value }) => ({
|
||||||
|
effect: (value?.effect ?? "allow") as "allow" | "deny" | "filter",
|
||||||
|
}));
|
||||||
const $bknd = useBknd();
|
const $bknd = useBknd();
|
||||||
const $permissions = useApiQuery((api) => api.system.permissions(), {
|
const $permissions = useApiQuery((api) => api.system.permissions(), {
|
||||||
use: [mountOnce],
|
use: [mountOnce],
|
||||||
|
|||||||
Reference in New Issue
Block a user