Merge pull request 'Progress - Frontend' (#48) from #16-progress into main

Reviewed-on: #48
pull/50/head
Anastasia Hanna Ougolnikova 2025-06-03 13:44:57 +02:00
commit b9d7f425e5
21 changed files with 493 additions and 7907 deletions

View File

@ -0,0 +1,21 @@
# 1. Python-Image verwenden
FROM python:3.11-alpine
# 2. Arbeitsverzeichnis im Container setzen
WORKDIR /app
# 3. production-style server mit gunicorn
RUN pip install gunicorn eventlet
# 4. requirements.txt kopieren und Pakete installieren
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 5. Quellcode kopieren (z.B. app.py)
COPY . .
ENV PYTHONUNBUFFERED=1
EXPOSE 5000
CMD ["gunicorn", "--worker-class", "eventlet", "-w", "1", "--bind", "0.0.0.0:5000", "app:app"]

View File

@ -1,10 +1,14 @@
from flask import Flask from flask import Flask
from flask_cors import CORS
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
from controller import register_routes from controller import register_routes
from model.database import init_db from model.database import init_db
from controller.socketIO import socketio
app = Flask(__name__) app = Flask(__name__)
CORS(app)
socketio.init_app(app)
load_dotenv() load_dotenv()
DATABASE_URL = os.getenv("DATABASE_URL") DATABASE_URL = os.getenv("DATABASE_URL")
@ -25,4 +29,4 @@ def health_check():
# für Docker wichtig: host='0.0.0.0' # für Docker wichtig: host='0.0.0.0'
if __name__ == "__main__": if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0") socketio.run(app,debug=True, host="0.0.0.0", port=5050)

View File

@ -1,9 +1,11 @@
from controller.spacy_contoller import spacy_controller from controller.spacy_contoller import spacy_controller
from controller.kpi_setting_controller import kpi_setting_controller from controller.kpi_setting_controller import kpi_setting_controller
from controller.pitch_book_controller import pitch_book_controller from controller.pitch_book_controller import pitch_book_controller
from controller.progress_controller import progress_controller
def register_routes(app): def register_routes(app):
app.register_blueprint(kpi_setting_controller) app.register_blueprint(kpi_setting_controller)
app.register_blueprint(pitch_book_controller) app.register_blueprint(pitch_book_controller)
app.register_blueprint(spacy_controller) app.register_blueprint(spacy_controller)
app.register_blueprint(progress_controller)

View File

@ -4,6 +4,7 @@ from model.pitch_book_model import PitchBookModel
from io import BytesIO from io import BytesIO
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
import puremagic import puremagic
from controller.socketIO import socketio
pitch_book_controller = Blueprint("pitch_books", __name__, url_prefix="/api/pitch_book") pitch_book_controller = Blueprint("pitch_books", __name__, url_prefix="/api/pitch_book")
@ -54,6 +55,7 @@ def upload_file():
db.session.add(new_file) db.session.add(new_file)
db.session.commit() db.session.commit()
socketio.emit("progress", {"id": new_file.id, "progress": 0})
return jsonify(new_file.to_dict()), 201 return jsonify(new_file.to_dict()), 201
except Exception as e: except Exception as e:
print(e) print(e)
@ -81,6 +83,7 @@ def update_file(id):
print(e) print(e)
if "kpi" in request.form: if "kpi" in request.form:
socketio.emit("progress", {"id": id, "progress": 100})
file.kpi = request.form.get("kpi") file.kpi = request.form.get("kpi")
db.session.commit() db.session.commit()

View File

@ -0,0 +1,19 @@
from flask import Blueprint, request, jsonify
from controller.socketIO import socketio
progress_controller = Blueprint("progress", __name__, url_prefix="/api/progress")
@progress_controller.route("/", methods=["POST"])
def progress():
data = request.get_json()
if 'id' not in data or 'progress' not in data:
return jsonify({"error": "Missing required fields. [id, progress]"}), 400
if not isinstance(data['progress'], (int, float)) or data['progress'] < 0 or data['progress'] >= 100:
return jsonify({"error": "Invalid progress value"}), 400
socketio.emit("progress", {"id": data["id"], "progress": data["progress"]})
# Process the data and return a response
return jsonify({"message": "Progress updated"})

View File

@ -0,0 +1,3 @@
from flask_socketio import SocketIO
socketio = SocketIO(cors_allowed_origins="*")

View File

@ -1,3 +1,4 @@
bidict==0.23.1
black==25.1.0 black==25.1.0
blinker==1.9.0 blinker==1.9.0
cfgv==3.4.0 cfgv==3.4.0
@ -6,8 +7,11 @@ distlib==0.3.9
filelock==3.18.0 filelock==3.18.0
flake8==7.2.0 flake8==7.2.0
Flask==3.1.1 Flask==3.1.1
flask-cors==6.0.0
Flask-SocketIO==5.5.1
Flask-SQLAlchemy==3.1.1 Flask-SQLAlchemy==3.1.1
greenlet==3.2.2 greenlet==3.2.2
h11==0.16.0
identify==2.6.12 identify==2.6.12
itsdangerous==2.2.0 itsdangerous==2.2.0
Jinja2==3.1.6 Jinja2==3.1.6
@ -24,8 +28,12 @@ puremagic==1.29
pycodestyle==2.13.0 pycodestyle==2.13.0
pyflakes==3.3.2 pyflakes==3.3.2
python-dotenv==1.1.0 python-dotenv==1.1.0
python-engineio==4.12.1
python-socketio==5.13.0
PyYAML==6.0.2 PyYAML==6.0.2
simple-websocket==1.1.0
SQLAlchemy==2.0.41 SQLAlchemy==2.0.41
typing_extensions==4.13.2 typing_extensions==4.13.2
virtualenv==20.31.2 virtualenv==20.31.2
Werkzeug==3.1.3 Werkzeug==3.1.3
wsproto==1.2.0

View File

@ -19,11 +19,11 @@ services:
coordinator: coordinator:
build: build:
context: backend/coordinator context: backend/coordinator
dockerfile: ../../Dockerfile
env_file: env_file:
- .env - .env
depends_on: depends_on:
- db db:
condition: service_healthy
healthcheck: healthcheck:
test: wget --spider --no-verbose http://127.0.0.1:5000/health || exit 1 test: wget --spider --no-verbose http://127.0.0.1:5000/health || exit 1
interval: 10s interval: 10s

View File

@ -12,7 +12,7 @@
}, },
"formatter": { "formatter": {
"enabled": true, "enabled": true,
"indentStyle": "space" "indentStyle": "tab"
}, },
"organizeImports": { "organizeImports": {
"enabled": true "enabled": true

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,8 @@
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-material-file-upload": "^0.0.4", "react-material-file-upload": "^0.0.4",
"react-pdf": "^9.2.1" "react-pdf": "^8.0.2",
"socket.io-client": "^4.8.1"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.9.4", "@biomejs/biome": "1.9.4",

View File

@ -0,0 +1,33 @@
import Box from "@mui/material/Box";
import CircularProgress, {
type CircularProgressProps,
} from "@mui/material/CircularProgress";
import Typography from "@mui/material/Typography";
export function CircularProgressWithLabel(
props: CircularProgressProps & { value: number },
) {
return (
<Box sx={{ position: "relative", display: "inline-flex" }}>
<CircularProgress {...props} />
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Typography
variant="subtitle1"
component="div"
sx={{ color: "inherit" }}
>{`${Math.round(props.value)}%`}</Typography>
</Box>
</Box>
);
}

View File

@ -1,15 +1,90 @@
import { useState } from 'react' import SettingsIcon from "@mui/icons-material/Settings";
import FileUpload from 'react-material-file-upload' import { Backdrop, Box, Button, IconButton, Paper } from "@mui/material";
import {Box, Button, IconButton, Paper} from '@mui/material' import { useNavigate } from "@tanstack/react-router";
import { useNavigate } from '@tanstack/react-router' import { useCallback, useEffect, useState } from "react";
import SettingsIcon from '@mui/icons-material/Settings'; import FileUpload from "react-material-file-upload";
import { socket } from "../socket";
import { CircularProgressWithLabel } from "./CircularProgressWithLabel";
const PROGRESS = false;
export default function UploadPage() { export default function UploadPage() {
const [files, setFiles] = useState<File[]>([]) const [files, setFiles] = useState<File[]>([]);
const [pageId, setPageId] = useState<string | null>(null);
const [loadingState, setLoadingState] = useState<number | null>(null);
const fileTypes = ["pdf"]; const fileTypes = ["pdf"];
const navigate = useNavigate() const navigate = useNavigate();
const uploadFile = useCallback(async () => {
const formData = new FormData();
formData.append("file", files[0]);
const response = await fetch("http://localhost:5050/api/pitch_book", {
method: "POST",
body: formData,
});
if (response.ok) {
console.log("File uploaded successfully");
const data = await response.json();
console.log(data);
setPageId(data.id);
setLoadingState(0);
!PROGRESS &&
navigate({
to: "/extractedResult/$pitchBook",
params: { pitchBook: data.id },
});
} else {
console.error("Failed to upload file");
}
}, [files, navigate]);
const onConnection = useCallback(() => {
console.log("connected");
}, []);
const onProgress = useCallback(
(progress: { id: number; progress: number }) => {
console.log("Progress:", progress);
console.log(pageId);
if (Number(pageId) === progress.id) {
setLoadingState(progress.progress);
if (progress.progress === 100) {
navigate({
to: "/extractedResult/$pitchBook",
params: { pitchBook: progress.id.toString() },
});
}
}
},
[pageId, navigate],
);
useEffect(() => {
socket.on("connect", onConnection);
socket.on("progress", onProgress);
return () => {
socket.off("connect", onConnection);
socket.off("progress", onProgress);
};
}, [onConnection, onProgress]);
return ( return (
<>
{PROGRESS && (
<Backdrop
sx={(theme) => ({ color: "#fff", zIndex: theme.zIndex.drawer + 1 })}
open={pageId !== null && loadingState !== null}
>
<CircularProgressWithLabel
color="inherit"
value={loadingState || 0}
size={60}
/>
</Backdrop>
)}
<Box <Box
display="flex" display="flex"
flexDirection="column" flexDirection="column"
@ -25,7 +100,7 @@ export default function UploadPage() {
justifyContent="flex-end" justifyContent="flex-end"
px={2} px={2}
> >
<IconButton onClick={() => navigate({ to: '/config' })}> <IconButton onClick={() => navigate({ to: "/config" })}>
<SettingsIcon fontSize="large" /> <SettingsIcon fontSize="large" />
</IconButton> </IconButton>
</Box> </Box>
@ -34,49 +109,51 @@ export default function UploadPage() {
sx={{ sx={{
width: 900, width: 900,
height: 500, height: 500,
backgroundColor: '#eeeeee', backgroundColor: "#eeeeee",
borderRadius: 4, borderRadius: 4,
display: 'flex', display: "flex",
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
}} }}
> >
<Box sx={{ <Box
height: '100%', sx={{
width: '100%', height: "100%",
maxWidth: '100%', width: "100%",
margin: '0px', maxWidth: "100%",
padding: '0px', margin: "0px",
'& .MuiBox-root': { padding: "0px",
display: 'flex', "& .MuiBox-root": {
flexDirection: 'column', display: "flex",
alignItems: 'center', flexDirection: "column",
justifyContent: 'center', alignItems: "center",
border: 'none', justifyContent: "center",
textAlign: 'center', border: "none",
textAlign: "center",
}, },
}}> }}
>
<FileUpload <FileUpload
value={files} value={files}
onChange={setFiles} onChange={setFiles}
accept={`.${fileTypes.join(', .')}`} accept={`.${fileTypes.join(", .")}`}
title="Hier Dokument hinziehen" title="Hier Dokument hinziehen"
buttonText="Datei auswählen" buttonText="Datei auswählen"
sx={{ sx={{
height: '100%', height: "100%",
width: '100%', width: "100%",
padding: '0px', padding: "0px",
'& svg': { "& svg": {
color: '#9e9e9e', color: "#9e9e9e",
}, },
'& .MuiOutlinedInput-notchedOutline': { "& .MuiOutlinedInput-notchedOutline": {
border: 'none', border: "none",
}, },
'& .MuiButton-root': { "& .MuiButton-root": {
backgroundColor: '#9e9e9e', backgroundColor: "#9e9e9e",
}, },
'& .MuiTypography-root': { "& .MuiTypography-root": {
fontSize: '1.25rem', fontSize: "1.25rem",
fontWeight: 500, fontWeight: 500,
marginBottom: 1, marginBottom: 1,
}, },
@ -88,13 +165,14 @@ export default function UploadPage() {
variant="contained" variant="contained"
sx={{ sx={{
mt: 4, mt: 4,
backgroundColor: '#383838', backgroundColor: "#383838",
}} }}
disabled={files.length === 0} disabled={files.length === 0}
onClick={() => navigate({ to: '/extractedResult' })} onClick={uploadFile}
> >
Kennzahlen extrahieren Kennzahlen extrahieren
</Button> </Button>
</Box> </Box>
) </>
);
} }

View File

@ -1,22 +1,18 @@
import { Document, Page, pdfjs } from "react-pdf"; import { useEffect, useRef, useState } from "react";
import { useState, useRef, useEffect } from 'react'; import { Document, Page } from "react-pdf";
import 'react-pdf/dist/esm/Page/AnnotationLayer.css'; import "react-pdf/dist/esm/Page/AnnotationLayer.css";
import 'react-pdf/dist/esm/Page/TextLayer.css'; import "react-pdf/dist/esm/Page/TextLayer.css";
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 { Box, IconButton } from "@mui/material";
import testPDF from '/example.pdf';
pdfjs.GlobalWorkerOptions.workerSrc = new URL( interface PDFViewerProps {
"pdfjs-dist/build/pdf.worker.min.mjs", pitchBookId: string;
import.meta.url, }
).toString(); export default function PDFViewer({ pitchBookId }: PDFViewerProps) {
export default function PDFViewer() {
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);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => { const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
@ -31,8 +27,8 @@ export default function PDFViewer() {
}; };
updateWidth(); updateWidth();
window.addEventListener('resize', updateWidth); window.addEventListener("resize", updateWidth);
return () => window.removeEventListener('resize', updateWidth); return () => window.removeEventListener("resize", updateWidth);
}, []); }, []);
return ( return (
@ -48,23 +44,24 @@ export default function PDFViewer() {
<Box <Box
ref={containerRef} ref={containerRef}
sx={{ sx={{
width: '100%', width: "100%",
maxHeight: '90vh', maxHeight: "90vh",
overflow: 'auto', overflow: "auto",
display: 'flex', display: "flex",
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
}} }}
> >
<Document file={testPDF} <Document
file={`http://localhost:5050/api/pitch_book/${pitchBookId}/download`}
onLoadSuccess={onDocumentLoadSuccess} onLoadSuccess={onDocumentLoadSuccess}
onLoadError={(error) => console.error('Es gab ein Fehler beim Laden des PDFs:', error)} onLoadError={(error) =>
onSourceError={(error) => console.error('Ungültige PDF:', error)}> console.error("Es gab ein Fehler beim Laden des PDFs:", error)
}
onSourceError={(error) => console.error("Ungültige PDF:", error)}
>
{containerWidth && ( {containerWidth && (
<Page <Page pageNumber={pageNumber} width={containerWidth * 0.8} />
pageNumber={pageNumber}
width={containerWidth * 0.8}
/>
)} )}
</Document> </Document>
</Box> </Box>
@ -75,13 +72,18 @@ export default function PDFViewer() {
justifyContent="center" justifyContent="center"
gap={1} gap={1}
> >
<IconButton disabled={pageNumber <= 1} onClick={() => setPageNumber(p => p - 1)}> <IconButton
disabled={pageNumber <= 1}
onClick={() => setPageNumber((p) => p - 1)}
>
<ArrowCircleLeftIcon fontSize="large" /> <ArrowCircleLeftIcon fontSize="large" />
</IconButton> </IconButton>
<span>{pageNumber} / {numPages}</span> <span>
{pageNumber} / {numPages}
</span>
<IconButton <IconButton
disabled={pageNumber >= (numPages || 1)} disabled={pageNumber >= (numPages || 1)}
onClick={() => setPageNumber(p => p + 1)} onClick={() => setPageNumber((p) => p + 1)}
> >
<ArrowCircleRightIcon fontSize="large" /> <ArrowCircleRightIcon fontSize="large" />
</IconButton> </IconButton>

View File

@ -34,9 +34,8 @@ declare module "@tanstack/react-router" {
} }
} }
// Initialize PDF.js worker
pdfjs.GlobalWorkerOptions.workerSrc = new URL( pdfjs.GlobalWorkerOptions.workerSrc = new URL(
"pdfjs-dist/build/pdf.worker.min.mjs", "pdfjs-dist/build/pdf.worker.min.js",
import.meta.url, import.meta.url,
).toString(); ).toString();

View File

@ -11,18 +11,12 @@
// Import Routes // Import Routes
import { Route as rootRoute } from './routes/__root' import { Route as rootRoute } from './routes/__root'
import { Route as ExtractedResultImport } from './routes/extractedResult'
import { Route as ConfigImport } from './routes/config' import { Route as ConfigImport } from './routes/config'
import { Route as IndexImport } from './routes/index' import { Route as IndexImport } from './routes/index'
import { Route as ExtractedResultPitchBookImport } from './routes/extractedResult.$pitchBook'
// Create/Update Routes // Create/Update Routes
const ExtractedResultRoute = ExtractedResultImport.update({
id: '/extractedResult',
path: '/extractedResult',
getParentRoute: () => rootRoute,
} as any)
const ConfigRoute = ConfigImport.update({ const ConfigRoute = ConfigImport.update({
id: '/config', id: '/config',
path: '/config', path: '/config',
@ -35,6 +29,12 @@ const IndexRoute = IndexImport.update({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
} as any) } as any)
const ExtractedResultPitchBookRoute = ExtractedResultPitchBookImport.update({
id: '/extractedResult/$pitchBook',
path: '/extractedResult/$pitchBook',
getParentRoute: () => rootRoute,
} as any)
// Populate the FileRoutesByPath interface // Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' { declare module '@tanstack/react-router' {
@ -53,11 +53,11 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ConfigImport preLoaderRoute: typeof ConfigImport
parentRoute: typeof rootRoute parentRoute: typeof rootRoute
} }
'/extractedResult': { '/extractedResult/$pitchBook': {
id: '/extractedResult' id: '/extractedResult/$pitchBook'
path: '/extractedResult' path: '/extractedResult/$pitchBook'
fullPath: '/extractedResult' fullPath: '/extractedResult/$pitchBook'
preLoaderRoute: typeof ExtractedResultImport preLoaderRoute: typeof ExtractedResultPitchBookImport
parentRoute: typeof rootRoute parentRoute: typeof rootRoute
} }
} }
@ -68,41 +68,41 @@ declare module '@tanstack/react-router' {
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
'/': typeof IndexRoute '/': typeof IndexRoute
'/config': typeof ConfigRoute '/config': typeof ConfigRoute
'/extractedResult': typeof ExtractedResultRoute '/extractedResult/$pitchBook': typeof ExtractedResultPitchBookRoute
} }
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/': typeof IndexRoute '/': typeof IndexRoute
'/config': typeof ConfigRoute '/config': typeof ConfigRoute
'/extractedResult': typeof ExtractedResultRoute '/extractedResult/$pitchBook': typeof ExtractedResultPitchBookRoute
} }
export interface FileRoutesById { export interface FileRoutesById {
__root__: typeof rootRoute __root__: typeof rootRoute
'/': typeof IndexRoute '/': typeof IndexRoute
'/config': typeof ConfigRoute '/config': typeof ConfigRoute
'/extractedResult': typeof ExtractedResultRoute '/extractedResult/$pitchBook': typeof ExtractedResultPitchBookRoute
} }
export interface FileRouteTypes { export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/config' | '/extractedResult' fullPaths: '/' | '/config' | '/extractedResult/$pitchBook'
fileRoutesByTo: FileRoutesByTo fileRoutesByTo: FileRoutesByTo
to: '/' | '/config' | '/extractedResult' to: '/' | '/config' | '/extractedResult/$pitchBook'
id: '__root__' | '/' | '/config' | '/extractedResult' id: '__root__' | '/' | '/config' | '/extractedResult/$pitchBook'
fileRoutesById: FileRoutesById fileRoutesById: FileRoutesById
} }
export interface RootRouteChildren { export interface RootRouteChildren {
IndexRoute: typeof IndexRoute IndexRoute: typeof IndexRoute
ConfigRoute: typeof ConfigRoute ConfigRoute: typeof ConfigRoute
ExtractedResultRoute: typeof ExtractedResultRoute ExtractedResultPitchBookRoute: typeof ExtractedResultPitchBookRoute
} }
const rootRouteChildren: RootRouteChildren = { const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute, IndexRoute: IndexRoute,
ConfigRoute: ConfigRoute, ConfigRoute: ConfigRoute,
ExtractedResultRoute: ExtractedResultRoute, ExtractedResultPitchBookRoute: ExtractedResultPitchBookRoute,
} }
export const routeTree = rootRoute export const routeTree = rootRoute
@ -117,7 +117,7 @@ export const routeTree = rootRoute
"children": [ "children": [
"/", "/",
"/config", "/config",
"/extractedResult" "/extractedResult/$pitchBook"
] ]
}, },
"/": { "/": {
@ -126,8 +126,8 @@ export const routeTree = rootRoute
"/config": { "/config": {
"filePath": "config.tsx" "filePath": "config.tsx"
}, },
"/extractedResult": { "/extractedResult/$pitchBook": {
"filePath": "extractedResult.tsx" "filePath": "extractedResult.$pitchBook.tsx"
} }
} }
} }

