resolve issues with new features
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 }}>
|
||||
|
||||
Reference in New Issue
Block a user