Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve the advanced editor on the Casdoor model editing page #175

Merged
merged 3 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 85 additions & 2 deletions app/components/ModelEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,32 @@ import { linter, lintGutter } from '@codemirror/lint';
import { casbinLinter } from '@/app/utils/casbinLinter';
import { newModel } from 'casbin';
import { setError } from '@/app/utils/errorManager';
import { buttonPlugin } from '@/app/components/editor/ButtonPlugin';
import { extractPageContent } from '@/app/utils/contentExtractor';
import { useLang } from '@/app/context/LangContext';
import SidePanelChat from '@/app/components/SidePanelChat';
import { example, ModelKind } from '@/app/components/editor/casbin-mode/example';
import { clsx } from 'clsx';

export const ModelEditor = () => {
const [modelText, setModelText] = useState('');
const [modelKind, setModelKind] = useState<ModelKind>('');
const editorRef = useRef<EditorView | null>(null);
const cursorPosRef = useRef<{ from: number; to: number } | null>(null);
const sidePanelChatRef = useRef<{ openDrawer: (message: string) => void } | null>(null);
const { t, lang, theme } = useLang();
const textClass = clsx(theme === 'dark' ? 'text-gray-200' : 'text-gray-800');

const openDrawerWithMessage = (message: string) => {
if (sidePanelChatRef.current) {
sidePanelChatRef.current.openDrawer(message);
}
};

const extractContent = (boxType: string) => {
const { message } = extractPageContent(boxType, t, lang);
return message;
};

const validateModel = useCallback(async (text: string) => {
try {
Expand All @@ -31,14 +52,18 @@ export const ModelEditor = () => {

const handleMessage = useCallback((event: MessageEvent) => {
if (event.data.type === 'initializeModel') {
setModelText(event.data.modelText);
if (event.data.modelText) {
setModelText(event.data.modelText);
}
} else if (event.data.type === 'getModelText') {
window.parent.postMessage({
type: 'modelUpdate',
modelText: modelText
}, '*');
} else if (event.data.type === 'updateModelText') {
setModelText(event.data.modelText);
if (event.data.modelText) {
setModelText(event.data.modelText);
}
}
}, [modelText]);

Expand Down Expand Up @@ -73,9 +98,63 @@ export const ModelEditor = () => {
}
}, [modelText]);

useEffect(() => {
if (modelKind && example[modelKind]) {
setModelText(example[modelKind].model);
}
}, [modelKind]);

return (
<div className="flex-grow overflow-auto h-full">
<div className="flex flex-col h-full">
<div className={clsx('h-10 pl-2', 'flex items-center justify-start gap-2')}>
<div className={clsx(textClass, 'font-bold')}>{t('Model')}</div>
<select
value={modelKind}
onChange={(e) => {
const selectedKind = e.target.value as ModelKind;
if (selectedKind && example[selectedKind]) {
setModelText(example[selectedKind].model);
setModelKind('');
window.parent.postMessage({
type: 'modelUpdate',
modelText: example[selectedKind].model
}, '*');
}
}}
className={'border-[#767676] border rounded'}
>
<option value="" disabled>
{t('Select your model')}
</option>
{Object.keys(example).map((n) => {
return (
<option key={n} value={n}>
{example[n as ModelKind].name}
</option>
);
})}
</select>
<button
className={clsx(
'rounded',
'text-[#453d7d]',
'px-1',
'border border-[#453d7d]',
'bg-[#efefef]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
onClick={() => {
const ok = window.confirm('Confirm Reset?');
if (ok) {
window.location.reload();
}
}}
>
{t('RESET')}
</button>
</div>
<CodeMirror
height="100%"
theme={monokai}
Expand All @@ -93,6 +172,7 @@ export const ModelEditor = () => {
EditorView.lineWrapping,
linter(casbinLinter),
lintGutter(),
buttonPlugin(openDrawerWithMessage, extractContent, 'model'),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
editorRef.current = update.view;
Expand All @@ -102,6 +182,9 @@ export const ModelEditor = () => {
className={'function flex-grow h-[300px]'}
value={modelText}
/>
<div className="mr-4">
<SidePanelChat ref={sidePanelChatRef} />
</div>
</div>
</div>
);
Expand Down
3 changes: 0 additions & 3 deletions app/components/SidePanelChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ const SidePanelChat = forwardRef((props, ref) => {

return (
<>
<div className="text-red-600 flex items-center">
<span>{t('Why this result')}</span>
</div>
{isOpen && <div className="fixed inset-0 bg-black bg-opacity-50 z-40" onClick={toggleDrawer}></div>}
<div
className={`fixed top-0 right-0 w-full sm:w-[500px] h-full bg-white z-50 shadow-lg transform transition-transform duration-300 ease-in-out ${
Expand Down
3 changes: 3 additions & 0 deletions app/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ export const EditorScreen = () => {
<div className={clsx('h-10 pl-2 font-bold', 'flex items-center justify-between')}>
<div className={textClass}>{t('Enforcement Result')}</div>
<div className="mr-4">
<div className="text-red-600 flex items-center">
<span>{t('Why this result')}</span>
</div>
<SidePanelChat ref={sidePanelChatRef} />
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/model-editor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { ModelEditor } from '../components/ModelEditor';

export default function ModelEditorPage() {
return (
<div style={{ width: '100%', height: '100vh' }}>
<main style={{ width: '100%', height: '100vh' }}>
<ModelEditor />
</div>
</main>
);
}
2 changes: 1 addition & 1 deletion app/utils/contentExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const extractPageContent = (boxType: string, t: (key: string) => string,
const mainContent = document.querySelector('main')?.innerText || 'No main content found';

const customConfigMatch = mainContent.match(new RegExp(`${t('Custom Functions')}\\s+([\\s\\S]*?)\\s+${t('Model')}`));
const modelMatch = mainContent.match(new RegExp(`${t('Model')}\\s+([\\s\\S]*?)\\s+${t('Policy')}`));
const modelMatch = mainContent.match(new RegExp(`${t('Model')}\\s+([\\s\\S]*?)(?:${t('Policy')}|$)`));
const policyMatch = mainContent.match(new RegExp(`${t('Policy')}\\s+([\\s\\S]*?)\\s+${t('Request')}`));
const requestMatch = mainContent.match(new RegExp(`${t('Request')}\\s+([\\s\\S]*?)\\s+${t('Enforcement Result')}`));
const enforcementResultMatch = mainContent.match(new RegExp(`${t('Enforcement Result')}\\s+([\\s\\S]*?)\\s+${t('RUN THE TEST')}`));
Expand Down
Loading