diff --git a/src-web/components/RequestSettingsDropdown.tsx b/src-web/components/RequestActionsDropdown.tsx similarity index 56% rename from src-web/components/RequestSettingsDropdown.tsx rename to src-web/components/RequestActionsDropdown.tsx index 9b453840..a595a843 100644 --- a/src-web/components/RequestSettingsDropdown.tsx +++ b/src-web/components/RequestActionsDropdown.tsx @@ -1,6 +1,8 @@ import type { HTMLAttributes, ReactElement } from 'react'; +import { useConfirm } from '../hooks/useConfirm'; import { useDeleteRequest } from '../hooks/useDeleteRequest'; import { useDuplicateRequest } from '../hooks/useDuplicateRequest'; +import { useRequest } from '../hooks/useRequest'; import { Dropdown } from './core/Dropdown'; import { Icon } from './core/Icon'; @@ -9,9 +11,11 @@ interface Props { children: ReactElement>; } -export function RequestSettingsDropdown({ requestId, children }: Props) { +export function RequestActionsDropdown({ requestId, children }: Props) { + const request = useRequest(requestId ?? null); const deleteRequest = useDeleteRequest(requestId ?? null); const duplicateRequest = useDuplicateRequest({ id: requestId, navigateAfter: true }); + const confirm = useConfirm(); return ( { + const confirmed = await confirm({ + title: 'Delete Request', + description: `Are you sure you want to delete "${request?.name}"?`, + confirmButtonColor: 'danger', + confirmButtonText: 'Delete', + }); + if (confirmed) { + deleteRequest.mutate(); + } + }, leftSlot: , }, ]} diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index c9da86ba..d9366d99 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -13,7 +13,7 @@ import { Button } from './core/Button'; import { IconButton } from './core/IconButton'; import { HStack, VStack } from './core/Stacks'; import { DropMarker } from './DropMarker'; -import { RequestSettingsDropdown } from './RequestSettingsDropdown'; +import { RequestActionsDropdown } from './RequestActionsDropdown'; import { ToggleThemeButton } from './ToggleThemeButton'; interface Props { @@ -218,7 +218,7 @@ const _SidebarItem = forwardRef(function SidebarItem( )} - + - + ); diff --git a/src-web/components/SidebarDisplayToggle.tsx b/src-web/components/SidebarActions.tsx similarity index 85% rename from src-web/components/SidebarDisplayToggle.tsx rename to src-web/components/SidebarActions.tsx index b418aea8..b83b2052 100644 --- a/src-web/components/SidebarDisplayToggle.tsx +++ b/src-web/components/SidebarActions.tsx @@ -2,9 +2,8 @@ import { memo, useCallback } from 'react'; import { useCreateRequest } from '../hooks/useCreateRequest'; import { useSidebarDisplay } from '../hooks/useSidebarDisplay'; import { IconButton } from './core/IconButton'; -import { HStack } from './core/Stacks'; -export const SidebarDisplayToggle = memo(function SidebarDisplayToggle() { +export const SidebarActions = memo(function SidebarDisplayToggle() { const sidebarDisplay = useSidebarDisplay(); const createRequest = useCreateRequest({ navigateAfter: true }); const handleCreateRequest = useCallback(() => { @@ -12,7 +11,7 @@ export const SidebarDisplayToggle = memo(function SidebarDisplayToggle() { }, []); return ( - + <> - + ); }); diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx index 615bb7d9..4639f645 100644 --- a/src-web/components/Workspace.tsx +++ b/src-web/components/Workspace.tsx @@ -11,11 +11,12 @@ import { useWindowSize } from 'react-use'; import { useSidebarDisplay } from '../hooks/useSidebarDisplay'; import { WINDOW_FLOATING_SIDEBAR_WIDTH } from '../lib/constants'; import { Button } from './core/Button'; +import { HStack } from './core/Stacks'; import { Overlay } from './Overlay'; import { RequestResponse } from './RequestResponse'; import { ResizeHandle } from './ResizeHandle'; import { Sidebar } from './Sidebar'; -import { SidebarDisplayToggle } from './SidebarDisplayToggle'; +import { SidebarActions } from './SidebarActions'; import { WorkspaceHeader } from './WorkspaceHeader'; const side = { gridArea: 'side' }; @@ -127,7 +128,9 @@ export default function Workspace() { )} > - + + + diff --git a/src-web/components/WorkspaceDropdown.tsx b/src-web/components/WorkspaceDropdown.tsx index 09ebe943..caa7a852 100644 --- a/src-web/components/WorkspaceDropdown.tsx +++ b/src-web/components/WorkspaceDropdown.tsx @@ -1,6 +1,7 @@ import classnames from 'classnames'; import { memo, useMemo } from 'react'; import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; +import { useConfirm } from '../hooks/useConfirm'; import { useCreateWorkspace } from '../hooks/useCreateWorkspace'; import { useDeleteWorkspace } from '../hooks/useDeleteWorkspace'; import { useRoutes } from '../hooks/useRoutes'; @@ -21,6 +22,7 @@ export const WorkspaceDropdown = memo(function WorkspaceDropdown({ className }: const createWorkspace = useCreateWorkspace({ navigateAfter: true }); const deleteWorkspace = useDeleteWorkspace(activeWorkspaceId); const routes = useRoutes(); + const confirm = useConfirm(); const items: DropdownItem[] = useMemo(() => { const workspaceItems = workspaces.map((w) => ({ @@ -46,7 +48,17 @@ export const WorkspaceDropdown = memo(function WorkspaceDropdown({ className }: { label: 'Delete Workspace', leftSlot: , - onSelect: () => deleteWorkspace.mutate(), + onSelect: async () => { + const confirmed = await confirm({ + title: 'Delete Workspace', + description: `Are you sure you want to delete "${activeWorkspace?.name}"?`, + confirmButtonColor: 'danger', + confirmButtonText: 'Delete', + }); + if (confirmed) { + deleteWorkspace.mutate(); + } + }, }, ]; }, [workspaces, activeWorkspaceId]); diff --git a/src-web/components/WorkspaceHeader.tsx b/src-web/components/WorkspaceHeader.tsx index 404d2a8c..23bc176c 100644 --- a/src-web/components/WorkspaceHeader.tsx +++ b/src-web/components/WorkspaceHeader.tsx @@ -3,8 +3,8 @@ import { memo } from 'react'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { IconButton } from './core/IconButton'; import { HStack } from './core/Stacks'; -import { RequestSettingsDropdown } from './RequestSettingsDropdown'; -import { SidebarDisplayToggle } from './SidebarDisplayToggle'; +import { RequestActionsDropdown } from './RequestActionsDropdown'; +import { SidebarActions } from './SidebarActions'; import { WorkspaceDropdown } from './WorkspaceDropdown'; interface Props { @@ -19,8 +19,8 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop alignItems="center" className={classnames(className, 'w-full h-full')} > - - + +
@@ -29,14 +29,14 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
{activeRequest && ( - + - + )}
diff --git a/src-web/hooks/Confirm.tsx b/src-web/hooks/Confirm.tsx index 7f2e703e..573fe5cb 100644 --- a/src-web/hooks/Confirm.tsx +++ b/src-web/hooks/Confirm.tsx @@ -1,21 +1,40 @@ +import type { ButtonProps } from '../components/core/Button'; import { Button } from '../components/core/Button'; import { HStack } from '../components/core/Stacks'; interface Props { hide: () => void; + onResult: (result: boolean) => void; + confirmButtonColor?: ButtonProps['color']; + confirmButtonText?: string; } -export function Confirm({ hide }: Props) { +export function Confirm({ + hide, + onResult, + confirmButtonColor = 'primary', + confirmButtonText = 'Confirm', +}: Props) { const focusRef = (el: HTMLButtonElement | null) => { el?.focus(); }; + const handleHide = () => { + onResult(false); + hide(); + }; + + const handleSuccess = () => { + onResult(true); + hide(); + }; + return ( - - ); diff --git a/src-web/hooks/useConfirm.tsx b/src-web/hooks/useConfirm.tsx index efb6da84..5ba0cf65 100644 --- a/src-web/hooks/useConfirm.tsx +++ b/src-web/hooks/useConfirm.tsx @@ -1,14 +1,34 @@ +import type { ButtonProps } from '../components/core/Button'; import { useDialog } from '../components/DialogContext'; import { Confirm } from './Confirm'; export function useConfirm() { const dialog = useDialog(); - return ({ title, description }: { title: string; description?: string }) => { - dialog.show({ - title, - description, - hideX: true, - render: Confirm, + return ({ + title, + description, + confirmButtonColor, + confirmButtonText, + }: { + title: string; + description?: string; + confirmButtonColor?: ButtonProps['color']; + confirmButtonText?: string; + }) => { + return new Promise((resolve: (r: boolean) => void) => { + dialog.show({ + title, + description, + hideX: true, + render: ({ hide }) => ( + + ), + }); }); }; }