From 9630bf5a7d7febfa8b7e8a7c8497f901baf562ac Mon Sep 17 00:00:00 2001 From: Matt Kaye Date: Mon, 2 Jun 2025 11:19:19 -0400 Subject: [PATCH] Fix: UI Bug Burndown, Part II (#1788) * fix: hard redirect * fix: set value * fix: drill * fix: start time default bug * fix: only show worker filter if there are active workers * fix: rm deps from callbacks * fix: worker filtering * hack: events runs on hover * feat: badge ui * fix: remove event detail page * fix: rm cruft * fix: loading state * fix: initial side sheet rework * feat: initial work un-borking the side sheet * fix: more ui * fix: so close! * fix: fixed height for main view * fix: height * fix: race * feat: improved handle * fix: docs * fix: close the side sheet on opening docs * fix: close docs on side sheet open * chore: lint * fix: doc sheet * fix: route * feat: persist panel width * feat: progress on combining docs and detail sheets * feat: wire up docs * feat: clean up some dead code, improve typing * fix: more layout tweaks * fix: more misc. things * fix: side panel * fix: rm more dead code * fix: rm duped use of sheet + docs providers * fix: worker detail * fix: remove side sheet! * fix: remove a ton more dead code (detailWithSheet thing) * fix: use docs button on sidebar * feat: remove the rest of the docs code * fix: attempts * chore: gen * fix: don't run tests on FE changes * Fix bug burndown part iii (#1789) * fix: hard redirect * fix: set value * fix: drill * fix: start time default bug * fix: only show worker filter if there are active workers * fix: rm deps from callbacks * fix: worker filtering * fix: auth * redirect issue * empty state and layout * trigger bug fixes * empty state --------- Co-authored-by: mrkaye97 --------- Co-authored-by: Gabe Ruttner --- .github/workflows/test.yml | 2 +- examples/python/events/event.py | 2 +- .../runs/run-event-log/run-event-log.tsx | 10 +- .../app/src/next/components/runs/run-id.tsx | 12 +- .../components/runs/runs-table/columns.tsx | 326 +++++------ .../components/runs/runs-table/runs-table.tsx | 149 ++--- .../components/runs/trigger-run-modal.tsx | 51 +- .../src/next/components/ui/docs-button.tsx | 20 +- .../app/src/next/components/ui/docs-sheet.tsx | 110 ---- .../next/components/ui/filters/filters.tsx | 5 + .../components/ui/sheet/side-sheet.layout.tsx | 180 ++---- .../app/src/next/components/ui/sidebar.tsx | 3 +- .../next/components/waterfall/waterfall.tsx | 38 +- frontend/app/src/next/hooks/use-docs-sheet.ts | 80 --- frontend/app/src/next/hooks/use-runs.tsx | 6 +- .../app/src/next/hooks/use-side-panel.tsx | 131 +++++ frontend/app/src/next/hooks/use-side-sheet.ts | 161 ------ .../src/next/hooks/use-task-run-detail.tsx | 3 + .../app/src/next/hooks/utils/use-filters.tsx | 34 +- .../src/next/hooks/utils/use-time-filters.tsx | 13 +- .../generated/snips/python/events/event.ts | 2 +- frontend/app/src/next/lib/routes.ts | 18 - .../src/next/pages/auth/login/login.page.tsx | 55 +- .../pages/auth/register/register.page.tsx | 15 +- .../dashboard/components/sidebar/sidebar.tsx | 21 +- .../dashboard/events/events-detail.page.tsx | 81 --- .../dashboard/events/events.page.tsx | 129 +++-- .../dashboard/events/events.router.tsx | 9 - .../runs/detail-sheet/run-detail-raw.tsx | 23 - .../runs/detail-sheet/run-detail-sheet.tsx | 525 +++++++++--------- .../dashboard/runs/run-detail.page.tsx | 21 +- .../dashboard/runs/runs.page.tsx | 22 +- .../selfhost/new-selfhost-pool.page.tsx | 2 +- .../dashboard/workers/worker-detail-page.tsx | 24 +- .../dashboard/workflows/workflows.page.tsx | 177 ++++-- frontend/app/src/next/pages/root.layout.tsx | 192 +++++-- frontend/app/src/pages/main/v1/index.tsx | 34 +- .../generated/snips/python/events/event.ts | 2 +- sdks/python/examples/events/event.py | 2 +- 39 files changed, 1230 insertions(+), 1460 deletions(-) delete mode 100644 frontend/app/src/next/components/ui/docs-sheet.tsx delete mode 100644 frontend/app/src/next/hooks/use-docs-sheet.ts create mode 100644 frontend/app/src/next/hooks/use-side-panel.tsx delete mode 100644 frontend/app/src/next/hooks/use-side-sheet.ts delete mode 100644 frontend/app/src/next/pages/authenticated/dashboard/events/events-detail.page.tsx delete mode 100644 frontend/app/src/next/pages/authenticated/dashboard/runs/detail-sheet/run-detail-raw.tsx diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a9cf26483..a80d73dfb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,7 +3,7 @@ on: pull_request: paths-ignore: - "sdks/**" - - "frontend/docs/**" + - "frontend/**" - "examples/**" jobs: diff --git a/examples/python/events/event.py b/examples/python/events/event.py index aede3f4b5..d831c052d 100644 --- a/examples/python/events/event.py +++ b/examples/python/events/event.py @@ -3,4 +3,4 @@ from hatchet_sdk import Hatchet hatchet = Hatchet() # > Event trigger -hatchet.event.push("user:create", {}) +hatchet.event.push("user:create", {"should_skip": False}) diff --git a/frontend/app/src/next/components/runs/run-event-log/run-event-log.tsx b/frontend/app/src/next/components/runs/run-event-log/run-event-log.tsx index decc7d131..d87695e45 100644 --- a/frontend/app/src/next/components/runs/run-event-log/run-event-log.tsx +++ b/frontend/app/src/next/components/runs/run-event-log/run-event-log.tsx @@ -44,16 +44,12 @@ import { FilterSelect, } from '@/next/components/ui/filters/filters'; import { useFilters } from '@/next/hooks/utils/use-filters'; -import { ROUTES } from '@/next/lib/routes'; import { FilterProvider } from '@/next/hooks/utils/use-filters'; interface RunEventLogProps { filters?: ActivityFilters; workflow: V1WorkflowRun; - onTaskSelect?: ( - event: V1TaskEvent, - options?: Parameters[3], - ) => void; + onTaskSelect?: (event: V1TaskEvent) => void; showFilters?: { search?: boolean; eventType?: boolean; @@ -305,7 +301,7 @@ const EventMessage = ({ event, onTaskSelect }: EventMessageProps) => { className="h-5 p-1 text-xs text-muted-foreground hover:text-muted-foreground/80 border-muted-foreground/50" onClick={(e) => { e.stopPropagation(); - onTaskSelect(event, { taskTab: 'worker' }); + onTaskSelect(event); }} > @@ -525,7 +521,7 @@ function RunEventLogContent({ ]); return ( -
+
{showFilters.search && ( diff --git a/frontend/app/src/next/components/runs/run-id.tsx b/frontend/app/src/next/components/runs/run-id.tsx index d6546b164..ad0e1136e 100644 --- a/frontend/app/src/next/components/runs/run-id.tsx +++ b/frontend/app/src/next/components/runs/run-id.tsx @@ -37,17 +37,7 @@ export function RunId({ ? ROUTES.runs.detail(tenantId, wfRun?.metadata.id || id || '') : taskRun?.type == V1WorkflowType.TASK ? undefined - : ROUTES.runs.detailWithSheet( - tenantId, - taskRun?.workflowRunExternalId || '', - { - type: 'task-detail', - props: { - selectedWorkflowRunId: taskRun?.workflowRunExternalId || '', - selectedTaskId: taskRun?.taskExternalId, - }, - }, - ); + : ROUTES.runs.detail(tenantId, taskRun?.workflowRunExternalId || ''); const displayNameIdPrefix = splitTime(displayName); const friendlyDisplayName = displayNameIdPrefix || displayName; diff --git a/frontend/app/src/next/components/runs/runs-table/columns.tsx b/frontend/app/src/next/components/runs/runs-table/columns.tsx index f276a4bdc..2a779c021 100644 --- a/frontend/app/src/next/components/runs/runs-table/columns.tsx +++ b/frontend/app/src/next/components/runs/runs-table/columns.tsx @@ -23,14 +23,19 @@ import { ROUTES } from '@/next/lib/routes'; import { useRuns } from '@/next/hooks/use-runs'; import { Checkbox } from '@/next/components/ui/checkbox'; import { Button } from '@/next/components/ui/button'; -import { ChevronDownIcon, ChevronRightIcon, Drill } from 'lucide-react'; +import { + ArrowDownFromLine, + ChevronDownIcon, + ChevronRightIcon, +} from 'lucide-react'; import { cn } from '@/next/lib/utils'; export const columns = ( rowClicked?: (row: V1TaskSummary) => void, selectAll?: boolean, -): ColumnDef[] => [ - { + allowSelection: boolean = true, +): ColumnDef[] => { + const selectCheckboxColumn: ColumnDef = { id: 'select', header: ({ table }) => ( , - cell: ({ row }) => { - const status = row.getValue('status') as V1TaskStatus; - return ( -
- -
- ); - }, - filterFn: (row, id, value) => { - return value.includes(row.getValue(id)); - }, - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: 'runId', - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const url = ROUTES.runs.detailWithSheet( - row.original.tenantId, - row.original.workflowRunExternalId || '', - { - type: 'task-detail', - props: { - selectedWorkflowRunId: row.original.workflowRunExternalId || '', - selectedTaskId: row.original.taskExternalId, - }, - }, - ); + }; - return ( -
- { - rowClicked?.(row.original); - }} - /> - {url && ( - e.stopPropagation()} - > - - - )} -
- ); + const contentColumns: ColumnDef[] = [ + { + accessorKey: 'status', + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const status = row.getValue('status') as V1TaskStatus; + return ( +
+ +
+ ); + }, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)); + }, + enableSorting: false, + enableHiding: false, }, - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: 'workflowName', - header: ({ column }) => ( - - ), - cell: ({ row }) =>
{row.getValue('workflowName')}
, - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: 'createdAt', - header: ({ column }) => ( - - ), - cell: ({ row }) => ( -
- {!selectAll ? ( -
- - -
- ) : ( -
- - -
- )} + {showActions && + (!selectAll ? ( +
+ + +
+ ) : ( +
+ + +
+ ))}
row.children || []} /> - - - - + {showPagination && ( + + + + + )} { if (selectedRunDetails?.run) { - setInput( - JSON.stringify((selectedRunDetails.run.input as any).input, null, 2), - ); + setInput(JSON.stringify(selectedRunDetails.run.input, null, 2)); setAddlMeta( JSON.stringify(selectedRunDetails.run.additionalMetadata, null, 2), ); @@ -221,9 +219,7 @@ function TriggerRunModalContent({ data: { input: inputObj, additionalMetadata: addlMetaObj, - triggerAt: new Date( - scheduleTime.getTime() - scheduleTime.getTimezoneOffset() * 60000, - ).toISOString(), + triggerAt: new Date(scheduleTime.getTime()).toISOString(), }, }, { @@ -346,24 +342,39 @@ function TriggerRunModalContent({ From Recent Run - + + + + + {(() => { + const filteredRuns = + recentRuns?.filter( + (run) => run.workflowId === selectedWorkflowId, + ) || []; + + if (filteredRuns.length === 0) { + return ( + + No recent runs available + + ); + } + + return filteredRuns.map((run) => ( + + {getFriendlyWorkflowRunId(run)} + + )); + })()} + + )} diff --git a/frontend/app/src/next/components/ui/docs-button.tsx b/frontend/app/src/next/components/ui/docs-button.tsx index dfc0b19c2..a1c756d30 100644 --- a/frontend/app/src/next/components/ui/docs-button.tsx +++ b/frontend/app/src/next/components/ui/docs-button.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Button, ButtonProps } from './button'; import { BookOpenIcon } from 'lucide-react'; -import { DocRef, useDocs } from '@/next/hooks/use-docs-sheet'; import { cn } from '@/next/lib/utils'; import { Tooltip, @@ -9,6 +8,12 @@ import { TooltipProvider, TooltipTrigger, } from './tooltip'; +import { useSidePanel } from '@/next/hooks/use-side-panel'; + +export type DocRef = { + title: string; + href: string; +}; interface DocsButtonProps extends React.ButtonHTMLAttributes { @@ -20,7 +25,7 @@ interface DocsButtonProps titleOverride?: string; } -const baseDocsUrl = 'https://docs.hatchet.run'; +export const baseDocsUrl = 'https://docs.hatchet.run'; export function DocsButton({ doc, @@ -31,12 +36,19 @@ export function DocsButton({ titleOverride, ...props }: DocsButtonProps) { - const { open } = useDocs(); + const { close: closeSideSheet, open } = useSidePanel(); const handleClick = (e: React.MouseEvent) => { if (method === 'sheet') { e.preventDefault(); - open(doc); + closeSideSheet(); + open({ + type: 'docs', + content: { + href: `${baseDocsUrl}${doc.href}`, + title: doc.title, + }, + }); } else { window.open(`${baseDocsUrl}${doc.href}`, '_blank'); } diff --git a/frontend/app/src/next/components/ui/docs-sheet.tsx b/frontend/app/src/next/components/ui/docs-sheet.tsx deleted file mode 100644 index 8ed942c43..000000000 --- a/frontend/app/src/next/components/ui/docs-sheet.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { Sheet, SheetContent, SheetHeader, SheetTitle } from './sheet'; -import { DocsSheet } from '@/next/hooks/use-docs-sheet'; -import { useIsMobile } from '@/next/hooks/use-mobile'; -import { Cross2Icon, ExternalLinkIcon } from '@radix-ui/react-icons'; - -interface DocsSheetProps { - sheet: DocsSheet; - onClose: () => void; - variant?: 'overlay' | 'push'; -} - -export function DocsSheetComponent({ - sheet, - onClose, - variant = 'push', -}: DocsSheetProps) { - const isMobile = useIsMobile(); - - // If using push variant, render as a side panel instead of using Sheet - if (variant === 'push' && !isMobile) { - return ( -
- {sheet.isOpen && ( -
-
-

- {sheet.title} -

-
- {sheet.url && ( - - - Open in new tab - - )} - -
-
-
- {sheet.url && ( -