Add Help-Floater
This commit is contained in:
parent
629086fdb6
commit
91372c73b4
@ -422,7 +422,7 @@ def _inject_meta_for_render(payload: Dict[str, Any], pa_id: str, pa_key: Optiona
|
|||||||
p2["pa"]["meta"]["key"] = pa_key
|
p2["pa"]["meta"]["key"] = pa_key
|
||||||
|
|
||||||
# Calculate total amount from costs dynamically
|
# Calculate total amount from costs dynamically
|
||||||
project = p2.get("pa", {}).get("project", {})
|
project = p2.get("pa", {}).get("project", {}) or {}
|
||||||
costs = project.get("costs", [])
|
costs = project.get("costs", [])
|
||||||
total_amount = 0.0
|
total_amount = 0.0
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ def _sanitize_payload_for_db(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
meta["key"] = None
|
meta["key"] = None
|
||||||
|
|
||||||
# Remove calculated total from database storage
|
# Remove calculated total from database storage
|
||||||
project = p2.get("pa", {}).get("project", {})
|
project = p2.get("pa", {}).get("project", {}) or {}
|
||||||
if "totals" in project and "requestedAmountEur" in project["totals"]:
|
if "totals" in project and "requestedAmountEur" in project["totals"]:
|
||||||
del project["totals"]["requestedAmountEur"]
|
del project["totals"]["requestedAmountEur"]
|
||||||
|
|
||||||
@ -517,8 +517,8 @@ def create_application(
|
|||||||
financing_data = project_data.get("financing", {})
|
financing_data = project_data.get("financing", {})
|
||||||
|
|
||||||
# Check which financing type has actual content (not just empty structure)
|
# Check which financing type has actual content (not just empty structure)
|
||||||
qsm_data = financing_data.get("qsm", {})
|
qsm_data = financing_data.get("qsm", {}) or {}
|
||||||
vsm_data = financing_data.get("vsm", {})
|
vsm_data = financing_data.get("vsm", {}) or {}
|
||||||
|
|
||||||
# QSM has 'code' and 'flags' fields when filled
|
# QSM has 'code' and 'flags' fields when filled
|
||||||
has_qsm_content = bool(qsm_data.get("code") or qsm_data.get("flags"))
|
has_qsm_content = bool(qsm_data.get("code") or qsm_data.get("flags"))
|
||||||
@ -527,7 +527,7 @@ def create_application(
|
|||||||
|
|
||||||
# Also check institution fields (VSM-specific)
|
# Also check institution fields (VSM-specific)
|
||||||
# Note: Institution name alone doesn't determine variant, as QSM can also have institution name
|
# Note: Institution name alone doesn't determine variant, as QSM can also have institution name
|
||||||
institution_data = pa_data.get("applicant", {}).get("institution", {})
|
institution_data = pa_data.get("applicant", {}).get("institution", {}) or {}
|
||||||
has_institution_type = bool(institution_data.get("type")) # Only type is VSM-specific
|
has_institution_type = bool(institution_data.get("type")) # Only type is VSM-specific
|
||||||
|
|
||||||
# Determine variant based on which fields have actual content
|
# Determine variant based on which fields have actual content
|
||||||
@ -746,12 +746,21 @@ def list_applications(
|
|||||||
rows = db.execute(q).scalars().all()
|
rows = db.execute(q).scalars().all()
|
||||||
result = []
|
result = []
|
||||||
for r in rows:
|
for r in rows:
|
||||||
# Extract project name from payload if available
|
# Extract project name and calculate total amount from payload if available
|
||||||
project_name = ""
|
project_name = ""
|
||||||
|
total_amount = 0.0
|
||||||
if r.payload_json:
|
if r.payload_json:
|
||||||
try:
|
try:
|
||||||
payload = json.loads(r.payload_json) if isinstance(r.payload_json, str) else r.payload_json
|
payload = json.loads(r.payload_json) if isinstance(r.payload_json, str) else r.payload_json
|
||||||
project_name = payload.get("pa", {}).get("project", {}).get("name", "")
|
project = payload.get("pa", {}).get("project", {})
|
||||||
|
project_name = project.get("name", "")
|
||||||
|
# Calculate total from costs
|
||||||
|
costs = project.get("costs", [])
|
||||||
|
for cost in costs:
|
||||||
|
if isinstance(cost, dict) and "amountEur" in cost:
|
||||||
|
amount = cost.get("amountEur")
|
||||||
|
if amount is not None and isinstance(amount, (int, float)):
|
||||||
|
total_amount += float(amount)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -760,6 +769,7 @@ def list_applications(
|
|||||||
"variant": "VSM" if r.variant == "COMMON" else r.variant,
|
"variant": "VSM" if r.variant == "COMMON" else r.variant,
|
||||||
"status": r.status,
|
"status": r.status,
|
||||||
"project_name": project_name,
|
"project_name": project_name,
|
||||||
|
"total_amount": total_amount,
|
||||||
"created_at": r.created_at.isoformat(),
|
"created_at": r.created_at.isoformat(),
|
||||||
"updated_at": r.updated_at.isoformat()
|
"updated_at": r.updated_at.isoformat()
|
||||||
})
|
})
|
||||||
@ -772,12 +782,21 @@ def list_applications(
|
|||||||
app_row: Application = auth.get("app")
|
app_row: Application = auth.get("app")
|
||||||
if not app_row:
|
if not app_row:
|
||||||
raise HTTPException(status_code=404, detail="Application not found")
|
raise HTTPException(status_code=404, detail="Application not found")
|
||||||
# Extract project name from payload if available
|
# Extract project name and calculate total amount from payload if available
|
||||||
project_name = ""
|
project_name = ""
|
||||||
|
total_amount = 0.0
|
||||||
if app_row.payload_json:
|
if app_row.payload_json:
|
||||||
try:
|
try:
|
||||||
payload = json.loads(app_row.payload_json) if isinstance(app_row.payload_json, str) else app_row.payload_json
|
payload = json.loads(app_row.payload_json) if isinstance(app_row.payload_json, str) else app_row.payload_json
|
||||||
project_name = payload.get("pa", {}).get("project", {}).get("name", "")
|
project = payload.get("pa", {}).get("project", {})
|
||||||
|
project_name = project.get("name", "")
|
||||||
|
# Calculate total from costs
|
||||||
|
costs = project.get("costs", [])
|
||||||
|
for cost in costs:
|
||||||
|
if isinstance(cost, dict) and "amountEur" in cost:
|
||||||
|
amount = cost.get("amountEur")
|
||||||
|
if amount is not None and isinstance(amount, (int, float)):
|
||||||
|
total_amount += float(amount)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -786,6 +805,7 @@ def list_applications(
|
|||||||
"variant": "VSM" if app_row.variant == "COMMON" else app_row.variant,
|
"variant": "VSM" if app_row.variant == "COMMON" else app_row.variant,
|
||||||
"status": app_row.status,
|
"status": app_row.status,
|
||||||
"project_name": project_name,
|
"project_name": project_name,
|
||||||
|
"total_amount": total_amount,
|
||||||
"created_at": app_row.created_at.isoformat(),
|
"created_at": app_row.created_at.isoformat(),
|
||||||
"updated_at": app_row.updated_at.isoformat()
|
"updated_at": app_row.updated_at.isoformat()
|
||||||
}]
|
}]
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import CssBaseline from "@mui/material/CssBaseline";
|
|||||||
import { SnackbarProvider } from "notistack";
|
import { SnackbarProvider } from "notistack";
|
||||||
import { QueryClient, QueryClientProvider } from "react-query";
|
import { QueryClient, QueryClientProvider } from "react-query";
|
||||||
import { ReactQueryDevtools } from "react-query/devtools";
|
import { ReactQueryDevtools } from "react-query/devtools";
|
||||||
import { IconButton, Box } from "@mui/material";
|
import { Fab, Box } from "@mui/material";
|
||||||
import { Brightness4, Brightness7 } from "@mui/icons-material";
|
import { Brightness4, Brightness7 } from "@mui/icons-material";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@ -25,6 +25,7 @@ import HelpPage from "./pages/HelpPage";
|
|||||||
import EditApplicationPage from "./pages/EditApplicationPage";
|
import EditApplicationPage from "./pages/EditApplicationPage";
|
||||||
import ErrorBoundary from "./components/ErrorBoundary/ErrorBoundary";
|
import ErrorBoundary from "./components/ErrorBoundary/ErrorBoundary";
|
||||||
import LoadingSpinner from "./components/LoadingSpinner/LoadingSpinner";
|
import LoadingSpinner from "./components/LoadingSpinner/LoadingSpinner";
|
||||||
|
import HelpButton from "./components/HelpButton";
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
import { useApplicationStore } from "./store/applicationStore";
|
import { useApplicationStore } from "./store/applicationStore";
|
||||||
@ -245,24 +246,28 @@ const App: React.FC = () => {
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
bottom: 16,
|
bottom: 24,
|
||||||
right: 16,
|
right: 24,
|
||||||
zIndex: 1200,
|
zIndex: 1200,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconButton
|
<Fab
|
||||||
onClick={handleToggleDarkMode}
|
onClick={handleToggleDarkMode}
|
||||||
color="inherit"
|
size="large"
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
boxShadow: 2,
|
boxShadow: 2,
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
bgcolor: "action.hover",
|
bgcolor: "action.hover",
|
||||||
},
|
},
|
||||||
|
color: darkMode ? "white" : "black",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{darkMode ? <Brightness7 /> : <Brightness4 />}
|
{darkMode ? <Brightness7 /> : <Brightness4 />}
|
||||||
</IconButton>
|
</Fab>
|
||||||
</Box>
|
</Box>
|
||||||
<SnackbarProvider
|
<SnackbarProvider
|
||||||
maxSnack={3}
|
maxSnack={3}
|
||||||
@ -351,6 +356,9 @@ const App: React.FC = () => {
|
|||||||
{process.env.NODE_ENV === "development" && (
|
{process.env.NODE_ENV === "development" && (
|
||||||
<ReactQueryDevtools initialIsOpen={false} />
|
<ReactQueryDevtools initialIsOpen={false} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Help Button - Always visible */}
|
||||||
|
<HelpButton />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|||||||
192
frontend/src/components/HelpButton/HelpButton.tsx
Normal file
192
frontend/src/components/HelpButton/HelpButton.tsx
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
Fab,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
ListItemIcon,
|
||||||
|
ListItemText,
|
||||||
|
Typography,
|
||||||
|
Divider,
|
||||||
|
Box,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { Help, Phone, Event, BugReport, Close } from "@mui/icons-material";
|
||||||
|
|
||||||
|
interface HelpButtonProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HelpButton: React.FC<HelpButtonProps> = ({ className }) => {
|
||||||
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
|
||||||
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePhoneClick = () => {
|
||||||
|
window.location.href = "tel:+4971212711099";
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBookingClick = () => {
|
||||||
|
window.open(
|
||||||
|
"https://app.reclaim.ai/m/verfasste-studierendenschaft/help",
|
||||||
|
"_blank",
|
||||||
|
"noopener,noreferrer",
|
||||||
|
);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFeedbackClick = () => {
|
||||||
|
window.open(
|
||||||
|
"https://cloud.reutlingen.university/apps/forms/s/gZwfmxt7q2YBDXm5dBwicqmn",
|
||||||
|
"_blank",
|
||||||
|
"noopener,noreferrer",
|
||||||
|
);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Fab
|
||||||
|
color="primary"
|
||||||
|
aria-label="help"
|
||||||
|
onClick={handleClick}
|
||||||
|
className={className}
|
||||||
|
size="large"
|
||||||
|
sx={{
|
||||||
|
position: "fixed",
|
||||||
|
bottom: 96,
|
||||||
|
right: 24,
|
||||||
|
zIndex: 1300,
|
||||||
|
boxShadow: 3,
|
||||||
|
"&:hover": {
|
||||||
|
boxShadow: 6,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{open ? <Close /> : <Help />}
|
||||||
|
</Fab>
|
||||||
|
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "top",
|
||||||
|
horizontal: "left",
|
||||||
|
}}
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: "bottom",
|
||||||
|
horizontal: "right",
|
||||||
|
}}
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
minWidth: 280,
|
||||||
|
boxShadow: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
px: 2,
|
||||||
|
py: 1.5,
|
||||||
|
backgroundColor: "primary.main",
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="subtitle1"
|
||||||
|
fontWeight="bold"
|
||||||
|
sx={{ color: "white" }}
|
||||||
|
>
|
||||||
|
Hilfe & Kontakt
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ opacity: 0.9 }}>
|
||||||
|
STUPA Reutlingen University
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* Phone Contact */}
|
||||||
|
<MenuItem onClick={handlePhoneClick} sx={{ py: 1.5 }}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Phone color="primary" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
<Typography variant="body1" fontWeight="medium">
|
||||||
|
STUPA-Büro anrufen
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
+49 7121 271 1099
|
||||||
|
</Typography>
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
{/* Booking Link */}
|
||||||
|
<MenuItem onClick={handleBookingClick} sx={{ py: 1.5 }}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Event color="primary" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
<Typography variant="body1" fontWeight="medium">
|
||||||
|
Termin vereinbaren
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Über Reclaim buchen
|
||||||
|
</Typography>
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
|
{/* Feedback Form */}
|
||||||
|
<MenuItem onClick={handleFeedbackClick} sx={{ py: 1.5 }}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<BugReport color="primary" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
<Typography variant="body1" fontWeight="medium">
|
||||||
|
Feedback & Fehler melden
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Formular ausfüllen
|
||||||
|
</Typography>
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* Footer Info */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
px: 2,
|
||||||
|
py: 1,
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.mode === "dark" ? "grey.900" : "grey.50",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
color="text.secondary"
|
||||||
|
align="center"
|
||||||
|
display="block"
|
||||||
|
>
|
||||||
|
Bei technischen Problemen oder Fragen zur Antragstellung
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Menu>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HelpButton;
|
||||||
1
frontend/src/components/HelpButton/index.ts
Normal file
1
frontend/src/components/HelpButton/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./HelpButton";
|
||||||
@ -126,6 +126,10 @@ const AdminDashboard: React.FC = () => {
|
|||||||
.length,
|
.length,
|
||||||
approved: applicationList.filter((app) => app.status === "approved").length,
|
approved: applicationList.filter((app) => app.status === "approved").length,
|
||||||
rejected: applicationList.filter((app) => app.status === "rejected").length,
|
rejected: applicationList.filter((app) => app.status === "rejected").length,
|
||||||
|
totalAmount: applicationList.reduce(
|
||||||
|
(sum, app) => sum + (app.total_amount || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -154,7 +158,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
|
|
||||||
{/* Statistics Cards */}
|
{/* Statistics Cards */}
|
||||||
<Grid container spacing={3} sx={{ mb: 4 }}>
|
<Grid container spacing={3} sx={{ mb: 4 }}>
|
||||||
<Grid item xs={12} sm={6} md={2.4}>
|
<Grid item xs={12} sm={6} md={2}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent sx={{ textAlign: "center" }}>
|
<CardContent sx={{ textAlign: "center" }}>
|
||||||
<Typography variant="h4" color="primary">
|
<Typography variant="h4" color="primary">
|
||||||
@ -166,7 +170,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6} md={2.4}>
|
<Grid item xs={12} sm={6} md={2}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent sx={{ textAlign: "center" }}>
|
<CardContent sx={{ textAlign: "center" }}>
|
||||||
<Typography variant="h4" color="info.main">
|
<Typography variant="h4" color="info.main">
|
||||||
@ -178,7 +182,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6} md={2.4}>
|
<Grid item xs={12} sm={6} md={2}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent sx={{ textAlign: "center" }}>
|
<CardContent sx={{ textAlign: "center" }}>
|
||||||
<Typography variant="h4" color="warning.main">
|
<Typography variant="h4" color="warning.main">
|
||||||
@ -190,7 +194,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6} md={2.4}>
|
<Grid item xs={12} sm={6} md={2}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent sx={{ textAlign: "center" }}>
|
<CardContent sx={{ textAlign: "center" }}>
|
||||||
<Typography variant="h4" color="success.main">
|
<Typography variant="h4" color="success.main">
|
||||||
@ -202,7 +206,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6} md={2.4}>
|
<Grid item xs={12} sm={6} md={2}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent sx={{ textAlign: "center" }}>
|
<CardContent sx={{ textAlign: "center" }}>
|
||||||
<Typography variant="h4" color="error.main">
|
<Typography variant="h4" color="error.main">
|
||||||
@ -214,6 +218,29 @@ const AdminDashboard: React.FC = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12} sm={6} md={2}>
|
||||||
|
<Card>
|
||||||
|
<CardContent sx={{ textAlign: "center" }}>
|
||||||
|
<Typography
|
||||||
|
variant="h5"
|
||||||
|
color="secondary.main"
|
||||||
|
sx={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: { xs: "1rem", sm: "1.25rem" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{stats.totalAmount.toLocaleString("de-DE", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "EUR",
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Gesamtsumme
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Filters and Search */}
|
{/* Filters and Search */}
|
||||||
@ -285,6 +312,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
<TableCell>Projektname</TableCell>
|
<TableCell>Projektname</TableCell>
|
||||||
<TableCell>Typ</TableCell>
|
<TableCell>Typ</TableCell>
|
||||||
<TableCell>Status</TableCell>
|
<TableCell>Status</TableCell>
|
||||||
|
<TableCell align="right">Summe</TableCell>
|
||||||
<TableCell>Erstellt</TableCell>
|
<TableCell>Erstellt</TableCell>
|
||||||
<TableCell>Geändert</TableCell>
|
<TableCell>Geändert</TableCell>
|
||||||
<TableCell align="right">Aktionen</TableCell>
|
<TableCell align="right">Aktionen</TableCell>
|
||||||
@ -293,7 +321,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{applicationList.length === 0 && !isLoading ? (
|
{applicationList.length === 0 && !isLoading ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={7} align="center">
|
<TableCell colSpan={8} align="center">
|
||||||
<Typography color="text.secondary">
|
<Typography color="text.secondary">
|
||||||
Keine Anträge gefunden
|
Keine Anträge gefunden
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -340,6 +368,14 @@ const AdminDashboard: React.FC = () => {
|
|||||||
<MenuItem value="rejected">Abgelehnt</MenuItem>
|
<MenuItem value="rejected">Abgelehnt</MenuItem>
|
||||||
</TextField>
|
</TextField>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell align="right">
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: "medium" }}>
|
||||||
|
{(application.total_amount || 0).toLocaleString("de-DE", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "EUR",
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{dayjs(application.created_at).format("DD.MM.YY HH:mm")}
|
{dayjs(application.created_at).format("DD.MM.YY HH:mm")}
|
||||||
|
|||||||
@ -151,6 +151,7 @@ export interface ApplicationListItem {
|
|||||||
variant: string;
|
variant: string;
|
||||||
status: string;
|
status: string;
|
||||||
project_name?: string;
|
project_name?: string;
|
||||||
|
total_amount?: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user