Files
590ed362ab feat: Redesign for the integrations pages (#30476)
* Initial commit.

* Minor type fixes.

* Add a integration for Queues. Refactor some of the integration layout.

* Migrate the Cron integration to the new style.

* Add useInstalledIntegrations hook.

* Add an integration entry for vault.

* Add an integration entry for GraphiQL.

* Add supabase webhooks.

* Feat/integrations get layout (#30538)

* scroll based icon

* Update header.tsx

* remove dep from overview

* moar

* more table stuff

* moar

* alt nav put in

* fix MotionNumber issues

* more

* trying both layouts

* Fix bunch of type errors.

---------

Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>

* Migrate Vercel and Github files to their own folders.

* Switch all integrations with the new designs.

* More fixes for links, pages structure and other random stuff.

* Remove unneeded file.

* Another set of fixes. Migrated most of the extension integrations.

* Migrated Vault and webhooks to the new style.

* Various fixes to make the animation work.

* Remove extra code.

* Tiny fixes 😬 i swear its tiny

* Refactor IntegrationOverviewTab

* chore/ update integrations routes (#30585)

* init

* add child support in tabs

* add webhooks

* Update IntegrationPageHandler.tsx

* fix id issues

* use messageId instead

* animation tweaks

* Move the description to the wrappers array.

* The useInstalledIntegrations now provides integrations which could be installed.

* Add static content for the various integrations.

* Move the page handler logic into the integrations definitions.

* Clean up some extra code.

* Add logic to make the overview tab the default tab.

* Don't show the header until the integration id has been checked.

* Add logic to the integration pages to avoid weird loading bugs, deselecting tabs if the integration hasn't been installed etc.

* Fix the webhooks overview tab.

* Fix the buttons for enabling extensions.

* Add padding to all custom tab contents.

* Small fixes

* Prettier lint

* Fix icon color + add empty state for when available integrations are all installed

* Fix ts errors

* Fiox

* Add enable webhooks cta

* Fix key

* Fix all lints

* Fix the queues create sheet.

* Fix the deletion of wrappers.

* Fix the minimum version alert for the wrappers extension.

* Make the queues table fit the whole container.

* Fix an issue which reset the tab when installing an extension.

* Address comments

* Add loading state for installed integrations in side nav

* Fix edit secret not rendering value in input field after subsequent openings

* Fix vault keys auto filling search input with vault

* Fix search input placeholder for cron

* Minor fix in install database extension copy

* Fix a bad redirect when reloading.

* Fix bad url redirects.

* Fix scrolling in create new/edit wrapper sheet.

* Add y padding to the wrappers rows.

* Fix merge errors.

* More merge fixes.

* Fix bad imports during the merge.

---------

Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2024-11-25 13:26:22 +01:00

224 lines
7.7 KiB
TypeScript

import { useEscapeKeydown } from '@radix-ui/react-use-escape-keydown'
import { isNil, noop } from 'lodash'
import { Archive, Clock12, Trash2, X } from 'lucide-react'
import { useState } from 'react'
import { useParams } from 'common'
import { MonacoEditor } from 'components/grid/components/common/MonacoEditor'
import { RowAction, RowData } from 'components/interfaces/Auth/Users/UserOverview'
import { useDatabaseQueueMessageArchiveMutation } from 'data/database-queues/database-queue-messages-archive-mutation'
import { useDatabaseQueueMessageDeleteMutation } from 'data/database-queues/database-queue-messages-delete-mutation'
import { PostgresQueueMessage } from 'data/database-queues/database-queue-messages-infinite-query'
import { useDatabaseQueueMessageReadMutation } from 'data/database-queues/database-queue-messages-read-mutation'
import dayjs from 'dayjs'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { prettifyJSON } from 'lib/helpers'
import {
Button,
ResizablePanel,
Separator,
Tabs_Shadcn_,
TabsContent_Shadcn_,
TabsList_Shadcn_,
TabsTrigger_Shadcn_,
} from 'ui'
export const DATE_FORMAT = 'DD MMM, YYYY HH:mm'
interface MessageDetailsPanelProps {
selectedMessage: PostgresQueueMessage
setSelectedMessage: (value: number | null) => void
}
const tryFormatInitialValue = (value: string) => {
try {
const jsonValue = JSON.parse(value)
return JSON.stringify(jsonValue)
} catch (err) {
if (typeof value === 'string') {
return value.replaceAll(`\"`, `"`)
} else {
return JSON.stringify(value)
}
}
}
export const MessageDetailsPanel = ({
selectedMessage,
setSelectedMessage,
}: MessageDetailsPanelProps) => {
const { id: _id, childId: queueName } = useParams()
const project = useSelectedProject()
useEscapeKeydown(() => setSelectedMessage(null))
const {
mutate: archiveMessage,
isLoading: isLoadingArchive,
isSuccess: isSuccessArchive,
} = useDatabaseQueueMessageArchiveMutation()
const {
mutate: readMessage,
isLoading: isLoadingRead,
isSuccess: isSuccessRead,
} = useDatabaseQueueMessageReadMutation()
const {
mutate: deleteMessage,
isLoading: isLoadingDelete,
isSuccess: isSuccessDelete,
} = useDatabaseQueueMessageDeleteMutation()
const initialValue = JSON.stringify(selectedMessage?.message)
const jsonString = prettifyJSON(!isNil(initialValue) ? tryFormatInitialValue(initialValue) : '')
const [view, setView] = useState<'details' | 'suggestion'>('details')
return (
<ResizablePanel
defaultSize={30}
maxSize={45}
minSize={30}
collapsible
onCollapse={() => setSelectedMessage(null)}
className="bg-studio border-t pointer-events-auto"
>
<Button
type="text"
className="absolute top-3 right-3 px-1"
icon={<X />}
onClick={() => setSelectedMessage(null)}
/>
<Tabs_Shadcn_
value={view}
className="flex flex-col h-full"
onValueChange={(value: any) => {
setView(value)
}}
>
<TabsList_Shadcn_ className="px-5 flex gap-x-4 min-h-[46px]">
<TabsTrigger_Shadcn_
value="details"
className="px-0 pb-0 h-full text-xs data-[state=active]:bg-transparent !shadow-none"
>
Overview
</TabsTrigger_Shadcn_>
</TabsList_Shadcn_>
<TabsContent_Shadcn_ value="details" className="w-full mt-0 overflow-y-auto grow">
<div className="flex flex-col px-4 py-4 text-sm">
<RowData property="Message ID" value={`${selectedMessage.msg_id}`} />
<RowData
property="Added at"
value={dayjs(selectedMessage.enqueued_at).format(DATE_FORMAT)}
/>
<RowData
property="Available at"
value={dayjs(selectedMessage.vt).format(DATE_FORMAT)}
/>
<RowData property="Retries" value={`${selectedMessage.read_ct}`} />
<div>
<h3 className="text-foreground-light py-1">Payload</h3>
<MonacoEditor
key={selectedMessage.msg_id}
onChange={noop}
width="100%"
value={jsonString || 'NULL'}
language="json"
readOnly
/>
</div>
</div>
<Separator />
<div className="flex flex-col px-4 py-4 -space-y-1">
{!selectedMessage.archived_at ? (
<>
<RowAction
title="Postpone message"
description="The message will be postponed and won't show up in reads for 60 seconds."
button={{
icon: <Clock12 />,
text: 'Postpone',
isLoading: isLoadingRead,
onClick: () => {
readMessage({
projectRef: project!.ref,
connectionString: project?.connectionString,
queryName: queueName!,
messageId: selectedMessage.msg_id,
duration: 60,
})
},
}}
success={
isSuccessRead
? {
title: 'Postponed',
description: 'The message was postponed for 60 seconds.',
}
: undefined
}
/>
<RowAction
title="Archive message"
description="The message will be marked as archived and hidden from future reads by consumers. You can still access the message later."
button={{
icon: <Archive />,
text: 'Archive',
isLoading: isLoadingArchive,
type: 'warning',
onClick: () => {
archiveMessage({
projectRef: project!.ref,
connectionString: project?.connectionString,
queryName: queueName!,
messageId: selectedMessage.msg_id,
})
},
}}
success={
isSuccessArchive
? {
title: 'Archived',
description: 'The message was archived successfully.',
}
: undefined
}
/>
<RowAction
title="Delete message"
description="The message cannot be recovered afterwards."
button={{
icon: <Trash2 />,
text: 'Delete',
type: 'danger',
isLoading: isLoadingDelete,
onClick: () => {
deleteMessage({
projectRef: project!.ref,
connectionString: project?.connectionString,
queueName: queueName!,
messageId: selectedMessage.msg_id,
})
},
}}
success={
isSuccessDelete
? {
title: 'Deleted',
description: 'The message was deleted successfully.',
}
: undefined
}
/>
</>
) : null}
</div>
</TabsContent_Shadcn_>
</Tabs_Shadcn_>
</ResizablePanel>
)
}