- created_at pinned right before row actions in both lists; remaining cells wrap inside a shrinking area so the date never wraps to a left-aligned line
117 lines
4.6 KiB
TypeScript
117 lines
4.6 KiB
TypeScript
import React from "react";
|
|
import { Box, Paper, Typography, Button, IconButton, Skeleton, Tooltip } from "@mui/material";
|
|
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
|
import VisibilityIcon from "@mui/icons-material/Visibility";
|
|
import CachedIcon from "@mui/icons-material/Cached";
|
|
import { ListCellRenderer } from "../../react-openapi";
|
|
import type { ReportFieldConfigs } from "./types";
|
|
|
|
interface ReportListProps {
|
|
reports: any[];
|
|
loading: boolean;
|
|
fields: ReportFieldConfigs | null;
|
|
selectedId: string | null;
|
|
onView: (id: string) => void;
|
|
onRegenerate: (report: any) => void;
|
|
onDelete: (id: string) => void;
|
|
}
|
|
|
|
export function ReportList({ reports, loading, fields, selectedId, onView, onRegenerate, onDelete }: ReportListProps) {
|
|
if (loading && reports.length === 0) {
|
|
return (
|
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
|
|
{[0, 1, 2].map((i) => (
|
|
<Skeleton key={i} variant="rounded" height={88} sx={{ borderRadius: 2 }} />
|
|
))}
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
|
|
{reports.map((report) => {
|
|
const q = report.query ?? {};
|
|
const range = q.start_date || q.end_date ? `range ${q.start_date || "…"} → ${q.end_date || "…"}` : null;
|
|
const dims = Array.isArray(q.group_dims) ? q.group_dims.join(", ") : "";
|
|
const granularities = Array.isArray(q.granularities) ? q.granularities.join(", ") : "";
|
|
const accounts = Array.isArray(q.accounts) ? `${q.accounts.length} account${q.accounts.length === 1 ? "" : "s"}` : "all accounts";
|
|
const amounts =
|
|
q.min_amount != null || q.max_amount != null
|
|
? `amount ${q.min_amount ?? "0"} → ${q.max_amount ?? "∞"}`
|
|
: null;
|
|
return (
|
|
<Paper
|
|
key={report.id}
|
|
variant="outlined"
|
|
sx={{
|
|
p: 2,
|
|
borderRadius: 2,
|
|
borderColor: selectedId === report.id ? "primary.main" : "divider",
|
|
transition: "border-color 160ms ease",
|
|
"&:hover": { borderColor: "primary.light" },
|
|
}}
|
|
>
|
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
|
<Box sx={{ flex: "1 1 260px", minWidth: 0 }}>
|
|
<Typography variant="body1" fontWeight={600} noWrap sx={{ fontSize: "0.9375rem" }}>
|
|
{report.name || report.id}
|
|
</Typography>
|
|
<Box sx={{ display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap", mt: 0.25 }}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
{granularities}
|
|
</Typography>
|
|
<Typography variant="caption" color="text.disabled">
|
|
/
|
|
</Typography>
|
|
<Typography variant="caption" color="text.secondary">
|
|
{dims}
|
|
</Typography>
|
|
{range && (
|
|
<Typography variant="caption" color="text.disabled">
|
|
{range}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
<Box sx={{ display: "flex", gap: 1.5, mt: 0.5 }}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
{accounts}
|
|
</Typography>
|
|
{amounts && (
|
|
<Typography variant="caption" color="text.secondary">
|
|
{amounts}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
</Box>
|
|
|
|
{fields && (
|
|
<Box sx={{ ml: "auto", flexShrink: 0, color: "text.secondary" }}>
|
|
<ListCellRenderer field={fields.generatedAt} value={report.generated_at} />
|
|
</Box>
|
|
)}
|
|
|
|
<Box sx={{ display: "flex", gap: 0.5, flexShrink: 0 }}>
|
|
<Button size="small" variant="contained" startIcon={<VisibilityIcon />} onClick={() => onView(report.id)}>
|
|
View
|
|
</Button>
|
|
<Button
|
|
size="small"
|
|
variant="outlined"
|
|
startIcon={<CachedIcon />}
|
|
onClick={() => onRegenerate(report)}
|
|
>
|
|
Regenerate
|
|
</Button>
|
|
<Tooltip title="Delete">
|
|
<IconButton aria-label="Delete report" onClick={() => onDelete(report.id)}>
|
|
<DeleteOutlineIcon fontSize="small" />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</Box>
|
|
</Box>
|
|
</Paper>
|
|
);
|
|
})}
|
|
</Box>
|
|
);
|
|
} |