mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-17 14:29:46 +02:00
Confirm deletions
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import type { HTMLAttributes, ReactElement } from 'react';
|
import type { HTMLAttributes, ReactElement } from 'react';
|
||||||
|
import { useConfirm } from '../hooks/useConfirm';
|
||||||
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
||||||
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
|
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
|
||||||
|
import { useRequest } from '../hooks/useRequest';
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown } from './core/Dropdown';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
|
|
||||||
@@ -9,9 +11,11 @@ interface Props {
|
|||||||
children: ReactElement<HTMLAttributes<HTMLButtonElement>>;
|
children: ReactElement<HTMLAttributes<HTMLButtonElement>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RequestSettingsDropdown({ requestId, children }: Props) {
|
export function RequestActionsDropdown({ requestId, children }: Props) {
|
||||||
|
const request = useRequest(requestId ?? null);
|
||||||
const deleteRequest = useDeleteRequest(requestId ?? null);
|
const deleteRequest = useDeleteRequest(requestId ?? null);
|
||||||
const duplicateRequest = useDuplicateRequest({ id: requestId, navigateAfter: true });
|
const duplicateRequest = useDuplicateRequest({ id: requestId, navigateAfter: true });
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@@ -23,7 +27,17 @@ export function RequestSettingsDropdown({ requestId, children }: Props) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
onSelect: deleteRequest.mutate,
|
onSelect: async () => {
|
||||||
|
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: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
@@ -13,7 +13,7 @@ import { Button } from './core/Button';
|
|||||||
import { IconButton } from './core/IconButton';
|
import { IconButton } from './core/IconButton';
|
||||||
import { HStack, VStack } from './core/Stacks';
|
import { HStack, VStack } from './core/Stacks';
|
||||||
import { DropMarker } from './DropMarker';
|
import { DropMarker } from './DropMarker';
|
||||||
import { RequestSettingsDropdown } from './RequestSettingsDropdown';
|
import { RequestActionsDropdown } from './RequestActionsDropdown';
|
||||||
import { ToggleThemeButton } from './ToggleThemeButton';
|
import { ToggleThemeButton } from './ToggleThemeButton';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -218,7 +218,7 @@ const _SidebarItem = forwardRef(function SidebarItem(
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<RequestSettingsDropdown requestId={requestId}>
|
<RequestActionsDropdown requestId={requestId}>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="custom"
|
color="custom"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -229,7 +229,7 @@ const _SidebarItem = forwardRef(function SidebarItem(
|
|||||||
'group-hover/item:opacity-100 focus-visible:opacity-100',
|
'group-hover/item:opacity-100 focus-visible:opacity-100',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</RequestSettingsDropdown>
|
</RequestActionsDropdown>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import { memo, useCallback } from 'react';
|
|||||||
import { useCreateRequest } from '../hooks/useCreateRequest';
|
import { useCreateRequest } from '../hooks/useCreateRequest';
|
||||||
import { useSidebarDisplay } from '../hooks/useSidebarDisplay';
|
import { useSidebarDisplay } from '../hooks/useSidebarDisplay';
|
||||||
import { IconButton } from './core/IconButton';
|
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 sidebarDisplay = useSidebarDisplay();
|
||||||
const createRequest = useCreateRequest({ navigateAfter: true });
|
const createRequest = useCreateRequest({ navigateAfter: true });
|
||||||
const handleCreateRequest = useCallback(() => {
|
const handleCreateRequest = useCallback(() => {
|
||||||
@@ -12,7 +11,7 @@ export const SidebarDisplayToggle = memo(function SidebarDisplayToggle() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack space={1}>
|
<>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={sidebarDisplay.toggle}
|
onClick={sidebarDisplay.toggle}
|
||||||
className="pointer-events-auto"
|
className="pointer-events-auto"
|
||||||
@@ -27,6 +26,6 @@ export const SidebarDisplayToggle = memo(function SidebarDisplayToggle() {
|
|||||||
title="Show sidebar"
|
title="Show sidebar"
|
||||||
icon="plusCircle"
|
icon="plusCircle"
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -11,11 +11,12 @@ import { useWindowSize } from 'react-use';
|
|||||||
import { useSidebarDisplay } from '../hooks/useSidebarDisplay';
|
import { useSidebarDisplay } from '../hooks/useSidebarDisplay';
|
||||||
import { WINDOW_FLOATING_SIDEBAR_WIDTH } from '../lib/constants';
|
import { WINDOW_FLOATING_SIDEBAR_WIDTH } from '../lib/constants';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
|
import { HStack } from './core/Stacks';
|
||||||
import { Overlay } from './Overlay';
|
import { Overlay } from './Overlay';
|
||||||
import { RequestResponse } from './RequestResponse';
|
import { RequestResponse } from './RequestResponse';
|
||||||
import { ResizeHandle } from './ResizeHandle';
|
import { ResizeHandle } from './ResizeHandle';
|
||||||
import { Sidebar } from './Sidebar';
|
import { Sidebar } from './Sidebar';
|
||||||
import { SidebarDisplayToggle } from './SidebarDisplayToggle';
|
import { SidebarActions } from './SidebarActions';
|
||||||
import { WorkspaceHeader } from './WorkspaceHeader';
|
import { WorkspaceHeader } from './WorkspaceHeader';
|
||||||
|
|
||||||
const side = { gridArea: 'side' };
|
const side = { gridArea: 'side' };
|
||||||
@@ -127,7 +128,9 @@ export default function Workspace() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<HeaderSize className="border-transparent">
|
<HeaderSize className="border-transparent">
|
||||||
<SidebarDisplayToggle />
|
<HStack space={0.5}>
|
||||||
|
<SidebarActions />
|
||||||
|
</HStack>
|
||||||
</HeaderSize>
|
</HeaderSize>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||||
|
import { useConfirm } from '../hooks/useConfirm';
|
||||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||||
import { useDeleteWorkspace } from '../hooks/useDeleteWorkspace';
|
import { useDeleteWorkspace } from '../hooks/useDeleteWorkspace';
|
||||||
import { useRoutes } from '../hooks/useRoutes';
|
import { useRoutes } from '../hooks/useRoutes';
|
||||||
@@ -21,6 +22,7 @@ export const WorkspaceDropdown = memo(function WorkspaceDropdown({ className }:
|
|||||||
const createWorkspace = useCreateWorkspace({ navigateAfter: true });
|
const createWorkspace = useCreateWorkspace({ navigateAfter: true });
|
||||||
const deleteWorkspace = useDeleteWorkspace(activeWorkspaceId);
|
const deleteWorkspace = useDeleteWorkspace(activeWorkspaceId);
|
||||||
const routes = useRoutes();
|
const routes = useRoutes();
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
const items: DropdownItem[] = useMemo(() => {
|
const items: DropdownItem[] = useMemo(() => {
|
||||||
const workspaceItems = workspaces.map((w) => ({
|
const workspaceItems = workspaces.map((w) => ({
|
||||||
@@ -46,7 +48,17 @@ export const WorkspaceDropdown = memo(function WorkspaceDropdown({ className }:
|
|||||||
{
|
{
|
||||||
label: 'Delete Workspace',
|
label: 'Delete Workspace',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
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]);
|
}, [workspaces, activeWorkspaceId]);
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { memo } from 'react';
|
|||||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||||
import { IconButton } from './core/IconButton';
|
import { IconButton } from './core/IconButton';
|
||||||
import { HStack } from './core/Stacks';
|
import { HStack } from './core/Stacks';
|
||||||
import { RequestSettingsDropdown } from './RequestSettingsDropdown';
|
import { RequestActionsDropdown } from './RequestActionsDropdown';
|
||||||
import { SidebarDisplayToggle } from './SidebarDisplayToggle';
|
import { SidebarActions } from './SidebarActions';
|
||||||
import { WorkspaceDropdown } from './WorkspaceDropdown';
|
import { WorkspaceDropdown } from './WorkspaceDropdown';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -19,8 +19,8 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
|||||||
alignItems="center"
|
alignItems="center"
|
||||||
className={classnames(className, 'w-full h-full')}
|
className={classnames(className, 'w-full h-full')}
|
||||||
>
|
>
|
||||||
<HStack className="flex-1 pointer-events-none" alignItems="center">
|
<HStack space={0.5} className="flex-1 pointer-events-none" alignItems="center">
|
||||||
<SidebarDisplayToggle />
|
<SidebarActions />
|
||||||
<WorkspaceDropdown className="pointer-events-auto" />
|
<WorkspaceDropdown className="pointer-events-auto" />
|
||||||
</HStack>
|
</HStack>
|
||||||
<div className="flex-[2] text-center text-gray-800 text-sm truncate pointer-events-none">
|
<div className="flex-[2] text-center text-gray-800 text-sm truncate pointer-events-none">
|
||||||
@@ -29,14 +29,14 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
|||||||
<div className="flex-1 flex justify-end -mr-2 pointer-events-none">
|
<div className="flex-1 flex justify-end -mr-2 pointer-events-none">
|
||||||
<IconButton size="sm" title="" icon="magnifyingGlass" />
|
<IconButton size="sm" title="" icon="magnifyingGlass" />
|
||||||
{activeRequest && (
|
{activeRequest && (
|
||||||
<RequestSettingsDropdown requestId={activeRequest?.id}>
|
<RequestActionsDropdown requestId={activeRequest?.id}>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="sm"
|
size="sm"
|
||||||
title="Request Options"
|
title="Request Options"
|
||||||
icon="gear"
|
icon="gear"
|
||||||
className="pointer-events-auto"
|
className="pointer-events-auto"
|
||||||
/>
|
/>
|
||||||
</RequestSettingsDropdown>
|
</RequestActionsDropdown>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -1,21 +1,40 @@
|
|||||||
|
import type { ButtonProps } from '../components/core/Button';
|
||||||
import { Button } from '../components/core/Button';
|
import { Button } from '../components/core/Button';
|
||||||
import { HStack } from '../components/core/Stacks';
|
import { HStack } from '../components/core/Stacks';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
hide: () => void;
|
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) => {
|
const focusRef = (el: HTMLButtonElement | null) => {
|
||||||
el?.focus();
|
el?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleHide = () => {
|
||||||
|
onResult(false);
|
||||||
|
hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSuccess = () => {
|
||||||
|
onResult(true);
|
||||||
|
hide();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack space={2} justifyContent="end">
|
<HStack space={2} justifyContent="end">
|
||||||
<Button className="focus" color="gray" onClick={hide}>
|
<Button className="focus" color="gray" onClick={handleHide}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="focus" ref={focusRef} color="primary">
|
<Button className="focus" ref={focusRef} color={confirmButtonColor} onClick={handleSuccess}>
|
||||||
Confirm
|
{confirmButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,14 +1,34 @@
|
|||||||
|
import type { ButtonProps } from '../components/core/Button';
|
||||||
import { useDialog } from '../components/DialogContext';
|
import { useDialog } from '../components/DialogContext';
|
||||||
import { Confirm } from './Confirm';
|
import { Confirm } from './Confirm';
|
||||||
|
|
||||||
export function useConfirm() {
|
export function useConfirm() {
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
return ({ title, description }: { title: string; description?: string }) => {
|
return ({
|
||||||
dialog.show({
|
title,
|
||||||
title,
|
description,
|
||||||
description,
|
confirmButtonColor,
|
||||||
hideX: true,
|
confirmButtonText,
|
||||||
render: Confirm,
|
}: {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
confirmButtonColor?: ButtonProps['color'];
|
||||||
|
confirmButtonText?: string;
|
||||||
|
}) => {
|
||||||
|
return new Promise((resolve: (r: boolean) => void) => {
|
||||||
|
dialog.show({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
hideX: true,
|
||||||
|
render: ({ hide }) => (
|
||||||
|
<Confirm
|
||||||
|
hide={hide}
|
||||||
|
onResult={resolve}
|
||||||
|
confirmButtonColor={confirmButtonColor}
|
||||||
|
confirmButtonText={confirmButtonText}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user