feat(dashboard): Workflow executions (#6564)
**What** - v1 of executions domain **TODO in follow up PR** I think it might be a good idea for Carlos or Adrien to go over the design with Ludvig, and make sure we are displaying what is relevant to developers, and how things should be displayed. So this PR is just the initial implementation, and does not handle displaying things like compensation steps (not part of the current design), step input (not supported by the API but part of the design), etc. Closes CORE-1751, CORE-1755
This commit is contained in:
+44
-41
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
ArrowsPointingOut,
|
||||
CheckCircleMiniSolid,
|
||||
SquareTwoStackMini,
|
||||
XMarkMini,
|
||||
} from "@medusajs/icons"
|
||||
import { ArrowsPointingOut, XMarkMini } from "@medusajs/icons"
|
||||
import {
|
||||
Badge,
|
||||
Container,
|
||||
@@ -14,20 +9,27 @@ import {
|
||||
} from "@medusajs/ui"
|
||||
import Primitive from "@uiw/react-json-view"
|
||||
import { CSSProperties, Suspense } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
type JsonViewProps = {
|
||||
type JsonViewSectionProps = {
|
||||
data: object
|
||||
root?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
// TODO: Fix the positioning of the copy btn
|
||||
export const JsonViewSection = ({ data, root }: JsonViewProps) => {
|
||||
export const JsonViewSection = ({
|
||||
data,
|
||||
root,
|
||||
title = "JSON",
|
||||
}: JsonViewSectionProps) => {
|
||||
const { t } = useTranslation()
|
||||
const numberOfKeys = Object.keys(data).length
|
||||
|
||||
return (
|
||||
<Container className="flex items-center justify-between px-6 py-4">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Heading level="h2">JSON</Heading>
|
||||
<Heading level="h2">{title}</Heading>
|
||||
<Badge size="2xsmall">{numberOfKeys} keys</Badge>
|
||||
</div>
|
||||
<Drawer>
|
||||
@@ -40,10 +42,10 @@ export const JsonViewSection = ({ data, root }: JsonViewProps) => {
|
||||
<ArrowsPointingOut />
|
||||
</IconButton>
|
||||
</Drawer.Trigger>
|
||||
<Drawer.Content className="border-ui-code-border bg-ui-code-bg-base text-ui-code-text-base dark overflow-hidden border shadow-none max-md:inset-x-2 max-md:max-w-[calc(100%-16px)]">
|
||||
<div className="bg-ui-code-bg-header border-ui-code-border flex items-center justify-between border-b px-6 py-4">
|
||||
<Drawer.Content className="border-ui-code-border bg-ui-code-bg-base text-ui-code-fg-subtle dark overflow-hidden border shadow-none max-md:inset-x-2 max-md:max-w-[calc(100%-16px)]">
|
||||
<div className="bg-ui-code-bg-base border-ui-code-border flex items-center justify-between border-b px-6 py-4">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Heading>JSON</Heading>
|
||||
<Heading className="text-ui-code-fg-base">{title}</Heading>
|
||||
<Badge size="2xsmall">{numberOfKeys} keys</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
@@ -68,52 +70,53 @@ export const JsonViewSection = ({ data, root }: JsonViewProps) => {
|
||||
style={
|
||||
{
|
||||
"--w-rjv-font-family": "Roboto Mono, monospace",
|
||||
"--w-rjv-line-color": "#2E3035",
|
||||
"--w-rjv-curlybraces-color": "#ADB1B8",
|
||||
"--w-rjv-key-string": "#A78BFA",
|
||||
"--w-rjv-info-color": "#FBBF24",
|
||||
"--w-rjv-type-string-color": "#34D399",
|
||||
"--w-rjv-quotes-string-color": "#34D399",
|
||||
"--w-rjv-type-boolean-color": "#FBBF24",
|
||||
"--w-rjv-type-int-color": "#60A5FA",
|
||||
"--w-rjv-type-float-color": "#60A5FA",
|
||||
"--w-rjv-type-bigint-color": "#60A5FA",
|
||||
"--w-rjv-key-number": "#60A5FA",
|
||||
"--w-rjv-line-color": "var(--code-border)",
|
||||
"--w-rjv-curlybraces-color": "rgb(255,255,255)",
|
||||
"--w-rjv-key-string": "rgb(247,208,25)",
|
||||
"--w-rjv-info-color": "var(--code-fg-muted)",
|
||||
"--w-rjv-type-string-color": "rgb(73,209,110)",
|
||||
"--w-rjv-quotes-string-color": "rgb(73,209,110)",
|
||||
"--w-rjv-type-boolean-color": "rgb(187,77,96)",
|
||||
"--w-rjv-type-int-color": "rgb(247,208,25)",
|
||||
"--w-rjv-type-float-color": "rgb(247,208,25)",
|
||||
"--w-rjv-type-bigint-color": "rgb(247,208,25)",
|
||||
"--w-rjv-key-number": "rgb(247,208,25)",
|
||||
"--w-rjv-arrow-color": "rgb(255,255,255)",
|
||||
"--w-rjv-copied-color": "var(--code-fg-subtle)",
|
||||
"--w-rjv-copied-success-color": "var(--code-fg-base)",
|
||||
"--w-rjv-colon-color": "rgb(255,255,255)",
|
||||
} as CSSProperties
|
||||
}
|
||||
collapsed={1}
|
||||
>
|
||||
<Primitive.Copied
|
||||
// @ts-expect-error - types are missing the 'data-copied' prop
|
||||
render={({ "data-copied": copied, onClick }) => {
|
||||
if (copied) {
|
||||
return (
|
||||
<CheckCircleMiniSolid className="text-ui-fg-subtle cursor-pointer align-middle" />
|
||||
)
|
||||
}
|
||||
return (
|
||||
<SquareTwoStackMini
|
||||
className="text-ui-fg-subtle cursor-pointer align-middle"
|
||||
onClick={onClick}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Primitive.Quote render={() => " "} />
|
||||
<Primitive.Quote render={() => <span />} />
|
||||
<Primitive.Null
|
||||
render={() => (
|
||||
<span className="text-ui-tag-red-text">null</span>
|
||||
)}
|
||||
/>
|
||||
<Primitive.Undefined
|
||||
render={() => (
|
||||
<span className="text-ui-code-fg-muted">undefined</span>
|
||||
)}
|
||||
/>
|
||||
<Primitive.CountInfo
|
||||
render={(_props, { value }) => {
|
||||
return (
|
||||
<span className="text-ui-tag-neutral-text ml-2">
|
||||
{Object.keys(value as object).length} items
|
||||
{t("general.items", {
|
||||
count: Object.keys(value as object).length,
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{/* <Primitive.Arrow>
|
||||
<TriangleDownMini className="text-ui-code-fg-subtle -ml-[3px]" />
|
||||
</Primitive.Arrow> */}
|
||||
<Primitive.Colon>
|
||||
<span className="mr-1">:</span>
|
||||
</Primitive.Colon>
|
||||
</Primitive>
|
||||
</Suspense>
|
||||
</Drawer.Body>
|
||||
|
||||
+65
-8
@@ -1,5 +1,6 @@
|
||||
import { ArrowUturnLeft } from "@medusajs/icons"
|
||||
import { ArrowUturnLeft, MinusMini } from "@medusajs/icons"
|
||||
import { IconButton, Text } from "@medusajs/ui"
|
||||
import * as Collapsible from "@radix-ui/react-collapsible"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Link, useLocation } from "react-router-dom"
|
||||
@@ -48,10 +49,24 @@ const useSettingRoutes = (): NavItemProps[] => {
|
||||
label: t("salesChannels.domain"),
|
||||
to: "/settings/sales-channels",
|
||||
},
|
||||
],
|
||||
[t]
|
||||
)
|
||||
}
|
||||
|
||||
const useDeveloperRoutes = (): NavItemProps[] => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return useMemo(
|
||||
() => [
|
||||
{
|
||||
label: t("apiKeyManagement.domain"),
|
||||
to: "/settings/api-key-management",
|
||||
},
|
||||
{
|
||||
label: t("executions.domain"),
|
||||
to: "/settings/executions",
|
||||
},
|
||||
],
|
||||
[t]
|
||||
)
|
||||
@@ -59,6 +74,7 @@ const useSettingRoutes = (): NavItemProps[] => {
|
||||
|
||||
const SettingsSidebar = () => {
|
||||
const routes = useSettingRoutes()
|
||||
const developerRoutes = useDeveloperRoutes()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const location = useLocation()
|
||||
@@ -80,19 +96,60 @@ const SettingsSidebar = () => {
|
||||
</IconButton>
|
||||
</Link>
|
||||
<Text leading="compact" weight="plus" size="small">
|
||||
{t("general.settings")}
|
||||
{t("nav.settings")}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3">
|
||||
<div className="border-ui-border-strong h-px w-full border-b border-dashed" />
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-y-4 overflow-y-auto py-2">
|
||||
<nav className="flex flex-col gap-y-1">
|
||||
{routes.map((setting) => (
|
||||
<NavItem key={setting.to} {...setting} />
|
||||
))}
|
||||
</nav>
|
||||
<div className="flex flex-1 flex-col overflow-y-auto">
|
||||
<Collapsible.Root defaultOpen className="py-3">
|
||||
<div className="px-3">
|
||||
<div className="text-ui-fg-muted flex h-7 items-center justify-between px-2">
|
||||
<Text size="small" leading="compact">
|
||||
{t("nav.general")}
|
||||
</Text>
|
||||
<Collapsible.Trigger asChild>
|
||||
<IconButton size="2xsmall" variant="transparent">
|
||||
<MinusMini className="text-ui-fg-muted" />
|
||||
</IconButton>
|
||||
</Collapsible.Trigger>
|
||||
</div>
|
||||
</div>
|
||||
<Collapsible.Content>
|
||||
<div className="pt-0.5">
|
||||
<nav className="flex flex-col gap-y-1">
|
||||
{routes.map((setting) => (
|
||||
<NavItem key={setting.to} {...setting} />
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
<Collapsible.Root defaultOpen className="py-3">
|
||||
<div className="px-3">
|
||||
<div className="text-ui-fg-muted flex h-7 items-center justify-between px-2">
|
||||
<Text size="small" leading="compact">
|
||||
{t("nav.developer")}
|
||||
</Text>
|
||||
<Collapsible.Trigger asChild>
|
||||
<IconButton size="2xsmall" variant="transparent">
|
||||
<MinusMini className="text-ui-fg-muted" />
|
||||
</IconButton>
|
||||
</Collapsible.Trigger>
|
||||
</div>
|
||||
</div>
|
||||
<Collapsible.Content>
|
||||
<div className="pt-0.5">
|
||||
<nav className="flex flex-col gap-y-1">
|
||||
{developerRoutes.map((setting) => (
|
||||
<NavItem key={setting.to} {...setting} />
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user