diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5edd5ac1c..ccc88ff82 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -146,6 +146,31 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} run: npm publish --access public + sdk-dotnet: + runs-on: ubuntu-latest + needs: + - publish-docker + - prepare + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '6' + - name: Bump version + env: + TAG: ${{ needs.prepare.outputs.tag }} + run: sed -i "s/.*<\/PackageVersion>/${TAG}<\/PackageVersion>/g" sdk-dotnet/LittleHorse.Sdk/LittleHorse.Sdk.csproj + - name: Build Project + run: dotnet build sdk-dotnet/LittleHorse.Sdk/LittleHorse.Sdk.csproj + - name: Create Package + run: dotnet pack --configuration Release sdk-dotnet/LittleHorse.Sdk/LittleHorse.Sdk.csproj + - name: Publish Package to nuget.org + run: dotnet nuget push sdk-dotnet/LittleHorse.Sdk/bin/Release/*.nupkg -k $NUGET_AUTH_TOKEN -s https://api.nuget.org/v3/index.json + env: + NUGET_AUTH_TOKEN: ${{ secrets.NUGET_TOKEN }} + lhctl: runs-on: ubuntu-latest steps: @@ -174,6 +199,7 @@ jobs: - sdk-java - sdk-python - sdk-js + - sdk-dotnet runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e523db727..7e834f9dd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -133,3 +133,18 @@ jobs: run: | npm ci npm run test + + tests-sdk-dotnet: + if: ${{ !contains(github.event.head_commit.message, '[skip main]') }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '6' + - name: Build Project + run: dotnet build sdk-dotnet/LittleHorse.Sdk/LittleHorse.Sdk.csproj + - name: Test Dotnet + run: dotnet test sdk-dotnet/LittleHorse.Sdk.Tests \ No newline at end of file diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index c0685758c..89739f61e 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -19,11 +19,11 @@ "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.1", - "@radix-ui/react-tooltip": "^1.1.6", "@tanstack/react-query": "^5.37.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "cron-parser": "^4.9.0", "dagre": "^0.8.5", "littlehorse-client": "file://../sdk-js", "lucide-react": "^0.379.0", @@ -3663,255 +3663,6 @@ } } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.6.tgz", - "integrity": "sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.3", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.1", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", - "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", - "integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz", - "integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", - "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", - "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", - "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-visually-hidden": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz", - "integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", @@ -9853,6 +9604,18 @@ "devOptional": true, "license": "MIT" }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -17691,6 +17454,15 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", diff --git a/dashboard/package.json b/dashboard/package.json index ad84da305..0a989b96b 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -17,16 +17,16 @@ "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-scroll-area": "^1.2.2", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.1", - "@radix-ui/react-tooltip": "^1.1.6", + "@radix-ui/react-tabs": "^1.1.1", "@tanstack/react-query": "^5.37.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "cron-parser": "^4.9.0", "dagre": "^0.8.5", "littlehorse-client": "file://../sdk-js", "lucide-react": "^0.379.0", diff --git a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/actions/ScheduleWfRun.ts b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/actions/ScheduleWfRun.ts deleted file mode 100644 index cd2def909..000000000 --- a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/actions/ScheduleWfRun.ts +++ /dev/null @@ -1,19 +0,0 @@ -'use server' -import { lhClient } from '@/app/lhClient' -import { WithTenant } from '@/types' -import { ScheduleWfRequest } from 'littlehorse-client/proto' -import { ScheduledWfRun } from '../../../../../../../../sdk-js/dist/proto/scheduled_wf_run' - -export const ScheduleWfRun = async ({ - wfSpecName, - tenantId, - majorVersion, - revision, - parentWfRunId, - id, - variables, - cronExpression, -}: ScheduleWfRequest & WithTenant): Promise => { - const client = await lhClient({ tenantId }) - return client.scheduleWf({ wfSpecName, majorVersion, revision, parentWfRunId, id, variables, cronExpression }) -} diff --git a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/actions/getScheduleWfSpec.ts b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/actions/getScheduleWfSpec.ts index 347204681..aaceb50f3 100644 --- a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/actions/getScheduleWfSpec.ts +++ b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/actions/getScheduleWfSpec.ts @@ -1,20 +1,30 @@ 'use server' + import { lhClient } from '@/app/lhClient' import { WithTenant } from '@/types' -import { ScheduledWfRunIdList } from 'littlehorse-client/proto' +import { ScheduledWfRun } from 'littlehorse-client/proto' type GetWfSpecProps = { name: string version: string } & WithTenant -export const getScheduleWfSpec = async ({ name, version, tenantId }: GetWfSpecProps): Promise => { +export const getScheduleWfSpec = async ({ name, version, tenantId }: GetWfSpecProps): Promise => { const client = await lhClient({ tenantId }) const [majorVersion, revision] = version.split('.') - return client.searchScheduledWfRun({ - wfSpecName: name, - majorVersion: parseInt(majorVersion) || 0, - revision: parseInt(revision) | 0, - }) + + return Promise.all( + ( + await client.searchScheduledWfRun({ + wfSpecName: name, + majorVersion: parseInt(majorVersion) || 0, + revision: parseInt(revision) || 0, + }) + ).results.map(async scheduledWfRun => { + return await client.getScheduledWfRun({ + id: scheduledWfRun.id, + }) + }) + ) } diff --git a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/ScheduledWfRuns.tsx b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/ScheduledWfRuns.tsx new file mode 100644 index 000000000..17c3355d4 --- /dev/null +++ b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/ScheduledWfRuns.tsx @@ -0,0 +1,124 @@ +'use client' + +import { ScheduledWfRunIdList, WfSpec } from 'littlehorse-client/proto' +import { getScheduleWfSpec } from '../actions/getScheduleWfSpec' +import { SelectionLink } from '@/app/[tenantId]/components/SelectionLink' +import { ScheduledWfRun } from 'littlehorse-client/proto' +import { FUTURE_TIME_RANGES, SEARCH_DEFAULT_LIMIT, TimeRange } from '@/app/constants' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { ClockIcon } from 'lucide-react' +import { useEffect, useState, useMemo } from 'react' +import { getCronTimeWindow } from '@/app/utils/getCronTimeWindow' +import { parseExpression } from 'cron-parser' +import { utcToLocalDateTime } from '@/app/utils' +import { SearchVariableDialog } from './SearchVariableDialog' +import { SearchFooter } from '@/app/[tenantId]/components/SearchFooter' +import { useParams, useSearchParams } from 'next/navigation' +import { RefreshCwIcon } from 'lucide-react' + +export const ScheduledWfRuns = (spec: WfSpec) => { + const [currentWindow, setWindow] = useState(-1) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) + const [scheduledWfRuns, setScheduledWfRuns] = useState([]) + const tenantId = useParams().tenantId as string + + useEffect(() => { + let isMounted = true + + const fetchScheduledWfRuns = async () => { + try { + setIsLoading(true) + setError(null) + const runs = await getScheduleWfSpec({ + name: spec.id!.name, + version: spec.id!.majorVersion + '.' + spec.id!.revision, + tenantId: tenantId, + }) + if (isMounted) { + setScheduledWfRuns(runs) + } + } catch (err) { + if (isMounted) { + setError(err instanceof Error ? err : new Error('Failed to fetch scheduled runs')) + } + } finally { + if (isMounted) { + setIsLoading(false) + } + } + } + + fetchScheduledWfRuns() + + return () => { + isMounted = false + } + }, [spec.id, tenantId]) + + const filteredScheduledWfRuns = useMemo( + () => + scheduledWfRuns + .filter(scheduledWfRun => { + if (currentWindow === -1) return true + const timeWindow = getCronTimeWindow(scheduledWfRun.cronExpression) + return timeWindow && timeWindow <= currentWindow + }) + .sort((a, b) => { + const timeA = parseExpression(a.cronExpression).next().toDate().getTime() + const timeB = parseExpression(b.cronExpression).next().toDate().getTime() + return timeA - timeB + }), + [currentWindow, scheduledWfRuns] + ) + + if (isLoading) { + return ( +
+ +
+ ) + } + + if (error) { + return ( +
+

Error loading scheduled runs

+

{error.message}

+
+ ) + } + + return ( +
+
+ +
+
+ {filteredScheduledWfRuns.map(scheduledWfRun => ( + +

{scheduledWfRun.id?.id}

+
+ +

{utcToLocalDateTime(parseExpression(scheduledWfRun.cronExpression).next().toDate().toISOString())}

+
+
+ ))} +
+
+ ) +} diff --git a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRuns.tsx b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRuns.tsx index 2099bd7b0..b20605f55 100644 --- a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRuns.tsx +++ b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRuns.tsx @@ -1,5 +1,4 @@ 'use client' -import LinkWithTenant from '@/app/[tenantId]/components/LinkWithTenant' import { SearchFooter } from '@/app/[tenantId]/components/SearchFooter' import { SEARCH_DEFAULT_LIMIT, TIME_RANGES, TimeRange } from '@/app/constants' import { concatWfRunIds } from '@/app/utils' @@ -10,6 +9,7 @@ import { useParams, useSearchParams } from 'next/navigation' import { FC, Fragment, useMemo, useState } from 'react' import { PaginatedWfRunIdList, searchWfRun } from '../actions/searchWfRun' import { WfRunsHeader } from './WfRunsHeader' +import { SelectionLink } from '@/app/[tenantId]/components/SelectionLink' export const WfRuns: FC = spec => { const searchParams = useSearchParams() @@ -57,18 +57,13 @@ export const WfRuns: FC = spec => { ) : ( -
+
{data?.pages.map((page, i) => ( {page.results.map(wfRunId => ( -
- - {wfRunId.id} - -
+ +

{wfRunId.id}

+
))}
))} diff --git a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRunsHeader.tsx b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRunsHeader.tsx index 465f2e752..51f791acc 100644 --- a/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRunsHeader.tsx +++ b/dashboard/src/app/[tenantId]/(diagram)/wfSpec/[...props]/components/WfRunsHeader.tsx @@ -20,8 +20,6 @@ export const WfRunsHeader: FC = ({ spec, currentStatus, currentWindow, se return (
-

WfRun Search

-