@motiadev/workbench 0.4.1-beta.92 → 0.4.2-beta.94
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.d.ts +11 -0
- package/dist/src/components/endpoints/endpoint-body-panel.js +20 -0
- package/dist/src/components/endpoints/endpoint-call.d.ts +0 -1
- package/dist/src/components/endpoints/endpoint-call.js +15 -34
- package/dist/src/components/endpoints/endpoint-description.d.ts +7 -0
- package/dist/src/components/endpoints/endpoint-description.js +11 -0
- package/dist/src/components/endpoints/endpoint-path-params-panel.d.ts +8 -0
- package/dist/src/components/endpoints/endpoint-path-params-panel.js +28 -0
- package/dist/src/components/endpoints/endpoint-query-params-panel.d.ts +8 -0
- package/dist/src/components/endpoints/endpoint-query-params-panel.js +16 -0
- package/dist/src/components/endpoints/endpoint-response-schema.js +1 -1
- package/dist/src/components/endpoints/endpoint-response.js +11 -13
- package/dist/src/components/endpoints/endpoint-side-panel.d.ts +8 -0
- package/dist/src/components/endpoints/endpoint-side-panel.js +25 -0
- package/dist/src/components/endpoints/endpoints-page.js +2 -2
- package/dist/src/components/endpoints/hooks/utils.js +1 -1
- package/dist/src/components/endpoints/json-editor.d.ts +1 -1
- package/dist/src/components/endpoints/json-editor.js +2 -2
- package/dist/src/components/logs/logs-page.js +5 -2
- package/dist/src/components/states/states-page.js +1 -1
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ApiEndpoint } from '@/types/endpoint';
|
|
2
|
+
import { FC } from 'react';
|
|
3
|
+
import 'react18-json-view/src/dark.css';
|
|
4
|
+
import 'react18-json-view/src/style.css';
|
|
5
|
+
type Props = {
|
|
6
|
+
endpoint: ApiEndpoint;
|
|
7
|
+
onChange?: (body: string) => void;
|
|
8
|
+
onValidate?: (isValid: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const EndpointBodyPanel: FC<Props>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
|
|
3
|
+
import { Panel } from '@motiadev/ui';
|
|
4
|
+
import { JsonEditor } from './json-editor';
|
|
5
|
+
import ReactJson from 'react18-json-view';
|
|
6
|
+
import 'react18-json-view/src/dark.css';
|
|
7
|
+
import 'react18-json-view/src/style.css';
|
|
8
|
+
import { convertJsonSchemaToJson } from './hooks/utils';
|
|
9
|
+
export const EndpointBodyPanel = ({ endpoint, onChange, onValidate }) => {
|
|
10
|
+
const shouldHaveBody = ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase());
|
|
11
|
+
const { body, setBody } = useJsonSchemaToJson(endpoint.bodySchema);
|
|
12
|
+
const handleBodyChange = (body) => {
|
|
13
|
+
setBody(body);
|
|
14
|
+
onChange?.(body);
|
|
15
|
+
};
|
|
16
|
+
if (!shouldHaveBody) {
|
|
17
|
+
return null;
|
|
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 ?? {}), theme: "default", enableClipboard: false })) }));
|
|
20
|
+
};
|
|
@@ -1,38 +1,27 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { Loader2, Play, X } from 'lucide-react';
|
|
2
|
+
import { Button } from '@motiadev/ui';
|
|
3
|
+
import { Loader2, Play } from 'lucide-react';
|
|
5
4
|
import { useEffect, useMemo, useState } from 'react';
|
|
6
|
-
import {
|
|
5
|
+
import { EndpointBodyPanel } from './endpoint-body-panel';
|
|
6
|
+
import { EndpointPathParamsPanel } from './endpoint-path-params-panel';
|
|
7
|
+
import { EndpointQueryParamsPanel } from './endpoint-query-params-panel';
|
|
7
8
|
import { EndpointResponse } from './endpoint-response';
|
|
8
|
-
|
|
9
|
-
import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
|
|
10
|
-
import { usePathParams } from './hooks/use-path-params';
|
|
11
|
-
import { JsonEditor } from './json-editor';
|
|
12
|
-
export const EndpointCall = ({ endpoint, onClose }) => {
|
|
9
|
+
export const EndpointCall = ({ endpoint }) => {
|
|
13
10
|
const shouldHaveBody = ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase());
|
|
14
11
|
const [isRequestLoading, setIsRequestLoading] = useState(false);
|
|
15
12
|
const [responseCode, setResponseCode] = useState(undefined);
|
|
16
13
|
const [responseBody, setResponseBody] = useState(undefined);
|
|
17
14
|
const [executionTime, setExecutionTime] = useState(undefined);
|
|
18
|
-
const
|
|
15
|
+
const [body, setBody] = useState(undefined);
|
|
19
16
|
const [isBodyValid, setIsBodyValid] = useState(true);
|
|
20
|
-
const
|
|
21
|
-
const [
|
|
22
|
-
const [queryParamsValues, setQueryParamsValues] = useState(endpoint.queryParams?.reduce((acc, param) => ({ ...acc, [param.name]: '' }), {}) ?? {});
|
|
17
|
+
const [pathParamsValues, setPathParamsValues] = useState({});
|
|
18
|
+
const [queryParamsValues, setQueryParamsValues] = useState({});
|
|
23
19
|
const isPlayEnabled = useMemo(() => {
|
|
24
|
-
if (!pathParams)
|
|
25
|
-
return true;
|
|
26
20
|
if (shouldHaveBody && !isBodyValid)
|
|
27
21
|
return false;
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
setPathParamsValues((prev) => ({ ...prev, [param]: value }));
|
|
32
|
-
};
|
|
33
|
-
const onQueryParamChange = (param, value) => {
|
|
34
|
-
setQueryParamsValues((prev) => ({ ...prev, [param]: value }));
|
|
35
|
-
};
|
|
22
|
+
return (Object.values(pathParamsValues).every((value) => value) &&
|
|
23
|
+
Object.values(queryParamsValues).every((value) => value));
|
|
24
|
+
}, [pathParamsValues, shouldHaveBody, isBodyValid, queryParamsValues]);
|
|
36
25
|
useEffect(() => {
|
|
37
26
|
if (endpoint.id) {
|
|
38
27
|
setResponseCode(undefined);
|
|
@@ -45,8 +34,8 @@ export const EndpointCall = ({ endpoint, onClose }) => {
|
|
|
45
34
|
setIsRequestLoading(true);
|
|
46
35
|
const startTime = Date.now();
|
|
47
36
|
const path = new URL(window.location.origin +
|
|
48
|
-
|
|
49
|
-
return acc.replace(`:${param}`,
|
|
37
|
+
Object.entries(pathParamsValues).reduce((acc, [param, value]) => {
|
|
38
|
+
return acc.replace(`:${param}`, value);
|
|
50
39
|
}, endpoint.path));
|
|
51
40
|
for (const [key, value] of Object.entries(queryParamsValues)) {
|
|
52
41
|
path.searchParams.set(key, value);
|
|
@@ -64,13 +53,5 @@ export const EndpointCall = ({ endpoint, onClose }) => {
|
|
|
64
53
|
setExecutionTime(executionTime);
|
|
65
54
|
setIsRequestLoading(false);
|
|
66
55
|
};
|
|
67
|
-
return (_jsxs(
|
|
68
|
-
{
|
|
69
|
-
icon: _jsx(X, { className: "cursor-pointer w-4 h-4", onClick: onClose }),
|
|
70
|
-
onClick: onClose,
|
|
71
|
-
},
|
|
72
|
-
], children: [endpoint.description && (_jsx("div", { className: "rounded-lg border p-4 font-medium text-muted-foreground", children: endpoint.description })), !!pathParams.length && (_jsx(Panel, { title: "Path params", size: "sm", children: _jsx("table", { children: pathParams.map((param) => (_jsxs("tr", { children: [_jsx("td", { className: "flex flex-col font-bold leading-[36px]", children: param }), _jsx("td", { className: "w-2/3 pl-4", children: _jsx(Input, { className: "w-full", value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) }) })] }, param))) }) })), !!endpoint.queryParams?.length && (_jsx(Panel, { title: "Query params", size: "sm", children: _jsx("table", { children: endpoint.queryParams.map((param) => (_jsxs("tr", { children: [_jsxs("td", { className: "flex flex-col justify-start", children: [_jsx("span", { className: "font-bold", children: param.name }), _jsx("span", { className: "text-md text-muted-foreground", children: param.description })] }), _jsx("td", { className: "w-2/3 pl-4 align-top", children: _jsx(Input, { className: "w-full", value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) }) })] }, param.name))) }) })), shouldHaveBody && (_jsx(Panel, { title: "Body", size: "sm", contentClassName: "p-0", "data-testid": "endpoint-body-panel", children: _jsx(JsonEditor, { value: body, schema: endpoint.bodySchema, onChange: setBody, onValidate: setIsBodyValid }) })), _jsxs(Button, { className: "w-fit", onClick: handleRequest, variant: "accent", "data-testid": "endpoint-play-button", disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), _jsx(EndpointResponse, { responseCode: responseCode, responseBody: responseBody, executionTime: executionTime }), _jsx(EndpointResponseSchema, { items: Object.entries(endpoint?.responseSchema ?? {}).map(([status, schema]) => ({
|
|
73
|
-
responseCode: status,
|
|
74
|
-
bodySchema: schema,
|
|
75
|
-
})) })] }));
|
|
56
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx(EndpointPathParamsPanel, { endpoint: endpoint, onChange: setPathParamsValues }), _jsx(EndpointQueryParamsPanel, { endpoint: endpoint, onChange: setQueryParamsValues }), _jsx(EndpointBodyPanel, { endpoint: endpoint, onChange: setBody, onValidate: setIsBodyValid }), _jsxs(Button, { className: "w-fit", onClick: handleRequest, variant: "accent", "data-testid": "endpoint-play-button", disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), _jsx(EndpointResponse, { responseCode: responseCode, responseBody: responseBody, executionTime: executionTime })] }));
|
|
76
57
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { EndpointResponseSchema } from './endpoint-response-schema';
|
|
3
|
+
import { EndpointPathParamsPanel } from './endpoint-path-params-panel';
|
|
4
|
+
import { EndpointQueryParamsPanel } from './endpoint-query-params-panel';
|
|
5
|
+
import { EndpointBodyPanel } from './endpoint-body-panel';
|
|
6
|
+
export const EndpointDescription = ({ endpoint }) => {
|
|
7
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx(EndpointPathParamsPanel, { endpoint: endpoint }), _jsx(EndpointQueryParamsPanel, { endpoint: endpoint }), _jsx(EndpointBodyPanel, { endpoint: endpoint }), _jsx(EndpointResponseSchema, { items: Object.entries(endpoint?.responseSchema ?? {}).map(([status, schema]) => ({
|
|
8
|
+
responseCode: status,
|
|
9
|
+
bodySchema: schema,
|
|
10
|
+
})) })] }));
|
|
11
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Input, Panel } from '@motiadev/ui';
|
|
3
|
+
import { Fragment, useMemo, useState } from 'react';
|
|
4
|
+
import { usePathParams } from './hooks/use-path-params';
|
|
5
|
+
export const EndpointPathParamsPanel = ({ endpoint, onChange }) => {
|
|
6
|
+
const pathParams = usePathParams(endpoint.path);
|
|
7
|
+
const [pathParamsValues, setPathParamsValues] = useState(pathParams?.reduce((acc, param) => ({ ...acc, [param]: '' }), {}));
|
|
8
|
+
const onPathParamChange = (param, value) => {
|
|
9
|
+
const newPathParamsValues = { ...pathParamsValues, [param]: value };
|
|
10
|
+
setPathParamsValues(newPathParamsValues);
|
|
11
|
+
onChange?.(newPathParamsValues);
|
|
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
|
+
if (!pathParams.length) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return (_jsx(Panel, { title: "Path params", subtitle: subtitle, size: "sm", variant: "outlined", 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) })) : (_jsx("span", { className: "text-xs", children: `:${param}` })) })] }, param))) }) }));
|
|
28
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Input } from '@motiadev/ui';
|
|
3
|
+
import { Panel } from '@motiadev/ui';
|
|
4
|
+
import { Fragment, useState } from 'react';
|
|
5
|
+
export const EndpointQueryParamsPanel = ({ endpoint, onChange }) => {
|
|
6
|
+
const [queryParamsValues, setQueryParamsValues] = useState(endpoint.queryParams?.reduce((acc, param) => ({ ...acc, [param.name]: '' }), {}) ?? {});
|
|
7
|
+
const onQueryParamChange = (param, value) => {
|
|
8
|
+
const newQueryParamsValues = { ...queryParamsValues, [param]: value };
|
|
9
|
+
setQueryParamsValues(newQueryParamsValues);
|
|
10
|
+
onChange?.(newQueryParamsValues);
|
|
11
|
+
};
|
|
12
|
+
if (!endpoint.queryParams?.length) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return (_jsx(Panel, { title: "Query params", size: "sm", variant: "outlined", children: _jsx("div", { className: "grid gap-3", style: { gridTemplateColumns: '1fr 2fr', gridTemplateRows: '1fr 1fr' }, children: endpoint.queryParams.map((param) => (_jsxs(Fragment, { children: [_jsx("div", { className: "font-bold leading-[36px] flex text-xs", children: param.name }), _jsx("div", { className: "flex items-center text-xs ", children: onChange ? (_jsx(Input, { className: "text-xs", placeholder: param.description, value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) })) : (_jsx("span", { children: param.description })) })] }, param.name))) }) }));
|
|
16
|
+
};
|
|
@@ -13,5 +13,5 @@ export const EndpointResponseSchema = ({ items }) => {
|
|
|
13
13
|
if (items.length === 0) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
|
-
return (_jsx("div", { className: "flex flex-col rounded-lg border",
|
|
16
|
+
return (_jsx("div", { className: "flex flex-col rounded-lg border", children: _jsxs(Tabs, { defaultValue: items[0].responseCode, children: [_jsx("div", { className: "flex items-center justify-between bg-card", children: _jsx(TabsList, { className: "bg-transparent p-0", children: items.map((item) => (_jsx(TabsTrigger, { value: item.responseCode, className: "text-xs font-bold cursor-pointer", children: item.responseCode }, item.responseCode))) }) }), items.map((props) => (_jsx(EndpointResponseSchemaItem, { ...props }, props.responseCode)))] }) }));
|
|
17
17
|
};
|
|
@@ -35,18 +35,16 @@ export const EndpointResponse = ({ responseCode, executionTime, responseBody })
|
|
|
35
35
|
if (!responseBody || !responseCode) {
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
|
-
return (_jsx(Panel, { tabs: isStreamed
|
|
39
|
-
?
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
],
|
|
50
|
-
}
|
|
38
|
+
return (_jsx(Panel, { "data-testid": 'endpoint-response-container', tabs: isStreamed
|
|
39
|
+
? [
|
|
40
|
+
{
|
|
41
|
+
label: 'Streamed Response',
|
|
42
|
+
content: (_jsx(ReactJson, { src: data, dark: theme === 'dark', style: { backgroundColor: 'transparent' } })),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: 'Original',
|
|
46
|
+
content: (_jsx(ReactJson, { src: originalData, dark: theme === 'dark', style: { backgroundColor: 'transparent' } })),
|
|
47
|
+
},
|
|
48
|
+
]
|
|
51
49
|
: undefined, title: _jsxs("div", { className: "flex flex-row justify-between items-center flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [isError && _jsx(XCircle, { className: "text-red-500 w-4 h-4" }), _jsxs("span", { className: "font-bold text-xs", children: [responseCode, " - ", statusMessage] })] }), !!executionTime && (_jsxs("span", { className: "text-xs text-muted-foreground justify-self-end", children: ["Execution time: ", _jsxs("span", { className: "font-bold", children: [executionTime, "ms"] })] }))] }), subtitle: isStreamed && (_jsxs("span", { className: "col-span-2 flex flex-row items-center font-medium text-card-foreground text-xs mt-2", children: [_jsxs("span", { className: "inline-block w-2 h-2 rounded-full bg-green-500 mr-2 relative", children: [_jsx("span", { className: "absolute inset-0 rounded-full bg-green-500 animate-[ping_1.5s_ease-in-out_infinite]" }), _jsx("span", { className: "absolute inset-0 rounded-full bg-green-500" })] }), "Object is being streamed, this is not the actual response from the API Endpoint"] })), children: !isStreamed && (_jsx(ReactJson, { src: data, dark: theme === 'dark', style: { backgroundColor: 'transparent' } })) }));
|
|
52
50
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Sidebar } from '@/components/sidebar/sidebar';
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import { EndpointBadge } from './endpoint-badge';
|
|
5
|
+
import { EndpointCall } from './endpoint-call';
|
|
6
|
+
import { EndpointDescription } from './endpoint-description';
|
|
7
|
+
export const EndpointSidePanel = ({ endpoint, onClose }) => {
|
|
8
|
+
return (_jsx(Sidebar, { initialWidth: 600, subtitle: endpoint.description, title: _jsxs("div", { className: "flex flex-row gap-2 items-center", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-bold", children: endpoint.path })] }), onClose: onClose, tabs: [
|
|
9
|
+
{
|
|
10
|
+
label: 'Details',
|
|
11
|
+
content: _jsx(EndpointDescription, { endpoint: endpoint }),
|
|
12
|
+
'data-testid': 'endpoint-details-tab',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
label: 'Call',
|
|
16
|
+
content: _jsx(EndpointCall, { endpoint: endpoint }),
|
|
17
|
+
'data-testid': 'endpoint-call-tab',
|
|
18
|
+
},
|
|
19
|
+
], actions: [
|
|
20
|
+
{
|
|
21
|
+
icon: _jsx(X, { className: "cursor-pointer w-4 h-4", onClick: onClose }),
|
|
22
|
+
onClick: onClose,
|
|
23
|
+
},
|
|
24
|
+
] }));
|
|
25
|
+
};
|
|
@@ -3,12 +3,12 @@ import { cn } from '@/lib/utils';
|
|
|
3
3
|
import { useGlobalStore } from '@/stores/use-global-store';
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
5
|
import { EndpointBadge } from './endpoint-badge';
|
|
6
|
-
import {
|
|
6
|
+
import { EndpointSidePanel } from './endpoint-side-panel';
|
|
7
7
|
import { useGetEndpoints } from './hooks/use-get-endpoints';
|
|
8
8
|
export const EndpointsPage = () => {
|
|
9
9
|
const endpoints = useGetEndpoints();
|
|
10
10
|
const selectedEndpointId = useGlobalStore((state) => state.selectedEndpointId);
|
|
11
11
|
const selectEndpointId = useGlobalStore((state) => state.selectEndpointId);
|
|
12
12
|
const selectedEndpoint = useMemo(() => selectedEndpointId && endpoints.find((endpoint) => endpoint.id === selectedEndpointId), [endpoints, selectedEndpointId]);
|
|
13
|
-
return (_jsxs("div", { className: "flex flex-row w-full h-full", children: [_jsx("div", { className: "flex flex-col flex-1 overflow-y-auto", children: endpoints.map((endpoint) => (_jsx("div", { "data-testid": `endpoint-${endpoint.method}-${endpoint.path}`, className: cn(selectedEndpoint === endpoint && 'bg-muted-foreground/10', 'cursor-pointer select-none'), onClick: () => selectEndpointId(endpoint.id), children: _jsxs("div", { className: "flex flex-row gap-2 items-center hover:bg-muted-foreground/10 p-2", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-mono font-bold whitespace-nowrap", children: endpoint.path }), _jsx("span", { className: "text-md text-muted-foreground truncate", children: endpoint.description })] }) }, `${endpoint.method} ${endpoint.path}`))) }), selectedEndpoint && _jsx(
|
|
13
|
+
return (_jsxs("div", { className: "flex flex-row w-full h-full", children: [_jsx("div", { className: "flex flex-col flex-1 overflow-y-auto", children: endpoints.map((endpoint) => (_jsx("div", { "data-testid": `endpoint-${endpoint.method}-${endpoint.path}`, className: cn(selectedEndpoint === endpoint && 'bg-muted-foreground/10', 'cursor-pointer select-none'), onClick: () => selectEndpointId(endpoint.id), children: _jsxs("div", { className: "flex flex-row gap-2 items-center hover:bg-muted-foreground/10 p-2", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-mono font-bold whitespace-nowrap", children: endpoint.path }), _jsx("span", { className: "text-md text-muted-foreground truncate", children: endpoint.description })] }) }, `${endpoint.method} ${endpoint.path}`))) }), selectedEndpoint && (_jsx(EndpointSidePanel, { endpoint: selectedEndpoint, onClose: () => selectEndpointId(undefined) }))] }));
|
|
14
14
|
};
|
|
@@ -14,7 +14,7 @@ export const convertJsonSchemaToJson = (schema) => {
|
|
|
14
14
|
case 'array':
|
|
15
15
|
return [convertJsonSchemaToJson(schema.items)];
|
|
16
16
|
case 'string':
|
|
17
|
-
return schema.description ?? 'string';
|
|
17
|
+
return schema.enum?.[0] ?? schema.description ?? 'string';
|
|
18
18
|
case 'number':
|
|
19
19
|
return schema.description ?? 0;
|
|
20
20
|
case 'integer':
|
|
@@ -3,7 +3,7 @@ type JsonEditorProps = {
|
|
|
3
3
|
value: string;
|
|
4
4
|
schema: Record<string, unknown> | undefined;
|
|
5
5
|
onChange: (value: string) => void;
|
|
6
|
-
onValidate
|
|
6
|
+
onValidate?: (isValid: boolean) => void;
|
|
7
7
|
};
|
|
8
8
|
export declare const JsonEditor: FC<JsonEditorProps>;
|
|
9
9
|
export {};
|
|
@@ -22,8 +22,8 @@ export const JsonEditor = ({ value, schema, onChange, onValidate }) => {
|
|
|
22
22
|
}, [monaco, schema]);
|
|
23
23
|
return (_jsx(Editor, { "data-testid": "json-editor", height: "200px", language: "json", value: value, theme: editorTheme, onChange: (value) => {
|
|
24
24
|
if (!value) {
|
|
25
|
-
onValidate(false);
|
|
25
|
+
onValidate?.(false);
|
|
26
26
|
}
|
|
27
27
|
onChange(value ?? '');
|
|
28
|
-
}, onValidate: (markers) => onValidate(markers.length === 0), options: { lineNumbers: 'off', minimap: { enabled: false } } }));
|
|
28
|
+
}, onValidate: (markers) => onValidate?.(markers.length === 0), options: { lineNumbers: 'off', minimap: { enabled: false } } }));
|
|
29
29
|
};
|
|
@@ -6,7 +6,7 @@ import { useLogsStore } from '@/stores/use-logs-store';
|
|
|
6
6
|
import { useMemo, useState } from 'react';
|
|
7
7
|
import { LogDetail } from './log-detail';
|
|
8
8
|
import { LogLevelDot } from './log-level-dot';
|
|
9
|
-
import { Button, Input } from '@motiadev/ui';
|
|
9
|
+
import { Button, cn, Input } from '@motiadev/ui';
|
|
10
10
|
import { CircleX, Trash } from 'lucide-react';
|
|
11
11
|
export const LogsPage = () => {
|
|
12
12
|
const logs = useLogsStore((state) => state.logs);
|
|
@@ -22,5 +22,8 @@ export const LogsPage = () => {
|
|
|
22
22
|
log.step.toLowerCase().includes(search.toLowerCase()));
|
|
23
23
|
});
|
|
24
24
|
}, [logs, search]);
|
|
25
|
-
return (_jsxs("div", { className: "h-full flex flex-row", children: [_jsxs("div", { className: "flex-1 overflow-y-auto overflow-x-auto", children: [_jsxs("div", { className: "flex p-2 border-b gap-4", children: [_jsxs("div", { className: "flex-1 relative", children: [_jsx(Input, { variant: "shade", value: search, onChange: (e) => setSearch(e.target.value), className: "pr-10 font-medium" }), _jsx(CircleX, { className: "cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground", onClick: () => setSearch('') })] }), _jsxs(Button, { variant: "outline", onClick: resetLogs, children: [_jsx(Trash, {}), " Clear"] })] }), _jsx(Table, { children: _jsx(TableBody, { className: "font-mono font-medium", children: filteredLogs.map((log, index) => (_jsxs(TableRow, { className:
|
|
25
|
+
return (_jsxs("div", { className: "h-full flex flex-row", children: [_jsxs("div", { className: "flex-1 overflow-y-auto overflow-x-auto", children: [_jsxs("div", { className: "flex p-2 border-b gap-4", children: [_jsxs("div", { className: "flex-1 relative", children: [_jsx(Input, { variant: "shade", value: search, onChange: (e) => setSearch(e.target.value), className: "pr-10 font-medium" }), _jsx(CircleX, { className: "cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground", onClick: () => setSearch('') })] }), _jsxs(Button, { variant: "outline", onClick: resetLogs, children: [_jsx(Trash, {}), " Clear"] })] }), _jsx(Table, { children: _jsx(TableBody, { className: "font-mono font-medium", children: filteredLogs.map((log, index) => (_jsxs(TableRow, { className: cn('font-mono font-semibold cursor-pointer border-0', {
|
|
26
|
+
'bg-muted-foreground/10 hover:bg-muted-foreground/20': selectedLogId === log.id,
|
|
27
|
+
'hover:bg-muted-foreground/10': selectedLogId !== log.id,
|
|
28
|
+
}), onClick: () => selectLogId(log.id), children: [_jsxs(TableCell, { "data-testid": `time-${index}`, className: "whitespace-nowrap flex items-center gap-2 text-muted-foreground", children: [_jsx(LogLevelDot, { level: log.level }), formatTimestamp(log.time)] }), _jsx(TableCell, { "data-testid": `trace-${log.traceId}`, className: "whitespace-nowrap cursor-pointer hover:text-primary text-muted-foreground", onClick: () => setSearch(log.traceId), children: log.traceId }), _jsx(TableCell, { "data-testid": `step-${index}`, "aria-label": log.step, className: "whitespace-nowrap", children: log.step }), _jsx(TableCell, { "data-testid": `msg-${index}`, "aria-label": log.msg, className: "whitespace-nowrap max-w-[500px] truncate w-full", children: log.msg })] }, index))) }) })] }), _jsx(LogDetail, { log: selectedLog, onClose: () => selectLogId(undefined) })] }));
|
|
26
29
|
};
|
|
@@ -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(StateDetail, { 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', selectedItem === item
|
|
15
|
+
return (_jsxs("div", { className: "flex flex-row gap-4 h-full", children: [selectedItem && _jsx(StateDetail, { 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
|
};
|