commit
4ca8314ed2
|
|
@ -3,15 +3,14 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico?v=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Web site created using create-tsrouter-app"
|
content="Web site created using create-tsrouter-app"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<title>Create TanStack App - frontend</title>
|
<title>Pitchbook Extractor</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
|
|
@ -184,10 +184,10 @@ export default function KennzahlenTable({
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell width="25%">
|
<TableCell width="30%">
|
||||||
<strong>Kennzahl</strong>
|
<strong>Kennzahl</strong>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell width="60%">
|
<TableCell width="55%">
|
||||||
<strong>Wert</strong>
|
<strong>Wert</strong>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center" width="15%">
|
<TableCell align="center" width="15%">
|
||||||
|
|
@ -226,7 +226,11 @@ export default function KennzahlenTable({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={row.setting.name}>
|
<TableRow key={row.setting.name}>
|
||||||
<TableCell>{row.setting.name}</TableCell>
|
<TableCell>{row.setting.name}
|
||||||
|
{row.setting.mandatory && (
|
||||||
|
<span> *</span>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Only allow inline editing for non-multiple value cells
|
// Only allow inline editing for non-multiple value cells
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import SettingsIcon from "@mui/icons-material/Settings";
|
import SettingsIcon from "@mui/icons-material/Settings";
|
||||||
import { Backdrop, Box, Button, IconButton, Paper } from "@mui/material";
|
import { Backdrop, Box, Button, IconButton, Paper, Typography } from "@mui/material";
|
||||||
import { useNavigate, useRouter } from "@tanstack/react-router";
|
import { useNavigate, useRouter } from "@tanstack/react-router";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import FileUpload from "react-material-file-upload";
|
import FileUpload from "react-material-file-upload";
|
||||||
import { socket } from "../socket";
|
import { socket } from "../socket";
|
||||||
import { API_HOST } from "../util/api";
|
import { API_HOST } from "../util/api";
|
||||||
import { CircularProgressWithLabel } from "./CircularProgressWithLabel";
|
import { CircularProgressWithLabel } from "./CircularProgressWithLabel";
|
||||||
|
import DekaLogo from "../assets/Deka_logo.png";
|
||||||
|
|
||||||
export default function UploadPage() {
|
export default function UploadPage() {
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
|
|
@ -87,26 +88,50 @@ export default function UploadPage() {
|
||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="flex-start"
|
||||||
height="100vh"
|
height="100vh"
|
||||||
bgcolor="white"
|
bgcolor="white"
|
||||||
|
pt={3}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
width="100%"
|
width="100%"
|
||||||
maxWidth="1300px"
|
|
||||||
display="flex"
|
display="flex"
|
||||||
justifyContent="flex-end"
|
justifyContent="space-between"
|
||||||
px={2}
|
alignItems="center"
|
||||||
|
px={8}
|
||||||
|
py={5}
|
||||||
>
|
>
|
||||||
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
|
<img
|
||||||
|
src={DekaLogo}
|
||||||
|
alt="Company Logo"
|
||||||
|
style={{ height: "40px", width: "auto" }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<IconButton onClick={() => navigate({ to: "/config" })}>
|
<IconButton onClick={() => navigate({ to: "/config" })}>
|
||||||
<SettingsIcon fontSize="large" />
|
<SettingsIcon fontSize="large" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
component="h1"
|
||||||
|
sx={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
color: "#383838",
|
||||||
|
marginBottom: 12,
|
||||||
|
marginTop: 6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Pitchbook Extractor
|
||||||
|
</Typography>
|
||||||
|
|
||||||
<Paper
|
<Paper
|
||||||
elevation={3}
|
elevation={3}
|
||||||
sx={{
|
sx={{
|
||||||
width: 900,
|
width: 800,
|
||||||
height: 500,
|
height: 400,
|
||||||
backgroundColor: "#eeeeee",
|
backgroundColor: "#eeeeee",
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|
|
||||||
|
|
@ -95,53 +95,78 @@ export default function PDFViewer({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tmpPos: string[] = [];
|
const tmpPos: string[] = [];
|
||||||
const tmpPosHighlight: string[] = [];
|
const tmpPosHighlight: string[] = [];
|
||||||
const textItems = textContent.filter(
|
|
||||||
(e) => e.text !== "" && e.text !== " ",
|
|
||||||
);
|
|
||||||
|
|
||||||
textItems.forEach((e, i) => {
|
if (textContent.length === 0) {
|
||||||
for (const s of highlight
|
setPosHighlight([]);
|
||||||
.filter((h) => h.page === pageNumber)
|
setPosHighlightFocus([]);
|
||||||
.map((h) => h.text)) {
|
return;
|
||||||
if (s.split(" ")[0] === e.text) {
|
}
|
||||||
if (
|
const findTextPositions = (searchText: string): number[] => {
|
||||||
s.split(" ").reduce((prev, curr, j) => {
|
const positions: number[] = [];
|
||||||
return prev && curr === textItems[i + j].text;
|
const normalizedSearch = searchText.toLowerCase().trim();
|
||||||
}, true)
|
|
||||||
) {
|
textContent.forEach((item, index) => {
|
||||||
for (
|
if (item.text.toLowerCase().trim() === normalizedSearch) {
|
||||||
let k = textItems[i].i;
|
positions.push(index);
|
||||||
k < textItems[i + s.split(" ").length]?.i ||
|
}
|
||||||
k < textItems[i + s.split(" ").length - 1]?.i;
|
});
|
||||||
k++
|
|
||||||
|
if (positions.length === 0) {
|
||||||
|
let cumulativeText = '';
|
||||||
|
const textBoundaries: { start: number; end: number; index: number }[] = [];
|
||||||
|
|
||||||
|
textContent.forEach((item, index) => {
|
||||||
|
const start = cumulativeText.length;
|
||||||
|
cumulativeText += item.text;
|
||||||
|
const end = cumulativeText.length;
|
||||||
|
textBoundaries.push({ start, end, index });
|
||||||
|
});
|
||||||
|
|
||||||
|
const lowerCumulative = cumulativeText.toLowerCase();
|
||||||
|
let searchIndex = lowerCumulative.indexOf(normalizedSearch);
|
||||||
|
|
||||||
|
while (searchIndex !== -1) {
|
||||||
|
const endIndex = searchIndex + normalizedSearch.length;
|
||||||
|
|
||||||
|
textBoundaries.forEach(boundary => {
|
||||||
|
if (
|
||||||
|
(boundary.start <= searchIndex && searchIndex < boundary.end) || // Search starts in this item
|
||||||
|
(boundary.start < endIndex && endIndex <= boundary.end) || // Search ends in this item
|
||||||
|
(searchIndex <= boundary.start && boundary.end <= endIndex) // This item is completely within search
|
||||||
) {
|
) {
|
||||||
tmpPos.push(textContent[k].posKey);
|
if (!positions.includes(boundary.index)) {
|
||||||
|
positions.push(boundary.index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
searchIndex = lowerCumulative.indexOf(normalizedSearch, searchIndex + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return positions.sort((a, b) => a - b);
|
||||||
if (focusHighlight?.page === pageNumber) {
|
};
|
||||||
if (focusHighlight.text.split(" ")[0] === e.text) {
|
highlight
|
||||||
if (
|
.filter(h => h.page === pageNumber)
|
||||||
focusHighlight.text.split(" ").reduce((prev, curr, j) => {
|
.forEach(highlightItem => {
|
||||||
return prev && curr === textItems[i + j].text;
|
const positions = findTextPositions(highlightItem.text);
|
||||||
}, true)
|
positions.forEach(pos => {
|
||||||
) {
|
if (pos >= 0 && pos < textContent.length) {
|
||||||
for (
|
tmpPos.push(textContent[pos].posKey);
|
||||||
let k = textItems[i].i;
|
|
||||||
k < textItems[i + focusHighlight.text.split(" ").length]?.i ||
|
|
||||||
k < textItems[i + focusHighlight.text.split(" ").length - 1]?.i;
|
|
||||||
k++
|
|
||||||
) {
|
|
||||||
tmpPosHighlight.push(textContent[k].posKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (focusHighlight?.page === pageNumber && focusHighlight.text) {
|
||||||
|
const positions = findTextPositions(focusHighlight.text);
|
||||||
|
|
||||||
|
positions.forEach(pos => {
|
||||||
|
if (pos >= 0 && pos < textContent.length) {
|
||||||
|
tmpPosHighlight.push(textContent[pos].posKey);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
setPosHighlight(tmpPos);
|
|
||||||
setPosHighlightFocus(tmpPosHighlight);
|
setPosHighlight([...new Set(tmpPos)]);
|
||||||
|
setPosHighlightFocus([...new Set(tmpPosHighlight)]);
|
||||||
}, [highlight, focusHighlight, pageNumber, textContent]);
|
}, [highlight, focusHighlight, pageNumber, textContent]);
|
||||||
|
|
||||||
const onGetTextSuccess: OnGetTextSuccess = useCallback((fullText) => {
|
const onGetTextSuccess: OnGetTextSuccess = useCallback((fullText) => {
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ function ExtractedResultsPage() {
|
||||||
const [customValue, setCustomValue] = useState("");
|
const [customValue, setCustomValue] = useState("");
|
||||||
const [customPage, setCustomPage] = useState("");
|
const [customPage, setCustomPage] = useState("");
|
||||||
const [editingCustomPage, setEditingCustomPage] = useState(false);
|
const [editingCustomPage, setEditingCustomPage] = useState(false);
|
||||||
|
const [focusHighlightOverride, setFocusHighlightOverride] = useState<{ page: number; text: string } | null>(null);
|
||||||
|
|
||||||
const originalValue = kpiValues[0]?.entity || "";
|
const originalValue = kpiValues[0]?.entity || "";
|
||||||
const originalPage = kpiValues[0]?.page || 0;
|
const originalPage = kpiValues[0]?.page || 0;
|
||||||
|
|
@ -102,6 +103,11 @@ function ExtractedResultsPage() {
|
||||||
// Um zu prüfen, ob der Wert nur aus Leerzeichen besteht
|
// Um zu prüfen, ob der Wert nur aus Leerzeichen besteht
|
||||||
const isSelectedValueEmpty = selectedIndex === -1 ? customValue.trim() === "" : !selectedValue;
|
const isSelectedValueEmpty = selectedIndex === -1 ? customValue.trim() === "" : !selectedValue;
|
||||||
|
|
||||||
|
const focusHighlight = focusHighlightOverride || {
|
||||||
|
page: groupedKpiValues.at(selectedIndex)?.pages[0] || -1,
|
||||||
|
text: groupedKpiValues.at(selectedIndex)?.entity || "",
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const valueChanged = selectedValue !== originalValue;
|
const valueChanged = selectedValue !== originalValue;
|
||||||
const pageChanged = selectedPage !== originalPage;
|
const pageChanged = selectedPage !== originalPage;
|
||||||
|
|
@ -159,12 +165,14 @@ function ExtractedResultsPage() {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
if (value === "custom") {
|
if (value === "custom") {
|
||||||
setSelectedIndex(-1);
|
setSelectedIndex(-1);
|
||||||
|
setFocusHighlightOverride(null);
|
||||||
} else {
|
} else {
|
||||||
const index = Number.parseInt(value);
|
const index = Number.parseInt(value);
|
||||||
setSelectedIndex(index);
|
setSelectedIndex(index);
|
||||||
setCurrentPage(groupedKpiValues[index].pages[0]);
|
setCurrentPage(groupedKpiValues[index].pages[0]);
|
||||||
setCustomValue("");
|
setCustomValue("");
|
||||||
setCustomPage("");
|
setCustomPage("");
|
||||||
|
setFocusHighlightOverride(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -174,7 +182,8 @@ function ExtractedResultsPage() {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
setCustomValue(value);
|
setCustomValue(value);
|
||||||
setSelectedIndex(-1);
|
setSelectedIndex(-1);
|
||||||
};
|
setFocusHighlightOverride(null);
|
||||||
|
}
|
||||||
|
|
||||||
const handleCustomPageChange = (
|
const handleCustomPageChange = (
|
||||||
event: React.ChangeEvent<HTMLInputElement>,
|
event: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
|
@ -191,6 +200,15 @@ function ExtractedResultsPage() {
|
||||||
setSelectedIndex(index);
|
setSelectedIndex(index);
|
||||||
setCustomValue("");
|
setCustomValue("");
|
||||||
setCustomPage("");
|
setCustomPage("");
|
||||||
|
setFocusHighlightOverride(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageClick = (page: number, entity: string) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
setFocusHighlightOverride({
|
||||||
|
page: page,
|
||||||
|
text: entity,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBackClick = () => {
|
const handleBackClick = () => {
|
||||||
|
|
@ -325,7 +343,7 @@ function ExtractedResultsPage() {
|
||||||
component="button"
|
component="button"
|
||||||
onClick={(e: React.MouseEvent) => {
|
onClick={(e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setCurrentPage(page);
|
handlePageClick(page, item.entity);
|
||||||
}}
|
}}
|
||||||
sx={{ cursor: "pointer", ml: i > 0 ? 1 : 0 }}
|
sx={{ cursor: "pointer", ml: i > 0 ? 1 : 0 }}
|
||||||
>
|
>
|
||||||
|
|
@ -351,6 +369,7 @@ function ExtractedResultsPage() {
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedIndex(-1);
|
setSelectedIndex(-1);
|
||||||
|
setFocusHighlightOverride(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Radio
|
<Radio
|
||||||
|
|
@ -479,10 +498,7 @@ function ExtractedResultsPage() {
|
||||||
highlight={groupedKpiValues
|
highlight={groupedKpiValues
|
||||||
.map((k) => k.pages.map((page: number) => ({ page, text: k.entity })))
|
.map((k) => k.pages.map((page: number) => ({ page, text: k.entity })))
|
||||||
.reduce((acc, val) => acc.concat(val), [])}
|
.reduce((acc, val) => acc.concat(val), [])}
|
||||||
focusHighlight={{
|
focusHighlight={focusHighlight}
|
||||||
page: groupedKpiValues.at(selectedIndex)?.pages[0] || -1,
|
|
||||||
text: groupedKpiValues.at(selectedIndex)?.entity || "",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Box mt={2} display="flex" justifyContent="flex-end" gap={2}>
|
<Box mt={2} display="flex" justifyContent="flex-end" gap={2}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue