resolve issues with new features
This commit is contained in:
47
frontend/src/components/shared/ChatAwareFab.tsx
Normal file
47
frontend/src/components/shared/ChatAwareFab.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import { Fab } from '@mui/material';
|
||||
import type { SxProps, Theme } from '@mui/material/styles';
|
||||
import { useLayout } from '../../contexts/LayoutContext';
|
||||
|
||||
interface ChatAwareFabProps {
|
||||
onClick: () => void;
|
||||
children: React.ReactNode;
|
||||
color?: 'primary' | 'secondary' | 'default' | 'error' | 'info' | 'success' | 'warning';
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
'aria-label'?: string;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Fab that automatically shifts right to avoid overlapping the chat panel.
|
||||
* Must be rendered inside DashboardLayout (i.e. inside LayoutProvider).
|
||||
*/
|
||||
const ChatAwareFab = React.forwardRef<HTMLButtonElement, ChatAwareFabProps>(
|
||||
({ onClick, children, color = 'primary', size, 'aria-label': ariaLabel, sx }, ref) => {
|
||||
const { chatPanelOpen } = useLayout();
|
||||
return (
|
||||
<Fab
|
||||
ref={ref}
|
||||
color={color}
|
||||
size={size}
|
||||
aria-label={ariaLabel}
|
||||
onClick={onClick}
|
||||
sx={[
|
||||
{
|
||||
position: 'fixed',
|
||||
bottom: 32,
|
||||
right: chatPanelOpen ? 376 : 80,
|
||||
transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)',
|
||||
},
|
||||
...(Array.isArray(sx) ? sx : sx ? [sx] : []),
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</Fab>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ChatAwareFab.displayName = 'ChatAwareFab';
|
||||
|
||||
export default ChatAwareFab;
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
Fab,
|
||||
FormControl,
|
||||
Grid,
|
||||
IconButton,
|
||||
@@ -44,7 +43,7 @@ import {
|
||||
} from '@mui/icons-material';
|
||||
import { Link as RouterLink, useNavigate, useParams } from 'react-router-dom';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { useLayout } from '../contexts/LayoutContext';
|
||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||
import { equipmentApi } from '../services/equipment';
|
||||
import { fromGermanDate } from '../utils/dateInput';
|
||||
import {
|
||||
@@ -352,7 +351,6 @@ interface WartungTabProps {
|
||||
}
|
||||
|
||||
const WartungTab: React.FC<WartungTabProps> = ({ equipmentId, wartungslog, onAdded, canWrite }) => {
|
||||
const { chatPanelOpen } = useLayout();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
@@ -444,15 +442,13 @@ const WartungTab: React.FC<WartungTabProps> = ({ equipmentId, wartungslog, onAdd
|
||||
)}
|
||||
|
||||
{canWrite && (
|
||||
<Fab
|
||||
color="primary"
|
||||
<ChatAwareFab
|
||||
size="small"
|
||||
aria-label="Wartung eintragen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: chatPanelOpen ? 376 : 80, transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)' }}
|
||||
onClick={() => { setForm(emptyForm); setSaveError(null); setDialogOpen(true); }}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
</ChatAwareFab>
|
||||
)}
|
||||
|
||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} maxWidth="sm" fullWidth>
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
Fab,
|
||||
FormControl,
|
||||
Grid,
|
||||
IconButton,
|
||||
@@ -55,7 +54,7 @@ import {
|
||||
} from '@mui/icons-material';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { useLayout } from '../contexts/LayoutContext';
|
||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||
import { vehiclesApi } from '../services/vehicles';
|
||||
import { fromGermanDate } from '../utils/dateInput';
|
||||
import { equipmentApi } from '../services/equipment';
|
||||
@@ -323,7 +322,6 @@ const WARTUNG_ART_ICONS: Record<string, React.ReactElement> = {
|
||||
};
|
||||
|
||||
const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdded, canWrite }) => {
|
||||
const { chatPanelOpen } = useLayout();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
@@ -396,15 +394,13 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
)}
|
||||
|
||||
{canWrite && (
|
||||
<Fab
|
||||
color="primary"
|
||||
<ChatAwareFab
|
||||
size="small"
|
||||
aria-label="Wartung eintragen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: chatPanelOpen ? 376 : 80, transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)' }}
|
||||
onClick={() => { setForm(emptyForm); setDialogOpen(true); }}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
</ChatAwareFab>
|
||||
)}
|
||||
|
||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} maxWidth="sm" fullWidth>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Chip,
|
||||
CircularProgress,
|
||||
Container,
|
||||
Fab,
|
||||
Grid,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
@@ -29,7 +28,7 @@ import {
|
||||
} from '@mui/icons-material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { useLayout } from '../contexts/LayoutContext';
|
||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||
import { vehiclesApi } from '../services/vehicles';
|
||||
import { equipmentApi } from '../services/equipment';
|
||||
import type { VehicleEquipmentWarning } from '../types/equipment.types';
|
||||
@@ -272,7 +271,6 @@ const VehicleCard: React.FC<VehicleCardProps> = ({ vehicle, onClick, warnings =
|
||||
function Fahrzeuge() {
|
||||
const navigate = useNavigate();
|
||||
const { isAdmin } = usePermissions();
|
||||
const { chatPanelOpen } = useLayout();
|
||||
const [vehicles, setVehicles] = useState<FahrzeugListItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -414,14 +412,12 @@ function Fahrzeuge() {
|
||||
)}
|
||||
|
||||
{isAdmin && (
|
||||
<Fab
|
||||
color="primary"
|
||||
<ChatAwareFab
|
||||
aria-label="Fahrzeug hinzufügen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: chatPanelOpen ? 376 : 80, transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)' }}
|
||||
onClick={() => navigate('/fahrzeuge/neu')}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
</ChatAwareFab>
|
||||
)}
|
||||
</Container>
|
||||
</DashboardLayout>
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
Fab,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
@@ -70,7 +69,7 @@ import {
|
||||
} from '@mui/icons-material';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { useLayout } from '../contexts/LayoutContext';
|
||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||
import { toGermanDate, toGermanDateTime, fromGermanDate, fromGermanDateTime, isValidGermanDate, isValidGermanDateTime } from '../utils/dateInput';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useNotification } from '../contexts/NotificationContext';
|
||||
@@ -1682,7 +1681,6 @@ export default function Kalender() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { user } = useAuth();
|
||||
const notification = useNotification();
|
||||
const { chatPanelOpen } = useLayout();
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
@@ -2554,16 +2552,14 @@ export default function Kalender() {
|
||||
|
||||
{/* FAB: Create Veranstaltung */}
|
||||
{canWriteEvents && (
|
||||
<Fab
|
||||
color="primary"
|
||||
sx={{ position: 'fixed', bottom: 32, right: chatPanelOpen ? 376 : 80, transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)' }}
|
||||
<ChatAwareFab
|
||||
onClick={() => {
|
||||
setVeranstEditing(null);
|
||||
setVeranstFormOpen(true);
|
||||
}}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
</ChatAwareFab>
|
||||
)}
|
||||
|
||||
{/* Day Popover */}
|
||||
@@ -2927,13 +2923,9 @@ export default function Kalender() {
|
||||
|
||||
{/* FAB */}
|
||||
{canCreateBookings && (
|
||||
<Fab
|
||||
color="primary"
|
||||
sx={{ position: 'fixed', bottom: 32, right: chatPanelOpen ? 376 : 80, transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)' }}
|
||||
onClick={openBookingCreate}
|
||||
>
|
||||
<ChatAwareFab onClick={openBookingCreate}>
|
||||
<Add />
|
||||
</Fab>
|
||||
</ChatAwareFab>
|
||||
)}
|
||||
|
||||
{/* Booking detail popover */}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
InputAdornment,
|
||||
Chip,
|
||||
Avatar,
|
||||
Fab,
|
||||
Tooltip,
|
||||
Alert,
|
||||
CircularProgress,
|
||||
@@ -33,7 +32,7 @@ import {
|
||||
} from '@mui/icons-material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { useLayout } from '../contexts/LayoutContext';
|
||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { membersService } from '../services/members';
|
||||
import {
|
||||
@@ -74,8 +73,7 @@ function useDebounce<T>(value: T, delay: number): T {
|
||||
// ----------------------------------------------------------------
|
||||
function Mitglieder() {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
const { chatPanelOpen } = useLayout(); const canWrite = useCanWrite();
|
||||
const { user } = useAuth(); const canWrite = useCanWrite();
|
||||
|
||||
// --- redirect non-admin/non-kommando users to their own profile ---
|
||||
useEffect(() => {
|
||||
@@ -428,20 +426,13 @@ function Mitglieder() {
|
||||
{/* FAB — only visible to Kommandant/Admin */}
|
||||
{canWrite && (
|
||||
<Tooltip title="Neues Mitglied anlegen">
|
||||
<Fab
|
||||
color="primary"
|
||||
<ChatAwareFab
|
||||
aria-label="Neues Mitglied anlegen"
|
||||
onClick={() => navigate('/mitglieder/neu')}
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
bottom: 32,
|
||||
right: chatPanelOpen ? 376 : 80,
|
||||
transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)',
|
||||
zIndex: (theme) => theme.zIndex.speedDial,
|
||||
}}
|
||||
sx={{ zIndex: (theme) => theme.zIndex.speedDial }}
|
||||
>
|
||||
<AddIcon />
|
||||
</Fab>
|
||||
</ChatAwareFab>
|
||||
</Tooltip>
|
||||
)}
|
||||
</DashboardLayout>
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
Switch,
|
||||
Checkbox,
|
||||
FormGroup,
|
||||
Fab,
|
||||
Stack,
|
||||
List,
|
||||
ListItem,
|
||||
@@ -51,7 +50,7 @@ import {
|
||||
Delete as DeleteIcon,
|
||||
} from '@mui/icons-material';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { useLayout } from '../contexts/LayoutContext';
|
||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||
import { toGermanDate, toGermanDateTime, fromGermanDate, fromGermanDateTime } from '../utils/dateInput';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useNotification } from '../contexts/NotificationContext';
|
||||
@@ -1005,7 +1004,6 @@ function EventListView({ events, canWrite, onEdit, onCancel, onDelete }: ListVie
|
||||
|
||||
export default function Veranstaltungen() {
|
||||
const { user } = useAuth();
|
||||
const { chatPanelOpen } = useLayout();
|
||||
const notification = useNotification();
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
@@ -1316,14 +1314,12 @@ export default function Veranstaltungen() {
|
||||
|
||||
{/* FAB for creating events */}
|
||||
{canWrite && (
|
||||
<Fab
|
||||
color="primary"
|
||||
<ChatAwareFab
|
||||
aria-label="Veranstaltung erstellen"
|
||||
sx={{ position: 'fixed', bottom: 32, right: chatPanelOpen ? 376 : 80, transition: 'right 225ms cubic-bezier(0.4, 0, 0.6, 1)' }}
|
||||
onClick={() => { setEditingEvent(null); setFormOpen(true); }}
|
||||
>
|
||||
<Add />
|
||||
</Fab>
|
||||
</ChatAwareFab>
|
||||
)}
|
||||
|
||||
{/* Day Popover */}
|
||||
|
||||
Reference in New Issue
Block a user