featuer change for calendar
This commit is contained in:
@@ -423,11 +423,11 @@ function FahrzeugBuchungen() {
|
||||
<TableRow key={vehicle.id} hover>
|
||||
<TableCell>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
{vehicle.name}
|
||||
{vehicle.bezeichnung}
|
||||
</Typography>
|
||||
{vehicle.kennzeichen && (
|
||||
{vehicle.amtliches_kennzeichen && (
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{vehicle.kennzeichen}
|
||||
{vehicle.amtliches_kennzeichen}
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
@@ -632,8 +632,8 @@ function FahrzeugBuchungen() {
|
||||
>
|
||||
{vehicles.map((v) => (
|
||||
<MenuItem key={v.id} value={v.id}>
|
||||
{v.name}
|
||||
{v.kennzeichen ? ` (${v.kennzeichen})` : ''}
|
||||
{v.bezeichnung}
|
||||
{v.amtliches_kennzeichen ? ` (${v.amtliches_kennzeichen})` : ''}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
Edit as EditIcon,
|
||||
Event as EventIcon,
|
||||
HelpOutline as UnknownIcon,
|
||||
IosShare,
|
||||
Star as StarIcon,
|
||||
Today as TodayIcon,
|
||||
Tune,
|
||||
@@ -1085,6 +1086,12 @@ export default function Kalender() {
|
||||
const [cancelBookingGrund, setCancelBookingGrund] = useState('');
|
||||
const [cancelBookingLoading, setCancelBookingLoading] = useState(false);
|
||||
|
||||
// iCal subscription
|
||||
const [icalEventOpen, setIcalEventOpen] = useState(false);
|
||||
const [icalEventUrl, setIcalEventUrl] = useState('');
|
||||
const [icalBookingOpen, setIcalBookingOpen] = useState(false);
|
||||
const [icalBookingUrl, setIcalBookingUrl] = useState('');
|
||||
|
||||
// ── Data loading ─────────────────────────────────────────────────────────────
|
||||
|
||||
const loadCalendarData = useCallback(async () => {
|
||||
@@ -1354,6 +1361,28 @@ export default function Kalender() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleIcalEventOpen = async () => {
|
||||
try {
|
||||
const { subscribeUrl } = await eventsApi.getCalendarToken();
|
||||
setIcalEventUrl(subscribeUrl);
|
||||
setIcalEventOpen(true);
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : 'Fehler beim Laden des Tokens';
|
||||
notification.showError(msg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleIcalBookingOpen = async () => {
|
||||
try {
|
||||
const { subscribeUrl } = await bookingApi.getCalendarToken();
|
||||
setIcalBookingUrl(subscribeUrl);
|
||||
setIcalBookingOpen(true);
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : 'Fehler beim Laden des Tokens';
|
||||
notification.showError(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Render ───────────────────────────────────────────────────────────────────
|
||||
|
||||
return (
|
||||
@@ -1451,6 +1480,16 @@ export default function Kalender() {
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{/* iCal subscribe */}
|
||||
<Button
|
||||
startIcon={<IosShare />}
|
||||
onClick={handleIcalEventOpen}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
Kalender
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Month navigation */}
|
||||
@@ -1594,6 +1633,37 @@ export default function Kalender() {
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* iCal Event subscription dialog */}
|
||||
<Dialog open={icalEventOpen} onClose={() => setIcalEventOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>Kalender abonnieren</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
Abonniere den Dienste- & Veranstaltungskalender in deiner Kalenderanwendung. Kopiere die URL und füge sie als neuen
|
||||
Kalender (per URL) in Apple Kalender, Google Kalender oder Outlook ein.
|
||||
</Typography>
|
||||
<TextField
|
||||
fullWidth
|
||||
value={icalEventUrl}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
endAdornment: (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(icalEventUrl);
|
||||
notification.showSuccess('URL kopiert!');
|
||||
}}
|
||||
>
|
||||
<CopyIcon />
|
||||
</IconButton>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setIcalEventOpen(false)}>Schließen</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -1649,6 +1719,16 @@ export default function Kalender() {
|
||||
Neue Buchung
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* iCal subscribe */}
|
||||
<Button
|
||||
startIcon={<IosShare />}
|
||||
onClick={handleIcalBookingOpen}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
Kalender
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{bookingsLoading && (
|
||||
@@ -1696,11 +1776,11 @@ export default function Kalender() {
|
||||
<TableRow key={vehicle.id} hover>
|
||||
<TableCell>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
{vehicle.name}
|
||||
{vehicle.bezeichnung}
|
||||
</Typography>
|
||||
{vehicle.kennzeichen && (
|
||||
{vehicle.amtliches_kennzeichen && (
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{vehicle.kennzeichen}
|
||||
{vehicle.amtliches_kennzeichen}
|
||||
</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
@@ -1900,7 +1980,7 @@ export default function Kalender() {
|
||||
>
|
||||
{vehicles.map((v) => (
|
||||
<MenuItem key={v.id} value={v.id}>
|
||||
{v.name}{v.kennzeichen ? ` (${v.kennzeichen})` : ''}
|
||||
{v.bezeichnung}{v.amtliches_kennzeichen ? ` (${v.amtliches_kennzeichen})` : ''}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
@@ -2041,6 +2121,37 @@ export default function Kalender() {
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* iCal Booking subscription dialog */}
|
||||
<Dialog open={icalBookingOpen} onClose={() => setIcalBookingOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>Kalender abonnieren</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
Abonniere den Fahrzeugbuchungskalender in deiner Kalenderanwendung. Kopiere die URL und füge sie als neuen
|
||||
Kalender (per URL) in Apple Kalender, Google Kalender oder Outlook ein.
|
||||
</Typography>
|
||||
<TextField
|
||||
fullWidth
|
||||
value={icalBookingUrl}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
endAdornment: (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(icalBookingUrl);
|
||||
notification.showSuccess('URL kopiert!');
|
||||
}}
|
||||
>
|
||||
<CopyIcon />
|
||||
</IconButton>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setIcalBookingOpen(false)}>Schließen</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
@@ -113,5 +113,5 @@ export const bookingApi = {
|
||||
export function fetchVehicles(): Promise<Fahrzeug[]> {
|
||||
return api
|
||||
.get<ApiResponse<Fahrzeug[]>>('/api/vehicles')
|
||||
.then((r) => r.data.data.filter((v: Fahrzeug) => !v.archived_at));
|
||||
.then((r) => r.data.data);
|
||||
}
|
||||
|
||||
@@ -41,11 +41,10 @@ export interface FahrzeugBuchung extends FahrzeugBuchungListItem {
|
||||
|
||||
export interface Fahrzeug {
|
||||
id: string;
|
||||
name: string;
|
||||
kennzeichen?: string | null;
|
||||
typ: string;
|
||||
is_active: boolean;
|
||||
archived_at?: string | null;
|
||||
bezeichnung: string;
|
||||
kurzname: string | null;
|
||||
amtliches_kennzeichen: string | null;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface CreateBuchungInput {
|
||||
|
||||
Reference in New Issue
Block a user