From 4b74bf81431cc2e03c8e75db8087235fdded9623 Mon Sep 17 00:00:00 2001 From: Gabriel Gigante Date: Tue, 5 Nov 2024 22:44:53 -0300 Subject: [PATCH 1/3] feat: implements delete order use-case --- src/api/controllers/orders-controller.ts | 28 +++++++++++++++++++++++ src/api/exposes/orders-api.ts | 4 ++++ src/api/handlers/orders-handler.ts | 3 +++ src/api/repositories/orders-repository.ts | 8 +++++++ 4 files changed, 43 insertions(+) diff --git a/src/api/controllers/orders-controller.ts b/src/api/controllers/orders-controller.ts index bba3eec..0c4f842 100644 --- a/src/api/controllers/orders-controller.ts +++ b/src/api/controllers/orders-controller.ts @@ -57,6 +57,13 @@ export interface CreateOrderRequest { export type CreateOrderResponse = Response +export interface DeleteOrderRequest { + loggedUserId: string + orderId: string +} + +export type DeleteOrderResponse = Response + export class OrdersController { private readonly usersRepository: UsersRepository private readonly ordersRepository: OrdersRepository @@ -174,4 +181,25 @@ export class OrdersController { return { data: response, err: null } } + + public async deleteOrder({ loggedUserId, orderId }: DeleteOrderRequest): Promise { + 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 } + } } diff --git a/src/api/exposes/orders-api.ts b/src/api/exposes/orders-api.ts index a3e04ce..b1b56a8 100644 --- a/src/api/exposes/orders-api.ts +++ b/src/api/exposes/orders-api.ts @@ -8,6 +8,8 @@ import { type GetOrderRequest, type GetOrderResponse, type GetOrderTemplateRequest, + type DeleteOrderRequest, + type DeleteOrderResponse, } from '../controllers/orders-controller' import { type Response } from '../types/response' @@ -15,6 +17,7 @@ export interface OrdersApi { list: (data: ListOrdersRequest) => Promise get: (data: GetOrderRequest) => Promise create: (data: CreateOrderRequest) => Promise + delete: (data: DeleteOrderRequest) => Promise downloadFile: (data: GetOrderTemplateRequest) => Promise> } @@ -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 diff --git a/src/api/handlers/orders-handler.ts b/src/api/handlers/orders-handler.ts index 57b9586..d56c79b 100644 --- a/src/api/handlers/orders-handler.ts +++ b/src/api/handlers/orders-handler.ts @@ -3,6 +3,7 @@ import { writeFile } from 'node:fs/promises' import { type CreateOrderRequest, + type DeleteOrderRequest, type GetOrderRequest, type GetOrderTemplateRequest, type ListOrdersRequest, @@ -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', diff --git a/src/api/repositories/orders-repository.ts b/src/api/repositories/orders-repository.ts index 74f86a9..90a2e86 100644 --- a/src/api/repositories/orders-repository.ts +++ b/src/api/repositories/orders-repository.ts @@ -110,4 +110,12 @@ export class OrdersRepository { return response! } + + public async deleteOrder(orderId: string): Promise { + await db.transaction(async (tx) => { + await tx.delete(ordersProducts).where(eq(ordersProducts.orderId, orderId)) + + await tx.delete(orders).where(eq(orders.id, orderId)) + }) + } } From eb845575372a2bb3ff28626540040c4ae14070a3 Mon Sep 17 00:00:00 2001 From: Gabriel Gigante Date: Wed, 6 Nov 2024 16:09:59 -0300 Subject: [PATCH 2/3] feat: integrates delete order use-case on the client --- src/view/hooks/mutations/orders.ts | 29 ++++++++- .../Orders/components/DeleteOrderAction.tsx | 30 +++++++++ .../pages/Orders/components/OrdersTable.tsx | 64 +++++++++++++++++-- .../components/DeleteCategoryAction.tsx | 2 - 4 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 src/view/pages/Orders/components/DeleteOrderAction.tsx diff --git a/src/view/hooks/mutations/orders.ts b/src/view/hooks/mutations/orders.ts index 6b09edc..9bf1792 100644 --- a/src/view/hooks/mutations/orders.ts +++ b/src/view/hooks/mutations/orders.ts @@ -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() { @@ -29,3 +34,25 @@ export function useMutateOnCreateOrder() { }, }) } + +export function useMutateOnDeleteBrand() { + return useMutation({ + mutationFn: async ({ loggedUserId, orderId }) => { + const { data, err } = await (window as unknown as Record).ordersApi.delete({ + loggedUserId, + orderId, + }) + + if (err) { + throw err + } + + return data + }, + onSuccess: (response) => { + queryClient.invalidateQueries({ queryKey: ['orders'] }) + + return response + }, + }) +} diff --git a/src/view/pages/Orders/components/DeleteOrderAction.tsx b/src/view/pages/Orders/components/DeleteOrderAction.tsx new file mode 100644 index 0000000..7250fa4 --- /dev/null +++ b/src/view/pages/Orders/components/DeleteOrderAction.tsx @@ -0,0 +1,30 @@ +import { Button } from '@/view/components/ui/button' +import { Alert } from '@/view/components/Alert' + +interface DeleteOrderActionProps { + isOpen: boolean + onDelete: () => Promise + onClose: () => void +} + +export function DeleteOrderAction({ isOpen, onDelete, onClose }: DeleteOrderActionProps) { + return ( + Cancelar} + proceedButton={ + + } + onClose={onClose} + /> + ) +} diff --git a/src/view/pages/Orders/components/OrdersTable.tsx b/src/view/pages/Orders/components/OrdersTable.tsx index bcc1b64..e14efdb 100644 --- a/src/view/pages/Orders/components/OrdersTable.tsx +++ b/src/view/pages/Orders/components/OrdersTable.tsx @@ -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' @@ -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() + + const { mutateAsync } = useMutateOnDeleteBrand() + + const [selectedOrderId, setSelectedOrderId] = useState() const [isOrderDetailsDialogOpen, setIsOrderDetailsDialogOpen] = useState(false) + const [isDeleteOrderDialogOpen, setIsDeleteOrderDialogOpen] = useState(false) async function handleSaveOrderFile(orderId: string) { if (!user) return @@ -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 ( <> @@ -79,7 +115,7 @@ export function OrdersTable({ orders }: OrdersTableProps) { variant="outline" size="sm" onClick={() => { - setSelectedOrder(id) + setSelectedOrderId(id) setIsOrderDetailsDialogOpen(true) }} > @@ -95,6 +131,20 @@ export function OrdersTable({ orders }: OrdersTableProps) { > + + ))} @@ -102,10 +152,16 @@ export function OrdersTable({ orders }: OrdersTableProps) {
setIsOrderDetailsDialogOpen(false)} /> + + setIsDeleteOrderDialogOpen(false)} + /> ) } diff --git a/src/view/pages/Products/components/CategoriesTab/components/DeleteCategoryAction.tsx b/src/view/pages/Products/components/CategoriesTab/components/DeleteCategoryAction.tsx index 128029a..1b725eb 100644 --- a/src/view/pages/Products/components/CategoriesTab/components/DeleteCategoryAction.tsx +++ b/src/view/pages/Products/components/CategoriesTab/components/DeleteCategoryAction.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import { Button } from '@/view/components/ui/button' import { Alert } from '@/view/components/Alert' From ad5827e49485bc989a104a9dcf7a1c876a3e29c9 Mon Sep 17 00:00:00 2001 From: Gabriel Gigante Date: Wed, 6 Nov 2024 16:18:05 -0300 Subject: [PATCH 3/3] chore: bump app version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e2f71f..ba6a37f 100644 --- a/package.json +++ b/package.json @@ -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": "gabrielgigante29@gmail.com"