From 1846b24de836e30c4595b265157bf68c63651ff9 Mon Sep 17 00:00:00 2001 From: s8613 Date: Fri, 13 Jun 2025 16:21:41 +0200 Subject: [PATCH 01/17] added Snackbar and copy logic --- .../src/routes/extractedResult.$pitchBook.tsx | 97 ++++++++++++++++++- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/project/frontend/src/routes/extractedResult.$pitchBook.tsx b/project/frontend/src/routes/extractedResult.$pitchBook.tsx index db7c47e..921b669 100644 --- a/project/frontend/src/routes/extractedResult.$pitchBook.tsx +++ b/project/frontend/src/routes/extractedResult.$pitchBook.tsx @@ -1,5 +1,5 @@ import ContentPasteIcon from "@mui/icons-material/ContentPaste"; -import { Box, Button, Paper, Typography } from "@mui/material"; +import { Box, Button, Paper, Typography, Snackbar, Alert } from "@mui/material"; import { useSuspenseQuery } from "@tanstack/react-query"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; @@ -7,6 +7,17 @@ import KennzahlenTable from "../components/KennzahlenTable"; import PDFViewer from "../components/pdfViewer"; import { kpiQueryOptions } from "../util/query"; +// SETTINGS von KennzahlenTable component (mock) +const SETTINGS = [ + { name: "Rendite", position: 1, active: true, mandatory: true }, + { name: "Ausschüttungsrendite", position: 2, active: true, mandatory: true }, + { name: "Laufzeit", position: 3, active: true, mandatory: true }, + { name: "Länderallokation", position: 4, active: true, mandatory: true }, + { name: "Managmentgebühren", position: 5, active: true, mandatory: true }, + { name: "Risikoprofil", position: 6, active: false, mandatory: true }, + { name: "Irgendwas", position: 7, active: true, mandatory: true }, +]; + export const Route = createFileRoute("/extractedResult/$pitchBook")({ component: ExtractedResultsPage, loader: ({ context: { queryClient }, params: { pitchBook } }) => @@ -18,6 +29,8 @@ function ExtractedResultsPage() { const navigate = useNavigate(); const status: "green" | "yellow" | "red" = "red"; const [currentPage, setCurrentPage] = useState(1); + const [copied, setCopied] = useState(false); + const [snackbarOpen, setSnackbarOpen] = useState(false); const statusColor = { red: "#f43131", @@ -27,6 +40,57 @@ function ExtractedResultsPage() { const { data: kpi } = useSuspenseQuery(kpiQueryOptions(pitchBook)); + const prepareClipboardData = () => { + const activeSettings = SETTINGS + .filter(setting => setting.active) + .sort((a, b) => a.position - b.position); + + const values = activeSettings.map(setting => { + const settingData = kpi[setting.name.toUpperCase()]; + if (!settingData || settingData.length === 0) { + return ""; + } + return settingData[0]?.entity || ""; + }); + return values.join('\t'); + }; + + const handleCopyToClipboard = async () => { + try { + const textToCopy = prepareClipboardData(); + + // Use the modern Clipboard API if available + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(textToCopy); + } else { + // Fallback for older browsers or non-secure contexts + const textArea = document.createElement("textarea"); + textArea.value = textToCopy; + textArea.style.position = "fixed"; + textArea.style.left = "-999999px"; + textArea.style.top = "-999999px"; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + document.execCommand('copy'); + textArea.remove(); + } + + // Show success feedback + setCopied(true); + setSnackbarOpen(true); + setTimeout(() => setCopied(false), 2000); + + } catch (err) { + console.error('Failed to copy text: ', err); + // You could show an error snackbar here if needed + } + }; + + const handleCloseSnackbar = () => { + setSnackbarOpen(false); + }; + return ( @@ -93,9 +157,19 @@ function ExtractedResultsPage() { - From 1b256e9d99e802b48bba2551d31a16dd930d4d06 Mon Sep 17 00:00:00 2001 From: s8613 Date: Sat, 14 Jun 2025 10:42:36 +0200 Subject: [PATCH 03/17] merge conflicts --- .../src/routes/extractedResult.$pitchBook.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/project/frontend/src/routes/extractedResult.$pitchBook.tsx b/project/frontend/src/routes/extractedResult.$pitchBook.tsx index 8888bd8..cfe8bae 100644 --- a/project/frontend/src/routes/extractedResult.$pitchBook.tsx +++ b/project/frontend/src/routes/extractedResult.$pitchBook.tsx @@ -7,17 +7,6 @@ import KennzahlenTable from "../components/KennzahlenTable"; import PDFViewer from "../components/pdfViewer"; import { kpiQueryOptions, settingsQueryOptions } from "../util/query"; -// SETTINGS von KennzahlenTable component (mock) -const SETTINGS = [ - { name: "Rendite", position: 1, active: true, mandatory: true }, - { name: "Ausschüttungsrendite", position: 2, active: true, mandatory: true }, - { name: "Laufzeit", position: 3, active: true, mandatory: true }, - { name: "Länderallokation", position: 4, active: true, mandatory: true }, - { name: "Managmentgebühren", position: 5, active: true, mandatory: true }, - { name: "Risikoprofil", position: 6, active: false, mandatory: true }, - { name: "Irgendwas", position: 7, active: true, mandatory: true }, -]; - export const Route = createFileRoute("/extractedResult/$pitchBook")({ component: ExtractedResultsPage, loader: ({ context: { queryClient }, params: { pitchBook } }) => @@ -45,7 +34,7 @@ function ExtractedResultsPage() { const { data: settings } = useSuspenseQuery(settingsQueryOptions()); const prepareClipboardData = () => { - const activeSettings = SETTINGS + const activeSettings = settings .filter(setting => setting.active) .sort((a, b) => a.position - b.position); From 935cf0c1762e9ad6d01077c1f893ede859a762ac Mon Sep 17 00:00:00 2001 From: s8613 Date: Sat, 14 Jun 2025 12:43:59 +0200 Subject: [PATCH 04/17] Added new page overview of all pitchbooks. --- .../frontend/src/components/ConfigTable.tsx | 10 +- .../src/components/PitchBooksTable.tsx | 186 ++++++++++++++++++ .../frontend/src/components/UploadPage.tsx | 13 ++ project/frontend/src/routeTree.gen.ts | 26 +++ project/frontend/src/routes/config-add.tsx | 15 +- .../src/routes/config-detail.$kpiId.tsx | 17 +- project/frontend/src/routes/config.tsx | 22 ++- .../src/routes/extractedResult.$pitchBook.tsx | 17 +- project/frontend/src/routes/pitchbooks.tsx | 59 ++++++ project/frontend/src/util/api.ts | 8 + project/frontend/src/util/query.ts | 9 +- 11 files changed, 370 insertions(+), 12 deletions(-) create mode 100644 project/frontend/src/components/PitchBooksTable.tsx create mode 100644 project/frontend/src/routes/pitchbooks.tsx diff --git a/project/frontend/src/components/ConfigTable.tsx b/project/frontend/src/components/ConfigTable.tsx index 74675d7..5176a21 100644 --- a/project/frontend/src/components/ConfigTable.tsx +++ b/project/frontend/src/components/ConfigTable.tsx @@ -6,7 +6,11 @@ import type { Kennzahl } from "../types/kpi"; import { getDisplayType } from "../types/kpi"; import { fetchKennzahlen as fetchK } from "../util/api"; -export function ConfigTable() { +type ConfigTableProps = { + from?: string; +}; + +export function ConfigTable({ from }: ConfigTableProps) { const navigate = useNavigate(); const [kennzahlen, setKennzahlen] = useState([]); const [draggedItem, setDraggedItem] = useState(null); @@ -160,12 +164,10 @@ export function ConfigTable() { return; } - console.log("Navigating to detail page for KPI:", kennzahl); - console.log("KPI ID:", kennzahl.id); - navigate({ to: `/config-detail/$kpiId`, params: { kpiId: kennzahl.id.toString() }, + search: from ? { from } : undefined, }); }; diff --git a/project/frontend/src/components/PitchBooksTable.tsx b/project/frontend/src/components/PitchBooksTable.tsx new file mode 100644 index 0000000..7a86ada --- /dev/null +++ b/project/frontend/src/components/PitchBooksTable.tsx @@ -0,0 +1,186 @@ +import { Box, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, CircularProgress, Chip } from "@mui/material"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import { useNavigate } from "@tanstack/react-router"; +import { pitchBooksQueryOptions } from "../util/query"; +import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty'; + +interface PitchBook { + id: number; + filename: string; + created_at: string; + kpi?: string | { + [key: string]: { + label: string; + entity: string; + page: number; + status: string; + source: string; + }[]; + }; + status?: 'processing' | 'completed'; +} + +export function PitchBooksTable() { + const navigate = useNavigate(); + const { data: pitchBooks, isLoading } = useSuspenseQuery(pitchBooksQueryOptions()); + + const handleRowClick = (pitchBookId: number) => { + navigate({ + to: "/extractedResult/$pitchBook", + params: { pitchBook: pitchBookId.toString() }, + search: { from: "overview" } + }); + }; + + const getKPIValue = (pitchBook: PitchBook, fieldName: string): string => { + if (!pitchBook.kpi || typeof pitchBook.kpi === 'string') { + try { + const parsedKPI = JSON.parse(pitchBook.kpi as string); + // Convert array to object format if needed + const kpiObj = Array.isArray(parsedKPI) ? + parsedKPI.reduce((acc: any, item: any) => { + if (!acc[item.label]) acc[item.label] = []; + acc[item.label].push(item); + return acc; + }, {}) : parsedKPI; + + return kpiObj[fieldName]?.[0]?.entity || 'N/A'; + } catch { + return 'N/A'; + } + } + + return (pitchBook.kpi as any)[fieldName]?.[0]?.entity || 'N/A'; + }; + + const getStatus = (pitchBook: PitchBook) => { + if (pitchBook.kpi && + ((typeof pitchBook.kpi === 'string' && pitchBook.kpi !== '{}') || + (typeof pitchBook.kpi === 'object' && Object.keys(pitchBook.kpi).length > 0))) { + return 'completed'; + } + return 'processing'; + }; + + if (isLoading) { + return ( + + + + ); + } + + return ( + + + + + + Fondsname + Fondsmanager + Dateiname + Status + + + + {pitchBooks.map((pitchBook: PitchBook) => { + const status = getStatus(pitchBook); + const fundName = getKPIValue(pitchBook, 'FONDSNAME') || + getKPIValue(pitchBook, 'FUND_NAME') || + getKPIValue(pitchBook, 'NAME'); + + const manager = getKPIValue(pitchBook, 'FONDSMANAGER') || + getKPIValue(pitchBook, 'MANAGER') || + getKPIValue(pitchBook, 'PORTFOLIO_MANAGER'); + + return ( + handleRowClick(pitchBook.id)} + sx={{ + cursor: "pointer", + "&:hover": { + backgroundColor: "#f9f9f9", + }, + }} + > + + + + + + + + {fundName} + + + {manager} + + + {pitchBook.filename} + + + + {status === 'completed' ? ( + } + label="Abgeschlossen" + size="small" + sx={{ + backgroundColor: "#e8f5e9", + color: "#2e7d32", + "& .MuiChip-icon": { + color: "#2e7d32", + }, + }} + /> + ) : ( + } + label="In Bearbeitung" + size="small" + sx={{ + backgroundColor: "#fff3e0", + color: "#e65100", + "& .MuiChip-icon": { + color: "#e65100", + }, + }} + /> + )} + + + ); + })} + +
+ {pitchBooks.length === 0 && ( + + + Keine Pitch Books vorhanden + + + )} +
+ ); +} \ No newline at end of file diff --git a/project/frontend/src/components/UploadPage.tsx b/project/frontend/src/components/UploadPage.tsx index 66fe220..3a2a85c 100644 --- a/project/frontend/src/components/UploadPage.tsx +++ b/project/frontend/src/components/UploadPage.tsx @@ -178,6 +178,19 @@ export default function UploadPage() { > Kennzahlen extrahieren +
); diff --git a/project/frontend/src/routeTree.gen.ts b/project/frontend/src/routeTree.gen.ts index 489b1cf..5377387 100644 --- a/project/frontend/src/routeTree.gen.ts +++ b/project/frontend/src/routeTree.gen.ts @@ -11,6 +11,7 @@ // Import Routes import { Route as rootRoute } from './routes/__root' +import { Route as PitchbooksImport } from './routes/pitchbooks' import { Route as ConfigAddImport } from './routes/config-add' import { Route as ConfigImport } from './routes/config' import { Route as IndexImport } from './routes/index' @@ -20,6 +21,12 @@ import { Route as ExtractedResultPitchBookKpiImport } from './routes/extractedRe // Create/Update Routes +const PitchbooksRoute = PitchbooksImport.update({ + id: '/pitchbooks', + path: '/pitchbooks', + getParentRoute: () => rootRoute, +} as any) + const ConfigAddRoute = ConfigAddImport.update({ id: '/config-add', path: '/config-add', @@ -82,6 +89,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ConfigAddImport parentRoute: typeof rootRoute } + '/pitchbooks': { + id: '/pitchbooks' + path: '/pitchbooks' + fullPath: '/pitchbooks' + preLoaderRoute: typeof PitchbooksImport + parentRoute: typeof rootRoute + } '/config-detail/$kpiId': { id: '/config-detail/$kpiId' path: '/config-detail/$kpiId' @@ -112,6 +126,7 @@ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/config': typeof ConfigRoute '/config-add': typeof ConfigAddRoute + '/pitchbooks': typeof PitchbooksRoute '/config-detail/$kpiId': typeof ConfigDetailKpiIdRoute '/extractedResult/$pitchBook': typeof ExtractedResultPitchBookRoute '/extractedResult/$pitchBook/$kpi': typeof ExtractedResultPitchBookKpiRoute @@ -121,6 +136,7 @@ export interface FileRoutesByTo { '/': typeof IndexRoute '/config': typeof ConfigRoute '/config-add': typeof ConfigAddRoute + '/pitchbooks': typeof PitchbooksRoute '/config-detail/$kpiId': typeof ConfigDetailKpiIdRoute '/extractedResult/$pitchBook': typeof ExtractedResultPitchBookRoute '/extractedResult/$pitchBook/$kpi': typeof ExtractedResultPitchBookKpiRoute @@ -131,6 +147,7 @@ export interface FileRoutesById { '/': typeof IndexRoute '/config': typeof ConfigRoute '/config-add': typeof ConfigAddRoute + '/pitchbooks': typeof PitchbooksRoute '/config-detail/$kpiId': typeof ConfigDetailKpiIdRoute '/extractedResult/$pitchBook': typeof ExtractedResultPitchBookRoute '/extractedResult_/$pitchBook/$kpi': typeof ExtractedResultPitchBookKpiRoute @@ -142,6 +159,7 @@ export interface FileRouteTypes { | '/' | '/config' | '/config-add' + | '/pitchbooks' | '/config-detail/$kpiId' | '/extractedResult/$pitchBook' | '/extractedResult/$pitchBook/$kpi' @@ -150,6 +168,7 @@ export interface FileRouteTypes { | '/' | '/config' | '/config-add' + | '/pitchbooks' | '/config-detail/$kpiId' | '/extractedResult/$pitchBook' | '/extractedResult/$pitchBook/$kpi' @@ -158,6 +177,7 @@ export interface FileRouteTypes { | '/' | '/config' | '/config-add' + | '/pitchbooks' | '/config-detail/$kpiId' | '/extractedResult/$pitchBook' | '/extractedResult_/$pitchBook/$kpi' @@ -168,6 +188,7 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute ConfigRoute: typeof ConfigRoute ConfigAddRoute: typeof ConfigAddRoute + PitchbooksRoute: typeof PitchbooksRoute ConfigDetailKpiIdRoute: typeof ConfigDetailKpiIdRoute ExtractedResultPitchBookRoute: typeof ExtractedResultPitchBookRoute ExtractedResultPitchBookKpiRoute: typeof ExtractedResultPitchBookKpiRoute @@ -177,6 +198,7 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, ConfigRoute: ConfigRoute, ConfigAddRoute: ConfigAddRoute, + PitchbooksRoute: PitchbooksRoute, ConfigDetailKpiIdRoute: ConfigDetailKpiIdRoute, ExtractedResultPitchBookRoute: ExtractedResultPitchBookRoute, ExtractedResultPitchBookKpiRoute: ExtractedResultPitchBookKpiRoute, @@ -195,6 +217,7 @@ export const routeTree = rootRoute "/", "/config", "/config-add", + "/pitchbooks", "/config-detail/$kpiId", "/extractedResult/$pitchBook", "/extractedResult_/$pitchBook/$kpi" @@ -209,6 +232,9 @@ export const routeTree = rootRoute "/config-add": { "filePath": "config-add.tsx" }, + "/pitchbooks": { + "filePath": "pitchbooks.tsx" + }, "/config-detail/$kpiId": { "filePath": "config-detail.$kpiId.tsx" }, diff --git a/project/frontend/src/routes/config-add.tsx b/project/frontend/src/routes/config-add.tsx index b90d7b1..e63e072 100644 --- a/project/frontend/src/routes/config-add.tsx +++ b/project/frontend/src/routes/config-add.tsx @@ -6,10 +6,23 @@ import type { Kennzahl } from "../types/kpi"; export const Route = createFileRoute("/config-add")({ component: ConfigAddPage, + validateSearch: (search: Record): { from?: string } => { + return { + from: search.from as string | undefined, + }; + }, }); function ConfigAddPage() { const navigate = useNavigate(); + const { from } = Route.useSearch(); + + const handleBack = () => { + navigate({ + to: "/config", + search: from ? { from } : undefined, + }); + }; const handleSave = async (formData: Partial) => { try { @@ -68,7 +81,7 @@ function ConfigAddPage() { mb={4} > - navigate({ to: "/config" })}> + diff --git a/project/frontend/src/routes/config-detail.$kpiId.tsx b/project/frontend/src/routes/config-detail.$kpiId.tsx index 16ddce9..eee7898 100644 --- a/project/frontend/src/routes/config-detail.$kpiId.tsx +++ b/project/frontend/src/routes/config-detail.$kpiId.tsx @@ -9,16 +9,29 @@ import { typeDisplayMapping } from "../types/kpi"; export const Route = createFileRoute("/config-detail/$kpiId")({ component: KPIDetailPage, + validateSearch: (search: Record): { from?: string } => { + return { + from: search.from as string | undefined, + }; + }, }); function KPIDetailPage() { const { kpiId } = Route.useParams(); const navigate = useNavigate(); + const { from } = Route.useSearch(); const [kennzahl, setKennzahl] = useState(null); const [isEditing, setIsEditing] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const handleBack = () => { + navigate({ + to: "/config", + search: from ? { from } : undefined + }); + }; + useEffect(() => { const fetchKennzahl = async () => { try { @@ -138,7 +151,7 @@ function KPIDetailPage() { mb={4} > - navigate({ to: "/config" })}> + @@ -249,7 +262,7 @@ function KPIDetailPage() { mb={4} > - navigate({ to: "/config" })}> + diff --git a/project/frontend/src/routes/config.tsx b/project/frontend/src/routes/config.tsx index eddf16e..ac64c3d 100644 --- a/project/frontend/src/routes/config.tsx +++ b/project/frontend/src/routes/config.tsx @@ -6,13 +6,29 @@ import { ConfigTable } from "../components/ConfigTable"; export const Route = createFileRoute("/config")({ component: ConfigPage, + validateSearch: (search: Record): { from?: string } => { + const from = typeof search.from === "string" ? search.from : undefined; + return { from }; + } }); function ConfigPage() { const navigate = useNavigate(); + const { from } = Route.useSearch(); const handleAddNewKPI = () => { - navigate({ to: "/config-add" }); + navigate({ + to: "/config-add", + search: from ? { from } : undefined + }); + }; + + const handleBack = () => { + if (from === "pitchbooks") { + navigate({ to: "/pitchbooks" }); + } else { + navigate({ to: "/" }); + } }; return ( @@ -34,7 +50,7 @@ function ConfigPage() { px={4} > - navigate({ to: "/" })}> + @@ -53,7 +69,7 @@ function ConfigPage() { - + ); diff --git a/project/frontend/src/routes/extractedResult.$pitchBook.tsx b/project/frontend/src/routes/extractedResult.$pitchBook.tsx index 6878542..feba82f 100644 --- a/project/frontend/src/routes/extractedResult.$pitchBook.tsx +++ b/project/frontend/src/routes/extractedResult.$pitchBook.tsx @@ -1,5 +1,6 @@ import ContentPasteIcon from "@mui/icons-material/ContentPaste"; -import { Box, Button, Paper, Typography } from "@mui/material"; +import ArrowBackIcon from "@mui/icons-material/ArrowBack"; +import { Box, Button, Paper, Typography, IconButton } from "@mui/material"; import { useSuspenseQuery } from "@tanstack/react-query"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; @@ -9,6 +10,11 @@ import { kpiQueryOptions, settingsQueryOptions } from "../util/query"; export const Route = createFileRoute("/extractedResult/$pitchBook")({ component: ExtractedResultsPage, + validateSearch: (search: Record): { from?: string } => { + return { + from: search.from as string | undefined, + }; + }, loader: ({ context: { queryClient }, params: { pitchBook } }) => Promise.allSettled([ queryClient.ensureQueryData(kpiQueryOptions(pitchBook)), @@ -19,6 +25,7 @@ export const Route = createFileRoute("/extractedResult/$pitchBook")({ function ExtractedResultsPage() { const { pitchBook } = Route.useParams(); const navigate = useNavigate(); + const { from } = Route.useSearch(); const status: "green" | "yellow" | "red" = "red"; const [currentPage, setCurrentPage] = useState(1); @@ -34,6 +41,14 @@ function ExtractedResultsPage() { return ( + {from === "overview" && ( + navigate({ to: "/pitchbooks" })} + sx={{ ml: -1 }} + > + + + )} + queryClient.ensureQueryData(pitchBooksQueryOptions()), +}); + +function PitchBooksPage() { + const navigate = useNavigate(); + + return ( + + + + navigate({ to: "/" })}> + + + + Übersicht aller Pitch Books + + + navigate({ + to: "/config", + search: { from: "pitchbooks" } + })} + > + + + + + + + + ); +} \ No newline at end of file diff --git a/project/frontend/src/util/api.ts b/project/frontend/src/util/api.ts index 5a8c3c6..ac3cda2 100644 --- a/project/frontend/src/util/api.ts +++ b/project/frontend/src/util/api.ts @@ -107,3 +107,11 @@ export const fetchKennzahlen = async (): Promise => { const data = await response.json(); return data; }; + +export async function fetchPitchBooks() { + const response = await fetch("http://localhost:5050/api/pitch_book/"); + if (!response.ok) { + throw new Error("Failed to fetch pitch books"); + } + return response.json(); +} diff --git a/project/frontend/src/util/query.ts b/project/frontend/src/util/query.ts index 0821301..db07917 100644 --- a/project/frontend/src/util/query.ts +++ b/project/frontend/src/util/query.ts @@ -1,5 +1,5 @@ import { queryOptions } from "@tanstack/react-query"; -import { fetchKPI, fetchKennzahlen } from "./api"; +import { fetchKPI, fetchKennzahlen, fetchPitchBooks } from "./api"; export const kpiQueryOptions = (pitchBookId: string) => queryOptions({ @@ -12,3 +12,10 @@ export const settingsQueryOptions = () => queryKey: ["pitchBookSettings"], queryFn: () => fetchKennzahlen(), }); + +export const pitchBooksQueryOptions = () => + queryOptions({ + queryKey: ["pitchBooks"], + queryFn: fetchPitchBooks, + staleTime: 30000, + }); \ No newline at end of file From 940b198003b79efc10a251437dd9ad4b2a3fb193 Mon Sep 17 00:00:00 2001 From: s8613 Date: Sat, 14 Jun 2025 13:01:44 +0200 Subject: [PATCH 05/17] Fixed redirect when error. --- .../src/routes/extractedResult.$pitchBook.tsx | 14 +++++++++++--- .../routes/extractedResult_.$pitchBook.$kpi.tsx | 12 ++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/project/frontend/src/routes/extractedResult.$pitchBook.tsx b/project/frontend/src/routes/extractedResult.$pitchBook.tsx index feba82f..68acdb4 100644 --- a/project/frontend/src/routes/extractedResult.$pitchBook.tsx +++ b/project/frontend/src/routes/extractedResult.$pitchBook.tsx @@ -7,6 +7,7 @@ import { useState } from "react"; import KennzahlenTable from "../components/KennzahlenTable"; import PDFViewer from "../components/pdfViewer"; import { kpiQueryOptions, settingsQueryOptions } from "../util/query"; +import { redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/extractedResult/$pitchBook")({ component: ExtractedResultsPage, @@ -15,11 +16,18 @@ export const Route = createFileRoute("/extractedResult/$pitchBook")({ from: search.from as string | undefined, }; }, - loader: ({ context: { queryClient }, params: { pitchBook } }) => - Promise.allSettled([ + loader: async ({ context: { queryClient }, params: { pitchBook } }) => { + const results = await Promise.allSettled([ queryClient.ensureQueryData(kpiQueryOptions(pitchBook)), queryClient.ensureQueryData(settingsQueryOptions()), - ]), + ]); + if (results[0].status === "rejected") { + throw redirect({ + to: "/" + }); + } + return results; + } }); function ExtractedResultsPage() { diff --git a/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx b/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx index ccf34b8..21dc929 100644 --- a/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx +++ b/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx @@ -23,11 +23,19 @@ import PDFViewer from "../components/pdfViewer"; import { kpiQueryOptions } from "../util/query"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import {fetchPutKPI} from "../util/api"; +import { redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/extractedResult_/$pitchBook/$kpi")({ component: ExtractedResultsPage, - loader: ({ context: { queryClient }, params: { pitchBook } }) => - queryClient.ensureQueryData(kpiQueryOptions(pitchBook)), + loader: async ({ context: { queryClient }, params: { pitchBook } }) => { + try { + return await queryClient.ensureQueryData(kpiQueryOptions(pitchBook)); + } catch (err) { + throw redirect({ + to: "/" + }); + } + }, }); function ExtractedResultsPage() { From 4a347951e906b4770425b0042a2f2abefca46872 Mon Sep 17 00:00:00 2001 From: s8613 Date: Sat, 14 Jun 2025 13:36:00 +0200 Subject: [PATCH 06/17] fixed redirect for extracedresults. --- .../frontend/src/components/KennzahlenTable.tsx | 5 ++++- .../src/routes/extractedResult.$pitchBook.tsx | 1 + .../routes/extractedResult_.$pitchBook.$kpi.tsx | 15 ++++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/project/frontend/src/components/KennzahlenTable.tsx b/project/frontend/src/components/KennzahlenTable.tsx index 406798e..c9729dd 100644 --- a/project/frontend/src/components/KennzahlenTable.tsx +++ b/project/frontend/src/components/KennzahlenTable.tsx @@ -34,6 +34,7 @@ interface KennzahlenTableProps { source: string; }[]; }; + from?: string; } export default function KennzahlenTable({ @@ -41,10 +42,11 @@ export default function KennzahlenTable({ data, pdfId, settings, + from }: KennzahlenTableProps) { const [editingIndex, setEditingIndex] = useState(""); const [editValue, setEditValue] = useState(""); - const navigate = useNavigate({ from: "/extractedResult/$pitchBook" }); + const navigate = useNavigate(); const queryClient = useQueryClient(); @@ -120,6 +122,7 @@ export default function KennzahlenTable({ pitchBook: pdfId, kpi: settingName, }, + search: { from: from ?? undefined }, }); }; diff --git a/project/frontend/src/routes/extractedResult.$pitchBook.tsx b/project/frontend/src/routes/extractedResult.$pitchBook.tsx index 68acdb4..59a8039 100644 --- a/project/frontend/src/routes/extractedResult.$pitchBook.tsx +++ b/project/frontend/src/routes/extractedResult.$pitchBook.tsx @@ -98,6 +98,7 @@ function ExtractedResultsPage() { onPageClick={setCurrentPage} data={kpi} pdfId={pitchBook} + from={from} /> ) => { + return { + from: typeof search.from === "string" ? search.from : undefined, + }; + }, loader: async ({ context: { queryClient }, params: { pitchBook } }) => { try { return await queryClient.ensureQueryData(kpiQueryOptions(pitchBook)); @@ -43,6 +48,7 @@ function ExtractedResultsPage() { const { pitchBook, kpi } = params; const navigate = useNavigate(); const queryClient = useQueryClient(); + const { from } = Route.useSearch(); const { data: kpiData @@ -76,7 +82,8 @@ function ExtractedResultsPage() { }); navigate({ to: "/extractedResult/$pitchBook", - params: { pitchBook } + params: { pitchBook }, + search: from ? { from } : undefined }); }, onError: (error) => { @@ -112,7 +119,8 @@ function ExtractedResultsPage() { } else { navigate({ to: "/extractedResult/$pitchBook", - params: { pitchBook } + params: { pitchBook }, + search: from ? { from } : undefined }); } }; @@ -121,7 +129,8 @@ function ExtractedResultsPage() { setShowConfirmDialog(false); navigate({ to: "/extractedResult/$pitchBook", - params: { pitchBook } + params: { pitchBook }, + search: from ? { from } : undefined }); }; From 6285be6d40b164c927cfbdc48ea1ea599fa3e913 Mon Sep 17 00:00:00 2001 From: s8613 Date: Sun, 15 Jun 2025 12:05:07 +0200 Subject: [PATCH 07/17] merge conflicts and small bug of page number --- .../src/components/KennzahlenTable.tsx | 28 +++++++++++-------- .../extractedResult_.$pitchBook.$kpi.tsx | 16 +++++++++-- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/project/frontend/src/components/KennzahlenTable.tsx b/project/frontend/src/components/KennzahlenTable.tsx index f4db2d7..10058ca 100644 --- a/project/frontend/src/components/KennzahlenTable.tsx +++ b/project/frontend/src/components/KennzahlenTable.tsx @@ -286,18 +286,22 @@ export default function KennzahlenTable({ )} - - onPageClick?.( - Number(row.extractedValues.at(0)?.page), - row.extractedValues.at(0)?.entity || "", - ) - } - sx={{ cursor: "pointer" }} - > - {row.extractedValues.at(0)?.page} - + {(row.extractedValues.at(0)?.page ?? 0) > 0 ? ( + { + const extractedValue = row.extractedValues.at(0); + if (extractedValue?.page && extractedValue.page > 0) { + onPageClick?.(Number(extractedValue.page), extractedValue.entity || ""); + } + }} + sx={{ cursor: "pointer" }} + > + {row.extractedValues.at(0)?.page} + + ) : ( + "" + )} ); diff --git a/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx b/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx index b2f6b17..22ec938 100644 --- a/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx +++ b/project/frontend/src/routes/extractedResult_.$pitchBook.$kpi.tsx @@ -30,8 +30,6 @@ import { useEffect, useState } from "react"; import PDFViewer from "../components/pdfViewer"; import { fetchPutKPI } from "../util/api"; import { kpiQueryOptions } from "../util/query"; -import ArrowBackIcon from "@mui/icons-material/ArrowBack"; -import {fetchPutKPI} from "../util/api"; import { redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/extractedResult_/$pitchBook/$kpi")({ @@ -78,9 +76,21 @@ function ExtractedResultsPage() { const { mutate: updateKPI } = useMutation({ mutationFn: () => { const updatedData = { ...kpiData }; + let baseObject; + if (selectedIndex >= 0) { + baseObject = kpiValues[selectedIndex]; + } else { + baseObject = { + label: kpi.toUpperCase(), + entity: selectedValue, + page: 0, + status: "single-source", + source: "manual", + }; + } updatedData[kpi.toUpperCase()] = [ { - ...kpiValues[0], + ...baseObject, entity: selectedValue, }, ]; From ad931d0c41d52fdb716bcebacf3d973a36b365cb Mon Sep 17 00:00:00 2001 From: Zainab2604 Date: Sun, 15 Jun 2025 13:37:22 +0200 Subject: [PATCH 08/17] Add Tooltip for missing values --- .../src/components/KennzahlenTable.tsx | 114 ++++++++++-------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/project/frontend/src/components/KennzahlenTable.tsx b/project/frontend/src/components/KennzahlenTable.tsx index 5808a5f..5a94dff 100644 --- a/project/frontend/src/components/KennzahlenTable.tsx +++ b/project/frontend/src/components/KennzahlenTable.tsx @@ -225,61 +225,69 @@ export default function KennzahlenTable({ ) : ( - - - {hasNoValue && ( - - )} - {editingIndex === row.setting.name ? ( - setEditValue(e.target.value)} - onKeyDown={(e) => - handleKeyPress(e, row.setting.name) - } - onBlur={() => handleSave(row.setting.name)} - autoFocus - size="small" - fullWidth - variant="standard" - sx={{ margin: "-8px 0" }} + + + + {hasNoValue && ( + + )} + {editingIndex === row.setting.name ? ( + setEditValue(e.target.value)} + onKeyDown={(e) => + handleKeyPress(e, row.setting.name) + } + onBlur={() => handleSave(row.setting.name)} + autoFocus + size="small" + fullWidth + variant="standard" + sx={{ margin: "-8px 0" }} + /> + ) : ( + + {row.extractedValues.at(0)?.entity || "—"} + + )} + + { + e.stopPropagation(); + startEditing( + row.extractedValues.at(0)?.entity || "", + row.setting.name, + ); + }} /> - ) : ( - - {row.extractedValues.at(0)?.entity || "—"} - - )} - - { - e.stopPropagation(); - startEditing( - row.extractedValues.at(0)?.entity || "", - row.setting.name, - ); - }} - /> - + + + )} From e666db3f7dc1274716c4bc0adf4807492c7a040f Mon Sep 17 00:00:00 2001 From: Zainab2604 Date: Sun, 15 Jun 2025 13:38:20 +0200 Subject: [PATCH 09/17] Fix typo --- project/frontend/src/components/KennzahlenTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/frontend/src/components/KennzahlenTable.tsx b/project/frontend/src/components/KennzahlenTable.tsx index 5a94dff..efd613d 100644 --- a/project/frontend/src/components/KennzahlenTable.tsx +++ b/project/frontend/src/components/KennzahlenTable.tsx @@ -226,7 +226,7 @@ export default function KennzahlenTable({ ) : ( From 52ed664350be3130276b74fe98481df2ee99e42d Mon Sep 17 00:00:00 2001 From: Anastasia Hanna Ougolnikova <3019483@stud.hs-mannheim.de> Date: Sun, 15 Jun 2025 14:16:12 +0200 Subject: [PATCH 10/17] Update project/Dockerfile Fixed worker number for Validate-Service --- project/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dockerfile b/project/Dockerfile index 591bb75..db0c480 100644 --- a/project/Dockerfile +++ b/project/Dockerfile @@ -17,4 +17,4 @@ COPY . . ENV PYTHONUNBUFFERED=1 -CMD ["gunicorn", "--timeout", "600", "--workers", "2", "--bind", "0.0.0.0:5000", "app:app"] +CMD ["gunicorn", "--timeout", "600", "--workers", "1", "--bind", "0.0.0.0:5000", "app:app"] From a075a9bb3578ec7c4a24b5aaf0cb7a556d929cc3 Mon Sep 17 00:00:00 2001 From: Zainab2604 Date: Sun, 15 Jun 2025 14:38:41 +0200 Subject: [PATCH 11/17] Add Problem to title --- project/frontend/src/components/KennzahlenTable.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/project/frontend/src/components/KennzahlenTable.tsx b/project/frontend/src/components/KennzahlenTable.tsx index efd613d..db0a891 100644 --- a/project/frontend/src/components/KennzahlenTable.tsx +++ b/project/frontend/src/components/KennzahlenTable.tsx @@ -226,7 +226,13 @@ export default function KennzahlenTable({ ) : ( + Problem +
+ Es wurden keine Kennzahlen gefunden. Bitte ergänzen! + : "" + } placement="bottom" arrow > From 5999bc591c4c23be050db908bab65f80c9544136 Mon Sep 17 00:00:00 2001 From: s8613 Date: Sun, 15 Jun 2025 16:50:09 +0200 Subject: [PATCH 12/17] Added dynamic changing host logic. --- project/docker-compose.yml | 3 +++ project/frontend/Dockerfile | 13 +++++++++++ project/frontend/docker/docker-entrypoint.sh | 23 +++++++++++++++++++ .../frontend/src/components/ConfigTable.tsx | 5 ++-- .../frontend/src/components/UploadPage.tsx | 3 ++- project/frontend/src/components/pdfViewer.tsx | 3 ++- project/frontend/src/routes/config-add.tsx | 5 ++-- .../src/routes/config-detail.$kpiId.tsx | 5 ++-- project/frontend/src/socket.ts | 3 ++- project/frontend/src/util/api.ts | 10 +++++--- 10 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 project/frontend/docker/docker-entrypoint.sh diff --git a/project/docker-compose.yml b/project/docker-compose.yml index 3cfe028..703914a 100644 --- a/project/docker-compose.yml +++ b/project/docker-compose.yml @@ -4,6 +4,9 @@ services: context: frontend ports: - 8080:80 + environment: + - API_HOST=http://coordinator:5000 + db: image: postgres:17-alpine env_file: diff --git a/project/frontend/Dockerfile b/project/frontend/Dockerfile index 617e964..69ae868 100644 --- a/project/frontend/Dockerfile +++ b/project/frontend/Dockerfile @@ -8,8 +8,21 @@ RUN bun install --frozen-lockfile COPY . . +# dummy environment variable for build - DONT CHANGE!!! +ENV VITE_API_HOST=SECRET_HOST + RUN bun run build FROM nginx:1.28.0-alpine3.21 + +# default environment variable that will be replaced at runtime +ENV API_HOST=http://localhost:5050 + COPY docker/nginx.conf /etc/nginx/conf.d/default.conf COPY --from=base /usr/src/app/dist /usr/share/nginx/html + +# entrypoint script +COPY docker/docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh + +CMD ["/docker-entrypoint.sh"] \ No newline at end of file diff --git a/project/frontend/docker/docker-entrypoint.sh b/project/frontend/docker/docker-entrypoint.sh new file mode 100644 index 0000000..fc1b6be --- /dev/null +++ b/project/frontend/docker/docker-entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# Replace API host in built JavaScript files +echo "Replacing SECRET_HOST with ${API_HOST}" + +# Replace in main JS files +for i in /usr/share/nginx/html/*.js; do + if [ -f "$i" ]; then + sed -i "s|SECRET_HOST|${API_HOST}|g" "$i" + fi +done + +# Replace in assets folder JS files +for i in /usr/share/nginx/html/assets/*.js; do + if [ -f "$i" ]; then + sed -i "s|SECRET_HOST|${API_HOST}|g" "$i" + fi +done + +echo "Replacement completed. Starting nginx..." + +# Start nginx and replace the shell process +exec nginx -g 'daemon off;' \ No newline at end of file diff --git a/project/frontend/src/components/ConfigTable.tsx b/project/frontend/src/components/ConfigTable.tsx index 74675d7..3b7b6d4 100644 --- a/project/frontend/src/components/ConfigTable.tsx +++ b/project/frontend/src/components/ConfigTable.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from "react"; import type { Kennzahl } from "../types/kpi"; import { getDisplayType } from "../types/kpi"; import { fetchKennzahlen as fetchK } from "../util/api"; +import { API_HOST } from "../util/api"; export function ConfigTable() { const navigate = useNavigate(); @@ -42,7 +43,7 @@ export function ConfigTable() { try { const response = await fetch( - `http://localhost:5050/api/kpi_setting/${id}`, + `${API_HOST}/api/kpi_setting/${id}`, { method: "PUT", headers: { @@ -79,7 +80,7 @@ export function ConfigTable() { })); const response = await fetch( - `http://localhost:5050/api/kpi_setting/update-kpi-positions`, + `${API_HOST}/api/kpi_setting/update-kpi-positions`, { method: "PUT", headers: { diff --git a/project/frontend/src/components/UploadPage.tsx b/project/frontend/src/components/UploadPage.tsx index 66fe220..20d6f7b 100644 --- a/project/frontend/src/components/UploadPage.tsx +++ b/project/frontend/src/components/UploadPage.tsx @@ -5,6 +5,7 @@ import { useCallback, useEffect, useState } from "react"; import FileUpload from "react-material-file-upload"; import { socket } from "../socket"; import { CircularProgressWithLabel } from "./CircularProgressWithLabel"; +import { API_HOST } from "../util/api"; const PROGRESS = true; @@ -18,7 +19,7 @@ export default function UploadPage() { const uploadFile = useCallback(async () => { const formData = new FormData(); formData.append("file", files[0]); - const response = await fetch("http://localhost:5050/api/pitch_book", { + const response = await fetch(`${API_HOST}/api/pitch_book`, { method: "POST", body: formData, }); diff --git a/project/frontend/src/components/pdfViewer.tsx b/project/frontend/src/components/pdfViewer.tsx index b39421d..972f5f5 100644 --- a/project/frontend/src/components/pdfViewer.tsx +++ b/project/frontend/src/components/pdfViewer.tsx @@ -11,6 +11,7 @@ import type { } from "node_modules/react-pdf/dist/esm/shared/types"; import { socket } from "../socket"; import { highlightPattern } from "../util/highlighting"; +import { API_HOST } from "../util/api"; interface PDFViewerProps { pitchBookId: string; @@ -172,7 +173,7 @@ export default function PDFViewer({ > console.error("Es gab ein Fehler beim Laden des PDFs:", error) diff --git a/project/frontend/src/routes/config-add.tsx b/project/frontend/src/routes/config-add.tsx index b90d7b1..5d536bd 100644 --- a/project/frontend/src/routes/config-add.tsx +++ b/project/frontend/src/routes/config-add.tsx @@ -3,6 +3,7 @@ import { Box, Typography, IconButton } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import { KPIForm } from "../components/KPIForm"; import type { Kennzahl } from "../types/kpi"; +import { API_HOST } from "../util/api"; export const Route = createFileRoute("/config-add")({ component: ConfigAddPage, @@ -13,7 +14,7 @@ function ConfigAddPage() { const handleSave = async (formData: Partial) => { try { - const existingKPIsResponse = await fetch('http://localhost:5050/api/kpi_setting/'); + const existingKPIsResponse = await fetch(`${API_HOST}/api/kpi_setting/`); const existingKPIs = await existingKPIsResponse.json(); const maxPosition = existingKPIs.length > 0 ? Math.max(...existingKPIs.map((kpi: Kennzahl) => kpi.position)) @@ -25,7 +26,7 @@ function ConfigAddPage() { active: formData.active !== false }; - const response = await fetch('http://localhost:5050/api/kpi_setting/', { + const response = await fetch(`${API_HOST}/api/kpi_setting/`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/project/frontend/src/routes/config-detail.$kpiId.tsx b/project/frontend/src/routes/config-detail.$kpiId.tsx index 16ddce9..92a647f 100644 --- a/project/frontend/src/routes/config-detail.$kpiId.tsx +++ b/project/frontend/src/routes/config-detail.$kpiId.tsx @@ -6,6 +6,7 @@ import { useEffect, useState } from "react"; import type { Kennzahl } from "../types/kpi"; import { KPIForm } from "../components/KPIForm"; import { typeDisplayMapping } from "../types/kpi"; +import { API_HOST } from "../util/api"; export const Route = createFileRoute("/config-detail/$kpiId")({ component: KPIDetailPage, @@ -23,7 +24,7 @@ function KPIDetailPage() { const fetchKennzahl = async () => { try { setLoading(true); - const response = await fetch(`http://localhost:5050/api/kpi_setting/${kpiId}`); + const response = await fetch(`${API_HOST}/api/kpi_setting/${kpiId}`); if (!response.ok) { if (response.status === 404) { setError('KPI not found'); @@ -47,7 +48,7 @@ function KPIDetailPage() { const handleSave = async (formData: Partial) => { try { - const response = await fetch(`http://localhost:5050/api/kpi_setting/${kpiId}`, { + const response = await fetch(`${API_HOST}/api/kpi_setting/${kpiId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', diff --git a/project/frontend/src/socket.ts b/project/frontend/src/socket.ts index 763c173..563ef27 100644 --- a/project/frontend/src/socket.ts +++ b/project/frontend/src/socket.ts @@ -1,6 +1,7 @@ import { io } from "socket.io-client"; +import { API_HOST } from "./util/api"; // "undefined" means the URL will be computed from the `window.location` object // const URL = process.env.NODE_ENV === 'production' ? undefined : 'http://localhost:4000'; -export const socket = io("http://localhost:5050"); +export const socket = io(`${API_HOST}`); diff --git a/project/frontend/src/util/api.ts b/project/frontend/src/util/api.ts index 5a8c3c6..6ba0d6e 100644 --- a/project/frontend/src/util/api.ts +++ b/project/frontend/src/util/api.ts @@ -1,5 +1,9 @@ import type { Kennzahl } from "@/types/kpi"; +const API_HOST = import.meta.env.VITE_API_HOST || 'http://localhost:5050'; + +export { API_HOST }; + export const fetchKPI = async ( pitchBookId: string, ): Promise<{ @@ -12,7 +16,7 @@ export const fetchKPI = async ( }[]; }> => { const response = await fetch( - `http://localhost:5050/api/pitch_book/${pitchBookId}`, + `${API_HOST}/api/pitch_book/${pitchBookId}`, ); const data = await response.json(); @@ -43,7 +47,7 @@ export const fetchPutKPI = async ( formData.append("kpi", JSON.stringify(flattenKPIArray(kpi))); const response = await fetch( - `http://localhost:5050/api/pitch_book/${pitchBookId}`, + `${API_HOST}/api/pitch_book/${pitchBookId}`, { method: "PUT", body: formData, @@ -99,7 +103,7 @@ export const flattenKPIArray = (kpi: { }; export const fetchKennzahlen = async (): Promise => { - const response = await fetch("http://localhost:5050/api/kpi_setting/"); + const response = await fetch(`${API_HOST}/api/kpi_setting/`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } From ee4d0fa6186b47d7b63e9de57663dd677eddbb6c Mon Sep 17 00:00:00 2001 From: Jaronim Pracht Date: Sun, 15 Jun 2025 18:08:17 +0200 Subject: [PATCH 13/17] fix highlighting on elements on page end --- project/frontend/src/components/pdfViewer.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/project/frontend/src/components/pdfViewer.tsx b/project/frontend/src/components/pdfViewer.tsx index 972f5f5..ee68c8d 100644 --- a/project/frontend/src/components/pdfViewer.tsx +++ b/project/frontend/src/components/pdfViewer.tsx @@ -10,8 +10,8 @@ import type { OnGetTextSuccess, } from "node_modules/react-pdf/dist/esm/shared/types"; import { socket } from "../socket"; -import { highlightPattern } from "../util/highlighting"; import { API_HOST } from "../util/api"; +import { highlightPattern } from "../util/highlighting"; interface PDFViewerProps { pitchBookId: string; @@ -111,7 +111,8 @@ export default function PDFViewer({ ) { for ( let k = textItems[i].i; - k < textItems[i + s.split(" ").length].i; + k < textItems[i + s.split(" ").length]?.i || + k < textItems[i + s.split(" ").length - 1]?.i; k++ ) { tmpPos.push(textContent[k].posKey); @@ -129,7 +130,8 @@ export default function PDFViewer({ ) { for ( let k = textItems[i].i; - k < textItems[i + focusHighlight.text.split(" ").length].i; + k < textItems[i + focusHighlight.text.split(" ").length]?.i || + k < textItems[i + focusHighlight.text.split(" ").length - 1]?.i; k++ ) { tmpPosHighlight.push(textContent[k].posKey); From 4b48419622d48e50c1b820c89364de34792bd3c3 Mon Sep 17 00:00:00 2001 From: Jaronim Pracht Date: Sun, 15 Jun 2025 18:08:47 +0200 Subject: [PATCH 14/17] Fix host in docker-compose to localhost --- project/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/docker-compose.yml b/project/docker-compose.yml index 703914a..6c2544d 100644 --- a/project/docker-compose.yml +++ b/project/docker-compose.yml @@ -5,7 +5,7 @@ services: ports: - 8080:80 environment: - - API_HOST=http://coordinator:5000 + - API_HOST=http://localhost:5050 db: image: postgres:17-alpine From d8f29adeba6c7ea564c7ed2b6e086dbd956a13ea Mon Sep 17 00:00:00 2001 From: s8613 Date: Sun, 15 Jun 2025 19:03:50 +0200 Subject: [PATCH 15/17] Fixed one endpoint call to match ticket #60 dynamic changing host. --- project/frontend/src/util/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/frontend/src/util/api.ts b/project/frontend/src/util/api.ts index 095990c..65d3b2e 100644 --- a/project/frontend/src/util/api.ts +++ b/project/frontend/src/util/api.ts @@ -113,7 +113,7 @@ export const fetchKennzahlen = async (): Promise => { }; export async function fetchPitchBooks() { - const response = await fetch("http://localhost:5050/api/pitch_book/"); + const response = await fetch(`${API_HOST}/api/pitch_book/`); if (!response.ok) { throw new Error("Failed to fetch pitch books"); } From 6f905970cd07c562a530c01c45a6a9f3d23c3a2f Mon Sep 17 00:00:00 2001 From: Zainab2604 Date: Sun, 15 Jun 2025 22:17:09 +0200 Subject: [PATCH 16/17] =?UTF-8?q?Hinzuf=C3=BCgen=20der=20Ampelsystem=20Log?= =?UTF-8?q?ik?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/routes/extractedResult.$pitchBook.tsx | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/project/frontend/src/routes/extractedResult.$pitchBook.tsx b/project/frontend/src/routes/extractedResult.$pitchBook.tsx index bee005d..ef01d71 100644 --- a/project/frontend/src/routes/extractedResult.$pitchBook.tsx +++ b/project/frontend/src/routes/extractedResult.$pitchBook.tsx @@ -2,7 +2,7 @@ import ContentPasteIcon from "@mui/icons-material/ContentPaste"; import { Box, Button, Paper, Typography } from "@mui/material"; import { useSuspenseQuery } from "@tanstack/react-query"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; -import { useCallback, useState } from "react"; +import { useCallback, useState, useMemo } from "react"; import KennzahlenTable from "../components/KennzahlenTable"; import PDFViewer from "../components/pdfViewer"; import { kpiQueryOptions, settingsQueryOptions } from "../util/query"; @@ -19,7 +19,6 @@ export const Route = createFileRoute("/extractedResult/$pitchBook")({ function ExtractedResultsPage() { const { pitchBook } = Route.useParams(); const navigate = useNavigate(); - const status: "green" | "yellow" | "red" = "red"; const [currentPage, setCurrentPage] = useState(1); const [focusHighlight, setFocusHighlight] = useState({ page: 5, @@ -31,15 +30,38 @@ function ExtractedResultsPage() { setFocusHighlight({ page, text: entity }); }, []); + const { data: kpi } = useSuspenseQuery(kpiQueryOptions(pitchBook)); + const { data: settings } = useSuspenseQuery(settingsQueryOptions()); + + const status = useMemo(() => { + let hasRedBorders = false; + let hasYellowBorders = false; + + settings + .filter((setting) => setting.active) + .forEach((setting) => { + const values = kpi[setting.name.toUpperCase()] || []; + const hasNoValue = setting.mandatory && (values.length === 0 || values[0]?.entity === ""); + const hasMultipleValues = values.length > 1; + + if (hasNoValue) { + hasRedBorders = true; + } else if (hasMultipleValues) { + hasYellowBorders = true; + } + }); + + if (hasRedBorders) return "red"; + if (hasYellowBorders) return "yellow"; + return "green"; + }, [kpi, settings]); + const statusColor = { red: "#f43131", yellow: "#f6ed48", green: "#3fd942", }[status]; - const { data: kpi } = useSuspenseQuery(kpiQueryOptions(pitchBook)); - const { data: settings } = useSuspenseQuery(settingsQueryOptions()); - return ( From 3e6750f4177e78de7f45a7c7e88a2de999da7031 Mon Sep 17 00:00:00 2001 From: s8613 Date: Mon, 16 Jun 2025 18:54:06 +0200 Subject: [PATCH 17/17] Fixed merge conflict --- project/frontend/src/routes/extractedResult.$pitchBook.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/project/frontend/src/routes/extractedResult.$pitchBook.tsx b/project/frontend/src/routes/extractedResult.$pitchBook.tsx index 7ad03dc..63b3c96 100644 --- a/project/frontend/src/routes/extractedResult.$pitchBook.tsx +++ b/project/frontend/src/routes/extractedResult.$pitchBook.tsx @@ -64,9 +64,6 @@ function ExtractedResultsPage() { green: "#3fd942", }[status]; - const { data: kpi } = useSuspenseQuery(kpiQueryOptions(pitchBook)); - const { data: settings } = useSuspenseQuery(settingsQueryOptions()); - const prepareClipboardData = () => { const activeSettings = settings .filter(setting => setting.active)