fix(ui): align shared board plumbing with ky v2 and react-router

- api.ts: use ky v2 hook object args, prefix (not prefixUrl), and normalizeApiRequestError
- replace react-router-dom with react-router in layout, topbar, and vendor-pos view
- move MUI style props (justifyContent, alignItems, flexWrap, display, fontWeight) into sx
- resource-list-table/query-key: use local types instead of missing work-order-list module
This commit is contained in:
Arthur Bassi 2026-07-17 13:31:56 -03:00
parent 01939cbb70
commit a3e8a533b1
10 changed files with 32 additions and 35 deletions

View file

@ -1,8 +1,8 @@
import ky, { type HTTPError, type KyInstance, type Options } from "ky";
import ky, { type KyInstance, type Options } from "ky";
import { env } from "@/lib/env";
import { resolveApiPrefixUrl } from "@/lib/resolve-api-prefix-url";
import { API_PATHS } from "@/api/api-paths";
import { createApiErrorFromResponse, mapHttpStatusToMessage } from "@/api/api-error";
import { mapHttpStatusToMessage, normalizeApiRequestError } from "@/api/api-error";
import { queryKeys } from "@/infra/query-key/query-key";
import queryClient from "@/lib/query/query-client";
import { isAuthBypassEnabled } from "@/lib/auth/auth-bypass";
@ -22,36 +22,23 @@ function isAuthLoginRequest(request: Request): boolean {
}
export const api: KyInstance = ky.create({
prefixUrl: resolveApiPrefixUrl(env.apiUrl),
prefix: resolveApiPrefixUrl(env.apiUrl),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
hooks: {
beforeRequest: [
(request) => {
({ request }) => {
const token = getAccessToken();
if (token) {
request.headers.set("Authorization", token);
}
},
],
beforeError: [
async (error: HTTPError) => {
const { response } = error;
if (!response) {
error.message = "No response from server. Please check your connection.";
return error;
}
const apiError = await createApiErrorFromResponse(response);
error.message = apiError.message;
return error;
},
],
beforeError: [({ error }) => normalizeApiRequestError(error)],
afterResponse: [
async (request, _options, response) => {
({ request, response }) => {
if (response.status !== 401 || isAuthLoginRequest(request) || isAuthBypassEnabled()) {
return response;
}

View file

@ -1,5 +1,5 @@
import { useState } from "react";
import { Outlet } from "react-router-dom";
import { Outlet } from "react-router";
import { Box } from "@mui/material";
import { ProtectedRoute } from "@/components/auth/protected-route";

View file

@ -1,5 +1,5 @@
import { useState, type ReactNode } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useNavigate, useParams } from "react-router";
import {
Alert,
Box,
@ -33,7 +33,7 @@ function formatMoney(value: number | null | undefined): string {
function InfoRow({ label, children }: { label: string; children: ReactNode }) {
return (
<Box>
<Typography variant="caption" color="text.secondary" display="block">
<Typography variant="caption" color="text.secondary" sx={{ display: "block" }}>
{label}
</Typography>
<Typography variant="body2">
@ -107,7 +107,11 @@ export default function VendorPoViewPage() {
return (
<Box className="mx-auto max-w-5xl p-4">
<Stack direction="row" justifyContent="space-between" alignItems="center" className="mb-4">
<Stack
direction="row"
className="mb-4"
sx={{ justifyContent: "space-between", alignItems: "center" }}
>
<Button variant="text" onClick={() => navigate(-1)}>
← Back
</Button>
@ -118,7 +122,7 @@ export default function VendorPoViewPage() {
<Stack spacing={1} className="mb-4">
<Typography variant="h5">{dispatch.poNumber || "(no PO #)"}</Typography>
<Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap">
<Stack direction="row" spacing={1} sx={{ alignItems: "center", flexWrap: "wrap" }}>
<Typography variant="body2" color="text.secondary">
Dispatch {dispatch.dispatchNumber || "—"}
</Typography>
@ -256,7 +260,10 @@ export default function VendorPoViewPage() {
<Stack spacing={2}>
{dispatch.upliftRequests.map((u) => (
<Box key={String(u.id)} className="rounded border border-border p-3">
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack
direction="row"
sx={{ justifyContent: "space-between", alignItems: "center" }}
>
<Typography variant="body2" className="font-semibold">
{formatMoney(u.currentNTE)} → {formatMoney(u.requestedNTE)}
<Typography

View file

@ -38,7 +38,7 @@ export function CommentTimeline({
<Stack spacing={2} className={`${maxHeightClass} overflow-y-auto`}>
{sorted.map((item) => (
<Box key={String(item.id)} className="rounded border border-border p-3">
<Stack direction="row" justifyContent="space-between" className="mb-1">
<Stack direction="row" className="mb-1" sx={{ justifyContent: "space-between" }}>
<Typography variant="caption" className="font-semibold">
{item.author}
</Typography>

View file

@ -21,7 +21,7 @@ export function DetailField({ label, value, className, highlight }: DetailFieldP
return (
<Box className={className}>
<Typography variant="caption" color="text.secondary" display="block">
<Typography variant="caption" color="text.secondary" sx={{ display: "block" }}>
{label}
</Typography>
<Typography variant="body2" className={colorClass}>

View file

@ -1,5 +1,5 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import LogoutOutlinedIcon from "@mui/icons-material/LogoutOutlined";
import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined";
import {
@ -199,7 +199,7 @@ export function AppTopbar({ onToggleNav }: AppTopbarProps) {
{userInitials}
</Box>
<Box>
<Typography variant="body2" fontWeight={600}>
<Typography variant="body2" sx={{ fontWeight: 600 }}>
{user?.fullname || "User"}
</Typography>
<Typography variant="caption" color="text.secondary">

View file

@ -24,13 +24,15 @@ export function PageHeader({
return (
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
className={cn(
surface &&
"-mx-2.5 -mt-2.5 shrink-0 border-b border-(--color-border) bg-(--color-bg-surface) px-5 py-4",
className,
)}
sx={{
alignItems: "center",
justifyContent: "space-between",
}}
>
<Stack spacing={0}>
{eyebrow && (
@ -53,7 +55,7 @@ export function PageHeader({
)}
</Stack>
{actions && (
<Stack direction="row" spacing={1} alignItems="center" flexWrap="nowrap">
<Stack direction="row" spacing={1} sx={{ alignItems: "center", flexWrap: "nowrap" }}>
{actions}
</Stack>
)}

View file

@ -9,7 +9,8 @@ import {
TableRow,
} from "@mui/material";
import { TableEmptyRow, TableLoadingRow } from "@/components/ui/table-state-rows";
import type { SortDirection } from "@/domain/work-orders/types/work-order-list";
export type SortDirection = "asc" | "desc" | "";
export type ResourceListColumn<T> = {
id: string;

View file

@ -16,7 +16,7 @@ export function StatusChipFilter({
return (
<div>
<span className="mb-1 block text-[11px] font-semibold text-muted-foreground">{label}</span>
<Stack direction="row" spacing={0.75} flexWrap="wrap" useFlexGap>
<Stack direction="row" spacing={0.75} useFlexGap sx={{ flexWrap: "wrap" }}>
<Chip
label="All"
size="small"

View file

@ -1,4 +1,4 @@
import type { WorkOrdersListParams } from "@/domain/work-orders/types/work-order-list";
type WorkOrdersListParams = Record<string, unknown>;
export const queryKeys = {
auth: {