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} > + + Company Logo + + 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} />