@motiadev/workbench 0.4.3-beta.96 → 0.4.4-beta.98-640191
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/dist/src/components/endpoints/endpoint-body-panel.js +1 -1
- package/dist/src/components/endpoints/endpoint-path-params-panel.d.ts +1 -1
- package/dist/src/components/endpoints/endpoint-path-params-panel.js +2 -13
- package/dist/src/components/endpoints/hooks/utils.d.ts +1 -1
- package/dist/src/components/endpoints/json-editor.d.ts +2 -1
- package/dist/src/components/endpoints/json-editor.js +12 -10
- package/dist/src/components/states/state-details.d.ts +7 -0
- package/dist/src/components/states/state-details.js +3 -0
- package/dist/src/components/states/state-editor.d.ts +7 -0
- package/dist/src/components/states/state-editor.js +71 -0
- package/dist/src/components/states/{state-detail.d.ts → state-sidebar.d.ts} +1 -1
- package/dist/src/components/states/state-sidebar.js +17 -0
- package/dist/src/components/states/states-page.js +2 -2
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/src/components/states/state-detail.js +0 -18
- package/dist/src/components/states/state-value.d.ts +0 -8
- package/dist/src/components/states/state-value.js +0 -51
|
@@ -16,5 +16,5 @@ export const EndpointBodyPanel = ({ endpoint, onChange, onValidate }) => {
|
|
|
16
16
|
if (!shouldHaveBody) {
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
|
-
return (_jsx(Panel, { title: "Body", size: "sm", contentClassName: "p-0", "data-testid": "endpoint-body-panel", children: onChange ? (_jsx(JsonEditor, { value: body, schema: endpoint.bodySchema, onChange: handleBodyChange, onValidate: onValidate })) : (_jsx(ReactJson, { src: convertJsonSchemaToJson(endpoint.bodySchema
|
|
19
|
+
return (_jsx(Panel, { title: "Body", size: "sm", contentClassName: "p-0", "data-testid": "endpoint-body-panel", children: onChange ? (_jsx(JsonEditor, { value: body, schema: endpoint.bodySchema, onChange: handleBodyChange, onValidate: onValidate })) : (_jsx(ReactJson, { src: convertJsonSchemaToJson(endpoint.bodySchema), theme: "default", enableClipboard: false, style: { backgroundColor: 'transparent' } })) }));
|
|
20
20
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Input, Panel } from '@motiadev/ui';
|
|
3
|
-
import { Fragment,
|
|
3
|
+
import { Fragment, useState } from 'react';
|
|
4
4
|
import { usePathParams } from './hooks/use-path-params';
|
|
5
5
|
export const EndpointPathParamsPanel = ({ endpoint, onChange }) => {
|
|
6
6
|
const pathParams = usePathParams(endpoint.path);
|
|
@@ -10,19 +10,8 @@ export const EndpointPathParamsPanel = ({ endpoint, onChange }) => {
|
|
|
10
10
|
setPathParamsValues(newPathParamsValues);
|
|
11
11
|
onChange?.(newPathParamsValues);
|
|
12
12
|
};
|
|
13
|
-
const subtitle = useMemo(() => {
|
|
14
|
-
if (onChange) {
|
|
15
|
-
return Object.entries(pathParamsValues).reduce((acc, [param, value]) => {
|
|
16
|
-
if (!value) {
|
|
17
|
-
return acc;
|
|
18
|
-
}
|
|
19
|
-
return acc.replace(`:${param}`, value);
|
|
20
|
-
}, endpoint.path);
|
|
21
|
-
}
|
|
22
|
-
return endpoint.path;
|
|
23
|
-
}, [pathParamsValues, endpoint.path, onChange]);
|
|
24
13
|
if (!pathParams.length) {
|
|
25
14
|
return null;
|
|
26
15
|
}
|
|
27
|
-
return (_jsx(Panel, { title: "Path params",
|
|
16
|
+
return (_jsx(Panel, { title: "Path params", size: "sm", variant: "default", children: _jsx("div", { className: "grid gap-3", style: { gridTemplateColumns: '1fr 2fr' }, children: pathParams.map((param) => (_jsxs(Fragment, { children: [_jsx("div", { className: "font-bold leading-[36px] flex text-xs", children: param }), _jsx("div", { className: "flex items-center text-xs", children: onChange && (_jsx(Input, { className: "w-full text-xs", placeholder: param, value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) })) })] }, param))) }) }));
|
|
28
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const convertJsonSchemaToJson: (schema
|
|
1
|
+
export declare const convertJsonSchemaToJson: (schema?: Record<string, any>) => any;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
type JsonEditorProps = {
|
|
3
3
|
value: string;
|
|
4
|
-
|
|
4
|
+
height?: number | string;
|
|
5
|
+
schema?: Record<string, unknown>;
|
|
5
6
|
onChange: (value: string) => void;
|
|
6
7
|
onValidate?: (isValid: boolean) => void;
|
|
7
8
|
};
|
|
@@ -2,25 +2,27 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useMemo } from 'react';
|
|
3
3
|
import Editor, { useMonaco } from '@monaco-editor/react';
|
|
4
4
|
import { useThemeStore } from '@/stores/use-theme-store';
|
|
5
|
-
export const JsonEditor = ({ value, schema, onChange, onValidate }) => {
|
|
5
|
+
export const JsonEditor = ({ value, height = 300, schema, onChange, onValidate }) => {
|
|
6
6
|
const monaco = useMonaco();
|
|
7
7
|
const theme = useThemeStore((state) => state.theme);
|
|
8
8
|
const editorTheme = useMemo(() => (theme === 'dark' ? 'vs-dark' : 'light'), [theme]);
|
|
9
9
|
useEffect(() => {
|
|
10
|
-
if (!monaco
|
|
10
|
+
if (!monaco)
|
|
11
11
|
return;
|
|
12
12
|
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
|
13
13
|
validate: true,
|
|
14
|
-
schemas:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
schemas: schema
|
|
15
|
+
? [
|
|
16
|
+
{
|
|
17
|
+
uri: window.location.href,
|
|
18
|
+
fileMatch: ['*'],
|
|
19
|
+
schema,
|
|
20
|
+
},
|
|
21
|
+
]
|
|
22
|
+
: [],
|
|
21
23
|
});
|
|
22
24
|
}, [monaco, schema]);
|
|
23
|
-
return (_jsx(Editor, { "data-testid": "json-editor", height:
|
|
25
|
+
return (_jsx(Editor, { "data-testid": "json-editor", height: height, language: "json", value: value, theme: editorTheme, onChange: (value) => {
|
|
24
26
|
if (!value) {
|
|
25
27
|
onValidate?.(false);
|
|
26
28
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@motiadev/ui';
|
|
3
|
+
import { AlertCircle, Check, Loader2, Save } from 'lucide-react';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { JsonEditor } from '../endpoints/json-editor';
|
|
6
|
+
export const StateEditor = ({ state }) => {
|
|
7
|
+
const [isRequestLoading, setIsRequestLoading] = useState(false);
|
|
8
|
+
const [isValid, setIsValid] = useState(true);
|
|
9
|
+
const [jsonValue, setJsonValue] = useState(JSON.stringify(state.value, null, 2));
|
|
10
|
+
const [hasChanges, setHasChanges] = useState(false);
|
|
11
|
+
const [saveStatus, setSaveStatus] = useState('idle');
|
|
12
|
+
const lastSavedValue = useRef(JSON.stringify(state.value, null, 2));
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
setJsonValue(JSON.stringify(state.value, null, 2));
|
|
15
|
+
}, [state.value]);
|
|
16
|
+
const handleJsonChange = useCallback((value) => {
|
|
17
|
+
setHasChanges(value !== lastSavedValue.current);
|
|
18
|
+
setJsonValue(value);
|
|
19
|
+
setSaveStatus('idle');
|
|
20
|
+
}, []);
|
|
21
|
+
const handleSave = async () => {
|
|
22
|
+
if (!isValid || !hasChanges)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
setIsRequestLoading(true);
|
|
26
|
+
setSaveStatus('idle');
|
|
27
|
+
const response = await fetch('/motia/state', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
key: state.key,
|
|
34
|
+
groupId: state.groupId,
|
|
35
|
+
value: JSON.parse(jsonValue),
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
40
|
+
}
|
|
41
|
+
lastSavedValue.current = jsonValue;
|
|
42
|
+
setSaveStatus('success');
|
|
43
|
+
setHasChanges(false);
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
setSaveStatus('idle');
|
|
46
|
+
}, 3000);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Failed to save state:', error);
|
|
50
|
+
setSaveStatus('error');
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
setIsRequestLoading(false);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const resetChanges = useCallback(() => {
|
|
57
|
+
setJsonValue(JSON.stringify(state.value, null, 2));
|
|
58
|
+
setHasChanges(false);
|
|
59
|
+
setSaveStatus('idle');
|
|
60
|
+
}, [state.value]);
|
|
61
|
+
const statusView = useMemo(() => {
|
|
62
|
+
if (saveStatus === 'success') {
|
|
63
|
+
return (_jsx("div", { className: "bg-green-50 dark:bg-green-950/20 border border-green-200 dark:border-green-800 rounded-lg p-2", children: _jsxs("div", { className: "flex items-center gap-2 text-green-700 dark:text-green-400 text-sm", children: [_jsx(Check, { className: "w-4 h-4" }), "State saved successfully!"] }) }));
|
|
64
|
+
}
|
|
65
|
+
if (saveStatus === 'error') {
|
|
66
|
+
return (_jsx("div", { className: "bg-red-50 dark:bg-red-950/20 border border-red-200 dark:border-red-800 rounded-lg p-2", children: _jsxs("div", { className: "flex items-center gap-2 text-red-700 dark:text-red-400 text-sm", children: [_jsx(AlertCircle, { className: "w-4 h-4" }), "Failed to save state. Please try again."] }) }));
|
|
67
|
+
}
|
|
68
|
+
return (_jsx("div", { className: "text-xs text-muted-foreground", children: hasChanges ? (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-2 h-2 bg-orange-500 rounded-full" }), "Unsaved changes"] })) : (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-2 h-2 bg-green-500 rounded-full" }), "Up to date"] })) }));
|
|
69
|
+
}, [saveStatus, hasChanges]);
|
|
70
|
+
return (_jsxs("div", { className: "flex flex-col gap-2 h-full", children: [_jsx("p", { className: "text-xs text-muted-foreground", children: "Modify the state value using the JSON editor below." }), _jsx("div", { className: "space-y-3 pt-2 flex flex-col", children: _jsxs("div", { className: "relative flex-1", children: [_jsx(JsonEditor, { value: jsonValue, onChange: handleJsonChange, onValidate: setIsValid, height: 'calc(100vh - 300px)' }), !isValid && (_jsxs("div", { className: "absolute top-2 right-2 bg-destructive/90 text-destructive-foreground px-2 py-1 rounded text-xs flex items-center gap-1", children: [_jsx(AlertCircle, { className: "w-3 h-3" }), "Invalid JSON"] }))] }) }), _jsxs("div", { className: "flex items-center justify-between pt-2", children: [statusView, _jsxs("div", { className: "flex items-center gap-2", children: [hasChanges && (_jsx(Button, { variant: "secondary", onClick: resetChanges, disabled: isRequestLoading, children: "Reset" })), _jsx(Button, { onClick: handleSave, variant: "accent", disabled: isRequestLoading || !isValid || !hasChanges, "data-testid": "state-save-button", children: isRequestLoading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-3 h-3 animate-spin mr-1" }), "Saving..."] })) : (_jsxs(_Fragment, { children: [_jsx(Save, { className: "w-3 h-3 mr-1" }), "Save Changes"] })) })] })] })] }));
|
|
71
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Sidebar } from '@/components/sidebar/sidebar';
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import { StateDetails } from './state-details';
|
|
5
|
+
import { StateEditor } from './state-editor';
|
|
6
|
+
export const StateSidebar = ({ state, onClose }) => {
|
|
7
|
+
return (_jsx(Sidebar, { onClose: onClose, title: "State Details", initialWidth: 500, tabs: [
|
|
8
|
+
{
|
|
9
|
+
label: 'Overview',
|
|
10
|
+
content: _jsx(StateDetails, { state: state }),
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: 'Editor',
|
|
14
|
+
content: _jsx(StateEditor, { state: state }),
|
|
15
|
+
},
|
|
16
|
+
], actions: [{ icon: _jsx(X, {}), onClick: onClose, label: 'Close' }] }));
|
|
17
|
+
};
|
|
@@ -4,7 +4,7 @@ import { cn } from '@motiadev/ui';
|
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
5
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table';
|
|
6
6
|
import { useGetStateItems } from './hooks/states-hooks';
|
|
7
|
-
import {
|
|
7
|
+
import { StateSidebar } from './state-sidebar';
|
|
8
8
|
export const StatesPage = () => {
|
|
9
9
|
const selectedStateId = useGlobalStore((state) => state.selectedStateId);
|
|
10
10
|
const selectStateId = useGlobalStore((state) => state.selectStateId);
|
|
@@ -12,7 +12,7 @@ export const StatesPage = () => {
|
|
|
12
12
|
const selectedItem = useMemo(() => (selectedStateId ? items.find((item) => `${item.groupId}:${item.key}` === selectedStateId) : null), [items, selectedStateId]);
|
|
13
13
|
const handleRowClick = (item) => selectStateId(`${item.groupId}:${item.key}`);
|
|
14
14
|
const onClose = () => selectStateId(undefined);
|
|
15
|
-
return (_jsxs("div", { className: "flex flex-row gap-4 h-full", children: [selectedItem && _jsx(
|
|
15
|
+
return (_jsxs("div", { className: "flex flex-row gap-4 h-full", children: [selectedItem && _jsx(StateSidebar, { state: selectedItem, onClose: onClose }), _jsxs(Table, { children: [_jsx(TableHeader, { className: "sticky top-0 bg-background", children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "rounded-0", children: "Group ID" }), _jsx(TableHead, { children: "Key" }), _jsx(TableHead, { children: "Type" })] }) }), _jsx(TableBody, { children: items.map((item) => (_jsxs(TableRow, { "data-testid": `item-${item}`, onClick: () => handleRowClick(item), className: cn('font-mono font-semibold cursor-pointer border-0', selectedItem === item
|
|
16
16
|
? 'bg-muted-foreground/10 hover:bg-muted-foreground/20'
|
|
17
17
|
: 'hover:bg-muted-foreground/10'), children: [_jsx(TableCell, { className: "hover:bg-transparent", children: item.groupId }), _jsx(TableCell, { className: "hover:bg-transparent", children: item.key }), _jsx(TableCell, { className: "hover:bg-transparent", children: item.type })] }, `${item.groupId}:${item.key}`))) })] })] }));
|
|
18
18
|
};
|