@motiadev/workbench 0.3.1-beta.87 → 0.3.1-beta.88-406962
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/index.d.ts +3 -4
- package/dist/index.html +3 -2
- package/dist/index.js +2 -3
- package/dist/public/icon.png +0 -0
- package/dist/public/motia-dark.png +0 -0
- package/dist/public/motia-light.png +0 -0
- package/dist/src/App.d.ts +2 -0
- package/dist/src/App.js +19 -0
- package/dist/src/components/endpoints/endpoint-badge.d.ts +1 -1
- package/dist/src/components/endpoints/endpoint-badge.js +8 -8
- package/dist/src/components/endpoints/endpoint-call.d.ts +3 -3
- package/dist/src/components/endpoints/endpoint-call.js +28 -23
- package/dist/src/components/endpoints/endpoint-response-schema.d.ts +10 -0
- package/dist/src/components/endpoints/endpoint-response-schema.js +17 -0
- package/dist/src/components/endpoints/endpoint-response.d.ts +8 -0
- package/dist/src/components/endpoints/endpoint-response.js +39 -0
- package/dist/src/components/endpoints/endpoints-page.js +14 -0
- package/dist/src/components/endpoints/hooks/use-get-endpoints.d.ts +1 -15
- package/dist/src/components/endpoints/json-editor.d.ts +9 -0
- package/dist/src/components/endpoints/json-editor.js +31 -0
- package/dist/src/components/flow/flow-page.d.ts +1 -0
- package/dist/src/components/flow/flow-page.js +20 -0
- package/dist/src/components/flow/flow-tab-menu-item.d.ts +1 -0
- package/dist/src/components/flow/flow-tab-menu-item.js +15 -0
- package/dist/src/{views → components}/flow/flow-view.d.ts +1 -2
- package/dist/src/components/flow/flow-view.js +21 -0
- package/dist/src/components/flow/hooks/use-get-flow-state.d.ts +10 -0
- package/dist/src/{views → components}/flow/hooks/use-get-flow-state.js +17 -10
- package/dist/src/{views → components}/flow/hooks/use-organize-nodes.d.ts +1 -1
- package/dist/src/{views → components}/flow/hooks/use-save-workflow-config.d.ts +1 -1
- package/dist/src/components/flow/nodes/api-flow-node.d.ts +2 -0
- package/dist/src/{views → components}/flow/nodes/api-flow-node.js +1 -1
- package/dist/src/components/flow/nodes/cron-flow-node.d.ts +2 -0
- package/dist/src/components/flow/nodes/cron-flow-node.js +5 -0
- package/dist/src/{views → components}/flow/nodes/event-flow-node.d.ts +1 -1
- package/dist/src/components/flow/nodes/event-flow-node.js +5 -0
- package/dist/src/components/flow/nodes/noop-flow-node.d.ts +2 -0
- package/dist/src/{views → components}/flow/nodes/noop-flow-node.js +1 -1
- package/dist/src/components/header/header.js +3 -11
- package/dist/src/components/logs/log-detail.d.ts +3 -1
- package/dist/src/components/logs/log-detail.js +33 -11
- package/dist/src/components/logs/log-level-badge.js +6 -6
- package/dist/src/components/logs/log-level-dot.js +7 -7
- package/dist/src/components/logs/logs-page.js +26 -0
- package/dist/src/components/observability/events/code/function-call.js +1 -1
- package/dist/src/components/observability/events/trace-event.d.ts +1 -1
- package/dist/src/components/observability/events/trace-log-event.d.ts +1 -1
- package/dist/src/components/observability/events/trace-log-event.js +2 -2
- package/dist/src/components/observability/hooks/use-get-endtime.d.ts +1 -1
- package/dist/src/components/observability/trace-item/trace-item-detail.d.ts +1 -0
- package/dist/src/components/observability/trace-item/trace-item-detail.js +5 -4
- package/dist/src/components/observability/trace-item/trace-item.d.ts +1 -0
- package/dist/src/components/observability/trace-item/trace-item.js +11 -17
- package/dist/src/components/observability/trace-status.d.ts +6 -10
- package/dist/src/components/observability/trace-status.js +7 -32
- package/dist/src/components/observability/trace-timeline.js +16 -4
- package/dist/src/components/observability/traces-groups.d.ts +2 -2
- package/dist/src/components/observability/traces-groups.js +4 -4
- package/dist/src/components/observability/traces-page.js +12 -0
- package/dist/src/components/sidebar/sidebar.d.ts +8 -0
- package/dist/src/components/sidebar/sidebar.js +39 -0
- package/dist/src/components/states/hooks/states-hooks.d.ts +7 -3
- package/dist/src/components/states/hooks/states-hooks.js +5 -33
- package/dist/src/components/states/state-detail.d.ts +2 -1
- package/dist/src/components/states/state-detail.js +15 -9
- package/dist/src/components/states/state-value.js +7 -7
- package/dist/src/components/states/states-page.js +18 -0
- package/dist/src/components/ui/badge.d.ts +1 -1
- package/dist/src/components/ui/table.js +1 -1
- package/dist/src/components/ui/theme-toggle.js +4 -3
- package/dist/src/hooks/use-fetch-flows.d.ts +1 -5
- package/dist/src/hooks/use-fetch-flows.js +18 -13
- package/dist/src/hooks/use-log-listener.js +2 -2
- package/dist/src/hooks/use-update-handle-positions.d.ts +10 -0
- package/dist/src/hooks/use-update-handle-positions.js +35 -0
- package/dist/src/index.css +36 -176
- package/dist/src/lib/utils.d.ts +1 -0
- package/dist/src/lib/utils.js +4 -0
- package/dist/src/main.d.ts +1 -0
- package/dist/src/main.js +4 -11
- package/dist/src/publicComponents/api-node.js +2 -4
- package/dist/src/publicComponents/{base-handle.d.ts → base-node/base-handle.d.ts} +1 -0
- package/dist/src/publicComponents/base-node/base-handle.js +8 -0
- package/dist/src/publicComponents/base-node/base-node.d.ts +21 -0
- package/dist/src/publicComponents/base-node/base-node.js +13 -0
- package/dist/src/publicComponents/{emits.d.ts → base-node/emits.d.ts} +1 -1
- package/dist/src/publicComponents/base-node/emits.js +5 -0
- package/dist/src/{views/flow/nodes → publicComponents/base-node}/language-indicator.d.ts +1 -1
- package/dist/src/{views/flow/nodes → publicComponents/base-node}/language-indicator.js +8 -7
- package/dist/src/publicComponents/base-node/node-header.d.ts +12 -0
- package/dist/src/publicComponents/base-node/node-header.js +30 -0
- package/dist/src/publicComponents/base-node/node-sidebar.d.ts +18 -0
- package/dist/src/publicComponents/base-node/node-sidebar.js +20 -0
- package/dist/src/publicComponents/base-node/subscribe.js +4 -0
- package/dist/src/publicComponents/cron-node.d.ts +2 -1
- package/dist/src/publicComponents/cron-node.js +3 -4
- package/dist/src/publicComponents/event-node.d.ts +1 -5
- package/dist/src/publicComponents/event-node.js +4 -6
- package/dist/src/publicComponents/node-details.js +21 -12
- package/dist/src/publicComponents/node-props.d.ts +13 -6
- package/dist/src/publicComponents/noop-node.d.ts +3 -7
- package/dist/src/publicComponents/noop-node.js +3 -3
- package/dist/src/stores/use-flow-store.d.ts +18 -0
- package/dist/src/stores/use-flow-store.js +15 -0
- package/dist/src/stores/use-global-store.d.ts +24 -0
- package/dist/src/stores/use-global-store.js +20 -0
- package/dist/src/stores/{use-logs.d.ts → use-logs-store.d.ts} +1 -3
- package/dist/src/stores/use-logs-store.js +10 -0
- package/dist/src/stores/use-tabs-store.d.ts +17 -0
- package/dist/src/stores/use-tabs-store.js +13 -0
- package/dist/src/stores/use-theme-store.d.ts +17 -0
- package/dist/src/stores/use-theme-store.js +26 -0
- package/dist/src/types/endpoint.d.ts +14 -0
- package/dist/src/{views/flow/nodes/nodes.types.d.ts → types/flow.d.ts} +45 -0
- package/dist/src/types/flow.js +1 -0
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/package.json +28 -26
- package/dist/public/.empty +0 -0
- package/dist/src/components/app-sidebar.d.ts +0 -1
- package/dist/src/components/app-sidebar.js +0 -12
- package/dist/src/components/endpoints/endpoints.d.ts +0 -1
- package/dist/src/components/endpoints/endpoints.js +0 -34
- package/dist/src/components/endpoints/selected-endpoint.d.ts +0 -7
- package/dist/src/components/endpoints/selected-endpoint.js +0 -7
- package/dist/src/components/logs/log-console.d.ts +0 -1
- package/dist/src/components/logs/log-console.js +0 -69
- package/dist/src/components/logs/log-field.d.ts +0 -7
- package/dist/src/components/logs/log-field.js +0 -20
- package/dist/src/components/logs/logs.d.ts +0 -1
- package/dist/src/components/logs/logs.js +0 -18
- package/dist/src/components/observability/observability-stats.d.ts +0 -5
- package/dist/src/components/observability/observability-stats.js +0 -17
- package/dist/src/components/states/states.d.ts +0 -1
- package/dist/src/components/states/states.js +0 -21
- package/dist/src/components/ui/BadgeCount.d.ts +0 -7
- package/dist/src/components/ui/BadgeCount.js +0 -13
- package/dist/src/components/ui/button.d.ts +0 -11
- package/dist/src/components/ui/button.js +0 -33
- package/dist/src/components/ui/card.d.ts +0 -8
- package/dist/src/components/ui/card.js +0 -16
- package/dist/src/components/ui/collapsible.d.ts +0 -5
- package/dist/src/components/ui/collapsible.js +0 -5
- package/dist/src/components/ui/dialog.d.ts +0 -19
- package/dist/src/components/ui/dialog.js +0 -22
- package/dist/src/components/ui/dropdown-menu.d.ts +0 -25
- package/dist/src/components/ui/dropdown-menu.js +0 -50
- package/dist/src/components/ui/input.d.ts +0 -3
- package/dist/src/components/ui/input.js +0 -8
- package/dist/src/components/ui/logo-icon.d.ts +0 -5
- package/dist/src/components/ui/logo-icon.js +0 -5
- package/dist/src/components/ui/navigation-menu.d.ts +0 -13
- package/dist/src/components/ui/navigation-menu.js +0 -30
- package/dist/src/components/ui/scroll-area.d.ts +0 -5
- package/dist/src/components/ui/scroll-area.js +0 -9
- package/dist/src/components/ui/select.d.ts +0 -13
- package/dist/src/components/ui/select.js +0 -25
- package/dist/src/components/ui/separator.d.ts +0 -4
- package/dist/src/components/ui/separator.js +0 -8
- package/dist/src/components/ui/sheet.d.ts +0 -25
- package/dist/src/components/ui/sheet.js +0 -36
- package/dist/src/components/ui/sidebar.d.ts +0 -12
- package/dist/src/components/ui/sidebar.js +0 -25
- package/dist/src/components/ui/skeleton.d.ts +0 -3
- package/dist/src/components/ui/skeleton.js +0 -6
- package/dist/src/components/ui/switch.d.ts +0 -4
- package/dist/src/components/ui/switch.js +0 -7
- package/dist/src/components/ui/tabs.d.ts +0 -7
- package/dist/src/components/ui/tabs.js +0 -12
- package/dist/src/components/ui/textarea.d.ts +0 -3
- package/dist/src/components/ui/textarea.js +0 -8
- package/dist/src/components/ui/tooltip.d.ts +0 -7
- package/dist/src/components/ui/tooltip.js +0 -11
- package/dist/src/hooks/use-list-flows.d.ts +0 -9
- package/dist/src/hooks/use-list-flows.js +0 -8
- package/dist/src/hooks/use-theme.d.ts +0 -6
- package/dist/src/hooks/use-theme.js +0 -28
- package/dist/src/publicComponents/base-handle.js +0 -10
- package/dist/src/publicComponents/base-node.d.ts +0 -16
- package/dist/src/publicComponents/base-node.js +0 -25
- package/dist/src/publicComponents/colorMap.d.ts +0 -6
- package/dist/src/publicComponents/colorMap.js +0 -6
- package/dist/src/publicComponents/components/header-bar.d.ts +0 -11
- package/dist/src/publicComponents/components/header-bar.js +0 -15
- package/dist/src/publicComponents/emits.js +0 -6
- package/dist/src/publicComponents/subscribe.js +0 -5
- package/dist/src/route-wrapper.d.ts +0 -2
- package/dist/src/route-wrapper.js +0 -5
- package/dist/src/routes/endpoints-page.js +0 -5
- package/dist/src/routes/flow.d.ts +0 -1
- package/dist/src/routes/flow.js +0 -11
- package/dist/src/routes/index.d.ts +0 -1
- package/dist/src/routes/index.js +0 -5
- package/dist/src/routes/logs-page.js +0 -12
- package/dist/src/routes/states-page.js +0 -5
- package/dist/src/routes/traces-page.js +0 -14
- package/dist/src/stores/use-logs.js +0 -52
- package/dist/src/views/flow/arrow-head.d.ts +0 -8
- package/dist/src/views/flow/arrow-head.js +0 -6
- package/dist/src/views/flow/flow-view.js +0 -48
- package/dist/src/views/flow/hooks/use-get-flow-state.d.ts +0 -52
- package/dist/src/views/flow/legend.d.ts +0 -4
- package/dist/src/views/flow/legend.js +0 -51
- package/dist/src/views/flow/nodes/api-flow-node.d.ts +0 -4
- package/dist/src/views/flow/nodes/event-flow-node.js +0 -5
- package/dist/src/views/flow/nodes/noop-flow-node.d.ts +0 -4
- /package/dist/src/{routes → components/endpoints}/endpoints-page.d.ts +0 -0
- /package/dist/src/{views → components}/flow/base-edge.d.ts +0 -0
- /package/dist/src/{views → components}/flow/base-edge.js +0 -0
- /package/dist/src/{views → components}/flow/flow-loader.d.ts +0 -0
- /package/dist/src/{views → components}/flow/flow-loader.js +0 -0
- /package/dist/src/{views → components}/flow/hooks/use-organize-nodes.js +0 -0
- /package/dist/src/{views → components}/flow/hooks/use-save-workflow-config.js +0 -0
- /package/dist/src/{views → components}/flow/node-organizer.d.ts +0 -0
- /package/dist/src/{views → components}/flow/node-organizer.js +0 -0
- /package/dist/src/{routes → components/logs}/logs-page.d.ts +0 -0
- /package/dist/src/{routes → components/observability}/traces-page.d.ts +0 -0
- /package/dist/src/{routes → components/states}/states-page.d.ts +0 -0
- /package/dist/src/publicComponents/{subscribe.d.ts → base-node/subscribe.d.ts} +0 -0
- /package/dist/src/{views/flow/nodes/nodes.types.js → types/endpoint.js} +0 -0
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { BadgeCount } from '../ui/BadgeCount';
|
|
4
|
-
import { LogoIcon } from '../ui/logo-icon';
|
|
5
|
-
import { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, } from '../ui/navigation-menu';
|
|
2
|
+
import { useThemeStore } from '@/stores/use-theme-store';
|
|
6
3
|
import { ThemeToggle } from '../ui/theme-toggle';
|
|
7
|
-
import { useListFlows } from '@/hooks/use-list-flows';
|
|
8
|
-
import { NavigationMenuSub } from '@radix-ui/react-navigation-menu';
|
|
9
|
-
import { Workflow } from 'lucide-react';
|
|
10
4
|
export const Header = () => {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const isActive = (flowId) => pathname.includes(`/flow/${flowId}`);
|
|
14
|
-
return (_jsxs("header", { className: "min-h-16 px-4 gap-1 flex items-center bg-header text-header-foreground border-b border-header-border", children: [_jsx(LogoIcon, { className: "h-8 w-8 mr-16" }), _jsx(NavigationMenu, { children: _jsxs(NavigationMenuList, { children: [_jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsxs(Link, { "data-testid": "header-logs-link", to: "/logs", className: 'flex flex-row items-center pr-2 relative', children: ["Logs", _jsx(BadgeCount, { className: "absolute top-1 right-0", dotOnly: true })] }) }) }), _jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsx(Link, { "data-testid": "header-traces-link", to: "/traces", children: "Traces" }) }) }), _jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsx(Link, { "data-testid": "header-states-link", to: "/states", children: "States" }) }) }), _jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { asChild: true, children: _jsx(Link, { "data-testid": "header-endpoints-link", to: "/endpoints", children: "Endpoints" }) }) })] }) }), _jsx(NavigationMenu, { children: _jsxs(NavigationMenuList, { children: [_jsxs(NavigationMenuItem, { children: [_jsx(NavigationMenuTrigger, { className: 'cursor-pointer', "data-testid": "header-flows-link", children: "Flows" }), _jsx(NavigationMenuContent, { children: _jsx(NavigationMenuSub, { children: _jsx(NavigationMenuList, { className: "flex flex-col min-w-[200px]", children: flows.map((flow) => (_jsx(NavigationMenuItem, { className: "w-full", asChild: true, children: _jsx(NavigationMenuLink, { asChild: true, active: isActive(flow.id), children: _jsxs(Link, { "data-testid": `flow-${flow.name}-link`, to: `/flow/${flow.id}`, className: 'flex flex-row gap-2 items-center', children: [_jsx(Workflow, { className: "w-4 h-4" }), " ", flow.name] }) }) }, flow.id))) }) }) })] }), _jsx(NavigationMenuIndicator, {})] }) }), _jsx("div", { className: "ml-auto", children: _jsx(ThemeToggle, {}) })] }));
|
|
5
|
+
const theme = useThemeStore((state) => state.theme);
|
|
6
|
+
return (_jsxs("header", { className: "min-h-16 px-4 gap-1 flex items-center bg-default text-default-foreground border-b", children: [_jsx("img", { src: `/motia-${theme}.png`, className: "h-5" }), _jsx("div", { className: "flex-1" }), _jsx(ThemeToggle, {})] }));
|
|
15
7
|
};
|
|
@@ -1,15 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
2
|
+
import { formatTimestamp } from '@/lib/utils';
|
|
3
|
+
import { useMemo, useState } from 'react';
|
|
4
|
+
import ReactJson from 'react18-json-view';
|
|
5
|
+
import 'react18-json-view/src/dark.css';
|
|
6
|
+
import 'react18-json-view/src/style.css';
|
|
7
|
+
import { LogLevelDot } from './log-level-dot';
|
|
8
|
+
import { Sidebar } from '@/components/sidebar/sidebar';
|
|
9
|
+
import { X } from 'lucide-react';
|
|
10
|
+
const defaultProps = ['id', 'msg', 'time', 'level', 'step', 'flows', 'traceId'];
|
|
6
11
|
export const LogDetail = ({ log, onClose }) => {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
if (!
|
|
10
|
-
|
|
12
|
+
const [hasOtherProps, setHasOtherProps] = useState(false);
|
|
13
|
+
const otherPropsObject = useMemo(() => {
|
|
14
|
+
if (!log) {
|
|
15
|
+
return null;
|
|
11
16
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
const otherProps = Object.keys(log ?? {}).filter((key) => !defaultProps.includes(key));
|
|
18
|
+
setHasOtherProps(otherProps.length > 0);
|
|
19
|
+
return otherProps.reduce((acc, key) => {
|
|
20
|
+
acc[key] = log[key];
|
|
21
|
+
return acc;
|
|
22
|
+
}, {});
|
|
23
|
+
}, [log]);
|
|
24
|
+
if (!log) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return (_jsx(Sidebar, { onClose: onClose, title: "Logs Details", subtitle: "Details including custom properties", actions: [{ icon: _jsx(X, {}), onClick: onClose, label: 'Close' }], details: [
|
|
28
|
+
{
|
|
29
|
+
label: 'Level',
|
|
30
|
+
value: (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LogLevelDot, { level: log.level }), _jsx("div", { className: "capitalize", children: log.level })] })),
|
|
31
|
+
},
|
|
32
|
+
{ label: 'Time', value: formatTimestamp(log.time) },
|
|
33
|
+
{ label: 'Step', value: log.step },
|
|
34
|
+
{ label: 'Flows', value: log.flows.join(', ') },
|
|
35
|
+
{ label: 'Trace ID', value: log.traceId },
|
|
36
|
+
], children: hasOtherProps && _jsx(ReactJson, { src: otherPropsObject, theme: "default", enableClipboard: false }) }));
|
|
15
37
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Badge } from '../ui/badge';
|
|
3
|
+
const map = {
|
|
4
|
+
info: 'info',
|
|
5
|
+
error: 'error',
|
|
6
|
+
warn: 'warning',
|
|
7
|
+
debug: 'info',
|
|
8
|
+
};
|
|
3
9
|
export const LogLevelBadge = (props) => {
|
|
4
|
-
const map = {
|
|
5
|
-
info: 'info',
|
|
6
|
-
error: 'error',
|
|
7
|
-
warn: 'warning',
|
|
8
|
-
debug: 'info',
|
|
9
|
-
};
|
|
10
10
|
return (_jsx(Badge, { variant: map[props.level], className: props.className, children: props.level }));
|
|
11
11
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from 'class-variance-authority';
|
|
3
|
-
const badgeVariants = cva('text-xs font-medium tracking-wide
|
|
3
|
+
const badgeVariants = cva('text-xs font-medium tracking-wide rounded-full h-[6px] w-[6px] m-[4px] outline-[2px]', {
|
|
4
4
|
variants: {
|
|
5
5
|
variant: {
|
|
6
|
-
info: 'bg-
|
|
7
|
-
trace: 'bg-sky-500',
|
|
8
|
-
debug: 'bg-sky-500',
|
|
9
|
-
error: 'bg-rose-500',
|
|
10
|
-
fatal: 'bg-rose-500',
|
|
11
|
-
warn: 'bg-amber-500',
|
|
6
|
+
info: 'bg-[#2862FE] outline-[#2862FE]/20',
|
|
7
|
+
trace: 'bg-sky-500 outline-sky-500',
|
|
8
|
+
debug: 'bg-sky-500 outline-sky-500',
|
|
9
|
+
error: 'bg-rose-500 outline-rose-500',
|
|
10
|
+
fatal: 'bg-rose-500 outline-rose-500',
|
|
11
|
+
warn: 'bg-amber-500 outline-amber-500',
|
|
12
12
|
},
|
|
13
13
|
},
|
|
14
14
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
|
|
3
|
+
import { formatTimestamp } from '@/lib/utils';
|
|
4
|
+
import { useGlobalStore } from '@/stores/use-global-store';
|
|
5
|
+
import { useLogsStore } from '@/stores/use-logs-store';
|
|
6
|
+
import { useMemo, useState } from 'react';
|
|
7
|
+
import { LogDetail } from './log-detail';
|
|
8
|
+
import { LogLevelDot } from './log-level-dot';
|
|
9
|
+
import { Button, Input } from '@motiadev/ui';
|
|
10
|
+
import { CircleX, Trash } from 'lucide-react';
|
|
11
|
+
export const LogsPage = () => {
|
|
12
|
+
const logs = useLogsStore((state) => state.logs);
|
|
13
|
+
const resetLogs = useLogsStore((state) => state.resetLogs);
|
|
14
|
+
const selectedLogId = useGlobalStore((state) => state.selectedLogId);
|
|
15
|
+
const selectLogId = useGlobalStore((state) => state.selectLogId);
|
|
16
|
+
const selectedLog = useMemo(() => (selectedLogId ? logs.find((log) => log.id === selectedLogId) : undefined), [logs, selectedLogId]);
|
|
17
|
+
const [search, setSearch] = useState('');
|
|
18
|
+
const filteredLogs = useMemo(() => {
|
|
19
|
+
return logs.filter((log) => {
|
|
20
|
+
return (log.msg.toLowerCase().includes(search.toLowerCase()) ||
|
|
21
|
+
log.traceId.toLowerCase().includes(search.toLowerCase()) ||
|
|
22
|
+
log.step.toLowerCase().includes(search.toLowerCase()));
|
|
23
|
+
});
|
|
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: "cursor-pointer border-0", 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
|
+
};
|
|
@@ -7,7 +7,7 @@ export const Argument = ({ arg }) => {
|
|
|
7
7
|
return _jsx("span", { className: "font-mono text-blue-100 font-bold bg-blue-500/50 px-2 rounded-md", children: "value" });
|
|
8
8
|
}
|
|
9
9
|
const entries = Object.entries(arg);
|
|
10
|
-
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-mono text-green-500", children: '{ ' }), entries.map(([key, value], index) => (_jsxs("span", { children: [key, ": ", _jsx(Argument, { arg: value }), index < entries.length - 1 && _jsx(_Fragment, { children: ", " })] }, key))), _jsx("span", { className: "font-mono text-green-500", children: ' }' })] }));
|
|
10
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-mono text-green-500", children: '{ ' }), entries.map(([key, value], index) => (_jsxs("span", { children: [_jsx("span", { className: "font-mono text-green-500", children: key }), _jsx("span", { className: "font-mono text-muted-foreground", children: ":" }), " ", _jsx(Argument, { arg: value }), index < entries.length - 1 && _jsx(_Fragment, { children: ", " })] }, key))), _jsx("span", { className: "font-mono text-green-500", children: ' }' })] }));
|
|
11
11
|
};
|
|
12
12
|
export const FunctionCall = ({ topLevelClassName, objectName, functionName, args, callsQuantity }) => {
|
|
13
13
|
const hasCalls = callsQuantity && callsQuantity > 1;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { LogLevelDot } from '@/components/logs/log-level-dot';
|
|
3
3
|
export const TraceLogEvent = ({ event }) => {
|
|
4
|
-
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(
|
|
4
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LogLevelDot, { level: event.level }), " ", event.message] }));
|
|
5
5
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { TraceGroup } from '
|
|
1
|
+
import { TraceGroup } from '@/types/observability';
|
|
2
2
|
export declare const useGetEndTime: (group: TraceGroup | undefined | null) => number;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Sidebar } from '@/components/sidebar/sidebar';
|
|
2
3
|
import { Badge } from '@/components/ui/badge';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { formatDuration } from '@/lib/utils';
|
|
5
|
+
import { X } from 'lucide-react';
|
|
5
6
|
import { memo } from 'react';
|
|
6
7
|
import { EventIcon } from '../events/event-icon';
|
|
7
8
|
import { TraceEvent } from '../events/trace-event';
|
|
8
|
-
export const TraceItemDetail = memo(({ trace }) => {
|
|
9
|
-
return (_jsxs(
|
|
9
|
+
export const TraceItemDetail = memo(({ trace, onClose }) => {
|
|
10
|
+
return (_jsxs(Sidebar, { onClose: onClose, title: "Trace Details", subtitle: `Viewing details from step ${trace.name}`, actions: [{ icon: _jsx(X, {}), onClick: onClose, label: 'Close' }], children: [_jsxs("div", { className: "px-2 w-[800px] overflow-auto", children: [_jsxs("div", { className: "flex items-center gap-4 text-sm text-muted-foreground mb-4", children: [trace.endTime && _jsxs("span", { children: ["Duration: ", formatDuration(trace.endTime - trace.startTime)] }), _jsx("div", { className: "bg-blue-500 font-bold text-xs px-[4px] py-[2px] rounded-sm text-blue-100", children: trace.entryPoint.type }), trace.correlationId && _jsxs(Badge, { variant: "outline", children: ["Correlated: ", trace.correlationId] })] }), _jsx("div", { className: "pl-6 border-l-1 border-gray-500/40 font-mono text-xs flex flex-col gap-3", children: trace.events.map((event, index) => (_jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute -left-[26px] top-[8px] w-1 h-1 rounded-full bg-emerald-500 outline outline-2 outline-emerald-500/50" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(EventIcon, { event: event }), _jsxs("span", { className: "text-sm font-mono text-muted-foreground", children: ["+", Math.floor(event.timestamp - trace.startTime), "ms"] }), _jsx(TraceEvent, { event: event })] })] }, index))) })] }), trace.error && (_jsxs("div", { className: "p-4 bg-red-800/10", children: [_jsx("div", { className: "text-sm text-red-800 dark:text-red-400 font-semibold", children: trace.error.message }), _jsx("div", { className: "text-sm text-red-800 dark:text-red-400 pl-4", children: trace.error.stack })] }))] }));
|
|
10
11
|
});
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from '@/lib/utils';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}), onClick: () => setIsExpanded(!isExpanded), style: {
|
|
15
|
-
marginLeft: `${((trace.startTime - group.startTime) / (groupEndTime - group.startTime)) * 100}%`,
|
|
16
|
-
width: trace.endTime
|
|
17
|
-
? `${((trace.endTime - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`
|
|
18
|
-
: `${((Date.now() - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`,
|
|
19
|
-
} }) })] }), isExpanded && _jsx(TraceItemDetail, { trace: trace })] }));
|
|
3
|
+
export const TraceItem = ({ trace, group, groupEndTime, onExpand }) => {
|
|
4
|
+
return (_jsxs("div", { className: "flex hover:bg-muted-foreground/10 relative cursor-pointer", onClick: () => onExpand(trace.id), children: [_jsx("div", { className: "flex items-center min-w-[200px] max-w-[200px] h-[32px] max-h-[32px] py-4 px-2 text-sm font-semibold text-foreground truncate sticky left-0 dark:bg-[#121212] bg-[#f1f1f1] z-9", children: trace.name }), _jsx("div", { className: "flex w-full flex-row items-center hover:bg-muted/50 rounded-md", children: _jsx("div", { className: "relative w-full h-[32px] flex items-center", children: _jsx("div", { className: cn('h-[24px] rounded-[4px] hover:opacity-80 transition-all duration-200', {
|
|
5
|
+
'bg-[repeating-linear-gradient(140deg,#BEFE29,#BEFE29_8px,#ABE625_8px,#ABE625_16px)]': trace.status === 'running',
|
|
6
|
+
'bg-[repeating-linear-gradient(140deg,#2862FE,#2862FE_8px,#2358E5_8px,#2358E5_16px)]': trace.status === 'completed',
|
|
7
|
+
'bg-[repeating-linear-gradient(140deg,#EA2069,#EA2069_8px,#D41E60_8px,#D41E60_16px)]': trace.status === 'failed',
|
|
8
|
+
}), style: {
|
|
9
|
+
marginLeft: `${((trace.startTime - group.startTime) / (groupEndTime - group.startTime)) * 100}%`,
|
|
10
|
+
width: trace.endTime
|
|
11
|
+
? `${((trace.endTime - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`
|
|
12
|
+
: `${((Date.now() - trace.startTime) / (groupEndTime - group.startTime)) * 100}%`,
|
|
13
|
+
} }) }) })] }));
|
|
20
14
|
};
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { TraceGroup } from '@/types/observability';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export declare const LoadingSpinner: React.FC<{
|
|
4
|
-
className: string;
|
|
5
|
-
}>;
|
|
6
|
-
export declare const TraceStatus: React.FC<{
|
|
3
|
+
type Props = {
|
|
7
4
|
status: TraceGroup['status'];
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
export declare const TraceStatusBadge: React.FC<
|
|
11
|
-
|
|
12
|
-
}>;
|
|
5
|
+
duration?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const TraceStatusBadge: React.FC<Props>;
|
|
8
|
+
export {};
|
|
@@ -1,33 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from 'class-variance-authority';
|
|
3
|
-
|
|
4
|
-
import { cn } from '../../lib/utils';
|
|
5
|
-
export const LoadingSpinner = ({ className }) => {
|
|
6
|
-
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: cn('animate-spin', className), children: _jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
|
|
7
|
-
};
|
|
8
|
-
const getStatusIcon = (status) => {
|
|
9
|
-
switch (status) {
|
|
10
|
-
case 'running':
|
|
11
|
-
case 'active':
|
|
12
|
-
return _jsx(LoadingSpinner, { className: "w-4 h-4 text-blue-500" });
|
|
13
|
-
case 'completed':
|
|
14
|
-
return _jsx(CheckCircle, { className: "w-4 h-4 text-green-500" });
|
|
15
|
-
case 'failed':
|
|
16
|
-
return _jsx(XCircle, { className: "w-4 h-4 text-red-500" });
|
|
17
|
-
case 'stalled':
|
|
18
|
-
return _jsx(Clock, { className: "w-4 h-4 text-yellow-500" });
|
|
19
|
-
default:
|
|
20
|
-
return _jsx(Clock, { className: "w-4 h-4 text-gray-500" });
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
const statusVariants = cva('inline-flex items-center rounded-lg px-3 py-[2px] uppercase text-[10px] font-bold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', {
|
|
3
|
+
const statusVariants = cva('inline-flex items-center rounded-full px-4 py-1 text-xs font-bold transition-colors', {
|
|
24
4
|
variants: {
|
|
25
5
|
status: {
|
|
26
|
-
running: 'dark:bg-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
failed: 'dark:bg-red-800/30 dark:text-red-500 bg-red-100 text-red-700',
|
|
30
|
-
stalled: 'dark:bg-yellow-800/30 dark:text-yellow-500 bg-yellow-100 text-yellow-700',
|
|
6
|
+
running: 'dark:bg-accent-100 dark:text-accent-1000 bg-accent-200 text-accent-900 capitalize',
|
|
7
|
+
completed: 'bg-accent-1000 text-white',
|
|
8
|
+
failed: 'bg-destructive/10 text-destructive capitalize',
|
|
31
9
|
default: 'dark:bg-gray-800/30 dark:text-gray-500 bg-gray-100 text-gray-800',
|
|
32
10
|
},
|
|
33
11
|
},
|
|
@@ -35,9 +13,6 @@ const statusVariants = cva('inline-flex items-center rounded-lg px-3 py-[2px] up
|
|
|
35
13
|
status: 'default',
|
|
36
14
|
},
|
|
37
15
|
});
|
|
38
|
-
export const
|
|
39
|
-
return (
|
|
40
|
-
};
|
|
41
|
-
export const TraceStatusBadge = ({ status }) => {
|
|
42
|
-
return _jsx("div", { className: statusVariants({ status }), children: status });
|
|
16
|
+
export const TraceStatusBadge = ({ status, duration }) => {
|
|
17
|
+
return _jsx("div", { className: statusVariants({ status }), children: duration && status !== 'failed' ? duration : status });
|
|
43
18
|
};
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useGlobalStore } from '@/stores/use-global-store';
|
|
3
3
|
import { useStreamGroup, useStreamItem } from '@motiadev/stream-client-react';
|
|
4
|
-
import {
|
|
4
|
+
import { Button } from '@motiadev/ui';
|
|
5
|
+
import { Minus, Plus } from 'lucide-react';
|
|
6
|
+
import { memo, useMemo, useState } from 'react';
|
|
5
7
|
import { useGetEndTime } from './hooks/use-get-endtime';
|
|
8
|
+
import { TraceItem } from './trace-item/trace-item';
|
|
9
|
+
import { TraceItemDetail } from './trace-item/trace-item-detail';
|
|
6
10
|
export const TraceTimeline = memo(({ groupId }) => {
|
|
7
11
|
const { data: group } = useStreamItem({
|
|
8
12
|
streamName: 'motia-trace-group',
|
|
@@ -11,7 +15,15 @@ export const TraceTimeline = memo(({ groupId }) => {
|
|
|
11
15
|
});
|
|
12
16
|
const { data } = useStreamGroup({ streamName: 'motia-trace', groupId });
|
|
13
17
|
const endTime = useGetEndTime(group);
|
|
18
|
+
const [zoom, setZoom] = useState(1);
|
|
19
|
+
const selectedTraceId = useGlobalStore((state) => state.selectedTraceId);
|
|
20
|
+
const selectTraceId = useGlobalStore((state) => state.selectTraceId);
|
|
21
|
+
const selectedTrace = useMemo(() => data?.find((trace) => trace.id === selectedTraceId), [data, selectedTraceId]);
|
|
22
|
+
const zoomMinus = () => {
|
|
23
|
+
if (zoom > 0.5)
|
|
24
|
+
setZoom(zoom - 0.1);
|
|
25
|
+
};
|
|
14
26
|
if (!group)
|
|
15
27
|
return null;
|
|
16
|
-
return (_jsxs("div", { className: "flex flex-col
|
|
28
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex flex-col flex-1 overflow-x-auto h-full relative", children: _jsxs("div", { className: "flex flex-col items-center min-w-full sticky top-0", style: { width: `${zoom * 1000}px` }, children: [_jsxs("div", { className: "flex flex-1 w-full sticky top-0 bg-background z-10", children: [_jsxs("div", { className: "w-full min-h-[37px] h-[37px] min-w-[200px] max-w-[200px] flex items-center justify-center gap-2 sticky left-0 top-0 dark:bg-[#121212] bg-[#f1f1f1]", children: [_jsx(Button, { variant: "icon", size: "sm", className: "px-2", onClick: zoomMinus, children: _jsx(Minus, { className: "w-4 h-4 cursor-pointer" }) }), _jsxs("span", { className: "text-sm font-bold text-muted-foreground", children: [Math.floor(zoom * 100), "%"] }), _jsx(Button, { variant: "icon", size: "sm", className: "px-2", onClick: () => setZoom(zoom + 0.1), children: _jsx(Plus, { className: "w-4 h-4 cursor-pointer" }) })] }), _jsxs("div", { className: "flex justify-between font-mono p-2 w-full text-xs text-muted-foreground dark:bg-[#131313] bg-[#f1f1f1]", children: [_jsx("span", { children: "0ms" }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.25), "ms"] }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.5), "ms"] }), _jsxs("span", { children: [Math.floor((endTime - group.startTime) * 0.75), "ms"] }), _jsxs("span", { children: [Math.floor(endTime - group.startTime), "ms"] }), _jsxs("div", { className: "absolute bottom-[-4px] w-full flex justify-between", children: [_jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" }), _jsx("span", { className: "w-[1px] h-full bg-blue-500" })] })] })] }), _jsx("div", { className: "flex flex-col w-full h-full", children: data?.map((trace) => (_jsx(TraceItem, { trace: trace, group: group, groupEndTime: endTime, onExpand: selectTraceId }, trace.id))) })] }) }), selectedTrace && _jsx(TraceItemDetail, { trace: selectedTrace, onClose: () => selectTraceId(undefined) })] }));
|
|
17
29
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { TraceGroup } from '@/types/observability';
|
|
2
|
+
import React from 'react';
|
|
3
3
|
interface Props {
|
|
4
4
|
groups: TraceGroup[];
|
|
5
|
-
selectedGroupId
|
|
5
|
+
selectedGroupId?: string;
|
|
6
6
|
onGroupSelect: (group: TraceGroup) => void;
|
|
7
7
|
}
|
|
8
8
|
export declare const TracesGroups: React.FC<Props>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { Card, CardContent } from '@/components/ui/card';
|
|
2
|
+
import { cn } from '@/lib/utils';
|
|
4
3
|
import { formatDistanceToNow } from 'date-fns';
|
|
5
|
-
import {
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { TraceStatusBadge } from './trace-status';
|
|
6
6
|
export const TracesGroups = memo(({ groups, selectedGroupId, onGroupSelect }) => {
|
|
7
7
|
const formatDuration = (duration) => {
|
|
8
8
|
if (!duration)
|
|
@@ -11,5 +11,5 @@ export const TracesGroups = memo(({ groups, selectedGroupId, onGroupSelect }) =>
|
|
|
11
11
|
return `${duration}ms`;
|
|
12
12
|
return `${(duration / 1000).toFixed(1)}s`;
|
|
13
13
|
};
|
|
14
|
-
return (
|
|
14
|
+
return (_jsx("div", { className: "overflow-auto", children: groups.length > 0 && (_jsx("div", { children: [...groups].reverse().map((group) => (_jsx("div", { "data-testid": `trace-${group.id}`, className: cn('cursor-pointer transition-colors', selectedGroupId === group.id ? 'bg-muted-foreground/10' : 'hover:bg-muted/70'), onClick: () => onGroupSelect(group), children: _jsxs("div", { className: "p-3 flex flex-col gap-1", children: [_jsxs("div", { className: "flex flex-row justify-between items-center gap-2", children: [_jsx("span", { className: "font-semibold text-lg", children: group.name }), _jsx(TraceStatusBadge, { status: group.status, duration: group.endTime ? formatDuration(group.endTime - group.startTime) : undefined })] }), _jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("div", { "data-testid": "trace-id", className: "text-xs text-muted-foreground font-mono tracking-[1px]", children: group.id }), _jsxs("span", { children: [group.metadata.totalSteps, " steps"] })] }), _jsxs("div", { className: "flex justify-between", children: [group.endTime && _jsxs("span", { children: ["Duration: ", formatDuration(group.endTime - group.startTime)] }), _jsxs("span", { children: [formatDistanceToNow(group.startTime), " ago"] })] }), group.metadata.activeSteps > 0 && (_jsxs("div", { className: "text-blue-600", children: [group.metadata.activeSteps, " active"] }))] })] }) }, group.id))) })) }));
|
|
15
15
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TraceTimeline } from '@/components/observability/trace-timeline';
|
|
3
|
+
import { useStreamGroup } from '@motiadev/stream-client-react';
|
|
4
|
+
import { TracesGroups } from '@/components/observability/traces-groups';
|
|
5
|
+
import { useGlobalStore } from '../../stores/use-global-store';
|
|
6
|
+
export const TracesPage = () => {
|
|
7
|
+
const selectedGroupId = useGlobalStore((state) => state.selectedTraceGroupId);
|
|
8
|
+
const selectTraceGroupId = useGlobalStore((state) => state.selectTraceGroupId);
|
|
9
|
+
const { data } = useStreamGroup({ streamName: 'motia-trace-group', groupId: 'default' });
|
|
10
|
+
const handleGroupSelect = (group) => selectTraceGroupId(group.id);
|
|
11
|
+
return (_jsxs("div", { className: "flex flex-1 overflow-hidden h-full", children: [_jsx("div", { className: "max-w-1/3 border-r border-border overflow-auto h-full", "data-testid": "traces-container", children: _jsx(TracesGroups, { groups: data, selectedGroupId: selectedGroupId, onGroupSelect: handleGroupSelect }) }), _jsxs("div", { className: "flex-2 overflow-auto", "data-testid": "trace-details", children: [selectedGroupId && _jsx(TraceTimeline, { groupId: selectedGroupId }), !selectedGroupId && (_jsx("div", { className: "flex items-center justify-center h-full text-muted-foreground", children: "Select a trace or trace group to view the timeline" }))] })] }));
|
|
12
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type PanelProps } from '@motiadev/ui';
|
|
2
|
+
import { FC } from 'react';
|
|
3
|
+
export declare const APP_SIDEBAR_CONTAINER_ID = "app-sidebar-container";
|
|
4
|
+
export type SidebarProps = PanelProps & {
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
initialWidth?: number;
|
|
7
|
+
};
|
|
8
|
+
export declare const Sidebar: FC<SidebarProps>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Panel } from '@motiadev/ui';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { useEffect, useMemo } from 'react';
|
|
5
|
+
import { useResizable } from 'react-use-resizable';
|
|
6
|
+
import { Equal } from 'lucide-react';
|
|
7
|
+
export const APP_SIDEBAR_CONTAINER_ID = 'app-sidebar-container';
|
|
8
|
+
const CLOSE_PREVIOUS_SIDEBAR_EVENT = 'close-previous-sidebar';
|
|
9
|
+
export const Sidebar = ({ initialWidth, onClose, ...props }) => {
|
|
10
|
+
const sidebarId = useMemo(() => Symbol(), []);
|
|
11
|
+
const { getRootProps, getHandleProps } = useResizable({
|
|
12
|
+
lockVertical: true,
|
|
13
|
+
initialWidth: initialWidth ?? 400,
|
|
14
|
+
initialHeight: '100%',
|
|
15
|
+
onDragStart: () => {
|
|
16
|
+
document.body.style.userSelect = 'none';
|
|
17
|
+
},
|
|
18
|
+
onDragEnd: () => {
|
|
19
|
+
document.body.style.userSelect = '';
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const event = new CustomEvent(CLOSE_PREVIOUS_SIDEBAR_EVENT, { detail: { sidebarId } });
|
|
24
|
+
window.dispatchEvent(event);
|
|
25
|
+
const handleClose = (e) => {
|
|
26
|
+
const customEvent = e;
|
|
27
|
+
if (customEvent.detail.sidebarId !== sidebarId) {
|
|
28
|
+
onClose();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
window.addEventListener(CLOSE_PREVIOUS_SIDEBAR_EVENT, handleClose);
|
|
32
|
+
return () => {
|
|
33
|
+
window.removeEventListener(CLOSE_PREVIOUS_SIDEBAR_EVENT, handleClose);
|
|
34
|
+
};
|
|
35
|
+
}, [sidebarId, onClose]);
|
|
36
|
+
return createPortal(_jsxs("div", { ...getRootProps(), className: "pr-2 py-2 relative", children: [_jsx("div", { ...getHandleProps({
|
|
37
|
+
reverse: true,
|
|
38
|
+
}), className: "flex h-6 w-6 items-center justify-center rounded-full bg-background border border-border absolute top-1/2 -translate-y-1/2 -left-4 z-20", children: _jsx(Equal, { className: "rotate-90 w-4 h-4 text-muted-foreground" }) }), _jsx(Panel, { ...props, className: "max-h-[calc(100vh-80px)] h-full" })] }), document.querySelector(`#${APP_SIDEBAR_CONTAINER_ID}`));
|
|
39
|
+
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export interface StateItem {
|
|
2
|
+
groupId: string;
|
|
3
|
+
key: string;
|
|
4
|
+
type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null';
|
|
5
|
+
value: string | number | boolean | object | unknown[] | null;
|
|
6
|
+
}
|
|
7
|
+
export declare const useGetStateItems: () => StateItem[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
export const
|
|
3
|
-
const [
|
|
2
|
+
export const useGetStateItems = () => {
|
|
3
|
+
const [items, setItems] = useState([]);
|
|
4
4
|
useEffect(() => {
|
|
5
5
|
fetch('/motia/state')
|
|
6
6
|
.then(async (res) => {
|
|
@@ -11,36 +11,8 @@ export const useGetTraces = () => {
|
|
|
11
11
|
throw await res.json();
|
|
12
12
|
}
|
|
13
13
|
})
|
|
14
|
-
.then(
|
|
15
|
-
.catch((err) =>
|
|
16
|
-
console.error(err);
|
|
17
|
-
});
|
|
14
|
+
.then(setItems)
|
|
15
|
+
.catch((err) => console.error(err));
|
|
18
16
|
}, []);
|
|
19
|
-
return
|
|
20
|
-
};
|
|
21
|
-
export const useGetFields = (traceId) => {
|
|
22
|
-
const [fields, setFields] = useState([]);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (!traceId) {
|
|
25
|
-
setFields([]);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
fetch(`/motia/state/${traceId}`)
|
|
29
|
-
.then((res) => res.json())
|
|
30
|
-
.then((data) => setFields(data));
|
|
31
|
-
}
|
|
32
|
-
}, [traceId]);
|
|
33
|
-
return fields;
|
|
34
|
-
};
|
|
35
|
-
export const useGetValues = (traceId, field) => {
|
|
36
|
-
const [values, setValues] = useState();
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
setValues(undefined);
|
|
39
|
-
if (traceId && field) {
|
|
40
|
-
fetch(`/motia/state/${traceId}/${field}`)
|
|
41
|
-
.then((res) => res.json())
|
|
42
|
-
.then((data) => setValues(data));
|
|
43
|
-
}
|
|
44
|
-
}, [traceId, field]);
|
|
45
|
-
return values;
|
|
17
|
+
return items;
|
|
46
18
|
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Sidebar } from '@/components/sidebar/sidebar';
|
|
3
|
+
import { Braces, X } from 'lucide-react';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import JsonView from 'react18-json-view';
|
|
2
6
|
import { StateValue } from './state-value';
|
|
3
|
-
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '../ui/sheet';
|
|
4
7
|
export const StateDetail = ({ state, onClose }) => {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const [isCodeEnabled, setIsCodeEnabled] = useState(false);
|
|
9
|
+
return (_jsx(Sidebar, { onClose: onClose, title: "State details", subtitle: `${state.groupId} ${state.key}`, actions: [
|
|
10
|
+
{
|
|
11
|
+
active: isCodeEnabled,
|
|
12
|
+
icon: _jsx(Braces, {}),
|
|
13
|
+
onClick: () => setIsCodeEnabled(!isCodeEnabled),
|
|
14
|
+
label: 'Code',
|
|
15
|
+
},
|
|
16
|
+
{ icon: _jsx(X, {}), onClick: onClose, label: 'Close' },
|
|
17
|
+
], children: _jsx("div", { className: "flex flex-col gap-2", children: isCodeEnabled ? _jsx(JsonView, { src: state.value }) : _jsx(StateValue, { value: state.value, isRoot: true }) }) }));
|
|
12
18
|
};
|