#76-Hinzufügen-einer-Seitenanzahl-bei-neuen-Kennzahlen #78

Merged
3023730 merged 8 commits from #76-Hinzufügen-einer-Seitenanzahl-bei-neuen-Kennzahlen into main 2025-06-20 10:32:25 +02:00
2 changed files with 278 additions and 38 deletions

View File

@ -46,21 +46,39 @@ export default function KennzahlenTable({
}: KennzahlenTableProps) {
const [editingIndex, setEditingIndex] = useState<string>("");
const [editValue, setEditValue] = useState("");
const [editingPageIndex, setEditingPageIndex] = useState<string>("");
const [editPageValue, setEditPageValue] = useState("");
const [hoveredPageIndex, setHoveredPageIndex] = useState<string>("");
const navigate = useNavigate();
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn: (id: string) => {
mutationFn: (params: { id: string; newValue?: string; newPage?: number }) => {
const { id, newValue, newPage } = params;
const key = id.toUpperCase();
const updatedData = { ...data };
updatedData[key] = data[key]?.map((item) => ({
...item,
entity: editValue,
})) || [{ label: key, entity: editValue }];
if (data[key] && data[key].length > 0) {
updatedData[key] = data[key].map((item) => ({
...item,
...(newValue !== undefined && { entity: newValue }),
...(newPage !== undefined && { page: newPage }),
}));
} else {
updatedData[key] = [{
label: key,
entity: newValue || "",
page: newPage || 0,
status: "single-source",
source: "manual"
}];
}
return fetchPutKPI(Number(pdfId), updatedData);
},
onMutate: async (id: string) => {
onMutate: async (params: { id: string; newValue?: string; newPage?: number }) => {
const { id, newValue, newPage } = params;
await queryClient.cancelQueries({
queryKey: ["pitchBookKPI", pdfId],
});
@ -71,10 +89,23 @@ export default function KennzahlenTable({
queryClient.setQueryData(["pitchBookKPI", pdfId], () => {
const updatedData = { ...data };
updatedData[key] = data[key]?.map((item) => ({
...item,
entity: editValue,
})) || [{ label: key, entity: editValue }];
if (data[key] && data[key].length > 0) {
updatedData[key] = data[key].map((item) => ({
...item,
...(newValue !== undefined && { entity: newValue }),
...(newPage !== undefined && { page: newPage }),
}));
} else {
updatedData[key] = [{
label: key,
entity: newValue || "",
page: newPage || 0,
status: "single-source",
source: "manual"
}];
}
return updatedData;
});
@ -99,19 +130,39 @@ export default function KennzahlenTable({
setEditValue(value);
};
const startPageEditing = (value: number, index: string) => {
setEditingPageIndex(index);
setEditPageValue(value.toString());
};
// Bearbeitung beenden und Wert speichern
const handleSave = async (index: string) => {
// await updateKennzahl(rows[index].label, editValue);
mutate(index);
mutate({ id: index, newValue: editValue });
setEditingIndex("");
};
const handlePageSave = async (index: string) => {
const pageNumber = parseInt(editPageValue);
if (!isNaN(pageNumber) && pageNumber > 0) {
mutate({ id: index, newPage: pageNumber });
}
setEditingPageIndex("");
};
// Tastatureingaben verarbeiten
const handleKeyPress = (e: KeyboardEvent<HTMLDivElement>, index: string) => {
if (e.key === "Enter") {
handleSave(index);
} else if (e.key === "Escape") {
setEditingIndex("null");
setEditingIndex("");
}
};
const handlePageKeyPress = (e: KeyboardEvent<HTMLDivElement>, index: string) => {
if (e.key === "Enter") {
handlePageSave(index);
} else if (e.key === "Escape") {
setEditingPageIndex("");
}
};
@ -131,14 +182,16 @@ export default function KennzahlenTable({
<Table>
<TableHead>
<TableRow>
<TableCell>
<TableCell width="25%">
<strong>Kennzahl</strong>
</TableCell>
<TableCell>
<TableCell width="60%">
<strong>Wert</strong>
</TableCell>
<TableCell align="center">
<strong>Seite</strong>
<TableCell align="center" width="15%">
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 1 }}>
<strong>Seite</strong>
</Box>
</TableCell>
</TableRow>
</TableHead>
@ -165,6 +218,10 @@ export default function KennzahlenTable({
borderColor = "#f6ed48";
}
const currentPage = row.extractedValues.at(0)?.page ?? 0;
const isPageHovered = hoveredPageIndex === row.setting.name;
const canEditPage = !hasMultipleValues;
return (
<TableRow key={row.setting.name}>
<TableCell>{row.setting.name}</TableCell>
@ -308,24 +365,115 @@ export default function KennzahlenTable({
)}
</TableCell>
<TableCell align="center">
{(row.extractedValues.at(0)?.page ?? 0) > 0 ? (
<Link
component="button"
onClick={() => {
const extractedValue = row.extractedValues.at(0);
if (extractedValue?.page && extractedValue.page > 0) {
onPageClick?.(
Number(extractedValue.page),
extractedValue.entity || "",
);
{editingPageIndex === row.setting.name ? (
<TextField
value={editPageValue}
onChange={(e) => {
const value = e.target.value;
if (value === '' || /^\d+$/.test(value) && parseInt(value) > 0) {
setEditPageValue(value);
}
}}
sx={{ cursor: "pointer" }}
>
{row.extractedValues.at(0)?.page}
</Link>
onKeyDown={(e) => handlePageKeyPress(e, row.setting.name)}
onBlur={() => handlePageSave(row.setting.name)}
autoFocus
size="small"
variant="standard"
sx={{
width: "60px",
"& .MuiInput-input": {
textAlign: "center"
}
}}
inputProps={{
min: 0,
style: { textAlign: 'center' }
}}
/>
) : (
""
<>
{currentPage > 0 ? (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
position: "relative",
cursor: canEditPage ? "pointer" : "default",
borderRadius: "4px",
minHeight: "32px",
minWidth: "100px",
transition: "all 0.2s ease",
}}
onMouseEnter={() => canEditPage && setHoveredPageIndex(row.setting.name)}
onMouseLeave={() => setHoveredPageIndex("")}
onClick={() => {
if (canEditPage) {
startPageEditing(currentPage, row.setting.name);
}
}}
>
<Link
component="button"
onClick={(e) => {
e.stopPropagation();
const extractedValue = row.extractedValues.at(0);
if (extractedValue?.page && extractedValue.page > 0) {
onPageClick?.(Number(extractedValue.page), extractedValue.entity || "");
}
}}
sx={{ cursor: "pointer" }}
>
{currentPage}
</Link>
{isPageHovered && canEditPage && (
<EditIcon
fontSize="small"
sx={{
position: "absolute",
left: "70px",
color: "#666",
opacity: 0.7,
transition: "opacity 0.2s ease",
}}
/>
)}
</Box>
) : canEditPage ? (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
position: "relative",
cursor: "pointer",
minHeight: "32px",
minWidth: "100px",
borderRadius: "4px",
backgroundColor: isPageHovered ? "#f8f9fa" : "transparent",
}}
onMouseEnter={() => setHoveredPageIndex(row.setting.name)}
onMouseLeave={() => setHoveredPageIndex("")}
onClick={() => startPageEditing(0, row.setting.name)}
>
<span style={{ color: "#999" }}>...</span>
<EditIcon
fontSize="small"
sx={{
position: "absolute",
left: "70px",
color: "#555",
cursor: "pointer",
opacity: 0.7,
transition: "opacity 0.2s ease",
}}
/>
</Box>
) : (
""
)}
</>
)}
</TableCell>
</TableRow>

View File

@ -1,4 +1,5 @@
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import EditIcon from "@mui/icons-material/Edit";
import {
Box,
Button,
@ -27,6 +28,7 @@ import {
} from "@tanstack/react-query";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { useEffect, useState } from "react";
import type { KeyboardEvent } from "react";
import PDFViewer from "../components/pdfViewer";
import { fetchPutKPI } from "../util/api";
import { kpiQueryOptions } from "../util/query";
@ -65,13 +67,23 @@ function ExtractedResultsPage() {
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const [hasChanges, setHasChanges] = useState(false);
const [customValue, setCustomValue] = useState("");
const [customPage, setCustomPage] = useState("");
const [editingCustomPage, setEditingCustomPage] = useState(false);
const originalValue = kpiValues[0]?.entity || "";
const originalPage = kpiValues[0]?.page || 0;
const selectedValue =
selectedIndex === -1 ? customValue : kpiValues[selectedIndex]?.entity || "";
const selectedPage =
selectedIndex === -1
? (parseInt(customPage) > 0 ? parseInt(customPage) : 1)
: kpiValues[selectedIndex]?.page || 1;
useEffect(() => {
setHasChanges(selectedValue !== originalValue);
}, [selectedValue, originalValue]);
const valueChanged = selectedValue !== originalValue;
const pageChanged = selectedPage !== originalPage;
setHasChanges(valueChanged || pageChanged);
}, [selectedValue, selectedPage, originalValue, originalPage]);
const { mutate: updateKPI } = useMutation({
mutationFn: () => {
@ -83,7 +95,7 @@ function ExtractedResultsPage() {
baseObject = {
label: kpi.toUpperCase(),
entity: selectedValue,
page: 0,
page: selectedPage,
status: "single-source",
source: "manual",
};
@ -92,6 +104,7 @@ function ExtractedResultsPage() {
{
...baseObject,
entity: selectedValue,
page: selectedPage,
},
];
return fetchPutKPI(Number(pitchBook), updatedData);
@ -120,6 +133,7 @@ function ExtractedResultsPage() {
setSelectedIndex(index);
setCurrentPage(kpiValues[index].page);
setCustomValue("");
setCustomPage("");
}
};
@ -131,10 +145,21 @@ function ExtractedResultsPage() {
setSelectedIndex(-1);
};
const handleCustomPageChange = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
const value = event.target.value;
// Allow empty string or positive numbers only (no 0)
if (value === '' || (/^\d+$/.test(value) && parseInt(value) > 0)) {
setCustomPage(value);
}
};
const handleRowClick = (index: number) => {
setCurrentPage(kpiValues[index].page);
setSelectedIndex(index);
setCustomValue("");
setCustomPage("");
};
const handleBackClick = () => {
@ -166,6 +191,18 @@ function ExtractedResultsPage() {
updateKPI();
};
const startCustomPageEditing = () => {
setEditingCustomPage(true);
};
const handleCustomPageKeyPress = (e: KeyboardEvent<HTMLDivElement>) => {
if (e.key === "Enter") {
setEditingCustomPage(false);
} else if (e.key === "Escape") {
setEditingCustomPage(false);
}
};
return (
<Box p={4}>
<Box sx={{ display: "flex", alignItems: "center", mb: 3 }}>
@ -203,10 +240,10 @@ function ExtractedResultsPage() {
<Table>
<TableHead>
<TableRow>
<TableCell>
<TableCell width="85%">
<strong>Gefundene Werte</strong>
</TableCell>
<TableCell align="center">
<TableCell align="center" width="15%">
<strong>Seite</strong>
</TableCell>
</TableRow>
@ -318,6 +355,61 @@ function ExtractedResultsPage() {
/>
</Box>
</TableCell>
<TableCell align="center">
{editingCustomPage ? (
<TextField
value={customPage}
onChange={handleCustomPageChange}
onKeyDown={handleCustomPageKeyPress}
onBlur={() => setEditingCustomPage(false)}
autoFocus
size="small"
variant="standard"
sx={{
width: "60px",
"& .MuiInput-input": {
textAlign: "center"
}
}}
inputProps={{
min: 0,
style: { textAlign: 'center' }
}}
/>
) : (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 1,
cursor: "pointer",
minHeight: "24px",
minWidth: "100px",
margin: "0 auto",
}}
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
startCustomPageEditing();
}}
>
<span>{customPage || "..."}</span>
<EditIcon
fontSize="small"
sx={{
color: "#666",
opacity: 0.7,
transition: "opacity 0.2s ease",
ml: 1
}}
onClick={(e) => {
e.stopPropagation();
startCustomPageEditing();
}}
/>
</Box>
)}
</TableCell>
</TableRow>
</TableBody>
</Table>