|
1 | 1 | 'use client' |
2 | 2 |
|
3 | 3 | import { Borrow } from '@/lib/types/borrow' |
4 | | -import { useState } from 'react' |
| 4 | +import { useTransition, useState } from 'react' |
5 | 5 | import { Button, ButtonProps } from '../ui/button' |
6 | | -import { Lock, Unlock } from 'lucide-react' |
7 | | -import { ReturnBorrow } from '@/lib/actions/return-borrow' |
| 6 | +import { Lock, Unlock, Loader } from 'lucide-react' |
| 7 | +import { actionReturnBorrow } from '@/lib/actions/return-borrow' |
8 | 8 | import { formatDate } from '@/lib/utils' |
| 9 | +import { toast } from '../hooks/use-toast' |
9 | 10 |
|
10 | 11 | export const BtnReturnBook: React.FC< |
11 | 12 | ButtonProps & { |
12 | 13 | borrow: Borrow |
13 | 14 | } |
14 | 15 | > = ({ borrow, ...props }) => { |
15 | | - const [confirm, setConfirm] = useState<NodeJS.Timeout>() |
| 16 | + const [confirmTimeout, setConfirmTimeout] = useState<NodeJS.Timeout>() |
| 17 | + const [clientBorrow, setClientBorrow] = useState<Borrow>(borrow) |
| 18 | + const [isPending, startTransition] = useTransition() |
16 | 19 |
|
17 | 20 | const onUnlock = () => { |
18 | 21 | const timerId = setTimeout(() => { |
19 | | - setConfirm(undefined) |
| 22 | + setConfirmTimeout(undefined) |
20 | 23 | }, 3_000) |
21 | | - setConfirm(timerId) |
| 24 | + setConfirmTimeout(timerId) |
22 | 25 | } |
23 | 26 |
|
24 | 27 | const onClick = () => { |
25 | | - ReturnBorrow(borrow.id) |
26 | | - if (confirm) clearTimeout(confirm) |
| 28 | + startTransition(async () => { |
| 29 | + clearTimeout(confirmTimeout) |
| 30 | + const res = await actionReturnBorrow(borrow.id) |
| 31 | + if ('error' in res) { |
| 32 | + toast({ |
| 33 | + title: 'Failed to return book', |
| 34 | + description: res.error, |
| 35 | + variant: 'destructive', |
| 36 | + }) |
| 37 | + return |
| 38 | + } |
| 39 | + // optimistic update |
| 40 | + setClientBorrow((prev) => ({ |
| 41 | + ...prev, |
| 42 | + returning: { |
| 43 | + returned_at: new Date().toISOString(), |
| 44 | + } as Borrow['returning'], |
| 45 | + })) |
| 46 | + toast({ |
| 47 | + title: 'Success', |
| 48 | + description: 'Book returned successfully', |
| 49 | + variant: 'default', |
| 50 | + }) |
| 51 | + }) |
27 | 52 | } |
28 | 53 |
|
29 | | - if (borrow.returning) |
| 54 | + if (clientBorrow.returning) |
30 | 55 | return ( |
31 | 56 | <Button {...props} variant="secondary" disabled> |
32 | | - {formatDate(borrow.returning.returned_at)} |
| 57 | + {formatDate(clientBorrow.returning.returned_at)} |
33 | 58 | </Button> |
34 | 59 | ) |
35 | 60 |
|
36 | | - if (!confirm) { |
| 61 | + if (!confirmTimeout) { |
37 | 62 | return ( |
38 | 63 | <Button onClick={onUnlock} {...props}> |
39 | 64 | <Lock /> |
40 | 65 | Return |
41 | 66 | </Button> |
42 | 67 | ) |
43 | 68 | } |
| 69 | + |
44 | 70 | return ( |
45 | | - <Button onClick={onClick} {...props} variant="default"> |
46 | | - <Unlock /> |
| 71 | + <Button onClick={onClick} {...props} variant="default" disabled={isPending}> |
| 72 | + {isPending ? <Loader className="animate-spin" /> : <Unlock />} |
47 | 73 | Confirm |
48 | 74 | </Button> |
49 | 75 | ) |
|
0 commit comments