Implementiere PDF-Highlighting für Kennzahlen (Ticket #31)
parent
76a060a563
commit
0dd3785fdd
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
type: 'all' | 'selected';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Eine farbige Box, die über dem PDF angezeigt wird
|
||||||
|
export default function HighlightOverlay({ x, y, width, height, type }: Props) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: y,
|
||||||
|
left: x,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
backgroundColor: type === 'all' ? 'rgba(255, 255, 0, 0.4)' : 'rgba(255, 165, 0, 0.4)',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: type === 'all' ? '#FFD700' : '#FF8C00',
|
||||||
|
borderRadius: '2px',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
zIndex: 10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
|
||||||
|
|
||||||
// Beispiel-Daten
|
// Beispiel-Daten
|
||||||
|
|
@ -19,7 +20,12 @@ import {
|
||||||
];
|
];
|
||||||
|
|
||||||
// React-Komponente
|
// React-Komponente
|
||||||
export default function KennzahlenTable() {
|
|
||||||
|
type Props = {
|
||||||
|
setSelectedLabel: Dispatch<SetStateAction<string | null>>;
|
||||||
|
};
|
||||||
|
export default function KennzahlenTable({ setSelectedLabel }: Props) {
|
||||||
|
|
||||||
// Zustand für bearbeitbare Daten
|
// Zustand für bearbeitbare Daten
|
||||||
const [rows, setRows] = useState(exampleData);
|
const [rows, setRows] = useState(exampleData);
|
||||||
|
|
||||||
|
|
@ -67,7 +73,13 @@ import {
|
||||||
else if (row.status === 'warning') borderColor = '#f6ed48';
|
else if (row.status === 'warning') borderColor = '#f6ed48';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={index}>
|
<TableRow
|
||||||
|
key={index}
|
||||||
|
onClick={() => setSelectedLabel(row.label)}
|
||||||
|
hover
|
||||||
|
sx={{ cursor: 'pointer' }}
|
||||||
|
>
|
||||||
|
|
||||||
{/* Kennzahl */}
|
{/* Kennzahl */}
|
||||||
<TableCell>{row.label}</TableCell>
|
<TableCell>{row.label}</TableCell>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Beispielhafte Highlight-Daten mit Labels
|
||||||
|
export const highlightData = [
|
||||||
|
{
|
||||||
|
label: 'Risikoprofil',
|
||||||
|
page: 1,
|
||||||
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
width: 120,
|
||||||
|
height: 20,
|
||||||
|
type: 'all', // gelb
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Risikoprofil',
|
||||||
|
page: 1,
|
||||||
|
x: 100,
|
||||||
|
y: 300,
|
||||||
|
width: 140,
|
||||||
|
height: 25,
|
||||||
|
type: 'selected', // orange (nur wenn ausgewählt)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
@ -6,13 +6,22 @@ import { Box, IconButton } from '@mui/material';
|
||||||
import ArrowCircleLeftIcon from '@mui/icons-material/ArrowCircleLeft';
|
import ArrowCircleLeftIcon from '@mui/icons-material/ArrowCircleLeft';
|
||||||
import ArrowCircleRightIcon from '@mui/icons-material/ArrowCircleRight';
|
import ArrowCircleRightIcon from '@mui/icons-material/ArrowCircleRight';
|
||||||
import testPDF from '/example.pdf';
|
import testPDF from '/example.pdf';
|
||||||
|
import HighlightOverlay from './HighlightOverlay';
|
||||||
|
import { highlightData } from './highlightData';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||||
"pdfjs-dist/build/pdf.worker.min.mjs",
|
"pdfjs-dist/build/pdf.worker.min.mjs",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
).toString();
|
).toString();
|
||||||
|
|
||||||
export default function PDFViewer() {
|
type Props = {
|
||||||
|
selectedLabel: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PDFViewer({ selectedLabel }: Props) {
|
||||||
const [numPages, setNumPages] = useState<number | null>(null);
|
const [numPages, setNumPages] = useState<number | null>(null);
|
||||||
const [pageNumber, setPageNumber] = useState(1);
|
const [pageNumber, setPageNumber] = useState(1);
|
||||||
const [containerWidth, setContainerWidth] = useState<number | null>(null);
|
const [containerWidth, setContainerWidth] = useState<number | null>(null);
|
||||||
|
|
@ -56,17 +65,34 @@ export default function PDFViewer() {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Document file={testPDF}
|
<Document
|
||||||
onLoadSuccess={onDocumentLoadSuccess}
|
file={testPDF}
|
||||||
onLoadError={(error) => console.error('Es gab ein Fehler beim Laden des PDFs:', error)}
|
onLoadSuccess={onDocumentLoadSuccess}
|
||||||
onSourceError={(error) => console.error('Ungültige PDF:', error)}>
|
onLoadError={(error) => console.error('Fehler beim Laden:', error)}
|
||||||
{containerWidth && (
|
onSourceError={(error) => console.error('Ungültige PDF:', error)}
|
||||||
<Page
|
>
|
||||||
pageNumber={pageNumber}
|
{containerWidth && (
|
||||||
width={containerWidth * 0.8}
|
<Box position="relative">
|
||||||
|
<Page
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
width={containerWidth * 0.8}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Highlights einfügen */}
|
||||||
|
{highlightData
|
||||||
|
.filter((h) => h.page === pageNumber)
|
||||||
|
.map((h, idx) => (
|
||||||
|
<HighlightOverlay
|
||||||
|
key={idx}
|
||||||
|
{...h}
|
||||||
|
type={selectedLabel && h.label === selectedLabel ? 'selected' : 'all'}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Document>
|
</Document>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
mt={2}
|
mt={2}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {createFileRoute, useNavigate} from '@tanstack/react-router';
|
||||||
import PDFViewer from '../components/pdfViewer';
|
import PDFViewer from '../components/pdfViewer';
|
||||||
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
||||||
import KennzahlenTable from "../components/KennzahlenTable";
|
import KennzahlenTable from "../components/KennzahlenTable";
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/extractedResult')({
|
export const Route = createFileRoute('/extractedResult')({
|
||||||
|
|
@ -10,6 +11,7 @@ export const Route = createFileRoute('/extractedResult')({
|
||||||
});
|
});
|
||||||
|
|
||||||
function ExtractedResultsPage() {
|
function ExtractedResultsPage() {
|
||||||
|
const [selectedLabel, setSelectedLabel] = useState<string | null>(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const status: 'green' | 'yellow' | 'red' = 'red';
|
const status: 'green' | 'yellow' | 'red' = 'red';
|
||||||
|
|
||||||
|
|
@ -57,7 +59,8 @@ function ExtractedResultsPage() {
|
||||||
overflow: 'auto', // Scrollen falls Tabelle zu lang
|
overflow: 'auto', // Scrollen falls Tabelle zu lang
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KennzahlenTable />
|
<KennzahlenTable setSelectedLabel={setSelectedLabel} />
|
||||||
|
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
|
|
@ -78,7 +81,7 @@ function ExtractedResultsPage() {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PDFViewer/>
|
<PDFViewer selectedLabel={selectedLabel} />
|
||||||
</Paper>
|
</Paper>
|
||||||
<Box mt={2} display="flex" justifyContent="flex-end" gap={2}>
|
<Box mt={2} display="flex" justifyContent="flex-end" gap={2}>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue