fix(fetch-requests): resolve FK displayFormat in custom detail page
- Import useAppContext and applyDisplayFormat for target resource resolution - Fix detail field rendering to use FK target resource displayFormat - Fix title to resolve FK objects via displayFormat instead of non-existent account_name - Move displayTitle useMemo before early returns to avoid hook ordering violation
This commit is contained in:
@@ -19,7 +19,7 @@ import WarningAmberIcon from "@mui/icons-material/WarningAmber";
|
|||||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||||
import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline";
|
import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline";
|
||||||
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
||||||
import { useResource, useItemSse, DetailFieldRenderer } from "../../react-openapi";
|
import { useResource, useItemSse, useAppContext, DetailFieldRenderer, applyDisplayFormat } from "../../react-openapi";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { RETRY_MAX, formatApiError } from "../features/fetch-requests";
|
import { RETRY_MAX, formatApiError } from "../features/fetch-requests";
|
||||||
import type { FetchRequestStatus, SSEEvent, ProgressMessage } from "../features/fetch-requests";
|
import type { FetchRequestStatus, SSEEvent, ProgressMessage } from "../features/fetch-requests";
|
||||||
@@ -71,6 +71,7 @@ export default function FetchRequestDetail() {
|
|||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { get, patch, resource } = useResource("fetch-requests");
|
const { get, patch, resource } = useResource("fetch-requests");
|
||||||
|
const { resources: allResources } = useAppContext();
|
||||||
const [stepStats, setStepStats] = useState<Record<string, number>>({});
|
const [stepStats, setStepStats] = useState<Record<string, number>>({});
|
||||||
const [liveParsedCount, setLiveParsedCount] = useState<number | undefined>(undefined);
|
const [liveParsedCount, setLiveParsedCount] = useState<number | undefined>(undefined);
|
||||||
const [retrying, setRetrying] = useState(false);
|
const [retrying, setRetrying] = useState(false);
|
||||||
@@ -158,6 +159,29 @@ export default function FetchRequestDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const req = fetchRequest as any;
|
||||||
|
const retryCount = req?.retry_count ?? 0;
|
||||||
|
const isRetryExhausted = retryCount >= RETRY_MAX;
|
||||||
|
const status = req?.status as FetchRequestStatus | undefined;
|
||||||
|
const detailFields = (resource?.orderedFields ?? []).filter(
|
||||||
|
(f) => f.name !== "source",
|
||||||
|
);
|
||||||
|
|
||||||
|
const displayTitle = useMemo(() => {
|
||||||
|
if (!resource || !req) return "";
|
||||||
|
const resolved = Object.fromEntries(
|
||||||
|
resource.orderedFields.map((field) => {
|
||||||
|
const value = req[field.name];
|
||||||
|
if (field.fk && typeof value === "object" && value != null) {
|
||||||
|
const target = allResources.find((r) => r.name === field.fk!.resource);
|
||||||
|
if (target) return [field.name, applyDisplayFormat(value, target.displayFormat)];
|
||||||
|
}
|
||||||
|
return [field.name, value];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return applyDisplayFormat(resolved, resource.displayFormat);
|
||||||
|
}, [resource, req, allResources]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", justifyContent: "center", p: 8 }}>
|
<Box sx={{ display: "flex", justifyContent: "center", p: 8 }}>
|
||||||
@@ -177,15 +201,6 @@ export default function FetchRequestDetail() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const req = fetchRequest as any;
|
|
||||||
const retryCount = req.retry_count ?? 0;
|
|
||||||
const isRetryExhausted = retryCount >= RETRY_MAX;
|
|
||||||
const status = req.status as FetchRequestStatus;
|
|
||||||
|
|
||||||
const detailFields = (resource?.orderedFields ?? []).filter(
|
|
||||||
(f) => f.name !== "source",
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container sx={{ mt: 4, mb: 4 }}>
|
<Container sx={{ mt: 4, mb: 4 }}>
|
||||||
<Button startIcon={<ArrowBackIcon />} onClick={() => navigate("/fetch-requests")} sx={{ mb: 2 }}>
|
<Button startIcon={<ArrowBackIcon />} onClick={() => navigate("/fetch-requests")} sx={{ mb: 2 }}>
|
||||||
@@ -199,7 +214,7 @@ export default function FetchRequestDetail() {
|
|||||||
label={status.replace(/_/g, " ")}
|
label={status.replace(/_/g, " ")}
|
||||||
color={statusColors[status]}
|
color={statusColors[status]}
|
||||||
/>
|
/>
|
||||||
<Typography variant="h6" fontWeight={600}>{req.account_name}</Typography>
|
<Typography variant="h6" fontWeight={600}>{displayTitle}</Typography>
|
||||||
<Chip
|
<Chip
|
||||||
label={"path" in (req.source ?? {}) ? "File" : "Email"}
|
label={"path" in (req.source ?? {}) ? "File" : "Email"}
|
||||||
size="small"
|
size="small"
|
||||||
@@ -209,14 +224,22 @@ export default function FetchRequestDetail() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: "flex", gap: 4, flexWrap: "wrap", mb: 2 }}>
|
<Box sx={{ display: "flex", gap: 4, flexWrap: "wrap", mb: 2 }}>
|
||||||
{detailFields.map((field) => (
|
{detailFields.map((field) => {
|
||||||
<DetailFieldRenderer
|
const value = req[field.name];
|
||||||
key={field.name}
|
let fmt = resource?.displayFormat;
|
||||||
field={field}
|
if (field.fk && typeof value === "object" && value != null) {
|
||||||
value={req[field.name]}
|
const target = allResources.find((r) => r.name === field.fk!.resource);
|
||||||
displayFormat={resource?.displayFormat}
|
if (target) fmt = target.displayFormat;
|
||||||
/>
|
}
|
||||||
))}
|
return (
|
||||||
|
<DetailFieldRenderer
|
||||||
|
key={field.name}
|
||||||
|
field={field}
|
||||||
|
value={value}
|
||||||
|
displayFormat={fmt}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user