import React, { useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { Box, Typography, Button, Paper, Grid, CircularProgress, Tabs, Tab, } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import EditIcon from "@mui/icons-material/Edit"; import type { ResourceConfig } from "../types"; import { useResource } from "../context/useResource"; import { useAppContext } from "../context/AppContext"; import { DetailFieldRenderer, applyDisplayFormat } from "./fields"; import { SseStreamView } from "./SseStreamView"; interface ResourceDetailProps { resource: ResourceConfig; basePath: string; } function TabPanel({ children, value, index }: { children: React.ReactNode; value: number; index: number }) { if (value !== index) return null; return {children}; } export function ResourceDetail({ resource, basePath }: ResourceDetailProps) { const navigate = useNavigate(); const { id } = useParams(); const crud = useResource(resource.name); const { resources: allResources } = useAppContext(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [tabIndex, setTabIndex] = useState(0); useEffect(() => { if (id) { setLoading(true); crud .get(id) .then(setData) .catch(() => navigate(`${basePath}/${resource.name}`)) .finally(() => setLoading(false)); } }, [id]); if (loading) { return ( ); } if (!data) { return ( Record not found ); } const visibleFields = resource.orderedFields.filter((f) => !f.hidden?.detail); const tabs = [{ label: "Details", key: "details" }]; if (resource.subResources) { for (const subName of resource.subResources) { const sub = allResources.find((r) => r.name === subName); if (sub) { tabs.push({ label: sub.displayName, key: subName }); } } } return ( {applyDisplayFormat( Object.fromEntries( resource.orderedFields.map((field) => { const value = data[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]; }) ), resource.displayFormat )} {resource.operations.update && ( )} {tabs.length > 1 && ( setTabIndex(v)} sx={{ mb: 1 }}> {tabs.map((t) => ( ))} )} {visibleFields.map((field) => { let value = data[field.name]; let fmt = resource.displayFormat; if (field.fk && typeof value === "object") { const targetRes = allResources.find((r) => r.name === field.fk!.resource); fmt = targetRes!.displayFormat; } else if (field.refSchema && !field.fk && typeof value === "object") { fmt = field.inlineDisplayFormat ?? resource.displayFormat; } return ( ); })} {tabs.slice(1).map((t, i) => { const sub = allResources.find((r) => r.name === t.key)!; const pathParam = sub.parent?.pathParam ?? "id"; return ( {sub.streaming ? ( ) : null} ); })} ); }