View File

@ -0,0 +1,100 @@
import ContentPasteIcon from "@mui/icons-material/ContentPaste";
import { Box, Button, Paper, Typography } from "@mui/material";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import KennzahlenTable from "../components/KennzahlenTable";
import PDFViewer from "../components/pdfViewer";
export const Route = createFileRoute("/extractedResult/$pitchBook")({
component: ExtractedResultsPage,
});
function ExtractedResultsPage() {
const { pitchBook } = Route.useParams();
const navigate = useNavigate();
const status: "green" | "yellow" | "red" = "red";
const statusColor = {
red: "#f43131",
yellow: "#f6ed48",
green: "#3fd942",
}[status];
return (
<Box p={4}>
<Box display="flex" alignItems="center" gap={3}>
<Box
sx={{
width: 45,
height: 45,
borderRadius: "50%",
backgroundColor: statusColor,
top: 32,
left: 32,
}}
/>
<Typography variant="h5" gutterBottom>
Kennzahlen extrahiert aus: <br />
<strong>FONDSNAME: TODO</strong>
</Typography>
</Box>
<Box
display="flex"
gap={4}
sx={{
width: "100vw",
maxWidth: "100%",
height: "80vh",
mt: 4,
}}
>
<Paper
elevation={2}
sx={{
width: "45%",
height: "100%",
borderRadius: 2,
backgroundColor: "#eeeeee",
padding: 2,
overflow: "auto",
}}
>
<KennzahlenTable />
</Paper>
<Box
display="flex"
flexDirection="column"
justifyContent="space-between"
gap={5}
sx={{ width: "55%", height: "95%" }}
>
<Paper
elevation={2}
sx={{
height: "100%",
borderRadius: 2,
backgroundColor: "#eeeeee",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<PDFViewer pitchBookId={pitchBook} />
</Paper>
<Box mt={2} display="flex" justifyContent="flex-end" gap={2}>
<Button variant="contained" sx={{ backgroundColor: "#383838" }}>
<ContentPasteIcon sx={{ fontSize: 18, mr: 1 }} />
Kennzahlenzeile kopieren
</Button>
<Button
variant="contained"
sx={{ backgroundColor: "#383838" }}
onClick={() => navigate({ to: "/" })}
>
Neu hochladen
</Button>
</Box>
</Box>
</Box>
</Box>
);
}

View File

@ -1,103 +0,0 @@
import { Box, Paper, Typography, Button } from '@mui/material';
import {createFileRoute, useNavigate} from '@tanstack/react-router';
import PDFViewer from '../components/pdfViewer';
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
import KennzahlenTable from "../components/KennzahlenTable";
export const Route = createFileRoute('/extractedResult')({
component: ExtractedResultsPage,
});
function ExtractedResultsPage() {
const navigate = useNavigate();
const status: 'green' | 'yellow' | 'red' = 'red';
const statusColor = {
red: '#f43131',
yellow: '#f6ed48',
green: '#3fd942',
}[status];
return (
<Box p={4}>
<Box display="flex" alignItems="center" gap={3}>
<Box
sx={{
width: 45,
height: 45,
borderRadius: '50%',
backgroundColor: statusColor,
top: 32,
left: 32,
}}
/>
<Typography variant="h5" gutterBottom>
Kennzahlen extrahiert aus: <br/><strong>FONDSNAME: TODO</strong>
</Typography>
</Box>
<Box
display="flex"
gap={4}
sx={{
width: '100vw',
maxWidth: '100%',
height: '80vh',
mt: 4,
}}
>
<Paper
elevation={2}
sx={{
width: '45%',
height: '100%',
borderRadius: 2,
backgroundColor: '#eeeeee',
padding: 2, // Etwas Abstand innen
overflow: 'auto', // Scrollen falls Tabelle zu lang
}}
>
<KennzahlenTable />
</Paper>
<Box
display="flex"
flexDirection="column"
justifyContent="space-between"
gap={5}
sx={{ width: '55%', height: '95%' }}
>
<Paper
elevation={2}
sx={{
height: '100%',
borderRadius: 2,
backgroundColor: '#eeeeee',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<PDFViewer/>
</Paper>
<Box mt={2} display="flex" justifyContent="flex-end" gap={2}>
<Button
variant="contained"
sx={{ backgroundColor: '#383838' }}
>
<ContentPasteIcon sx={{ fontSize: 18, mr: 1 }} />
Kennzahlenzeile kopieren
</Button>
<Button
variant="contained"
sx={{ backgroundColor: '#383838' }}
onClick={() => navigate({ to: '/' })}
>
Neu hochladen
</Button>
</Box>
</Box>
</Box>
</Box>
);
}

View File

@ -0,0 +1,6 @@
import { io } from "socket.io-client";
// "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");