From 655e7ed3d1cb2ac17d1673cba22f42eb1a680609 Mon Sep 17 00:00:00 2001 From: David Moore Date: Tue, 2 Apr 2024 15:53:55 +1100 Subject: [PATCH] fix: replace headlessui components and cleanup lower screens --- .../src/components/apis/APIExplorer.tsx | 62 +- .../src/components/apis/APIHistory.tsx | 202 +++--- .../frontend/src/components/apis/APIMenu.tsx | 75 +-- .../src/components/events/EventsExplorer.tsx | 44 +- .../src/components/events/EventsHistory.tsx | 78 +-- .../src/components/events/EventsMenu.tsx | 59 +- .../src/components/layout/AppLayout.tsx | 576 ++++++++---------- .../components/shared/HistoryAccordion.tsx | 82 +++ .../shared/ResourceDropdownMenu.tsx | 25 + .../frontend/src/components/shared/Select.tsx | 138 ++--- .../frontend/src/components/shared/Tabs.tsx | 37 +- .../frontend/src/components/shared/index.ts | 3 +- .../components/storage/StorageExplorer.tsx | 40 +- 13 files changed, 681 insertions(+), 740 deletions(-) create mode 100644 pkg/dashboard/frontend/src/components/shared/HistoryAccordion.tsx create mode 100644 pkg/dashboard/frontend/src/components/shared/ResourceDropdownMenu.tsx diff --git a/pkg/dashboard/frontend/src/components/apis/APIExplorer.tsx b/pkg/dashboard/frontend/src/components/apis/APIExplorer.tsx index 9ee4f2c04..d2b36cff1 100644 --- a/pkg/dashboard/frontend/src/components/apis/APIExplorer.tsx +++ b/pkg/dashboard/frontend/src/components/apis/APIExplorer.tsx @@ -6,7 +6,6 @@ import type { LocalStorageHistoryItem, } from '../../types' import { - Select, Badge, Spinner, Tabs, @@ -43,6 +42,15 @@ import { APIMethodBadge } from './APIMethodBadge' import { Button } from '../ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip' import BreadCrumbs from '../layout/BreadCrumbs' +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from '../ui/select' const getTabCount = (rows: FieldRow[]) => { if (!rows) return 0 @@ -391,26 +399,34 @@ const APIExplorer = () => { />
- - items={paths} - label="Endpoint" - id="endpoint-select" - className="w-full" - selected={selectedApiEndpoint} - setSelected={setSelectedApiEndpoint} - display={(v) => ( -
- -
- {v?.api} - {v?.path} -
-
- )} - /> +
@@ -472,7 +488,7 @@ const APIExplorer = () => {
-
+
{
-
+
diff --git a/pkg/dashboard/frontend/src/components/apis/APIHistory.tsx b/pkg/dashboard/frontend/src/components/apis/APIHistory.tsx index dd58a7ce2..73aa7b48a 100644 --- a/pkg/dashboard/frontend/src/components/apis/APIHistory.tsx +++ b/pkg/dashboard/frontend/src/components/apis/APIHistory.tsx @@ -1,14 +1,11 @@ -import type { ApiHistoryItem, Endpoint } from '../../types' -import Badge from '../shared/Badge' -import { formatJSON, getDateString } from '../../lib/utils' -import { Disclosure } from '@headlessui/react' -import { ChevronUpIcon } from '@heroicons/react/20/solid' +import type { ApiHistoryItem } from '../../types' +import { formatJSON } from '../../lib/utils' import { useState } from 'react' import { Tabs } from '../shared' import CodeEditor from './CodeEditor' import APIResponseContent from './APIResponseContent' import TableGroup from '../shared/TableGroup' -import { ScrollArea } from '../ui/scroll-area' +import HistoryAccordion from '../shared/HistoryAccordion' interface Props { history: ApiHistoryItem[] @@ -45,13 +42,14 @@ const APIHistory: React.FC = ({ history, selectedRequest }) => { } return ( - -
- {requestHistory.map((h, idx) => ( - - ))} -
-
+ ({ + label: h.event.api + h.event.request.path, + time: h.time, + status: h.event?.response?.status, + content: , + }))} + /> ) } @@ -65,9 +63,8 @@ function isJSON(data: string | undefined) { return true } -const ApiHistoryAccordion: React.FC = ({ - time, - event: { api, request, response }, +const ApiHistoryAccordionContent: React.FC = ({ + event: { request, response }, }) => { const [tabIndex, setTabIndex] = useState(0) @@ -78,116 +75,81 @@ const ApiHistoryAccordion: React.FC = ({ const jsonTabs = [...tabs, { name: 'Payload' }] return ( - - {({ open }) => ( - <> - -
-
- {response.status && ( - - Status: - {response.status} - - )} -

- {api} - {request.path} -

-
-
-

{getDateString(time)}

- +
+ +
+ {tabIndex === 0 && ( + key && value) + .map(([key, value]) => [ + key.toLowerCase(), + value.join(', '), + ]), + }, + { + name: 'Response Headers', + rows: Object.entries(response.headers ?? []) + .filter(([key, value]) => key && value) + .map(([key, value]) => [key.toLowerCase(), value]), + }, + ]} + /> + )} + {tabIndex === 1 && ( +
+
+

Response Data

+
- - -
-
- -
- {tabIndex === 0 && ( - key && value) - .map(([key, value]) => [ - key.toLowerCase(), - value.join(', '), - ]), - }, - { - name: 'Response Headers', - rows: Object.entries(response.headers ?? []) - .filter(([key, value]) => key && value) - .map(([key, value]) => [key.toLowerCase(), value]), - }, - ]} - /> - )} - {tabIndex === 1 && ( -
-
-

Response Data

- -
-
+ )} + {tabIndex === 2 && ( +
+
+

Request Body

+ -
-

Request Body

- -
-
- {request.queryParams && ( - key && value) - .map(({ key, value }) => [key, value]), - }, - ]} - /> - )} -
-
- )} -
+ title="Request Body" + /> +
+
+ {request.queryParams && ( + key && value) + .map(({ key, value }) => [key, value]), + }, + ]} + /> + )}
- - - )} - + )} +
+
+
) } diff --git a/pkg/dashboard/frontend/src/components/apis/APIMenu.tsx b/pkg/dashboard/frontend/src/components/apis/APIMenu.tsx index 5188a7c4d..c72da811f 100644 --- a/pkg/dashboard/frontend/src/components/apis/APIMenu.tsx +++ b/pkg/dashboard/frontend/src/components/apis/APIMenu.tsx @@ -1,10 +1,16 @@ -import { Menu, Transition } from '@headlessui/react' -import { EllipsisHorizontalIcon } from '@heroicons/react/20/solid' -import { Fragment } from 'react' import type { Endpoint } from '../../types' import { LOCAL_STORAGE_KEY } from './APIExplorer' import { useHistory } from '../../lib/hooks/use-history' -import { cn, formatJSON } from '@/lib/utils' +import { formatJSON } from '@/lib/utils' +import { + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, +} from '../ui/dropdown-menu' +import TrashIcon from '@heroicons/react/24/outline/TrashIcon' +import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline' +import ResourceDropdownMenu from '../shared/ResourceDropdownMenu' interface Props { selected: Endpoint @@ -44,53 +50,20 @@ const APIMenu: React.FC = ({ selected, onAfterClear }) => { } return ( - - - Open options - - - - - {({ active }) => ( - - )} - - - {({ active }) => ( - - )} - - - - + + API Menu + + + + + Export Spec + + + + Clear History + + + ) } diff --git a/pkg/dashboard/frontend/src/components/events/EventsExplorer.tsx b/pkg/dashboard/frontend/src/components/events/EventsExplorer.tsx index 38b668edf..e314551fd 100644 --- a/pkg/dashboard/frontend/src/components/events/EventsExplorer.tsx +++ b/pkg/dashboard/frontend/src/components/events/EventsExplorer.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' import { useWebSocket } from '../../lib/hooks/use-web-socket' import type { APIResponse, EventHistoryItem, Schedule, Topic } from '@/types' -import { Badge, Select, Spinner, Tabs, Loading } from '../shared' +import { Badge, Spinner, Tabs, Loading } from '../shared' import APIResponseContent from '../apis/APIResponseContent' import { fieldRowArrToHeaders, @@ -24,6 +24,14 @@ import { capitalize } from 'radash' import { Button } from '../ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip' import BreadCrumbs from '../layout/BreadCrumbs' +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from '../ui/select' interface Props { workerType: 'schedules' | 'topics' @@ -182,18 +190,28 @@ const EventsExplorer: React.FC = ({ workerType }) => {
{data![workerType] && ( )}
diff --git a/pkg/dashboard/frontend/src/components/events/EventsHistory.tsx b/pkg/dashboard/frontend/src/components/events/EventsHistory.tsx index 4ec1965f1..dc944bfda 100644 --- a/pkg/dashboard/frontend/src/components/events/EventsHistory.tsx +++ b/pkg/dashboard/frontend/src/components/events/EventsHistory.tsx @@ -4,12 +4,9 @@ import type { Topic, TopicHistoryItem, } from '../../types' -import Badge from '../shared/Badge' -import { formatJSON, getDateString } from '../../lib/utils' -import { Disclosure } from '@headlessui/react' -import { ChevronUpIcon } from '@heroicons/react/20/solid' +import { formatJSON } from '@/lib/utils' import CodeEditor from '../apis/CodeEditor' -import { ScrollArea } from '../ui/scroll-area' +import HistoryAccordion from '../shared/HistoryAccordion' interface Props { history: EventHistoryItem[] @@ -33,58 +30,21 @@ const EventsHistory: React.FC = ({ return (
- -
- {requestHistory.map((h, idx) => ( - - ))} -
-
-
- ) -} + { + let payload = '' -const EventHistoryAccordion: React.FC< - EventHistoryItem & Pick -> = ({ event, time, workerType }) => { - let payload = '' + if (workerType === 'topics') { + payload = (h.event as TopicHistoryItem['event']).payload + } - if (workerType === 'topics') { - payload = (event as TopicHistoryItem['event']).payload - } - - const formattedPayload = payload ? formatJSON(payload) : '' + const formattedPayload = payload ? formatJSON(payload) : '' - return ( - - {({ open }) => ( - <> - -
-
- - {event.success ? 'success' : 'failure'} - -

{event.name}

-
-
-

{getDateString(time)}

- {payload && ( - - )} -
-
-
- - {formattedPayload && ( - + return { + label: h.event.name, + time: h.time, + success: Boolean(h.event.success), + content: formattedPayload ? (

Payload

@@ -96,11 +56,11 @@ const EventHistoryAccordion: React.FC< />
-
- )} - - )} -
+ ) : undefined, + } + })} + /> +
) } diff --git a/pkg/dashboard/frontend/src/components/events/EventsMenu.tsx b/pkg/dashboard/frontend/src/components/events/EventsMenu.tsx index fbb093501..23a59d07e 100644 --- a/pkg/dashboard/frontend/src/components/events/EventsMenu.tsx +++ b/pkg/dashboard/frontend/src/components/events/EventsMenu.tsx @@ -1,8 +1,13 @@ -import { Menu, Transition } from '@headlessui/react' -import { EllipsisHorizontalIcon } from '@heroicons/react/20/solid' -import { cn } from '@/lib/utils' -import { Fragment } from 'react' +import { TrashIcon } from '@heroicons/react/20/solid' + import { useHistory } from '../../lib/hooks/use-history' +import ResourceDropdownMenu from '../shared/ResourceDropdownMenu' +import { + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, +} from '../ui/dropdown-menu' interface Props { storageKey: string @@ -37,40 +42,18 @@ const EventsMenu: React.FC = ({ } return ( - - - Open options - - - - - {({ active }) => ( - - )} - - - - + + + {workerType} Menu + + + + + + Clear History + + + ) } diff --git a/pkg/dashboard/frontend/src/components/layout/AppLayout.tsx b/pkg/dashboard/frontend/src/components/layout/AppLayout.tsx index 0cc185fe9..5dfe26d61 100644 --- a/pkg/dashboard/frontend/src/components/layout/AppLayout.tsx +++ b/pkg/dashboard/frontend/src/components/layout/AppLayout.tsx @@ -1,10 +1,4 @@ -import { - Fragment, - type PropsWithChildren, - type ReactNode, - useState, -} from 'react' -import { Dialog, Popover, Transition } from '@headlessui/react' +import type { PropsWithChildren, ReactNode } from 'react' import { DocumentDuplicateIcon, Bars3Icon, @@ -12,7 +6,6 @@ import { XMarkIcon, ClockIcon, ArchiveBoxIcon, - CircleStackIcon, MegaphoneIcon, QuestionMarkCircleIcon, PaperAirplaneIcon, @@ -33,6 +26,8 @@ import { Button } from '../ui/button' import { ExclamationCircleIcon } from '@heroicons/react/20/solid' import { Alert, AlertDescription, AlertTitle } from '../ui/alert' import { Spinner } from '../shared' +import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover' +import { Sheet, SheetContent, SheetTrigger } from '../ui/sheet' const DiscordLogo: React.FC> = ({ className, @@ -103,7 +98,6 @@ const AppLayout: React.FC = ({ routePath = '/', }) => { const { data, state } = useWebSocket() - const [sidebarOpen, setSidebarOpen] = useState(false) // remove trailing slash routePath = routePath !== '/' ? routePath.replace(/\/$/, '') : routePath @@ -149,140 +143,6 @@ const AppLayout: React.FC = ({ <> - - - -
- - -
- - - -
- -
-
- {/* Sidebar component, swap this element with another sidebar if you like */} -
-
- Nitric Logo -
- -
-
-
-
-
-
@@ -344,14 +204,91 @@ const AppLayout: React.FC = ({
- + + + + + +
+
+ Nitric Logo + + {data?.projectName} + +
+ +
+
+
{/* Separator */}
= ({ /> {data?.projectName && (
- {data.projectName} /{' '} + + {data.projectName} + {' '} + /{' '} + + +
+

+ A new version of Nitric is available +

+
+
+
+
+ + Upgrade from version ' + {data.currentVersion}' to ' + {data.latestVersion}' + + +

+ To upgrade, visit the installation docs for + instructions and release notes +

+
+
+
-
-
-

- Reach out to the community -

-
- {communityLinks.map((item) => ( - - - ))} -
-
-
-
- - - - )} +
+
+

+ Reach out to the community +

+
+ {communityLinks.map((item) => ( + + + ))} +
+
+
+ ) : null} - + Local Dashboard - - {({ open }) => ( - <> - +
+ + + + + +
+

+ Need help with your project? +

+ {resourceLinks.map((item) => ( +
+
+
- -
-
-

- Reach out to the community -

-
- {communityLinks.map((item) => ( - - - ))} -
-

- CLI Version: v{data?.currentVersion} -

-
+
+ + {item.name} + + +

+ {item.description} +

- - - - )} - + ))} +
+ +
+
+

+ Reach out to the community +

+
+ {communityLinks.map((item) => ( + + + ))} +
+

+ CLI Version: v{data?.currentVersion} +

+
+
+ + +
diff --git a/pkg/dashboard/frontend/src/components/shared/HistoryAccordion.tsx b/pkg/dashboard/frontend/src/components/shared/HistoryAccordion.tsx new file mode 100644 index 000000000..131655724 --- /dev/null +++ b/pkg/dashboard/frontend/src/components/shared/HistoryAccordion.tsx @@ -0,0 +1,82 @@ +import { ScrollArea } from '@/components/ui/scroll-area' +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion' +import Badge from './Badge' +import { getDateString } from '@/lib/utils/get-date-string' +import { createElement } from 'react' + +interface HistoryAccordionItem { + status?: number + success?: boolean + time: number + label: string + content?: React.ReactNode +} + +interface Props { + items: HistoryAccordionItem[] +} + +const DivElement = (props: React.HTMLProps) => ( +
+) + +const HistoryAccordion = ({ items }: Props) => { + return ( + + + {items.map((item, idx) => { + const TriggerElement = item.content ? AccordionTrigger : DivElement + + return ( + + +
+ {typeof item.success === 'boolean' ? ( + + {item.success ? 'success' : 'failure'} + + ) : ( + item.status && ( + + + Status:{' '} + + {item.status} + + ) + )} +

+ {item.label} +

+

+ {getDateString(item.time)} +

+
+
+ {item.content && ( + +
+
{item.content}
+
+
+ )} +
+ ) + })} +
+
+ ) +} + +export default HistoryAccordion diff --git a/pkg/dashboard/frontend/src/components/shared/ResourceDropdownMenu.tsx b/pkg/dashboard/frontend/src/components/shared/ResourceDropdownMenu.tsx new file mode 100644 index 000000000..f7f092d0d --- /dev/null +++ b/pkg/dashboard/frontend/src/components/shared/ResourceDropdownMenu.tsx @@ -0,0 +1,25 @@ +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '../ui/dropdown-menu' +import { EllipsisHorizontalIcon } from '@heroicons/react/20/solid' + +import { Button } from '../ui/button' +import type { PropsWithChildren } from 'react' + +const ResourceDropdownMenu = ({ children }: PropsWithChildren) => { + return ( + + + + + {children} + + ) +} + +export default ResourceDropdownMenu diff --git a/pkg/dashboard/frontend/src/components/shared/Select.tsx b/pkg/dashboard/frontend/src/components/shared/Select.tsx index a81e1575e..6503e0600 100644 --- a/pkg/dashboard/frontend/src/components/shared/Select.tsx +++ b/pkg/dashboard/frontend/src/components/shared/Select.tsx @@ -1,5 +1,4 @@ import { Fragment } from 'react' -import { Listbox, Transition } from '@headlessui/react' import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid' import { cn } from '@/lib/utils' @@ -23,76 +22,77 @@ const Select = | string>({ className, }: Props) => { return ( - - {({ open }) => ( -
- - {label} - -
- - {display(selected as T)} - - - +
+ // + // {({ open }) => ( + //
+ // + // {label} + // + //
+ // + // {display(selected as T)} + // + // + // - - - {items.map((item, i) => ( - - cn( - active ? 'bg-blue-600 text-white' : 'text-gray-900', - 'relative cursor-default select-none py-2 pl-3 pr-9', - ) - } - value={item} - > - {({ selected, active }) => ( - <> - - {display(item)} - + // + // + // {items.map((item, i) => ( + // + // cn( + // active ? 'bg-blue-600 text-white' : 'text-gray-900', + // 'relative cursor-default select-none py-2 pl-3 pr-9', + // ) + // } + // value={item} + // > + // {({ selected, active }) => ( + // <> + // + // {display(item)} + // - {selected ? ( - - - ) : null} - - )} - - ))} - - -
-
- )} -
+ // {selected ? ( + // + // + // ) : null} + // + // )} + // + // ))} + // + // + //
+ //
+ // )} + //
) } diff --git a/pkg/dashboard/frontend/src/components/shared/Tabs.tsx b/pkg/dashboard/frontend/src/components/shared/Tabs.tsx index 86e786393..8874d692c 100644 --- a/pkg/dashboard/frontend/src/components/shared/Tabs.tsx +++ b/pkg/dashboard/frontend/src/components/shared/Tabs.tsx @@ -1,4 +1,11 @@ import { cn } from '@/lib/utils' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '../ui/select' interface Tab { name: string @@ -17,23 +24,21 @@ const Tabs: React.FC = ({ tabs, index, setIndex, round, pill }) => { return (
- - {/* Use an "onChange" listener to redirect the user to the selected tab URL. */} - setIndex(parseInt(value))} > - {tabs.map((tab, idx) => ( - - ))} - + + + + + {tabs.map((tab, idx) => ( + + {tab.name} + + ))} + +