feat(c3): consulta de pedidos — schema, api, web (OrdersModule + ClientDetailPage)
- Prisma: Order, OrderItem, OrderStatusHistory + migration - Seed: 17 pedidos em 7 clientes com itens, histórico e desnorm de clientes - @sar/api-interface: contratos Zod (OrderSummary, OrderDetail, OrderListQuery, etc.) - API: GET /orders, GET /orders/:id, GET /clients/:id/orders (últimos 10) - Web: OrdersPage (lista + filtro status/número + pending_approval highlighted) - Web: ClientDetailPage (ficha completa + últimos 10 pedidos) - Web: /pedidos e /pedidos/$id adicionados ao router; ClientDetailPage substitui placeholder Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
56
apps/web/src/lib/queries/orders.ts
Normal file
56
apps/web/src/lib/queries/orders.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
OrderListResponseSchema,
|
||||
OrderDetailSchema,
|
||||
type OrderListQuery,
|
||||
type OrderListResponse,
|
||||
type OrderDetail,
|
||||
type OrderSummary,
|
||||
} from '@sar/api-interface';
|
||||
import { apiFetch } from '../api-client';
|
||||
|
||||
export function useOrderList(params: Partial<OrderListQuery> = {}) {
|
||||
const search = new URLSearchParams();
|
||||
if (params.clientId) search.set('clientId', params.clientId);
|
||||
if (params.status) search.set('status', params.status);
|
||||
if (params.number) search.set('number', params.number);
|
||||
if (params.from) search.set('from', params.from);
|
||||
if (params.to) search.set('to', params.to);
|
||||
if (params.page) search.set('page', String(params.page));
|
||||
if (params.limit) search.set('limit', String(params.limit));
|
||||
|
||||
const qs = search.toString();
|
||||
return useQuery<OrderListResponse>({
|
||||
queryKey: ['orders', params],
|
||||
queryFn: async () => {
|
||||
const res = await apiFetch(`/orders${qs ? `?${qs}` : ''}`);
|
||||
if (!res.ok) throw new Error(`orders list error ${res.status}`);
|
||||
return OrderListResponseSchema.parse(await res.json());
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useOrderDetail(id: string | undefined) {
|
||||
return useQuery<OrderDetail>({
|
||||
queryKey: ['orders', id],
|
||||
enabled: !!id,
|
||||
queryFn: async () => {
|
||||
const res = await apiFetch(`/orders/${id}`);
|
||||
if (!res.ok) throw new Error(`order detail error ${res.status}`);
|
||||
return OrderDetailSchema.parse(await res.json());
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useClientOrders(clientId: string | undefined) {
|
||||
return useQuery<OrderSummary[]>({
|
||||
queryKey: ['clients', clientId, 'orders'],
|
||||
enabled: !!clientId,
|
||||
queryFn: async () => {
|
||||
const res = await apiFetch(`/clients/${clientId}/orders`);
|
||||
if (!res.ok) throw new Error(`client orders error ${res.status}`);
|
||||
const data = await res.json();
|
||||
return data as OrderSummary[];
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import { createRouter, createRootRoute, createRoute, Outlet } from '@tanstack/re
|
||||
import { AppShell } from '../components/layout/AppShell';
|
||||
import { RafaelPainel } from '../cockpits/rafael/RafaelPainel';
|
||||
import { ClientsPage } from '../cockpits/rafael/ClientsPage';
|
||||
import { ClientDetailPage } from '../cockpits/rafael/ClientDetailPage';
|
||||
import { OrdersPage } from '../cockpits/rafael/OrdersPage';
|
||||
|
||||
const rootRoute = createRootRoute({
|
||||
component: () => (
|
||||
@@ -29,15 +31,26 @@ const clientesRoute = createRoute({
|
||||
component: ClientsPage,
|
||||
});
|
||||
|
||||
// Placeholder detail route — ClientDetailPage virá em próxima iteração de C2
|
||||
const clienteDetailRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/clientes/$id',
|
||||
component: ClientDetailPage,
|
||||
});
|
||||
|
||||
const pedidosRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/pedidos',
|
||||
component: OrdersPage,
|
||||
});
|
||||
|
||||
const pedidoDetailRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/pedidos/$id',
|
||||
component: () => {
|
||||
const { id } = clienteDetailRoute.useParams();
|
||||
const { id } = pedidoDetailRoute.useParams();
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
<p>Ficha do cliente {id} — em construção</p>
|
||||
<p>Detalhe do pedido {id} — em construção</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -48,6 +61,8 @@ const routeTree = rootRoute.addChildren([
|
||||
rafaelRoute,
|
||||
clientesRoute,
|
||||
clienteDetailRoute,
|
||||
pedidosRoute,
|
||||
pedidoDetailRoute,
|
||||
]);
|
||||
|
||||
export const router = createRouter({
|
||||
|
||||
Reference in New Issue
Block a user