mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-18 23:16:59 +01:00
Confirm deletions
This commit is contained in:
@@ -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<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 duplicateRequest = useDuplicateRequest({ id: requestId, navigateAfter: true });
|
||||
const confirm = useConfirm();
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@@ -23,7 +27,17 @@ export function RequestSettingsDropdown({ requestId, children }: Props) {
|
||||
},
|
||||
{
|
||||
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" />,
|
||||
},
|
||||
]}
|
||||
@@ -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(
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
<RequestSettingsDropdown requestId={requestId}>
|
||||
<RequestActionsDropdown requestId={requestId}>
|
||||
<IconButton
|
||||
color="custom"
|
||||
size="sm"
|
||||
@@ -229,7 +229,7 @@ const _SidebarItem = forwardRef(function SidebarItem(
|
||||
'group-hover/item:opacity-100 focus-visible:opacity-100',
|
||||
)}
|
||||
/>
|
||||
</RequestSettingsDropdown>
|
||||
</RequestActionsDropdown>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<HStack space={1}>
|
||||
<>
|
||||
<IconButton
|
||||
onClick={sidebarDisplay.toggle}
|
||||
className="pointer-events-auto"
|
||||
@@ -27,6 +26,6 @@ export const SidebarDisplayToggle = memo(function SidebarDisplayToggle() {
|
||||
title="Show sidebar"
|
||||
icon="plusCircle"
|
||||
/>
|
||||
</HStack>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -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() {
|
||||
)}
|
||||
>
|
||||
<HeaderSize className="border-transparent">
|
||||
<SidebarDisplayToggle />
|
||||
<HStack space={0.5}>
|
||||
<SidebarActions />
|
||||
</HStack>
|
||||
</HeaderSize>
|
||||
<Sidebar />
|
||||
</motion.div>
|
||||
|
||||
@@ -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: <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]);
|
||||
|
||||
@@ -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')}
|
||||
>
|
||||
<HStack className="flex-1 pointer-events-none" alignItems="center">
|
||||
<SidebarDisplayToggle />
|
||||
<HStack space={0.5} className="flex-1 pointer-events-none" alignItems="center">
|
||||
<SidebarActions />
|
||||
<WorkspaceDropdown className="pointer-events-auto" />
|
||||
</HStack>
|
||||
<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">
|
||||
<IconButton size="sm" title="" icon="magnifyingGlass" />
|
||||
{activeRequest && (
|
||||
<RequestSettingsDropdown requestId={activeRequest?.id}>
|
||||
<RequestActionsDropdown requestId={activeRequest?.id}>
|
||||
<IconButton
|
||||
size="sm"
|
||||
title="Request Options"
|
||||
icon="gear"
|
||||
className="pointer-events-auto"
|
||||
/>
|
||||
</RequestSettingsDropdown>
|
||||
</RequestActionsDropdown>
|
||||
)}
|
||||
</div>
|
||||
</HStack>
|
||||
|
||||
@@ -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 (
|
||||
<HStack space={2} justifyContent="end">
|
||||
<Button className="focus" color="gray" onClick={hide}>
|
||||
<Button className="focus" color="gray" onClick={handleHide}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button className="focus" ref={focusRef} color="primary">
|
||||
Confirm
|
||||
<Button className="focus" ref={focusRef} color={confirmButtonColor} onClick={handleSuccess}>
|
||||
{confirmButtonText}
|
||||
</Button>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
@@ -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 }) => (
|
||||
<Confirm
|
||||
hide={hide}
|
||||
onResult={resolve}
|
||||
confirmButtonColor={confirmButtonColor}
|
||||
confirmButtonText={confirmButtonText}
|
||||
/>
|
||||
),
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user