resolve issues with new features

This commit is contained in:
Matthias Hochmeister
2026-03-12 11:37:25 +01:00
parent d5be68ca63
commit 71a04aee89
38 changed files with 699 additions and 108 deletions

View File

@@ -6,6 +6,7 @@ import ChatIcon from '@mui/icons-material/Chat';
import Typography from '@mui/material/Typography';
import Avatar from '@mui/material/Avatar';
import Badge from '@mui/material/Badge';
import Toolbar from '@mui/material/Toolbar';
import Tooltip from '@mui/material/Tooltip';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
@@ -39,6 +40,7 @@ const ChatPanelInner: React.FC = () => {
overflowY: 'auto',
}}
>
<Toolbar />
<IconButton onClick={() => setChatPanelOpen(true)} aria-label="Chat öffnen">
<ChatIcon />
</IconButton>
@@ -96,6 +98,7 @@ const ChatPanelInner: React.FC = () => {
overflow: 'hidden',
}}
>
<Toolbar />
<Box
sx={{
px: 1.5,

View File

@@ -77,7 +77,7 @@ const BookStackRecentWidget: React.FC = () => {
});
const configured = data?.configured ?? true;
const pages = data?.data ?? [];
const pages = (data?.data ?? []).slice(0, 5);
if (!configured) {
return (

View File

@@ -19,6 +19,7 @@ import { de } from 'date-fns/locale';
import { nextcloudApi } from '../../services/nextcloud';
import type { NextcloudConversation } from '../../types/nextcloud.types';
import { safeOpenUrl } from '../../utils/safeOpenUrl';
import { useCountUp } from '../../hooks/useCountUp';
const POLL_INTERVAL = 2000;
const POLL_TIMEOUT = 5 * 60 * 1000;
@@ -112,6 +113,7 @@ const NextcloudTalkWidget: React.FC = () => {
const connected = data?.connected ?? false;
const conversations = data?.conversations?.slice(0, 5) ?? [];
const totalUnread = data?.totalUnread ?? 0;
const animatedUnread = useCountUp(totalUnread);
const stopPolling = useCallback(() => {
if (pollIntervalRef.current) {
@@ -199,7 +201,7 @@ const NextcloudTalkWidget: React.FC = () => {
</Typography>
{connected && totalUnread > 0 && (
<Chip
label={`${totalUnread} ungelesen`}
label={`${animatedUnread} ungelesen`}
size="small"
color="primary"
variant="outlined"

View File

@@ -15,6 +15,7 @@ import { de } from 'date-fns/locale';
import { vikunjaApi } from '../../services/vikunja';
import type { VikunjaTask } from '../../types/vikunja.types';
import { safeOpenUrl } from '../../utils/safeOpenUrl';
import { useCountUp } from '../../hooks/useCountUp';
const PRIORITY_LABELS: Record<number, { label: string; color: 'default' | 'warning' | 'error' }> = {
0: { label: 'Keine', color: 'default' },
@@ -95,6 +96,7 @@ const VikunjaMyTasksWidget: React.FC = () => {
const configured = data?.configured ?? true;
const tasks = data?.data ?? [];
const animatedTaskCount = useCountUp(tasks.length);
if (!configured) {
return (
@@ -129,7 +131,7 @@ const VikunjaMyTasksWidget: React.FC = () => {
Meine Aufgaben
</Typography>
{!isLoading && !isError && tasks.length > 0 && (
<Chip label={tasks.length} size="small" color="primary" />
<Chip label={animatedTaskCount} size="small" color="primary" />
)}
</Box>

View File

@@ -215,7 +215,7 @@ const IncidentStatsChart: React.FC<IncidentStatsChartProps> = ({ stats, loading
outerRadius={100}
paddingAngle={2}
dataKey="value"
label={({ name, percent }) =>
label={({ percent }: { percent: number }) =>
percent > 0.05 ? `${(percent * 100).toFixed(0)}%` : ''
}
labelLine={false}

View File

@@ -1,4 +1,4 @@
import React, { Component, ErrorInfo, ReactNode } from 'react';
import { Component, ErrorInfo, ReactNode } from 'react';
import { Box, Card, CardContent, Typography, Button } from '@mui/material';
import { ErrorOutline, Refresh } from '@mui/icons-material';

View File

@@ -91,9 +91,12 @@ function Header({ onMenuClick }: HeaderProps) {
};
const linkEntries = externalLinks
? Object.entries(externalLinks).filter(([, url]) => !!url)
? Object.entries(externalLinks).filter(([key, url]) => key !== 'customLinks' && !!url)
: [];
const customLinks: Array<{ name: string; url: string }> =
externalLinks?.customLinks ?? [];
const linkLabels: Record<string, string> = {
nextcloud: 'Nextcloud Dateien',
bookstack: 'Wissensdatenbank',
@@ -125,7 +128,7 @@ function Header({ onMenuClick }: HeaderProps) {
{user && (
<>
{linkEntries.length > 0 && (
{(linkEntries.length > 0 || customLinks.length > 0) && (
<>
<Button
variant="text"
@@ -133,11 +136,11 @@ function Header({ onMenuClick }: HeaderProps) {
onClick={handleToolsOpen}
size="small"
startIcon={<Launch />}
aria-label="Externe Links"
aria-label="FF Rems Tools"
aria-controls="tools-menu"
aria-haspopup="true"
>
Externe Links
FF Rems Tools
</Button>
<Menu
@@ -166,6 +169,15 @@ function Header({ onMenuClick }: HeaderProps) {
{linkLabels[key] || key}
</MenuItem>
))}
{customLinks.length > 0 && linkEntries.length > 0 && <Divider />}
{customLinks.map((link, index) => (
<MenuItem key={`custom-${index}`} onClick={() => handleOpenExternal(link.url)}>
<ListItemIcon>
<Launch fontSize="small" />
</ListItemIcon>
{link.name}
</MenuItem>
))}
</Menu>
</>
)}

View File

@@ -20,6 +20,7 @@ import {
CalendarMonth,
MenuBook,
AdminPanelSettings,
Settings,
Menu as MenuIcon,
} from '@mui/icons-material';
import { useNavigate, useLocation } from 'react-router-dom';
@@ -78,6 +79,12 @@ const adminItem: NavigationItem = {
path: '/admin',
};
const adminSettingsItem: NavigationItem = {
text: 'Einstellungen',
icon: <Settings />,
path: '/admin/settings',
};
interface SidebarProps {
mobileOpen: boolean;
onMobileClose: () => void;
@@ -92,7 +99,7 @@ function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
const isAdmin = user?.groups?.includes('dashboard_admin') ?? false;
const navigationItems = useMemo(() => {
return isAdmin ? [...baseNavigationItems, adminItem] : baseNavigationItems;
return isAdmin ? [...baseNavigationItems, adminItem, adminSettingsItem] : baseNavigationItems;
}, [isAdmin]);
const handleNavigation = (path: string) => {

View File

@@ -25,12 +25,6 @@ function alertTypeLabel(type: InspectionAlertType): string {
type Urgency = 'overdue' | 'urgent' | 'warning';
function getUrgency(tage: number): Urgency {
if (tage < 0) return 'overdue';
if (tage <= 14) return 'urgent';
return 'warning';
}
const URGENCY_CONFIG: Record<Urgency, { severity: 'error' | 'warning'; label: string }> = {
overdue: { severity: 'error', label: 'Überfällig' },
urgent: { severity: 'error', label: 'Dringend (≤ 14 Tage)' },
@@ -96,11 +90,11 @@ const InspectionAlerts: React.FC<InspectionAlertsProps> = ({
const urgent = alerts.filter((a) => a.tage >= 0 && a.tage <= 14);
const warning = alerts.filter((a) => a.tage > 14);
const groups: Array<{ urgency: Urgency; items: InspectionAlert[] }> = [
{ urgency: 'overdue', items: overdue },
{ urgency: 'urgent', items: urgent },
{ urgency: 'warning', items: warning },
].filter((g) => g.items.length > 0);
const groups: Array<{ urgency: Urgency; items: InspectionAlert[] }> = ([
{ urgency: 'overdue' as Urgency, items: overdue },
{ urgency: 'urgent' as Urgency, items: urgent },
{ urgency: 'warning' as Urgency, items: warning },
] as Array<{ urgency: Urgency; items: InspectionAlert[] }>).filter((g) => g.items.length > 0);
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>