mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-21 23:19:09 +00:00
refactor: add confirmation dialogs for destructive actions
This commit is contained in:
committed by
Carlos Valente
parent
3838bdc54d
commit
2d9909577e
@@ -1,5 +1,10 @@
|
||||
import { CustomViewSummary } from 'ontime-types';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { deleteCustomView } from '../../../../common/api/customViews';
|
||||
import { maybeAxiosError } from '../../../../common/api/utils';
|
||||
import Button from '../../../../common/components/buttons/Button';
|
||||
import Dialog from '../../../../common/components/dialog/Dialog';
|
||||
import * as Panel from '../../panel-utils/PanelUtils';
|
||||
import CustomViewsListItem from './CustomViewsListItem';
|
||||
|
||||
@@ -13,21 +18,71 @@ interface CustomViewsListProps {
|
||||
}
|
||||
|
||||
export default function CustomViewsList({ views, onOpenUpload, onMutate, onError }: CustomViewsListProps) {
|
||||
const [targetSlug, setTargetSlug] = useState<string | null>(null);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
const openDelete = (slug: string) => {
|
||||
setTargetSlug(slug);
|
||||
};
|
||||
|
||||
const submitDelete = async () => {
|
||||
if (!targetSlug) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsDeleting(true);
|
||||
await deleteCustomView(targetSlug);
|
||||
onMutate();
|
||||
} catch (error) {
|
||||
onError(maybeAxiosError(error));
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
setTargetSlug(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Panel.Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>URL</th>
|
||||
<th className={style.actionsHeader} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{views.length === 0 && <Panel.TableEmpty handleClick={onOpenUpload} label='No custom views yet' />}
|
||||
{views.map((view, index) => (
|
||||
<CustomViewsListItem key={view.slug} slug={view.slug} index={index} onMutate={onMutate} onError={onError} />
|
||||
))}
|
||||
</tbody>
|
||||
</Panel.Table>
|
||||
<>
|
||||
<Panel.Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>URL</th>
|
||||
<th className={style.actionsHeader} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{views.length === 0 && <Panel.TableEmpty handleClick={onOpenUpload} label='No custom views yet' />}
|
||||
{views.map((view, index) => (
|
||||
<CustomViewsListItem
|
||||
key={view.slug}
|
||||
slug={view.slug}
|
||||
index={index}
|
||||
onDelete={openDelete}
|
||||
onError={onError}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</Panel.Table>
|
||||
<Dialog
|
||||
isOpen={targetSlug !== null}
|
||||
onClose={() => setTargetSlug(null)}
|
||||
title='Delete custom view'
|
||||
showBackdrop
|
||||
showCloseButton
|
||||
bodyElements='You will permanently delete this file. Are you sure?'
|
||||
footerElements={
|
||||
<>
|
||||
<Button size='large' onClick={() => setTargetSlug(null)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant='destructive' size='large' onClick={submitDelete} loading={isDeleting}>
|
||||
Delete custom view
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IoDownloadOutline, IoOpenOutline, IoTrash } from 'react-icons/io5';
|
||||
|
||||
import { deleteCustomView, downloadCustomView } from '../../../../common/api/customViews';
|
||||
import { downloadCustomView } from '../../../../common/api/customViews';
|
||||
import { maybeAxiosError } from '../../../../common/api/utils';
|
||||
import IconButton from '../../../../common/components/buttons/IconButton';
|
||||
import { handleLinks } from '../../../../common/utils/linkUtils';
|
||||
@@ -12,11 +12,11 @@ import style from './CustomViews.module.scss';
|
||||
interface CustomViewsListItemProps {
|
||||
slug: string;
|
||||
index: number;
|
||||
onMutate: () => void;
|
||||
onDelete: (slug: string) => void;
|
||||
onError: (message: string) => void;
|
||||
}
|
||||
|
||||
export default function CustomViewsListItem({ slug, index, onMutate, onError }: CustomViewsListItemProps) {
|
||||
export default function CustomViewsListItem({ slug, index, onDelete, onError }: CustomViewsListItemProps) {
|
||||
const handlePreview = () => {
|
||||
handleLinks(`external/${encodeURIComponent(slug)}/`);
|
||||
};
|
||||
@@ -29,15 +29,6 @@ export default function CustomViewsListItem({ slug, index, onMutate, onError }:
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteCustomView(slug);
|
||||
onMutate();
|
||||
} catch (error) {
|
||||
onError(maybeAxiosError(error));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>{slug}</td>
|
||||
@@ -62,7 +53,7 @@ export default function CustomViewsListItem({ slug, index, onMutate, onError }:
|
||||
</IconButton>
|
||||
<IconButton
|
||||
variant='ghosted-destructive'
|
||||
onClick={handleDelete}
|
||||
onClick={() => onDelete(slug)}
|
||||
aria-label='Delete custom view'
|
||||
data-testid={`custom-view__delete_${index}`}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user