Skip to content

Commit 6969b38

Browse files
committed
Simplify verbose confirmation dialog components
1 parent 61d1cd1 commit 6969b38

File tree

4 files changed

+161
-248
lines changed

4 files changed

+161
-248
lines changed
+16-37
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
1-
import React from 'react';
2-
import { Button } from '../ui/button';
31
import { Trash2 } from 'lucide-react';
4-
import { api } from '~/utils/api';
52
import { useRouter } from 'next/router';
6-
import {
7-
AlertDialog,
8-
AlertDialogAction,
9-
AlertDialogCancel,
10-
AlertDialogContent,
11-
AlertDialogDescription,
12-
AlertDialogFooter,
13-
AlertDialogHeader,
14-
AlertDialogTitle,
15-
AlertDialogTrigger,
16-
} from '../ui/alert-dialog';
3+
import React from 'react';
4+
import { api } from '~/utils/api';
5+
import { Button } from '../ui/button';
6+
import { SimpleConfirmationDialog } from '../ui/SimpleConfirmationDialog';
177

188
export const DeleteExpense: React.FC<{
199
expenseId: string;
@@ -39,28 +29,17 @@ export const DeleteExpense: React.FC<{
3929
};
4030

4131
return (
42-
<div>
43-
<AlertDialog>
44-
<AlertDialogTrigger asChild>
45-
<Button variant="ghost">
46-
<Trash2 className="text-red-400" size={23} />
47-
</Button>
48-
</AlertDialogTrigger>
49-
<AlertDialogContent className="max-w-xs rounded-lg">
50-
<AlertDialogHeader>
51-
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
52-
<AlertDialogDescription>
53-
This action cannot be undone. This will permanently delete your expense.
54-
</AlertDialogDescription>
55-
</AlertDialogHeader>
56-
<AlertDialogFooter>
57-
<AlertDialogCancel>Cancel</AlertDialogCancel>
58-
<AlertDialogAction size="sm" variant="destructive" onClick={onDeleteExpense}>
59-
Delete
60-
</AlertDialogAction>
61-
</AlertDialogFooter>
62-
</AlertDialogContent>
63-
</AlertDialog>
64-
</div>
32+
<SimpleConfirmationDialog
33+
title="Are you absolutely sure?"
34+
description="This action cannot be undone. This will permanently delete your expense."
35+
hasPermission
36+
onConfirm={onDeleteExpense}
37+
loading={deleteExpenseMutation.isLoading}
38+
variant="destructive"
39+
>
40+
<Button variant="ghost">
41+
<Trash2 className="text-red-400" size={23} />
42+
</Button>
43+
</SimpleConfirmationDialog>
6544
);
6645
};
+20-52
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
1-
import React, { useState } from 'react';
2-
import { Button } from '../ui/button';
31
import { Trash2 } from 'lucide-react';
4-
import { api } from '~/utils/api';
52
import { useRouter } from 'next/router';
6-
import {
7-
AlertDialog,
8-
AlertDialogAction,
9-
AlertDialogCancel,
10-
AlertDialogContent,
11-
AlertDialogDescription,
12-
AlertDialogFooter,
13-
AlertDialogHeader,
14-
AlertDialogTitle,
15-
AlertDialogTrigger,
16-
} from '../ui/alert-dialog';
3+
import React from 'react';
174
import { toast } from 'sonner';
5+
import { api } from '~/utils/api';
6+
import { Button } from '../ui/button';
7+
import { SimpleConfirmationDialog } from '../ui/SimpleConfirmationDialog';
188

199
export const DeleteFriend: React.FC<{
2010
friendId: number;
2111
disabled: boolean;
2212
}> = ({ friendId, disabled }) => {
2313
const router = useRouter();
24-
const [showTrigger, setShowTrigger] = useState(false);
2514

2615
const deleteFriendMutation = api.user.deleteFriend.useMutation();
2716
const utils = api.useUtils();
@@ -33,48 +22,27 @@ export const DeleteFriend: React.FC<{
3322
toast.error('Failed to delete user');
3423
return;
3524
}
36-
setShowTrigger(false);
3725
utils.user.getBalances.invalidate().catch(console.error);
3826

3927
await router.replace(`/balances`);
4028
};
4129

