diff --git a/project/frontend/index.html b/project/frontend/index.html
index 2f632de..be7861a 100644
--- a/project/frontend/index.html
+++ b/project/frontend/index.html
@@ -3,15 +3,14 @@
-
+
-
- Create TanStack App - frontend
+ Pitchbook Extractor
diff --git a/project/frontend/public/favicon.ico b/project/frontend/public/favicon.ico
index a11777c..df1c62a 100644
Binary files a/project/frontend/public/favicon.ico and b/project/frontend/public/favicon.ico differ
diff --git a/project/frontend/public/logo192.png b/project/frontend/public/logo192.png
deleted file mode 100644
index fc44b0a..0000000
Binary files a/project/frontend/public/logo192.png and /dev/null differ
diff --git a/project/frontend/public/logo512.png b/project/frontend/public/logo512.png
deleted file mode 100644
index a4e47a6..0000000
Binary files a/project/frontend/public/logo512.png and /dev/null differ
diff --git a/project/frontend/src/assets/Deka_logo.png b/project/frontend/src/assets/Deka_logo.png
new file mode 100644
index 0000000..d1b24f9
Binary files /dev/null and b/project/frontend/src/assets/Deka_logo.png differ
diff --git a/project/frontend/src/components/KennzahlenTable.tsx b/project/frontend/src/components/KennzahlenTable.tsx
index 4ad51f0..71091d2 100644
--- a/project/frontend/src/components/KennzahlenTable.tsx
+++ b/project/frontend/src/components/KennzahlenTable.tsx
@@ -184,10 +184,10 @@ export default function KennzahlenTable({
-
+
Kennzahl
-
+
Wert
@@ -226,7 +226,11 @@ export default function KennzahlenTable({
return (
- {row.setting.name}
+ {row.setting.name}
+ {row.setting.mandatory && (
+ *
+ )}
+
{
// Only allow inline editing for non-multiple value cells
diff --git a/project/frontend/src/components/UploadPage.tsx b/project/frontend/src/components/UploadPage.tsx
index e72c9da..b54e7a3 100644
--- a/project/frontend/src/components/UploadPage.tsx
+++ b/project/frontend/src/components/UploadPage.tsx
@@ -1,11 +1,12 @@
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 { useCallback, useEffect, useState } from "react";
import FileUpload from "react-material-file-upload";
import { socket } from "../socket";
import { API_HOST } from "../util/api";
import { CircularProgressWithLabel } from "./CircularProgressWithLabel";
+import DekaLogo from "../assets/Deka_logo.png";
export default function UploadPage() {
const [files, setFiles] = useState([]);
@@ -87,26 +88,50 @@ export default function UploadPage() {
display="flex"
flexDirection="column"
alignItems="center"
- justifyContent="center"
+ justifyContent="flex-start"
height="100vh"
bgcolor="white"
+ pt={3}
>
+
+
+
+
navigate({ to: "/config" })}>
+
+
+ Pitchbook Extractor
+
+
>
);
-}
+}
\ No newline at end of file
diff --git a/project/frontend/src/components/pdfViewer.tsx b/project/frontend/src/components/pdfViewer.tsx
index ee68c8d..4d171a7 100644
--- a/project/frontend/src/components/pdfViewer.tsx
+++ b/project/frontend/src/components/pdfViewer.tsx
@@ -95,53 +95,78 @@ export default function PDFViewer({
useEffect(() => {
const tmpPos: string[] = [];
const tmpPosHighlight: string[] = [];
- const textItems = textContent.filter(
- (e) => e.text !== "" && e.text !== " ",
- );
- textItems.forEach((e, i) => {
- for (const s of highlight
- .filter((h) => h.page === pageNumber)
- .map((h) => h.text)) {
- if (s.split(" ")[0] === e.text) {
- if (
- s.split(" ").reduce((prev, curr, j) => {
- return prev && curr === textItems[i + j].text;
- }, true)
- ) {
- for (
- let k = textItems[i].i;
- k < textItems[i + s.split(" ").length]?.i ||
- k < textItems[i + s.split(" ").length - 1]?.i;
- k++
+ if (textContent.length === 0) {
+ setPosHighlight([]);
+ setPosHighlightFocus([]);
+ return;
+ }
+ const findTextPositions = (searchText: string): number[] => {
+ const positions: number[] = [];
+ const normalizedSearch = searchText.toLowerCase().trim();
+
+ textContent.forEach((item, index) => {
+ if (item.text.toLowerCase().trim() === normalizedSearch) {
+ positions.push(index);
+ }
+ });
+
+ 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);
}
}
-
- if (focusHighlight?.page === pageNumber) {
- if (focusHighlight.text.split(" ")[0] === e.text) {
- if (
- focusHighlight.text.split(" ").reduce((prev, curr, j) => {
- return prev && curr === textItems[i + j].text;
- }, true)
- ) {
- for (
- 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);
- }
+ return positions.sort((a, b) => a - b);
+ };
+ highlight
+ .filter(h => h.page === pageNumber)
+ .forEach(highlightItem => {
+ const positions = findTextPositions(highlightItem.text);
+ positions.forEach(pos => {
+ if (pos >= 0 && pos < textContent.length) {
+ tmpPos.push(textContent[pos].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]);
const onGetTextSuccess: OnGetTextSuccess = useCallback((fullText) => {
diff --git a/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx b/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx
index 1f2abef..0194c09 100644
--- a/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx
+++ b/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx
@@ -68,6 +68,7 @@ function ExtractedResultsPage() {
const [customValue, setCustomValue] = useState("");
const [customPage, setCustomPage] = useState("");
const [editingCustomPage, setEditingCustomPage] = useState(false);
+ const [focusHighlightOverride, setFocusHighlightOverride] = useState<{ page: number; text: string } | null>(null);
const originalValue = kpiValues[0]?.entity || "";
const originalPage = kpiValues[0]?.page || 0;
@@ -102,6 +103,11 @@ function ExtractedResultsPage() {
// Um zu prüfen, ob der Wert nur aus Leerzeichen besteht
const isSelectedValueEmpty = selectedIndex === -1 ? customValue.trim() === "" : !selectedValue;
+ const focusHighlight = focusHighlightOverride || {
+ page: groupedKpiValues.at(selectedIndex)?.pages[0] || -1,
+ text: groupedKpiValues.at(selectedIndex)?.entity || "",
+ };
+
useEffect(() => {
const valueChanged = selectedValue !== originalValue;
const pageChanged = selectedPage !== originalPage;
@@ -159,12 +165,14 @@ function ExtractedResultsPage() {
const value = event.target.value;
if (value === "custom") {
setSelectedIndex(-1);
+ setFocusHighlightOverride(null);
} else {
const index = Number.parseInt(value);
setSelectedIndex(index);
setCurrentPage(groupedKpiValues[index].pages[0]);
setCustomValue("");
setCustomPage("");
+ setFocusHighlightOverride(null);
}
};
@@ -174,7 +182,8 @@ function ExtractedResultsPage() {
const value = event.target.value;
setCustomValue(value);
setSelectedIndex(-1);
- };
+ setFocusHighlightOverride(null);
+ }
const handleCustomPageChange = (
event: React.ChangeEvent,
@@ -191,6 +200,15 @@ function ExtractedResultsPage() {
setSelectedIndex(index);
setCustomValue("");
setCustomPage("");
+ setFocusHighlightOverride(null);
+ };
+
+ const handlePageClick = (page: number, entity: string) => {
+ setCurrentPage(page);
+ setFocusHighlightOverride({
+ page: page,
+ text: entity,
+ });
};
const handleBackClick = () => {
@@ -325,7 +343,7 @@ function ExtractedResultsPage() {
component="button"
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
- setCurrentPage(page);
+ handlePageClick(page, item.entity);
}}
sx={{ cursor: "pointer", ml: i > 0 ? 1 : 0 }}
>
@@ -351,6 +369,7 @@ function ExtractedResultsPage() {
}}
onClick={() => {
setSelectedIndex(-1);
+ setFocusHighlightOverride(null);
}}
>
k.pages.map((page: number) => ({ page, text: k.entity })))
.reduce((acc, val) => acc.concat(val), [])}
- focusHighlight={{
- page: groupedKpiValues.at(selectedIndex)?.pages[0] || -1,
- text: groupedKpiValues.at(selectedIndex)?.entity || "",
- }}
+ focusHighlight={focusHighlight}
/>