annoucement banners, calendar pdf export, vehicle booking quck-add, even quick-add
This commit is contained in:
@@ -6,6 +6,7 @@ import ServiceManagerTab from '../components/admin/ServiceManagerTab';
|
||||
import SystemHealthTab from '../components/admin/SystemHealthTab';
|
||||
import UserOverviewTab from '../components/admin/UserOverviewTab';
|
||||
import NotificationBroadcastTab from '../components/admin/NotificationBroadcastTab';
|
||||
import BannerManagementTab from '../components/admin/BannerManagementTab';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
interface TabPanelProps {
|
||||
@@ -39,6 +40,7 @@ function AdminDashboard() {
|
||||
<Tab label="System" />
|
||||
<Tab label="Benutzer" />
|
||||
<Tab label="Broadcast" />
|
||||
<Tab label="Banner" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
@@ -54,6 +56,9 @@ function AdminDashboard() {
|
||||
<TabPanel value={tab} index={3}>
|
||||
<NotificationBroadcastTab />
|
||||
</TabPanel>
|
||||
<TabPanel value={tab} index={4}>
|
||||
<BannerManagementTab />
|
||||
</TabPanel>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,18 @@ import VikunjaMyTasksWidget from '../components/dashboard/VikunjaMyTasksWidget';
|
||||
import VikunjaQuickAddWidget from '../components/dashboard/VikunjaQuickAddWidget';
|
||||
import VikunjaOverdueNotifier from '../components/dashboard/VikunjaOverdueNotifier';
|
||||
import AdminStatusWidget from '../components/dashboard/AdminStatusWidget';
|
||||
import AnnouncementBanner from '../components/dashboard/AnnouncementBanner';
|
||||
import VehicleBookingQuickAddWidget from '../components/dashboard/VehicleBookingQuickAddWidget';
|
||||
import EventQuickAddWidget from '../components/dashboard/EventQuickAddWidget';
|
||||
function Dashboard() {
|
||||
const { user } = useAuth();
|
||||
const isAdmin = user?.groups?.includes('dashboard_admin') ?? false;
|
||||
const canViewAtemschutz = user?.groups?.some(g =>
|
||||
['dashboard_admin', 'dashboard_kommando', 'dashboard_atemschutz', 'dashboard_moderator'].includes(g)
|
||||
) ?? false;
|
||||
const canWrite = user?.groups?.some(g =>
|
||||
['dashboard_admin', 'dashboard_kommando', 'dashboard_moderator', 'dashboard_gruppenfuehrer'].includes(g)
|
||||
) ?? false;
|
||||
const [dataLoading, setDataLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -38,6 +44,7 @@ function Dashboard() {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Container maxWidth={false} disableGutters>
|
||||
<AnnouncementBanner />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
@@ -148,6 +155,28 @@ function Dashboard() {
|
||||
</Fade>
|
||||
</Box>
|
||||
|
||||
{/* Vehicle Booking — Quick Add Widget */}
|
||||
{canWrite && (
|
||||
<Box>
|
||||
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '720ms' }}>
|
||||
<Box>
|
||||
<VehicleBookingQuickAddWidget />
|
||||
</Box>
|
||||
</Fade>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Event — Quick Add Widget */}
|
||||
{canWrite && (
|
||||
<Box>
|
||||
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '760ms' }}>
|
||||
<Box>
|
||||
<EventQuickAddWidget />
|
||||
</Box>
|
||||
</Fade>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Vikunja — Overdue Notifier (invisible, polling component) */}
|
||||
<VikunjaOverdueNotifier />
|
||||
|
||||
|
||||
@@ -693,6 +693,70 @@ async function generatePdf(
|
||||
doc.save(filename);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// PDF Export — Fahrzeugbuchungen
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
async function generateBookingsPdf(
|
||||
weekStart: Date,
|
||||
weekEnd: Date,
|
||||
bookings: FahrzeugBuchungListItem[],
|
||||
) {
|
||||
const { jsPDF } = await import('jspdf');
|
||||
const autoTable = (await import('jspdf-autotable')).default;
|
||||
|
||||
const doc = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' });
|
||||
|
||||
const startLabel = fnsFormat(weekStart, 'dd.MM.yyyy');
|
||||
const endLabel = fnsFormat(weekEnd, 'dd.MM.yyyy');
|
||||
const kwLabel = `KW ${fnsFormat(weekStart, 'w')}`;
|
||||
|
||||
// Header bar
|
||||
doc.setFillColor(183, 28, 28); // fire-red
|
||||
doc.rect(0, 0, 297, 18, 'F');
|
||||
doc.setTextColor(255, 255, 255);
|
||||
doc.setFontSize(14);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`Fahrzeugbuchungen — ${kwLabel} · ${startLabel} – ${endLabel}`, 10, 12);
|
||||
doc.setFontSize(9);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text('Feuerwehr Rems', 250, 12);
|
||||
|
||||
const formatDt = (iso: string) => {
|
||||
const d = new Date(iso);
|
||||
return fnsFormat(d, 'dd.MM.yyyy HH:mm');
|
||||
};
|
||||
|
||||
const active = bookings.filter((b) => !b.abgesagt);
|
||||
const rows = active.map((b) => [
|
||||
b.fahrzeug_name + (b.fahrzeug_kennzeichen ? `\n${b.fahrzeug_kennzeichen}` : ''),
|
||||
b.titel,
|
||||
formatDt(b.beginn),
|
||||
formatDt(b.ende),
|
||||
BUCHUNGS_ART_LABELS[b.buchungs_art],
|
||||
]);
|
||||
|
||||
autoTable(doc, {
|
||||
head: [['Fahrzeug', 'Titel', 'Beginn', 'Ende', 'Art']],
|
||||
body: rows,
|
||||
startY: 22,
|
||||
headStyles: { fillColor: [183, 28, 28], textColor: 255, fontStyle: 'bold' },
|
||||
alternateRowStyles: { fillColor: [250, 235, 235] },
|
||||
margin: { left: 10, right: 10 },
|
||||
styles: { fontSize: 9, cellPadding: 2 },
|
||||
columnStyles: {
|
||||
0: { cellWidth: 45 },
|
||||
1: { cellWidth: 90 },
|
||||
2: { cellWidth: 38 },
|
||||
3: { cellWidth: 38 },
|
||||
4: { cellWidth: 35 },
|
||||
},
|
||||
});
|
||||
|
||||
const filename = `fahrzeugbuchungen_${fnsFormat(weekStart, 'yyyy-MM-dd')}.pdf`;
|
||||
doc.save(filename);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// CSV Import Dialog
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
@@ -2303,6 +2367,16 @@ export default function Kalender() {
|
||||
>
|
||||
Kalender
|
||||
</Button>
|
||||
|
||||
{/* PDF Export */}
|
||||
<Tooltip title="PDF exportieren">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => generateBookingsPdf(currentWeekStart, weekEnd, bookings)}
|
||||
>
|
||||
<FileDownloadIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
{bookingsLoading && (
|
||||
|
||||
Reference in New Issue
Block a user