4230
return (
43-
<div>
44-
<AlertDialog
45-
open={showTrigger}
46-
onOpenChange={(status) => (status !== showTrigger ? setShowTrigger(status) : null)}
47-
>
48-
<AlertDialogTrigger asChild>
49-
<Button variant="ghost" className="px-0">
50-
<Trash2 className="text-red-500" size={20} />
51-
</Button>
52-
</AlertDialogTrigger>
53-
<AlertDialogContent className="max-w-xs rounded-lg">
54-
<AlertDialogHeader>
55-
<AlertDialogTitle>{disabled ? '' : 'Are you absolutely sure?'} </AlertDialogTitle>
56-
<AlertDialogDescription>
57-
{disabled
58-
? "Can't remove friend with outstanding balances. Settle up first"
59-
: 'Do you really want to continue'}
60-
</AlertDialogDescription>
61-
</AlertDialogHeader>
62-
<AlertDialogFooter>
63-
<AlertDialogCancel>Cancel</AlertDialogCancel>
64-
{!disabled ? (
65-
<Button
66-
size="sm"
67-
variant="destructive"
68-
onClick={onDeleteFriend}
69-
disabled={deleteFriendMutation.isLoading}
70-
loading={deleteFriendMutation.isLoading}
71-
>
72-
Delete
73-
</Button>
74-
) : null}
75-
</AlertDialogFooter>
76-
</AlertDialogContent>
77-
</AlertDialog>
78-
</div>
31+
<SimpleConfirmationDialog
32+
title={disabled ? '' : 'Are you absolutely sure?'}
33+
description={
34+
disabled
35+
? "Can't remove friend with outstanding balances. Settle up first"
36+
: 'Do you really want to continue'
37+
}
38+
hasPermission={!disabled}
39+
onConfirm={onDeleteFriend}
40+
loading={deleteFriendMutation.isLoading}
41+
variant="destructive"
42+
>
43+
<Button variant="ghost" className="px-0">
44+
<Trash2 className="text-red-500" size={20} />
45+
</Button>
46+
</SimpleConfirmationDialog>
7947
);
8048
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { type VariantProps } from 'class-variance-authority';
2+
import { useState } from 'react';
3+
import {
4+
AlertDialog,
5+
AlertDialogCancel,
6+
AlertDialogContent,
7+
AlertDialogDescription,
8+
AlertDialogFooter,
9+
AlertDialogHeader,
10+
AlertDialogTitle,
11+
AlertDialogTrigger,
12+
} from './alert-dialog';
13+
import { Button, type buttonVariants } from './button';
14+
15+
export const SimpleConfirmationDialog: React.FC<
16+
{
17+
title: string;
18+
description: string;
19+
hasPermission: boolean;
20+
onConfirm: () => void | Promise<void>;
21+
loading: boolean;
22+
children: React.ReactNode;
23+
} & VariantProps<typeof buttonVariants>
24+
> = ({ title, description, hasPermission, onConfirm, loading, variant, children }) => {
25+
const [open, setOpen] = useState(false);
26+
27+
return (
28+
<AlertDialog open={open} onOpenChange={setOpen}>
29+
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
30+
<AlertDialogContent className="max-w-xs rounded-lg">
31+
<AlertDialogHeader>
32+
<AlertDialogTitle>{title}</AlertDialogTitle>
33+
<AlertDialogDescription>{description}</AlertDialogDescription>
34+
</AlertDialogHeader>
35+
<AlertDialogFooter>
36+
<AlertDialogCancel>Cancel</AlertDialogCancel>
37+
{hasPermission && (
38+
<form
39+
onSubmit={async (e) => {
40+
e.preventDefault();
41+
await onConfirm();
42+
setOpen(false);
43+
}}
44+
>
45+
<Button
46+
type="submit"
47+
size="sm"
48+
variant={variant}
49+
disabled={loading}
50+
loading={loading}
51+
>
52+
Confirm
53+
</Button>
54+
</form>
55+
)}
56+
</AlertDialogFooter>
57+
</AlertDialogContent>
58+
</AlertDialog>
59+
);
60+
};

0 commit comments

Comments
 (0)