@nocobase/portal-template-default 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +70 -0
- package/components.json +3 -1
- package/package.json +19 -5
- package/registry/nocobase-acl/README.md +19 -0
- package/registry/nocobase-acl/components/acl-boundary.tsx +106 -0
- package/registry/nocobase-acl/components/index.ts +3 -0
- package/registry/nocobase-acl/components/role-options.ts +29 -0
- package/registry/nocobase-acl/components/role-switcher.tsx +130 -0
- package/registry/nocobase-acl/demo/boundary-api.tsx +115 -0
- package/registry/nocobase-acl/demo/components.tsx +210 -0
- package/registry/nocobase-acl/demo/index.tsx +474 -0
- package/registry/nocobase-acl/demo/lazy-route.tsx +15 -0
- package/registry/nocobase-acl/demo/prompt-generator.tsx +115 -0
- package/registry/nocobase-acl/demo/role-switcher-prompt-generator.tsx +179 -0
- package/registry/nocobase-acl/demo/scenario-section.tsx +37 -0
- package/registry/nocobase-acl/extension.tsx +71 -0
- package/registry/nocobase-acl/index.ts +1 -0
- package/registry/nocobase-acl/tests/acl-regression.mjs +313 -0
- package/registry/nocobase-ai/README.md +70 -0
- package/registry/nocobase-ai/adapters/react-hook-form.ts +20 -0
- package/registry/nocobase-ai/assets/nocobase-ai-chat.svg +50 -0
- package/registry/nocobase-ai/components/ai-root-provider.tsx +32 -0
- package/registry/nocobase-ai/components/chat/ai-employee-avatar.tsx +31 -0
- package/registry/nocobase-ai/components/chat/chat-attachment.tsx +92 -0
- package/registry/nocobase-ai/components/chat/chat-compact.tsx +90 -0
- package/registry/nocobase-ai/components/chat/chat-composer.tsx +429 -0
- package/registry/nocobase-ai/components/chat/chat-empty-state.tsx +51 -0
- package/registry/nocobase-ai/components/chat/chat-header.tsx +88 -0
- package/registry/nocobase-ai/components/chat/chat-history-dialog.tsx +77 -0
- package/registry/nocobase-ai/components/chat/chat-message.tsx +252 -0
- package/registry/nocobase-ai/components/chat/chat-messages.tsx +174 -0
- package/registry/nocobase-ai/components/chat/chat-window.tsx +118 -0
- package/registry/nocobase-ai/components/chat/conversation-list.tsx +345 -0
- package/registry/nocobase-ai/components/chat/markdown-message.tsx +165 -0
- package/registry/nocobase-ai/components/chat/model-select-options.tsx +30 -0
- package/registry/nocobase-ai/components/chat/reasoning-panel.tsx +29 -0
- package/registry/nocobase-ai/components/chat/sub-agent-conversation.tsx +142 -0
- package/registry/nocobase-ai/components/chat/tool-call-card.tsx +316 -0
- package/registry/nocobase-ai/components/chat/user-prompt-editor.tsx +99 -0
- package/registry/nocobase-ai/components/chat/work-context-chip.tsx +114 -0
- package/registry/nocobase-ai/components/index.ts +59 -0
- package/registry/nocobase-ai/components/page-elements/ai-form.tsx +44 -0
- package/registry/nocobase-ai/components/page-elements/page-element-provider.tsx +469 -0
- package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +31 -0
- package/registry/nocobase-ai/components/surfaces/chat-inline.tsx +18 -0
- package/registry/nocobase-ai/components/surfaces/chat-page.tsx +18 -0
- package/registry/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +54 -0
- package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +74 -0
- package/registry/nocobase-ai/components/surfaces/chat-surface-actions.tsx +54 -0
- package/registry/nocobase-ai/components/tools/builtin-tool-renderers.tsx +31 -0
- package/registry/nocobase-ai/components/tools/business-report-dialog.tsx +367 -0
- package/registry/nocobase-ai/components/tools/business-report-renderer.tsx +122 -0
- package/registry/nocobase-ai/components/tools/business-report-utils.tsx +282 -0
- package/registry/nocobase-ai/components/tools/chart-renderer.tsx +110 -0
- package/registry/nocobase-ai/components/tools/echarts-preview.tsx +62 -0
- package/registry/nocobase-ai/components/tools/echarts-runtime-advanced.ts +18 -0
- package/registry/nocobase-ai/components/tools/echarts-runtime-common.ts +20 -0
- package/registry/nocobase-ai/components/tools/echarts-runtime-components.ts +34 -0
- package/registry/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +24 -0
- package/registry/nocobase-ai/components/tools/echarts-runtime.ts +138 -0
- package/registry/nocobase-ai/components/tools/sub-agent-renderer.tsx +67 -0
- package/registry/nocobase-ai/components/tools/suggestions-renderer.tsx +59 -0
- package/registry/nocobase-ai/components/tools/tool-renderer-provider.tsx +55 -0
- package/registry/nocobase-ai/components/tools/tool-renderer-utils.ts +18 -0
- package/registry/nocobase-ai/components/tools/workflow-renderer.tsx +108 -0
- package/registry/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +73 -0
- package/registry/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +149 -0
- package/registry/nocobase-ai/demo/configuration-gate.tsx +52 -0
- package/registry/nocobase-ai/demo/container-showcase.tsx +210 -0
- package/registry/nocobase-ai/demo/floating.tsx +134 -0
- package/registry/nocobase-ai/demo/index.tsx +346 -0
- package/registry/nocobase-ai/demo/interaction-showcase.tsx +151 -0
- package/registry/nocobase-ai/demo/page-context-prompt-generator.tsx +355 -0
- package/registry/nocobase-ai/demo/page-context.tsx +884 -0
- package/registry/nocobase-ai/demo/page-element-showcase.tsx +197 -0
- package/registry/nocobase-ai/demo/prompt-card.tsx +41 -0
- package/registry/nocobase-ai/demo/prompt-generator.tsx +452 -0
- package/registry/nocobase-ai/demo/shortcut.tsx +1207 -0
- package/registry/nocobase-ai/demo/tool-cards.tsx +530 -0
- package/registry/nocobase-ai/extension.tsx +163 -0
- package/registry/nocobase-ai/global-ai-chat.tsx +213 -0
- package/registry/nocobase-ai/index.ts +3 -0
- package/registry/nocobase-ai/providers/ai-provider.tsx +323 -0
- package/registry/nocobase-ai/providers/avatars.ts +817 -0
- package/registry/nocobase-ai/providers/chat-context.tsx +109 -0
- package/registry/nocobase-ai/providers/chat-controller.ts +84 -0
- package/registry/nocobase-ai/providers/chat-message-utils.ts +198 -0
- package/registry/nocobase-ai/providers/chat-provider.tsx +871 -0
- package/registry/nocobase-ai/providers/chat-reducer.ts +147 -0
- package/registry/nocobase-ai/providers/chat-task-utils.ts +105 -0
- package/registry/nocobase-ai/providers/chat-transport.ts +184 -0
- package/registry/nocobase-ai/providers/form-registry.tsx +271 -0
- package/registry/nocobase-ai/providers/frontend-tool-registry.tsx +231 -0
- package/registry/nocobase-ai/providers/index.ts +61 -0
- package/registry/nocobase-ai/providers/model.ts +32 -0
- package/registry/nocobase-ai/providers/page-context.tsx +136 -0
- package/registry/nocobase-ai/providers/stream-coalescer.ts +60 -0
- package/registry/nocobase-ai/providers/stream-event-utils.ts +82 -0
- package/registry/nocobase-ai/providers/stream-parser.ts +61 -0
- package/registry/nocobase-ai/providers/sub-agent-stream.ts +316 -0
- package/registry/nocobase-ai/providers/types.ts +183 -0
- package/registry/nocobase-ai/providers/ui-message-stream.ts +463 -0
- package/registry/nocobase-ai/providers/use-automatic-tool-approval.ts +94 -0
- package/registry/nocobase-ai/providers/use-chat-attachments.ts +206 -0
- package/registry/nocobase-ai/providers/use-chat-message-actions.ts +554 -0
- package/registry/nocobase-ai/providers/use-chat-runtime.ts +210 -0
- package/registry/nocobase-ai/providers/use-chat-work-context.ts +93 -0
- package/registry/nocobase-ai/providers/use-conversation-catalog.ts +108 -0
- package/registry/nocobase-ai/providers/use-conversation-history.ts +178 -0
- package/registry/nocobase-ai/services/index.ts +7 -0
- package/registry/nocobase-ai/services/nocobase-ai-service.ts +454 -0
- package/registry/nocobase-ai/services/types.ts +82 -0
- package/registry/nocobase-ai/tests/business-report-regression.mjs +70 -0
- package/registry/nocobase-ai/tests/form-filler-regression.mjs +129 -0
- package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +128 -0
- package/registry/nocobase-ai/tests/page-context-regression.mjs +48 -0
- package/registry/nocobase-ai/tests/stream-regression.mjs +207 -0
- package/registry.config.json +143 -0
- package/scripts/registry.mjs +161 -0
- package/src/App.css +95 -0
- package/src/App.tsx +127 -50
- package/src/app/extension.ts +9 -0
- package/src/app/extensions.tsx +28 -0
- package/src/components/access-control/access-denied.tsx +30 -0
- package/src/components/access-control/acl-bootstrap.tsx +9 -0
- package/src/components/access-control/acl-gate.tsx +42 -0
- package/src/components/access-control/navigate-to-accessible-resource.tsx +17 -0
- package/src/components/access-control/resource-access-guard.tsx +21 -0
- package/src/components/access-control/use-acl-runtime.ts +28 -0
- package/src/components/app-shell/header.tsx +4 -1
- package/src/components/app-shell/sidebar.tsx +34 -4
- package/src/lib/nocobase/acl/action.ts +147 -0
- package/src/lib/nocobase/acl/data-source.ts +24 -0
- package/src/lib/nocobase/acl/index.ts +6 -0
- package/src/lib/nocobase/acl/menu.ts +41 -0
- package/src/lib/nocobase/acl/record-permissions.ts +111 -0
- package/src/lib/nocobase/acl/store.ts +182 -0
- package/src/lib/nocobase/acl/types.ts +96 -0
- package/src/lib/nocobase/client.ts +262 -0
- package/src/lib/nocobase/error.ts +32 -0
- package/src/providers/access-control.ts +34 -0
- package/src/providers/auth.ts +74 -60
- package/src/providers/constants.ts +2 -37
- package/src/providers/data.ts +242 -85
- package/vite.config.ts +17 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Check, Copy, Sparkles } from "lucide-react";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Input } from "@/components/ui/input";
|
|
6
|
+
import { Label } from "@/components/ui/label";
|
|
7
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
8
|
+
|
|
9
|
+
export type AclPromptGeneratorConfig = {
|
|
10
|
+
title: string;
|
|
11
|
+
description: string;
|
|
12
|
+
defaultScene: string;
|
|
13
|
+
defaultTarget: string;
|
|
14
|
+
requirements: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function AclScenarioPromptGenerator({
|
|
18
|
+
config,
|
|
19
|
+
}: {
|
|
20
|
+
config: AclPromptGeneratorConfig;
|
|
21
|
+
}) {
|
|
22
|
+
const [scene, setScene] = useState(config.defaultScene);
|
|
23
|
+
const [target, setTarget] = useState(config.defaultTarget);
|
|
24
|
+
const [copied, setCopied] = useState(false);
|
|
25
|
+
const [copyError, setCopyError] = useState(false);
|
|
26
|
+
|
|
27
|
+
const prompt = useMemo(
|
|
28
|
+
() => `Build a complete NocoBase ACL scene in the Refine Starter.
|
|
29
|
+
|
|
30
|
+
Business scene
|
|
31
|
+
- Page or feature: ${scene}
|
|
32
|
+
- Permission target: ${target}
|
|
33
|
+
- Use realistic fixed sample content so the permission behavior is visible immediately.
|
|
34
|
+
- Build the complete page section and user flow, not an isolated ACL snippet.
|
|
35
|
+
|
|
36
|
+
ACL scenario
|
|
37
|
+
${config.requirements}
|
|
38
|
+
|
|
39
|
+
Implementation contract
|
|
40
|
+
- Use the Starter's built-in NocoBase accessControlProvider and ACL store.
|
|
41
|
+
- NocoBase roles:check is the source of truth. Do not create a second permission store or hard-code role names.
|
|
42
|
+
- Keep backend ACL enforcement in place; frontend checks only control presentation and navigation.
|
|
43
|
+
- Import reusable components from the installed local entry point at @/extensions/nocobase-acl when page composition is needed.
|
|
44
|
+
- Resource actions use Refine names: list, show, create, edit, delete. Let the Starter map them to NocoBase list/get/create/update/destroy.
|
|
45
|
+
- Preserve dataSourceKey when the collection belongs to a non-main data source.
|
|
46
|
+
- Show an understandable forbidden or hidden state where appropriate.
|
|
47
|
+
- Deliver complete React component code, resource metadata, route integration, and the visible sample UI.`,
|
|
48
|
+
[config.requirements, scene, target]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<details className="group rounded-xl border bg-muted/15">
|
|
53
|
+
<summary className="flex cursor-pointer list-none items-center gap-2 px-4 py-3 text-sm font-medium marker:hidden">
|
|
54
|
+
<Sparkles className="size-4 text-primary" />
|
|
55
|
+
Prompt generator
|
|
56
|
+
<span className="ml-auto text-xs font-normal text-muted-foreground">
|
|
57
|
+
{config.title}
|
|
58
|
+
</span>
|
|
59
|
+
</summary>
|
|
60
|
+
<div className="grid gap-4 border-t p-4 lg:grid-cols-[minmax(0,320px)_minmax(0,1fr)]">
|
|
61
|
+
<div className="space-y-4">
|
|
62
|
+
<p className="text-xs leading-5 text-muted-foreground">
|
|
63
|
+
{config.description}
|
|
64
|
+
</p>
|
|
65
|
+
<div className="space-y-2">
|
|
66
|
+
<Label htmlFor={`${config.title}-scene`}>Business scene</Label>
|
|
67
|
+
<Input
|
|
68
|
+
id={`${config.title}-scene`}
|
|
69
|
+
value={scene}
|
|
70
|
+
onChange={(event) => setScene(event.target.value)}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
<div className="space-y-2">
|
|
74
|
+
<Label htmlFor={`${config.title}-target`}>Permission target</Label>
|
|
75
|
+
<Textarea
|
|
76
|
+
id={`${config.title}-target`}
|
|
77
|
+
value={target}
|
|
78
|
+
onChange={(event) => setTarget(event.target.value)}
|
|
79
|
+
className="min-h-24"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="min-w-0 overflow-hidden rounded-lg border bg-background">
|
|
84
|
+
<div className="flex items-center justify-between gap-3 border-b px-3 py-2">
|
|
85
|
+
<span className="text-xs font-medium">Generated prompt</span>
|
|
86
|
+
<Button
|
|
87
|
+
size="sm"
|
|
88
|
+
onClick={async () => {
|
|
89
|
+
try {
|
|
90
|
+
await navigator.clipboard.writeText(prompt);
|
|
91
|
+
setCopyError(false);
|
|
92
|
+
setCopied(true);
|
|
93
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
94
|
+
} catch {
|
|
95
|
+
setCopyError(true);
|
|
96
|
+
}
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
{copied ? <Check /> : <Copy />}
|
|
100
|
+
{copied ? "Copied" : "Copy prompt"}
|
|
101
|
+
</Button>
|
|
102
|
+
</div>
|
|
103
|
+
{copyError ? (
|
|
104
|
+
<p className="border-b px-3 py-2 text-xs text-destructive">
|
|
105
|
+
Clipboard access failed. Select the prompt text and copy it manually.
|
|
106
|
+
</p>
|
|
107
|
+
) : null}
|
|
108
|
+
<pre className="max-h-80 overflow-auto whitespace-pre-wrap p-4 font-mono text-xs leading-5 text-muted-foreground">
|
|
109
|
+
{prompt}
|
|
110
|
+
</pre>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</details>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Check, Copy } from "lucide-react";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
6
|
+
import { Input } from "@/components/ui/input";
|
|
7
|
+
import { Label } from "@/components/ui/label";
|
|
8
|
+
import {
|
|
9
|
+
Select,
|
|
10
|
+
SelectContent,
|
|
11
|
+
SelectItem,
|
|
12
|
+
SelectTrigger,
|
|
13
|
+
SelectValue,
|
|
14
|
+
} from "@/components/ui/select";
|
|
15
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
16
|
+
|
|
17
|
+
type Placement = "header" | "user-menu" | "settings" | "custom";
|
|
18
|
+
type UnavailableState = "hide" | "current-role";
|
|
19
|
+
|
|
20
|
+
const placementLabels: Record<Placement, string> = {
|
|
21
|
+
header: "Application header",
|
|
22
|
+
"user-menu": "User menu",
|
|
23
|
+
settings: "Account settings",
|
|
24
|
+
custom: "Custom surface",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const unavailableStateLabels: Record<UnavailableState, string> = {
|
|
28
|
+
"current-role": "Show current role",
|
|
29
|
+
hide: "Hide component",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const placementInstructions: Record<Placement, string> = {
|
|
33
|
+
header:
|
|
34
|
+
"Place it in the application header beside the existing account controls. Keep the trigger compact and preserve the responsive user menu.",
|
|
35
|
+
"user-menu":
|
|
36
|
+
"Place it inside the existing user/account popover as a full-width menu section, separated from profile and sign-out actions.",
|
|
37
|
+
settings:
|
|
38
|
+
"Place it in an account settings section with a field label and short explanation of how the active role changes visible data and actions.",
|
|
39
|
+
custom:
|
|
40
|
+
"Place it in the application surface described below, matching the surrounding layout and spacing.",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export function RoleSwitcherPromptGenerator() {
|
|
44
|
+
const [placement, setPlacement] = useState<Placement>("user-menu");
|
|
45
|
+
const [label, setLabel] = useState("Working as");
|
|
46
|
+
const [unavailableState, setUnavailableState] =
|
|
47
|
+
useState<UnavailableState>("current-role");
|
|
48
|
+
const [surface, setSurface] = useState(
|
|
49
|
+
"Use the Starter's existing account controls and visual language. Do not create a separate demo page."
|
|
50
|
+
);
|
|
51
|
+
const [copied, setCopied] = useState(false);
|
|
52
|
+
const [copyError, setCopyError] = useState(false);
|
|
53
|
+
|
|
54
|
+
const prompt = useMemo(
|
|
55
|
+
() => `Integrate the RoleSwitcher into the existing NocoBase Refine Starter.
|
|
56
|
+
|
|
57
|
+
Target placement
|
|
58
|
+
- Placement: ${placementLabels[placement]}
|
|
59
|
+
- ${placementInstructions[placement]}
|
|
60
|
+
- Application context: ${surface}
|
|
61
|
+
|
|
62
|
+
Component presentation
|
|
63
|
+
- Label: ${label.trim() ? label : "No label; pass label={false}."}
|
|
64
|
+
- When role switching is unavailable: ${
|
|
65
|
+
unavailableState === "current-role"
|
|
66
|
+
? "Keep the current identity visible by passing showWhenUnavailable."
|
|
67
|
+
: "Hide the component by keeping showWhenUnavailable false."
|
|
68
|
+
}
|
|
69
|
+
- Use className and triggerClassName only to fit the existing surface; do not fork or restyle the internal Select implementation.
|
|
70
|
+
|
|
71
|
+
Integration requirements
|
|
72
|
+
- Import and reuse RoleSwitcher from the installed local entry point at @/extensions/nocobase-acl.
|
|
73
|
+
- Update the existing application surface instead of creating an isolated example page.
|
|
74
|
+
- Do not fetch, hard-code, or pass a duplicate role list. The component reads roles from the current Refine identity.
|
|
75
|
+
- Do not reimplement Full permissions, Anonymous, or role-mode logic. The component derives these options from the Starter ACL store.
|
|
76
|
+
- Do not add a separate role-switch mutation. The component already calls users:setDefaultRole and reloads the application after a successful switch.
|
|
77
|
+
- Preserve existing account, responsive navigation, and sign-out behavior.
|
|
78
|
+
- Return the complete updated component for the target surface and mention any imports that were added.`,
|
|
79
|
+
[label, placement, surface, unavailableState]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="grid items-start gap-5 xl:grid-cols-[360px_minmax(0,1fr)]">
|
|
84
|
+
<Card className="gap-0 py-0">
|
|
85
|
+
<CardHeader className="border-b py-4">
|
|
86
|
+
<CardTitle>Placement settings</CardTitle>
|
|
87
|
+
</CardHeader>
|
|
88
|
+
<CardContent className="space-y-5 py-5">
|
|
89
|
+
<div className="space-y-2">
|
|
90
|
+
<Label>Placement</Label>
|
|
91
|
+
<Select
|
|
92
|
+
value={placement}
|
|
93
|
+
onValueChange={(value) => value && setPlacement(value as Placement)}
|
|
94
|
+
>
|
|
95
|
+
<SelectTrigger className="w-full">
|
|
96
|
+
<SelectValue>{placementLabels[placement]}</SelectValue>
|
|
97
|
+
</SelectTrigger>
|
|
98
|
+
<SelectContent>
|
|
99
|
+
<SelectItem value="header">Application header</SelectItem>
|
|
100
|
+
<SelectItem value="user-menu">User menu</SelectItem>
|
|
101
|
+
<SelectItem value="settings">Account settings</SelectItem>
|
|
102
|
+
<SelectItem value="custom">Custom surface</SelectItem>
|
|
103
|
+
</SelectContent>
|
|
104
|
+
</Select>
|
|
105
|
+
</div>
|
|
106
|
+
<div className="space-y-2">
|
|
107
|
+
<Label htmlFor="role-switcher-label">Label</Label>
|
|
108
|
+
<Input
|
|
109
|
+
id="role-switcher-label"
|
|
110
|
+
value={label}
|
|
111
|
+
onChange={(event) => setLabel(event.target.value)}
|
|
112
|
+
placeholder="Leave empty for no label"
|
|
113
|
+
/>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="space-y-2">
|
|
116
|
+
<Label>Single-role behavior</Label>
|
|
117
|
+
<Select
|
|
118
|
+
value={unavailableState}
|
|
119
|
+
onValueChange={(value) =>
|
|
120
|
+
value && setUnavailableState(value as UnavailableState)
|
|
121
|
+
}
|
|
122
|
+
>
|
|
123
|
+
<SelectTrigger className="w-full">
|
|
124
|
+
<SelectValue>
|
|
125
|
+
{unavailableStateLabels[unavailableState]}
|
|
126
|
+
</SelectValue>
|
|
127
|
+
</SelectTrigger>
|
|
128
|
+
<SelectContent>
|
|
129
|
+
<SelectItem value="current-role">Show current role</SelectItem>
|
|
130
|
+
<SelectItem value="hide">Hide component</SelectItem>
|
|
131
|
+
</SelectContent>
|
|
132
|
+
</Select>
|
|
133
|
+
</div>
|
|
134
|
+
<div className="space-y-2">
|
|
135
|
+
<Label htmlFor="role-switcher-surface">Application context</Label>
|
|
136
|
+
<Textarea
|
|
137
|
+
id="role-switcher-surface"
|
|
138
|
+
value={surface}
|
|
139
|
+
onChange={(event) => setSurface(event.target.value)}
|
|
140
|
+
className="min-h-28"
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
</CardContent>
|
|
144
|
+
</Card>
|
|
145
|
+
|
|
146
|
+
<Card className="gap-0 overflow-hidden py-0">
|
|
147
|
+
<CardHeader className="flex-row items-center justify-between gap-3 border-b py-3">
|
|
148
|
+
<CardTitle className="text-sm">Generated integration prompt</CardTitle>
|
|
149
|
+
<Button
|
|
150
|
+
size="sm"
|
|
151
|
+
onClick={async () => {
|
|
152
|
+
try {
|
|
153
|
+
await navigator.clipboard.writeText(prompt);
|
|
154
|
+
setCopyError(false);
|
|
155
|
+
setCopied(true);
|
|
156
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
157
|
+
} catch {
|
|
158
|
+
setCopyError(true);
|
|
159
|
+
}
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
{copied ? <Check /> : <Copy />}
|
|
163
|
+
{copied ? "Copied" : "Copy prompt"}
|
|
164
|
+
</Button>
|
|
165
|
+
</CardHeader>
|
|
166
|
+
{copyError ? (
|
|
167
|
+
<p className="border-b px-4 py-2 text-xs text-destructive">
|
|
168
|
+
Clipboard access failed. Select the prompt and copy it manually.
|
|
169
|
+
</p>
|
|
170
|
+
) : null}
|
|
171
|
+
<CardContent className="p-0">
|
|
172
|
+
<pre className="max-h-[560px] overflow-auto whitespace-pre-wrap p-5 font-mono text-xs leading-5 text-muted-foreground">
|
|
173
|
+
{prompt}
|
|
174
|
+
</pre>
|
|
175
|
+
</CardContent>
|
|
176
|
+
</Card>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import {
|
|
5
|
+
AclScenarioPromptGenerator,
|
|
6
|
+
type AclPromptGeneratorConfig,
|
|
7
|
+
} from "./prompt-generator";
|
|
8
|
+
|
|
9
|
+
export function AclScenarioSection({
|
|
10
|
+
eyebrow,
|
|
11
|
+
title,
|
|
12
|
+
description,
|
|
13
|
+
children,
|
|
14
|
+
prompt,
|
|
15
|
+
}: {
|
|
16
|
+
eyebrow: string;
|
|
17
|
+
title: string;
|
|
18
|
+
description: string;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
prompt: AclPromptGeneratorConfig;
|
|
21
|
+
}) {
|
|
22
|
+
return (
|
|
23
|
+
<section className="space-y-5 border-t pt-8">
|
|
24
|
+
<div>
|
|
25
|
+
<Badge variant="outline">{eyebrow}</Badge>
|
|
26
|
+
<h2 className="mt-3 text-2xl font-semibold tracking-[-0.025em]">
|
|
27
|
+
{title}
|
|
28
|
+
</h2>
|
|
29
|
+
<p className="mt-2 max-w-3xl text-sm leading-6 text-muted-foreground">
|
|
30
|
+
{description}
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
{children}
|
|
34
|
+
<AclScenarioPromptGenerator config={prompt} />
|
|
35
|
+
</section>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AppExtension } from "@/app/extension";
|
|
2
|
+
import { Blocks, PanelsTopLeft, ShieldCheck } from "lucide-react";
|
|
3
|
+
import { lazy } from "react";
|
|
4
|
+
import { Outlet, Route } from "react-router";
|
|
5
|
+
import { LazyAclRoute } from "./demo/lazy-route";
|
|
6
|
+
|
|
7
|
+
const AclComponentsPage = lazy(() =>
|
|
8
|
+
import("./demo/components").then((module) => ({
|
|
9
|
+
default: module.AclComponentsPage,
|
|
10
|
+
}))
|
|
11
|
+
);
|
|
12
|
+
const AclPatternsPage = lazy(() =>
|
|
13
|
+
import("./demo").then((module) => ({ default: module.AclPatternsPage }))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const nocobaseAclExtension: AppExtension = {
|
|
17
|
+
id: "nocobase-acl",
|
|
18
|
+
resources: [
|
|
19
|
+
{
|
|
20
|
+
name: "acl-integration",
|
|
21
|
+
meta: {
|
|
22
|
+
label: "Access control",
|
|
23
|
+
icon: <ShieldCheck />,
|
|
24
|
+
description: "NocoBase ACL integration for Refine applications.",
|
|
25
|
+
acl: { type: "authenticated" },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "acl-components",
|
|
30
|
+
list: "/acl",
|
|
31
|
+
meta: {
|
|
32
|
+
parent: "acl-integration",
|
|
33
|
+
label: "Role switcher",
|
|
34
|
+
icon: <Blocks />,
|
|
35
|
+
acl: { type: "authenticated" },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "acl-patterns",
|
|
40
|
+
list: "/acl/patterns",
|
|
41
|
+
meta: {
|
|
42
|
+
parent: "acl-integration",
|
|
43
|
+
label: "Permission patterns",
|
|
44
|
+
icon: <PanelsTopLeft />,
|
|
45
|
+
acl: { type: "authenticated" },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
routes: (
|
|
50
|
+
<Route key="nocobase-acl" path="/acl" element={<Outlet />}>
|
|
51
|
+
<Route
|
|
52
|
+
index
|
|
53
|
+
element={
|
|
54
|
+
<LazyAclRoute>
|
|
55
|
+
<AclComponentsPage />
|
|
56
|
+
</LazyAclRoute>
|
|
57
|
+
}
|
|
58
|
+
/>
|
|
59
|
+
<Route
|
|
60
|
+
path="patterns"
|
|
61
|
+
element={
|
|
62
|
+
<LazyAclRoute>
|
|
63
|
+
<AclPatternsPage />
|
|
64
|
+
</LazyAclRoute>
|
|
65
|
+
}
|
|
66
|
+
/>
|
|
67
|
+
</Route>
|
|
68
|
+
),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default nocobaseAclExtension;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./components";
|