Skip to content

Commit f54d1de

Browse files
committed
feat: add pizza table row and skeleton components
1 parent 49aa8a2 commit f54d1de

2 files changed

Lines changed: 264 additions & 0 deletions

File tree

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { useMutation, useQueryClient } from '@tanstack/react-query'
2+
import { ArrowRight, Loader2, Search, Trash } from 'lucide-react'
3+
import { useState } from 'react'
4+
import { toast } from 'sonner'
5+
6+
import { activePizza } from '@/api/active-pizza'
7+
import { disablePizza } from '@/api/disable-pizza'
8+
import { removePizza } from '@/api/remove-pizza'
9+
import { GetPizzasResponse } from '@/api/get-pizzas'
10+
import { PizzaStatus } from '@/components/pizza-status'
11+
import { PizzaType } from '@/components/pizza-type'
12+
import { PizzaSize } from '@/components/pizza-size'
13+
import { Button } from '@/components/ui/button'
14+
import { Dialog, DialogTrigger } from '@/components/ui/dialog'
15+
import { TableCell, TableRow } from '@/components/ui/table'
16+
17+
import { PizzaDetails } from './pizza-details'
18+
19+
export interface PizzaTableRowProps {
20+
pizza: {
21+
pizzaId: string
22+
active: boolean
23+
name: string
24+
description: string
25+
price: number
26+
image: string
27+
size: 'MEDIUM' | 'LARGE' | 'FAMILY'
28+
type: 'SWEET' | 'SALTY'
29+
slug: string
30+
}
31+
}
32+
33+
export function PizzaTableRow({ pizza }: PizzaTableRowProps) {
34+
const [isPizzaDetailsOpen, setIsPizzaDetailsOpen] = useState(false)
35+
const queryClient = useQueryClient()
36+
37+
function updatePizzaActiveOnCache(pizzaId: string, active: boolean) {
38+
const pizzasListingCache = queryClient.getQueriesData<GetPizzasResponse>({
39+
queryKey: ['pizzas'],
40+
})
41+
42+
pizzasListingCache.forEach(([cacheKey, cached]) => {
43+
if (!cached) {
44+
return
45+
}
46+
47+
queryClient.setQueryData<GetPizzasResponse>(cacheKey, {
48+
...cached,
49+
pizzas: cached.pizzas.map((pizza) => {
50+
if (pizza.pizzaId !== pizzaId) {
51+
return pizza
52+
}
53+
54+
return {
55+
...pizza,
56+
active,
57+
}
58+
}),
59+
})
60+
})
61+
62+
toast.success('Status da pizza alterado com sucesso!')
63+
}
64+
65+
const { mutateAsync: activatePizzaFn, isPending: isActivatingPizza } =
66+
useMutation({
67+
mutationFn: activePizza,
68+
onSuccess: async (_, { pizzaId }) => {
69+
updatePizzaActiveOnCache(pizzaId, true)
70+
},
71+
})
72+
73+
const { mutateAsync: disablePizzaFn, isPending: isDisablingPizza } =
74+
useMutation({
75+
mutationFn: disablePizza,
76+
onSuccess: async (_, { pizzaId }) => {
77+
updatePizzaActiveOnCache(pizzaId, false)
78+
},
79+
})
80+
81+
function removePizzaOnCache(pizzaId: string) {
82+
const pizzasListingCache = queryClient.getQueriesData<GetPizzasResponse>({
83+
queryKey: ['pizzas'],
84+
})
85+
86+
pizzasListingCache.forEach(([cacheKey, cached]) => {
87+
if (!cached) {
88+
return
89+
}
90+
91+
queryClient.setQueryData<GetPizzasResponse>(cacheKey, {
92+
...cached,
93+
pizzas: cached.pizzas.filter((pizza) => pizza.pizzaId !== pizzaId)
94+
})
95+
})
96+
97+
toast.success('Pizza removida com sucesso!')
98+
}
99+
100+
const { mutateAsync: removePizzaFn, isPending: isRemovingPizza } =
101+
useMutation({
102+
mutationFn: removePizza,
103+
onSuccess: async (_, { pizzaId }) => {
104+
removePizzaOnCache(pizzaId)
105+
}
106+
})
107+
108+
return (
109+
<TableRow>
110+
<TableCell>
111+
<Dialog onOpenChange={setIsPizzaDetailsOpen} open={isPizzaDetailsOpen}>
112+
<DialogTrigger asChild>
113+
<Button variant="outline" size="xs">
114+
<Search className="h-3 w-3" />
115+
<span className="sr-only">Detalhes da pizza</span>
116+
</Button>
117+
</DialogTrigger>
118+
119+
<PizzaDetails open={isPizzaDetailsOpen} id={pizza.pizzaId} />
120+
</Dialog>
121+
</TableCell>
122+
123+
<TableCell className="font-mono text-xs font-medium">
124+
{pizza.pizzaId}
125+
</TableCell>
126+
127+
<TableCell className="font-medium">{pizza.name}</TableCell>
128+
129+
<TableCell>{pizza.description}</TableCell>
130+
131+
<TableCell>
132+
<PizzaSize size={pizza.size} />
133+
</TableCell>
134+
135+
<TableCell>
136+
<PizzaType type={pizza.type} />
137+
</TableCell>
138+
139+
<TableCell>
140+
<PizzaStatus active={pizza.active} />
141+
</TableCell>
142+
143+
<TableCell>
144+
<span className="font-medium">
145+
{(pizza.price / 100).toLocaleString('pt-BR', {
146+
style: 'currency',
147+
currency: 'BRL',
148+
})}
149+
</span>
150+
</TableCell>
151+
152+
<TableCell>
153+
{pizza.active === false ? (
154+
<Button
155+
variant="outline"
156+
size="xs"
157+
disabled={isActivatingPizza}
158+
onClick={() => activatePizzaFn({ pizzaId: pizza.pizzaId })}
159+
>
160+
Ativar
161+
{isActivatingPizza ? (
162+
<Loader2 className="ml-2 h-3 w-3 animate-spin" />
163+
) : (
164+
<ArrowRight className="ml-2 h-3 w-3" />
165+
)}
166+
</Button>
167+
) : (
168+
<Button
169+
variant="outline"
170+
size="xs"
171+
disabled={isDisablingPizza}
172+
onClick={() => disablePizzaFn({ pizzaId: pizza.pizzaId })}
173+
>
174+
Desativar
175+
{isDisablingPizza ? (
176+
<Loader2 className="ml-2 h-3 w-3 animate-spin" />
177+
) : (
178+
<ArrowRight className="ml-2 h-3 w-3" />
179+
)}
180+
</Button>
181+
)
182+
}
183+
</TableCell>
184+
185+
<TableCell>
186+
<Button
187+
onDoubleClick={() => removePizzaFn({ pizzaId: pizza.pizzaId })}
188+
disabled={isRemovingPizza}
189+
variant="outline"
190+
size="xs"
191+
>
192+
{isRemovingPizza && (
193+
<Loader2 className="mr-2 h-3 w-3 animate-spin" />
194+
)}
195+
<Trash className="h-3 w-3" />
196+
<span className="sr-only">Deletar pizza</span>
197+
</Button>
198+
</TableCell>
199+
</TableRow>
200+
)
201+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Search, Trash } from 'lucide-react'
2+
3+
import { Button } from '@/components/ui/button'
4+
import { Skeleton } from '@/components/ui/skeleton'
5+
import { TableCell, TableRow } from '@/components/ui/table'
6+
7+
export function PizzasTableSkeleton() {
8+
return (
9+
<>
10+
{Array.from({ length: 10 }).map((_, i) => {
11+
return (
12+
<TableRow key={i}>
13+
<TableCell>
14+
<Button variant="outline" size="xs" disabled>
15+
<Search className="h-3 w-3" />
16+
<span className="sr-only">Detalhes da pizza</span>
17+
</Button>
18+
</TableCell>
19+
20+
<TableCell className="font-mono text-xs font-medium">
21+
<Skeleton className="h-4 w-[160px]" />
22+
</TableCell>
23+
24+
<TableCell className="text-muted-foreground">
25+
<Skeleton className="h-4 w-[120px]" />
26+
</TableCell>
27+
28+
<TableCell>
29+
<Skeleton className="h-4 w-auto" />
30+
</TableCell>
31+
32+
<TableCell className="font-medium">
33+
<Skeleton className="h-4 w-[60px]" />
34+
</TableCell>
35+
36+
<TableCell>
37+
<Skeleton className="h-4 w-[60px]" />
38+
</TableCell>
39+
40+
<TableCell>
41+
<Skeleton className="h-4 w-[70px]" />
42+
</TableCell>
43+
44+
<TableCell>
45+
<Skeleton className="h-4 w-[75px]" />
46+
</TableCell>
47+
48+
<TableCell>
49+
<Skeleton className="h-4 w-[100px]" />
50+
</TableCell>
51+
52+
<TableCell>
53+
<Button variant="outline" size="xs" disabled>
54+
<Trash className="h-3 w-3" />
55+
<span className="sr-only">Deletar pizza</span>
56+
</Button>
57+
</TableCell>
58+
</TableRow>
59+
)
60+
})}
61+
</>
62+
)
63+
}

0 commit comments

Comments
 (0)