calendar and vehicle booking rework
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
-- Migration 063: Split fahrzeugbuchungen into its own feature group
|
||||||
|
-- Moves kalender:view_bookings, kalender:manage_bookings, kalender:widget_bookings
|
||||||
|
-- into a new 'fahrzeugbuchungen' feature group with cleaner permission names.
|
||||||
|
|
||||||
|
-- 1. Add new feature group
|
||||||
|
INSERT INTO feature_groups (id, label, sort_order)
|
||||||
|
VALUES ('fahrzeugbuchungen', 'Fahrzeugbuchungen', 2)
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
|
|
||||||
|
-- Shift existing groups down to make room (kalender stays 1, fahrzeugbuchungen is 2)
|
||||||
|
UPDATE feature_groups SET sort_order = sort_order + 1
|
||||||
|
WHERE id NOT IN ('kalender', 'fahrzeugbuchungen') AND sort_order >= 2;
|
||||||
|
|
||||||
|
-- 2. Add new permissions
|
||||||
|
INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES
|
||||||
|
('fahrzeugbuchungen:view', 'fahrzeugbuchungen', 'Ansehen', 'Buchungsliste anzeigen', 1),
|
||||||
|
('fahrzeugbuchungen:create', 'fahrzeugbuchungen', 'Erstellen', 'Neue Fahrzeugbuchungen anlegen', 2),
|
||||||
|
('fahrzeugbuchungen:manage', 'fahrzeugbuchungen', 'Verwalten', 'Buchungen bearbeiten, stornieren, löschen; Kategorien verwalten', 3),
|
||||||
|
('fahrzeugbuchungen:widget', 'fahrzeugbuchungen', 'Widget', 'Dashboard-Widget für Fahrzeugbuchungen', 4)
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
|
|
||||||
|
-- 3. Migrate existing group_permissions
|
||||||
|
-- kalender:view_bookings → fahrzeugbuchungen:view
|
||||||
|
-- kalender:manage_bookings → fahrzeugbuchungen:manage + fahrzeugbuchungen:create + fahrzeugbuchungen:view
|
||||||
|
-- kalender:widget_bookings → fahrzeugbuchungen:widget
|
||||||
|
|
||||||
|
INSERT INTO group_permissions (authentik_group, permission_id)
|
||||||
|
SELECT authentik_group, 'fahrzeugbuchungen:view'
|
||||||
|
FROM group_permissions
|
||||||
|
WHERE permission_id = 'kalender:view_bookings'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO group_permissions (authentik_group, permission_id)
|
||||||
|
SELECT authentik_group, 'fahrzeugbuchungen:create'
|
||||||
|
FROM group_permissions
|
||||||
|
WHERE permission_id = 'kalender:manage_bookings'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO group_permissions (authentik_group, permission_id)
|
||||||
|
SELECT authentik_group, 'fahrzeugbuchungen:manage'
|
||||||
|
FROM group_permissions
|
||||||
|
WHERE permission_id = 'kalender:manage_bookings'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO group_permissions (authentik_group, permission_id)
|
||||||
|
SELECT authentik_group, 'fahrzeugbuchungen:view'
|
||||||
|
FROM group_permissions
|
||||||
|
WHERE permission_id = 'kalender:manage_bookings'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO group_permissions (authentik_group, permission_id)
|
||||||
|
SELECT authentik_group, 'fahrzeugbuchungen:widget'
|
||||||
|
FROM group_permissions
|
||||||
|
WHERE permission_id = 'kalender:widget_bookings'
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- 4. Remove old kalender booking permissions
|
||||||
|
DELETE FROM group_permissions WHERE permission_id IN (
|
||||||
|
'kalender:view_bookings', 'kalender:manage_bookings', 'kalender:widget_bookings'
|
||||||
|
);
|
||||||
|
DELETE FROM permissions WHERE id IN (
|
||||||
|
'kalender:view_bookings', 'kalender:manage_bookings', 'kalender:widget_bookings'
|
||||||
|
);
|
||||||
@@ -91,7 +91,10 @@ function buildReverseHierarchy(hierarchy: Record<string, string[]>): Record<stri
|
|||||||
const PERMISSION_SUB_GROUPS: Record<string, Record<string, string[]>> = {
|
const PERMISSION_SUB_GROUPS: Record<string, Record<string, string[]>> = {
|
||||||
kalender: {
|
kalender: {
|
||||||
'Termine': ['view', 'create'],
|
'Termine': ['view', 'create'],
|
||||||
'Buchungen': ['view_bookings', 'manage_bookings'],
|
},
|
||||||
|
fahrzeugbuchungen: {
|
||||||
|
'Buchungen': ['view', 'create', 'manage'],
|
||||||
|
'Widget': ['widget'],
|
||||||
},
|
},
|
||||||
bestellungen: {
|
bestellungen: {
|
||||||
'Bestellungen': ['view', 'create', 'manage_orders', 'delete', 'export'],
|
'Bestellungen': ['view', 'create', 'manage_orders', 'delete', 'export'],
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||||
|
import ServiceModePage from '../components/shared/ServiceModePage';
|
||||||
|
import { usePermissionContext } from '../contexts/PermissionContext';
|
||||||
import { useNotification } from '../contexts/NotificationContext';
|
import { useNotification } from '../contexts/NotificationContext';
|
||||||
import { bookingApi, fetchVehicles, kategorieApi } from '../services/bookings';
|
import { bookingApi, fetchVehicles, kategorieApi } from '../services/bookings';
|
||||||
import type { CreateBuchungInput, BuchungsArt } from '../types/booking.types';
|
import type { CreateBuchungInput, BuchungsArt } from '../types/booking.types';
|
||||||
@@ -46,6 +48,7 @@ const EMPTY_FORM: CreateBuchungInput = {
|
|||||||
function BookingFormPage() {
|
function BookingFormPage() {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { isFeatureEnabled } = usePermissionContext();
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
const isEdit = Boolean(id);
|
const isEdit = Boolean(id);
|
||||||
|
|
||||||
@@ -231,6 +234,9 @@ function BookingFormPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
|
{!isFeatureEnabled('fahrzeugbuchungen') ? (
|
||||||
|
<ServiceModePage message="Fahrzeugbuchungen befinden sich aktuell im Wartungsmodus." />
|
||||||
|
) : (
|
||||||
<Box sx={{ p: 3 }}>
|
<Box sx={{ p: 3 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
|
||||||
<IconButton onClick={() => navigate('/fahrzeugbuchungen')}>
|
<IconButton onClick={() => navigate('/fahrzeugbuchungen')}>
|
||||||
@@ -537,6 +543,7 @@ function BookingFormPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
)}
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { format, parseISO } from 'date-fns';
|
import { format, parseISO } from 'date-fns';
|
||||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||||
|
import ServiceModePage from '../components/shared/ServiceModePage';
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
import { usePermissionContext } from '../contexts/PermissionContext';
|
import { usePermissionContext } from '../contexts/PermissionContext';
|
||||||
import { useNotification } from '../contexts/NotificationContext';
|
import { useNotification } from '../contexts/NotificationContext';
|
||||||
@@ -93,7 +94,7 @@ function getCategoryLabel(buchungsArt: BuchungsArt, kategorien: BuchungsKategori
|
|||||||
|
|
||||||
function FahrzeugBuchungen() {
|
function FahrzeugBuchungen() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { hasPermission } = usePermissionContext();
|
const { hasPermission, isFeatureEnabled } = usePermissionContext();
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -230,6 +231,10 @@ function FahrzeugBuchungen() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ── Render ─────────────────────────────────────────────────────────────────
|
// ── Render ─────────────────────────────────────────────────────────────────
|
||||||
|
if (!isFeatureEnabled('fahrzeugbuchungen')) {
|
||||||
|
return <ServiceModePage message="Fahrzeugbuchungen befinden sich aktuell im Wartungsmodus." />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
<Container maxWidth="xl" sx={{ py: 3 }}>
|
<Container maxWidth="xl" sx={{ py: 3 }}>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||||
|
import ServiceModePage from '../components/shared/ServiceModePage';
|
||||||
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
import ChatAwareFab from '../components/shared/ChatAwareFab';
|
||||||
import { toGermanDateTime, fromGermanDate, fromGermanDateTime } from '../utils/dateInput';
|
import { toGermanDateTime, fromGermanDate, fromGermanDateTime } from '../utils/dateInput';
|
||||||
|
|
||||||
@@ -1625,7 +1626,7 @@ function VeranstaltungFormDialog({
|
|||||||
|
|
||||||
export default function Kalender() {
|
export default function Kalender() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { hasPermission } = usePermissionContext();
|
const { hasPermission, isFeatureEnabled } = usePermissionContext();
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
@@ -1873,6 +1874,10 @@ export default function Kalender() {
|
|||||||
|
|
||||||
// ── Render ───────────────────────────────────────────────────────────────────
|
// ── Render ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if (!isFeatureEnabled('kalender')) {
|
||||||
|
return <ServiceModePage message="Der Kalender befindet sich aktuell im Wartungsmodus." />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
<Box sx={{ maxWidth: 1100, mx: 'auto' }}>
|
<Box sx={{ maxWidth: 1100, mx: 'auto' }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user