Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: delete order use case #33

Merged
merged 3 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "gofer-app",
"productName": "gofer-app",
"description": "Gofer app",
"version": "1.6.0",
"version": "1.7.0",
"author": {
"name": "Gabriel Gigante",
"email": "[email protected]"
Expand Down
28 changes: 28 additions & 0 deletions src/api/controllers/orders-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ export interface CreateOrderRequest {

export type CreateOrderResponse = Response<Order>

export interface DeleteOrderRequest {
loggedUserId: string
orderId: string
}

export type DeleteOrderResponse = Response<null>

export class OrdersController {
private readonly usersRepository: UsersRepository
private readonly ordersRepository: OrdersRepository
Expand Down Expand Up @@ -174,4 +181,25 @@ export class OrdersController {

return { data: response, err: null }
}

public async deleteOrder({ loggedUserId, orderId }: DeleteOrderRequest): Promise<DeleteOrderResponse> {
const loggedUser = await this.usersRepository.getUserById(loggedUserId)

if (!loggedUser) {
const err = new WithoutPermissionError()
return { data: null, err }
}

const orderToDelete = await this.ordersRepository.getOrderById(orderId)

if (!orderToDelete) {
const err = new NotFoundError()

return { data: null, err }
}

await this.ordersRepository.deleteOrder(orderId)

return { data: null, err: null }
}
}
4 changes: 4 additions & 0 deletions src/api/exposes/orders-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import {
type GetOrderRequest,
type GetOrderResponse,
type GetOrderTemplateRequest,
type DeleteOrderRequest,
type DeleteOrderResponse,
} from '../controllers/orders-controller'
import { type Response } from '../types/response'

export interface OrdersApi {
list: (data: ListOrdersRequest) => Promise<ListOrdersResponse>
get: (data: GetOrderRequest) => Promise<GetOrderResponse>
create: (data: CreateOrderRequest) => Promise<CreateOrderResponse>
delete: (data: DeleteOrderRequest) => Promise<DeleteOrderResponse>
downloadFile: (data: GetOrderTemplateRequest) => Promise<Response<{ is_canceled: boolean }>>
}

Expand All @@ -24,6 +27,7 @@ const api = {
list: async (data) => await ipcRenderer.invoke('orders:list', data),
get: async (data) => await ipcRenderer.invoke('orders:get', data),
create: async (data) => await ipcRenderer.invoke('orders:create', data),
delete: async (data) => await ipcRenderer.invoke('orders:delete', data),
downloadFile: async (data) => await ipcRenderer.invoke('orders:download-file', data),
} satisfies OrdersApi

Expand Down
3 changes: 3 additions & 0 deletions src/api/handlers/orders-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { writeFile } from 'node:fs/promises'

import {
type CreateOrderRequest,
type DeleteOrderRequest,
type GetOrderRequest,
type GetOrderTemplateRequest,
type ListOrdersRequest,
Expand All @@ -21,6 +22,8 @@ ipcMain.handle('orders:get', async (_event, data: GetOrderRequest) => await orde

ipcMain.handle('orders:create', async (_event, data: CreateOrderRequest) => await ordersController.createOrder(data))

ipcMain.handle('orders:delete', async (_event, data: DeleteOrderRequest) => await ordersController.deleteOrder(data))

export const ordersHandler = (window: BrowserWindow) => {
ipcMain.handle(
'orders:download-file',
Expand Down
8 changes: 8 additions & 0 deletions src/api/repositories/orders-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,12 @@ export class OrdersRepository {

return response!
}

public async deleteOrder(orderId: string): Promise<void> {
await db.transaction(async (tx) => {
await tx.delete(ordersProducts).where(eq(ordersProducts.orderId, orderId))

await tx.delete(orders).where(eq(orders.id, orderId))
})
}
}
29 changes: 28 additions & 1 deletion src/view/hooks/mutations/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { useMutation } from '@tanstack/react-query'

import { queryClient } from '@/view/contexts/ReactQueryContext'

import { type CreateOrderRequest, type CreateOrderResponse } from '@/api/controllers/orders-controller'
import {
type DeleteOrderRequest,
type DeleteOrderResponse,
type CreateOrderRequest,
type CreateOrderResponse,
} from '@/api/controllers/orders-controller'
import { type OrdersApi, apiName } from '@/api/exposes/orders-api'

export function useMutateOnCreateOrder() {
Expand All @@ -29,3 +34,25 @@ export function useMutateOnCreateOrder() {
},
})
}

export function useMutateOnDeleteBrand() {
return useMutation<DeleteOrderResponse['data'], Error, DeleteOrderRequest>({
mutationFn: async ({ loggedUserId, orderId }) => {
const { data, err } = await (window as unknown as Record<typeof apiName, OrdersApi>).ordersApi.delete({
loggedUserId,
orderId,
})

if (err) {
throw err
}

return data
},
onSuccess: (response) => {
queryClient.invalidateQueries({ queryKey: ['orders'] })

return response
},
})
}
30 changes: 30 additions & 0 deletions src/view/pages/Orders/components/DeleteOrderAction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Button } from '@/view/components/ui/button'
import { Alert } from '@/view/components/Alert'

interface DeleteOrderActionProps {
isOpen: boolean
onDelete: () => Promise<void>
onClose: () => void
}

export function DeleteOrderAction({ isOpen, onDelete, onClose }: DeleteOrderActionProps) {
return (
<Alert
isOpen={isOpen}
title="Apagar pedido"
description="Deseja mesmo apagar este pedido?"
cancelButton={<Button variant="outline">Cancelar</Button>}
proceedButton={
<Button
variant="destructive"
onClick={async () => {
await onDelete()
}}
>
Apagar
</Button>
}
onClose={onClose}
/>
)
}
64 changes: 60 additions & 4 deletions src/view/pages/Orders/components/OrdersTable.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useState } from 'react'
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@/view/components/ui/table'
import { FaEye, FaFile } from 'react-icons/fa'
import { FaEye, FaFile, FaTrash } from 'react-icons/fa'

import { Button } from '@/view/components/ui/button'
import { OrdersDetailsDialog } from './OrdersDetailsDialog'
import { DeleteOrderAction } from './DeleteOrderAction'

import { useMutateOnDeleteBrand } from '@/view/hooks/mutations/orders'
import { useAuth } from '@/view/hooks/useAuth'
import { useToast } from '@/view/components/ui/use-toast'

Expand All @@ -23,8 +25,12 @@ const FORMATTER = new Intl.DateTimeFormat('pt-BR', { dateStyle: 'medium', timeSt
export function OrdersTable({ orders }: OrdersTableProps) {
const { toast } = useToast()
const { user } = useAuth()
const [selectedOrder, setSelectedOrder] = useState<string>()

const { mutateAsync } = useMutateOnDeleteBrand()

const [selectedOrderId, setSelectedOrderId] = useState<string>()
const [isOrderDetailsDialogOpen, setIsOrderDetailsDialogOpen] = useState(false)
const [isDeleteOrderDialogOpen, setIsDeleteOrderDialogOpen] = useState(false)

async function handleSaveOrderFile(orderId: string) {
if (!user) return
Expand All @@ -50,6 +56,36 @@ export function OrdersTable({ orders }: OrdersTableProps) {
})
}

function handleRequestOrderDeletion(orderId: string) {
setSelectedOrderId(orderId)
setIsDeleteOrderDialogOpen(true)
}

async function handleDeleteOrder() {
if (!selectedOrderId || !user) return

await mutateAsync(
{ loggedUserId: user.id, orderId: selectedOrderId },
{
onSuccess: () => {
toast({
title: 'Pedido removido com sucesso.',
duration: 3000,
})
},
onError: () => {
toast({
title: 'Houve um erro ao apagar o pedido. Tente novamente.',
duration: 3000,
})
},
},
)

setIsDeleteOrderDialogOpen(false)
setSelectedOrderId(undefined)
}

return (
<>
<Table>
Expand Down Expand Up @@ -79,7 +115,7 @@ export function OrdersTable({ orders }: OrdersTableProps) {
variant="outline"
size="sm"
onClick={() => {
setSelectedOrder(id)
setSelectedOrderId(id)
setIsOrderDetailsDialogOpen(true)
}}
>
Expand All @@ -95,17 +131,37 @@ export function OrdersTable({ orders }: OrdersTableProps) {
>
<FaFile className="w-3 h-3" />
</Button>

<Button
variant="destructive"
size="sm"
onClick={() => {
const order = orders.find((item) => item.id === id)

if (order) {
handleRequestOrderDeletion(order.id)
}
}}
>
<FaTrash className="w-3 h-3" />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>

<OrdersDetailsDialog
orderId={selectedOrder}
orderId={selectedOrderId}
isOpen={isOrderDetailsDialogOpen}
onClose={() => setIsOrderDetailsDialogOpen(false)}
/>

<DeleteOrderAction
onDelete={handleDeleteOrder}
isOpen={isDeleteOrderDialogOpen}
onClose={() => setIsDeleteOrderDialogOpen(false)}
/>
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react'

import { Button } from '@/view/components/ui/button'
import { Alert } from '@/view/components/Alert'

Expand Down