307 lines
7.9 KiB
TypeScript
307 lines
7.9 KiB
TypeScript
import type { Kennzahl } from "@/types/kpi";
|
|
import EditIcon from "@mui/icons-material/Edit";
|
|
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
|
import SearchIcon from "@mui/icons-material/Search";
|
|
import {
|
|
Box,
|
|
Link,
|
|
Paper,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableContainer,
|
|
TableHead,
|
|
TableRow,
|
|
TextField,
|
|
Tooltip,
|
|
} from "@mui/material";
|
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import { useNavigate } from "@tanstack/react-router";
|
|
import { useState } from "react";
|
|
import type { KeyboardEvent } from "react";
|
|
import { fetchPutKPI } from "../util/api";
|
|
|
|
interface KennzahlenTableProps {
|
|
onPageClick?: (page: number, text: string) => void;
|
|
pdfId: string;
|
|
settings: Kennzahl[];
|
|
data: {
|
|
[key: string]: {
|
|
label: string;
|
|
entity: string;
|
|
page: number;
|
|
status: string;
|
|
source: string;
|
|
}[];
|
|
};
|
|
}
|
|
|
|
export default function KennzahlenTable({
|
|
onPageClick,
|
|
data,
|
|
pdfId,
|
|
settings,
|
|
}: KennzahlenTableProps) {
|
|
const [editingIndex, setEditingIndex] = useState<string>("");
|
|
const [editValue, setEditValue] = useState("");
|
|
const navigate = useNavigate({ from: "/extractedResult/$pitchBook" });
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
const { mutate } = useMutation({
|
|
mutationFn: (id: string) => {
|
|
const key = id.toUpperCase();
|
|
const updatedData = { ...data };
|
|
updatedData[key] = data[key]?.map((item) => ({
|
|
...item,
|
|
entity: editValue,
|
|
})) || [{ label: key, entity: editValue }];
|
|
return fetchPutKPI(Number(pdfId), updatedData);
|
|
},
|
|
onMutate: async (id: string) => {
|
|
await queryClient.cancelQueries({
|
|
queryKey: ["pitchBookKPI", pdfId],
|
|
});
|
|
|
|
const snapshot = queryClient.getQueryData(["pitchBookKPI", pdfId]);
|
|
|
|
const key = id.toUpperCase();
|
|
|
|
queryClient.setQueryData(["pitchBookKPI", pdfId], () => {
|
|
const updatedData = { ...data };
|
|
updatedData[key] = data[key]?.map((item) => ({
|
|
...item,
|
|
entity: editValue,
|
|
})) || [{ label: key, entity: editValue }];
|
|
return updatedData;
|
|
});
|
|
|
|
return () => {
|
|
queryClient.setQueryData(["pitchBookKPI", pdfId], snapshot);
|
|
};
|
|
},
|
|
onError: (error, _variables, rollback) => {
|
|
console.log("error", error);
|
|
rollback?.();
|
|
},
|
|
onSettled: () => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: ["pitchBookKPI", pdfId],
|
|
});
|
|
},
|
|
});
|
|
|
|
// Bearbeitung starten
|
|
const startEditing = (value: string, index: string) => {
|
|
setEditingIndex(index);
|
|
setEditValue(value);
|
|
};
|
|
|
|
// Bearbeitung beenden und Wert speichern
|
|
const handleSave = async (index: string) => {
|
|
// await updateKennzahl(rows[index].label, editValue);
|
|
mutate(index);
|
|
setEditingIndex("");
|
|
};
|
|
|
|
// Tastatureingaben verarbeiten
|
|
const handleKeyPress = (e: KeyboardEvent<HTMLDivElement>, index: string) => {
|
|
if (e.key === "Enter") {
|
|
handleSave(index);
|
|
} else if (e.key === "Escape") {
|
|
setEditingIndex("null");
|
|
}
|
|
};
|
|
|
|
const handleNavigateToDetail = (settingName: string) => {
|
|
navigate({
|
|
to: "/extractedResult/$pitchBook/$kpi",
|
|
params: {
|
|
pitchBook: pdfId,
|
|
kpi: settingName,
|
|
},
|
|
});
|
|
};
|
|
|
|
return (
|
|
<TableContainer component={Paper}>
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell>
|
|
<strong>Kennzahl</strong>
|
|
</TableCell>
|
|
<TableCell>
|
|
<strong>Wert</strong>
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
<strong>Seite</strong>
|
|
</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
|
|
<TableBody>
|
|
{settings
|
|
.filter((setting) => setting.active)
|
|
.sort((a, b) => a.position - b.position)
|
|
.map((setting) => ({
|
|
setting: setting,
|
|
extractedValues: data[setting.name.toUpperCase()] || [],
|
|
}))
|
|
.map((row) => {
|
|
let borderColor = "transparent";
|
|
const hasMultipleValues = row.extractedValues.length > 1;
|
|
const hasNoValue =
|
|
row.setting.mandatory &&
|
|
(row.extractedValues.length === 0 ||
|
|
row.extractedValues.at(0)?.entity === "");
|
|
|
|
if (hasNoValue) {
|
|
borderColor = "red";
|
|
} else if (hasMultipleValues) {
|
|
borderColor = "#f6ed48";
|
|
}
|
|
|
|
return (
|
|
<TableRow key={row.setting.name}>
|
|
<TableCell>{row.setting.name}</TableCell>
|
|
<TableCell
|
|
onClick={() => {
|
|
// Only allow inline editing for non-multiple value cells
|
|
if (!hasMultipleValues) {
|
|
startEditing(
|
|
row.extractedValues.at(0)?.entity || "",
|
|
row.setting.name,
|
|
);
|
|
} else {
|
|
// Navigate to detail page for multiple values
|
|
handleNavigateToDetail(row.setting.name);
|
|
}
|
|
}}
|
|
>
|
|
{hasMultipleValues ? (
|
|
<Tooltip
|
|
title={
|
|
<>
|
|
<b>Problem</b>
|
|
<br />
|
|
Mehrere Werte für die Kennzahl gefunden.
|
|
</>
|
|
}
|
|
placement="bottom"
|
|
arrow
|
|
>
|
|
<Box
|
|
sx={{
|
|
border: `2px solid ${borderColor}`,
|
|
borderRadius: 1,
|
|
padding: "4px 8px",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
width: "100%",
|
|
cursor: "pointer",
|
|
"&:hover": {
|
|
backgroundColor: "#f5f5f5",
|
|
},
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 1,
|
|
width: "100%",
|
|
}}
|
|
>
|
|
<span>
|
|
{row.extractedValues.at(0)?.entity || "—"}
|
|
</span>
|
|
</Box>
|
|
<SearchIcon
|
|
fontSize="small"
|
|
sx={{ color: "#f6ed48" }}
|
|
/>
|
|
</Box>
|
|
</Tooltip>
|
|
) : (
|
|
<Box
|
|
sx={{
|
|
border: `2px solid ${borderColor}`,
|
|
borderRadius: 1,
|
|
padding: "4px 8px",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
width: "100%",
|
|
cursor: "text",
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 1,
|
|
width: "100%",
|
|
}}
|
|
>
|
|
{hasNoValue && (
|
|
<ErrorOutlineIcon fontSize="small" color="error" />
|
|
)}
|
|
{editingIndex === row.setting.name ? (
|
|
<TextField
|
|
value={editValue}
|
|
onChange={(e) => setEditValue(e.target.value)}
|
|
onKeyDown={(e) =>
|
|
handleKeyPress(e, row.setting.name)
|
|
}
|
|
onBlur={() => handleSave(row.setting.name)}
|
|
autoFocus
|
|
size="small"
|
|
fullWidth
|
|
variant="standard"
|
|
sx={{ margin: "-8px 0" }}
|
|
/>
|
|
) : (
|
|
<span>
|
|
{row.extractedValues.at(0)?.entity || "—"}
|
|
</span>
|
|
)}
|
|
</Box>
|
|
<EditIcon
|
|
fontSize="small"
|
|
sx={{ color: "#555", cursor: "pointer" }}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
startEditing(
|
|
row.extractedValues.at(0)?.entity || "",
|
|
row.setting.name,
|
|
);
|
|
}}
|
|
/>
|
|
</Box>
|
|
)}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
<Link
|
|
component="button"
|
|
onClick={() =>
|
|
onPageClick?.(
|
|
Number(row.extractedValues.at(0)?.page),
|
|
row.extractedValues.at(0)?.entity || "",
|
|
)
|
|
}
|
|
sx={{ cursor: "pointer" }}
|
|
>
|
|
{row.extractedValues.at(0)?.page}
|
|
</Link>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
);
|
|
}
|