Mit Terminen arbeiten
Termine sind die zentrale Datenstruktur in DayFlow. Hier erfahren Sie, wie Sie sie anlegen, aktualisieren, löschen und verwalten.
Die Schnittstelle Event
Die Bibliothek verwendet für die gesamte Datums- und Zeitverarbeitung die Temporal-API. Termine unterstützen drei Temporal-Typen:
- PlainDate: für ganztägige Termine (ohne Uhrzeit)
- PlainDateTime: für lokale Termine (Datum + Uhrzeit, ohne Zeitzone) – für die meisten Fälle empfohlen
- ZonedDateTime: für zeitzonenabhängige Termine (internationale Meetings, Flüge usw.)
import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';| Eigenschaft | Typ | Beschreibung | Erforderlich |
|---|---|---|---|
id | string | Eindeutige Kennung des Termins | Erforderlich |
title | string | Titel des Termins, wie er im Kalender erscheint | Erforderlich |
start | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime | Startdatum bzw. -zeit des Termins. PlainDate für ganztägig, PlainDateTime für lokal, ZonedDateTime bei Zeitzonenbezug | Erforderlich |
end | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime | Enddatum bzw. -zeit des Termins | Erforderlich |
description | string | Optionale Beschreibung oder Notizen zum Termin | Optional |
allDay | boolean | Gibt an, ob der Termin ganztägig ist (Standard: false) | Optional |
icon | boolean | Node | Eigenes Symbol für den Termin. true (Standard): Standardsymbol anzeigen, false: ausblenden, Node: eigenes Symbol | Optional |
calendarId | string | Verweis auf den Kalendertyp, wenn der Termin genau einem Kalender zugeordnet ist | Optional |
calendarIds | string[] | Liste der Kalender-IDs, zu denen der Termin gehört. Ist sie gesetzt, hat sie Vorrang vor calendarId. Der Termin ist sichtbar, sobald mindestens einer der genannten Kalender sichtbar ist. Die Darstellung erfolgt mit einem mehrfarbigen Diagonalstreifenmuster. | Optional |
meta | Record<string, any> | Zusätzliche eigene Metadaten (Ort, Teilnehmende, eigene Felder usw.) | Optional |
Termine anlegen
Einfaches Anlegen (empfohlen)
Verwenden Sie in den meisten Fällen die Hilfsfunktionen createEvent() und createAllDayEvent():
import { createEvent, createAllDayEvent } from '@dayflow/core';
import '@dayflow/core/dist/styles.css';
// Local timed event (no timezone complexity)
const meeting = createEvent({
id: '1',
title: 'Team Meeting',
start: new Date(2024, 9, 15, 10, 0), // October 15, 2024, 10:00 AM
end: new Date(2024, 9, 15, 11, 0), // October 15, 2024, 11:00 AM
calendarId: 'work',
});
// All-day event
const holiday = createAllDayEvent({
id: '2',
title: 'Conference',
start: new Date(2024, 9, 20),
calendarId: 'work',
});Fortgeschritten: die Temporal-API direkt verwenden
Wenn Sie mehr Kontrolle benötigen, arbeiten Sie direkt mit der Temporal-API:
import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';
// Local event with PlainDateTime (recommended)
const localEvent: Event = {
id: '1',
title: 'Team Meeting',
start: Temporal.PlainDateTime.from({
year: 2024,
month: 10,
day: 15,
hour: 10,
minute: 0,
}),
end: Temporal.PlainDateTime.from({
year: 2024,
month: 10,
day: 15,
hour: 11,
minute: 0,
}),
};
// All-day event with PlainDate
const allDayEvent: Event = {
id: '2',
title: 'Conference',
start: Temporal.PlainDate.from('2024-10-20'),
end: Temporal.PlainDate.from('2024-10-22'),
allDay: true,
};
// Timezone-aware event with ZonedDateTime
const timezoneEvent: Event = {
id: '3',
title: 'International Call',
start: Temporal.ZonedDateTime.from('2024-10-16T14:00:00[America/New_York]'),
end: Temporal.ZonedDateTime.from('2024-10-16T15:00:00[America/New_York]'),
};Termin mit Metadaten
import { createEvent } from '@dayflow/core';
const event = createEvent({
id: '3',
title: 'Client Call',
description: 'Discuss Q4 roadmap',
start: new Date(2024, 9, 16, 14, 0),
end: new Date(2024, 9, 16, 15, 0),
calendarId: 'work',
meta: {
location: 'Zoom',
attendees: ['john@example.com', 'jane@example.com'],
recurring: false,
},
});Termine verwalten
Termine hinzufügen
// Add a single event
calendar.addEvent(event);
// Add multiple events during initialization
const calendar = useCalendarApp({
views: [createMonthView()],
calendars: [
{
id: 'work',
name: 'Work',
colors: {
lineColor: '#2563eb',
eventColor: '#dbeafe',
eventSelectedColor: '#bfdbfe',
textColor: '#1e3a8a',
},
},
],
events: [event1, event2, event3],
});Termine aktualisieren
// Update an event
calendar.updateEvent('event-id', {
title: 'Updated Meeting Title',
start: new Date(2024, 9, 15, 11, 0),
end: new Date(2024, 9, 15, 12, 0),
});
// Update with pending state (for resize operations)
calendar.updateEvent('event-id', updatedEvent, true);Termine löschen
// Delete an event by ID
calendar.deleteEvent('event-id');Termine abrufen
// Get all events
const events = calendar.getEvents();
// Get current events from state
const { events } = calendar;Termin-Callbacks
DayFlow stellt Callbacks für den Lebenszyklus von Terminen bereit:
import { useCalendarApp, createMonthView, Event } from '@dayflow/react';
const calendar = useCalendarApp({
views: [createMonthView()],
events: initialEvents,
callbacks: {
onEventCreate: (event: Event) => {
console.log('New event created:', event);
// Sync with backend
api.createEvent(event);
},
onEventUpdate: (event: Event) => {
console.log('Event updated:', event);
// Sync with backend
api.updateEvent(event);
},
onEventDelete: (eventId: string) => {
console.log('Event deleted:', eventId);
// Sync with backend
api.deleteEvent(eventId);
},
onEventDoubleClick: (event: Event, e: MouseEvent) => {
console.log('Event double-clicked:', event);
// Use e.currentTarget as an anchor for a custom popover
},
},
});Zustandsverwaltung für Termine
Grundlegende Verwendung
Sie können Termine mit der nativen Zustandsverwaltung Ihres Frameworks (React, Vue, Svelte, Angular) verwalten.
import { useState } from 'react';
import {
useCalendarApp,
DayFlowCalendar,
createMonthView,
Event,
} from '@dayflow/react';
function MyCalendar() {
const [events, setEvents] = useState<Event[]>([]);
const calendar = useCalendarApp({
views: [createMonthView()],
events,
callbacks: {
onEventCreate: (event: Event) => {
setEvents(prev => [...prev, event]);
},
onEventUpdate: (event: Event) => {
setEvents(prev => prev.map(e => (e.id === event.id ? event : e)));
},
onEventDelete: (eventId: string) => {
setEvents(prev => prev.filter(e => e.id !== eventId));
},
},
});
return <DayFlowCalendar calendar={calendar} />;
}<template>
<DayFlowCalendar :calendar="calendar" />
</template>
<script setup>
import { ref } from 'vue';
import { DayFlowCalendar, useCalendarApp } from '@dayflow/vue';
import { createMonthView } from '@dayflow/core';
const events = ref([]);
const calendar = useCalendarApp({
views: [createMonthView()],
events,
callbacks: {
onEventCreate: (event) => {
events.value = [...events.value, event];
},
onEventUpdate: (event) => {
events.value = events.value.map(e => (e.id === event.id ? event : e));
},
onEventDelete: (eventId) => {
events.value = events.value.filter(e => e.id !== eventId);
},
},
});
</script>import { Component } from '@angular/core';
import { createMonthView, Event } from '@dayflow/core';
import { DayFlowCalendarModule } from '@dayflow/angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [DayFlowCalendarModule],
template: `
<dayflow-calendar [calendar]="calendar"></dayflow-calendar>
`
})
export class AppComponent {
events: Event[] = [];
calendar = {
views: [createMonthView()],
events: this.events,
callbacks: {
onEventCreate: (event: Event) => {
this.events = [...this.events, event];
this.calendar.events = this.events;
},
onEventUpdate: (event: Event) => {
this.events = this.events.map(e => (e.id === event.id ? event : e));
this.calendar.events = this.events;
},
onEventDelete: (eventId: string) => {
this.events = this.events.filter(e => e.id !== eventId);
this.calendar.events = this.events;
},
},
};
}<script>
import { DayFlowCalendar, useCalendarApp } from '@dayflow/svelte';
import { createMonthView } from '@dayflow/core';
let events = $state([]);
const calendar = useCalendarApp({
views: [createMonthView()],
events,
callbacks: {
onEventCreate: (event) => {
events = [...events, event];
},
onEventUpdate: (event) => {
events = events.map(e => (e.id === event.id ? event : e));
},
onEventDelete: (eventId) => {
events = events.filter(e => e.id !== eventId);
},
},
});
</script>
<DayFlowCalendar {calendar} />Mit dem Backend synchronisieren
import { useCalendarApp, createMonthView, Event } from '@dayflow/react';
const calendar = useCalendarApp({
views: [createMonthView()],
events,
callbacks: {
onEventCreate: async (event: Event) => {
try {
// Create event in backend
const savedEvent = await api.createEvent(event);
// Update local state with backend response
setEvents(prev => [...prev, savedEvent]);
} catch (error) {
console.error('Failed to create event:', error);
// Optionally remove the optimistic update
}
},
onEventUpdate: async (event: Event) => {
try {
await api.updateEvent(event);
setEvents(prev => prev.map(e => (e.id === event.id ? event : e)));
} catch (error) {
console.error('Failed to update event:', error);
}
},
onEventDelete: async (eventId: string) => {
try {
await api.deleteEvent(eventId);
setEvents(prev => prev.filter(e => e.id !== eventId));
} catch (error) {
console.error('Failed to delete event:', error);
}
},
},
});Termine über Kalendertypen gestalten
Gestalten Sie das Aussehen von Terminen, indem Sie sie über calendarId verschiedenen Kalendertypen zuordnen. Jeder Typ kann eigene Farben und Stile haben:
import { createEvent } from '@dayflow/core';
// Work event
const workEvent = createEvent({
id: '1',
title: 'Design Review',
start: new Date(2024, 9, 15, 14, 0),
end: new Date(2024, 9, 15, 15, 0),
calendarId: 'work', // Links to work calendar styling
});
// Personal event
const personalEvent = createEvent({
id: '2',
title: 'Dentist Appointment',
start: new Date(2024, 9, 16, 10, 0),
end: new Date(2024, 9, 16, 11, 0),
calendarId: 'personal', // Links to personal calendar styling
});
// Configure calendar types with colors
const calendars = [
{
id: 'work',
name: 'Work',
colors: {
eventColor: '#3b82f6',
eventSelectedColor: '#2563eb',
lineColor: '#3b82f6',
textColor: '#ffffff',
},
isVisible: true,
},
{
id: 'personal',
name: 'Personal',
colors: {
eventColor: '#10b981',
eventSelectedColor: '#059669',
lineColor: '#10b981',
textColor: '#ffffff',
},
isVisible: true,
},
];
const calendar = useCalendarApp({
views: [createMonthView()],
events: [workEvent, personalEvent],
calendars,
});Termine in mehreren Kalendern
Funktionsweise
- Sind beide gesetzt, hat
calendarIdsVorrang vorcalendarId. - Sichtbarkeit: Der Termin bleibt sichtbar, solange mindestens einer der genannten Kalender sichtbar ist.
- Visuelle Kennzeichnung: Termine mit mehreren Kalendern erhalten einen Hintergrund mit diagonalen Streifen (ein Streifen je Kalenderfarbe) und links eine Leiste mit mehrfarbigem Verlauf – so heben sie sich klar von Terminen mit nur einem Kalender ab.
- Ausgewählter Zustand: Es wird wieder die einfarbige Farbe des Hauptkalenders verwendet.
Beispiel
import { createEvent, useCalendarApp, createWeekView } from '@dayflow/react';
import { Temporal } from 'temporal-polyfill';
// This event belongs to both "team" and "marketing" calendars
const sharedEvent = createEvent({
id: 'shared-1',
title: 'Company All-Hands',
start: new Date(2024, 9, 15, 10, 0),
end: new Date(2024, 9, 15, 11, 30),
calendarIds: ['team', 'marketing'], // multi-calendar
});
// All-day event spanning three calendars
const crossCalendarDay = {
id: 'shared-2',
title: 'Team Offsite',
start: Temporal.PlainDate.from('2024-10-20'),
end: Temporal.PlainDate.from('2024-10-22'),
allDay: true,
calendarIds: ['team', 'personal', 'travel'],
};
const calendars = [
{
id: 'team',
name: 'Team',
colors: {
eventColor: '#3b82f6',
lineColor: '#3b82f6',
eventSelectedColor: '#2563eb',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'marketing',
name: 'Marketing',
colors: {
eventColor: '#f59e0b',
lineColor: '#f59e0b',
eventSelectedColor: '#d97706',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'personal',
name: 'Personal',
colors: {
eventColor: '#10b981',
lineColor: '#10b981',
eventSelectedColor: '#059669',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'travel',
name: 'Travel',
colors: {
eventColor: '#8b5cf6',
lineColor: '#8b5cf6',
eventSelectedColor: '#7c3aed',
textColor: '#fff',
},
isVisible: true,
},
];
const calendar = useCalendarApp({
views: [createWeekView()],
events: [sharedEvent, crossCalendarDay],
calendars,
});<template>
<DayFlowCalendar :calendar="calendar" />
</template>
<script setup>
import { DayFlowCalendar, useCalendarApp } from '@dayflow/vue';
import { createEvent, createWeekView } from '@dayflow/core';
import { Temporal } from 'temporal-polyfill';
const sharedEvent = createEvent({
id: 'shared-1',
title: 'Company All-Hands',
start: new Date(2024, 9, 15, 10, 0),
end: new Date(2024, 9, 15, 11, 30),
calendarIds: ['team', 'marketing'],
});
const crossCalendarDay = {
id: 'shared-2',
title: 'Team Offsite',
start: Temporal.PlainDate.from('2024-10-20'),
end: Temporal.PlainDate.from('2024-10-22'),
allDay: true,
calendarIds: ['team', 'personal', 'travel'],
};
const calendars = [
{
id: 'team',
name: 'Team',
colors: {
eventColor: '#3b82f6',
lineColor: '#3b82f6',
eventSelectedColor: '#2563eb',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'marketing',
name: 'Marketing',
colors: {
eventColor: '#f59e0b',
lineColor: '#f59e0b',
eventSelectedColor: '#d97706',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'personal',
name: 'Personal',
colors: {
eventColor: '#10b981',
lineColor: '#10b981',
eventSelectedColor: '#059669',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'travel',
name: 'Travel',
colors: {
eventColor: '#8b5cf6',
lineColor: '#8b5cf6',
eventSelectedColor: '#7c3aed',
textColor: '#fff',
},
isVisible: true,
},
];
const calendar = useCalendarApp({
views: [createWeekView()],
events: [sharedEvent, crossCalendarDay],
calendars,
});
</script>import { Component } from '@angular/core';
import { createEvent, createWeekView } from '@dayflow/core';
import { DayFlowCalendarModule } from '@dayflow/angular';
import { Temporal } from 'temporal-polyfill';
const sharedEvent = createEvent({
id: 'shared-1',
title: 'Company All-Hands',
start: new Date(2024, 9, 15, 10, 0),
end: new Date(2024, 9, 15, 11, 30),
calendarIds: ['team', 'marketing'],
});
const crossCalendarDay = {
id: 'shared-2',
title: 'Team Offsite',
start: Temporal.PlainDate.from('2024-10-20'),
end: Temporal.PlainDate.from('2024-10-22'),
allDay: true,
calendarIds: ['team', 'personal', 'travel'],
};
@Component({
selector: 'app-root',
standalone: true,
imports: [DayFlowCalendarModule],
template: `
<dayflow-calendar [calendar]="calendar"></dayflow-calendar>
`
})
export class AppComponent {
calendar = {
views: [createWeekView()],
events: [sharedEvent, crossCalendarDay],
calendars: [
{
id: 'team',
name: 'Team',
colors: {
eventColor: '#3b82f6',
lineColor: '#3b82f6',
eventSelectedColor: '#2563eb',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'marketing',
name: 'Marketing',
colors: {
eventColor: '#f59e0b',
lineColor: '#f59e0b',
eventSelectedColor: '#d97706',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'personal',
name: 'Personal',
colors: {
eventColor: '#10b981',
lineColor: '#10b981',
eventSelectedColor: '#059669',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'travel',
name: 'Travel',
colors: {
eventColor: '#8b5cf6',
lineColor: '#8b5cf6',
eventSelectedColor: '#7c3aed',
textColor: '#fff',
},
isVisible: true,
},
],
};
}<script>
import { DayFlowCalendar, useCalendarApp } from '@dayflow/svelte';
import { createEvent, createWeekView } from '@dayflow/core';
import { Temporal } from 'temporal-polyfill';
const sharedEvent = createEvent({
id: 'shared-1',
title: 'Company All-Hands',
start: new Date(2024, 9, 15, 10, 0),
end: new Date(2024, 9, 15, 11, 30),
calendarIds: ['team', 'marketing'],
});
const crossCalendarDay = {
id: 'shared-2',
title: 'Team Offsite',
start: Temporal.PlainDate.from('2024-10-20'),
end: Temporal.PlainDate.from('2024-10-22'),
allDay: true,
calendarIds: ['team', 'personal', 'travel'],
};
const calendars = [
{
id: 'team',
name: 'Team',
colors: {
eventColor: '#3b82f6',
lineColor: '#3b82f6',
eventSelectedColor: '#2563eb',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'marketing',
name: 'Marketing',
colors: {
eventColor: '#f59e0b',
lineColor: '#f59e0b',
eventSelectedColor: '#d97706',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'personal',
name: 'Personal',
colors: {
eventColor: '#10b981',
lineColor: '#10b981',
eventSelectedColor: '#059669',
textColor: '#fff',
},
isVisible: true,
},
{
id: 'travel',
name: 'Travel',
colors: {
eventColor: '#8b5cf6',
lineColor: '#8b5cf6',
eventSelectedColor: '#7c3aed',
textColor: '#fff',
},
isVisible: true,
},
];
const calendar = useCalendarApp({
views: [createWeekView()],
events: [sharedEvent, crossCalendarDay],
calendars,
});
</script>
<DayFlowCalendar {calendar} />Mehrtägige Termine
Ein Termin kann sich über mehrere Tage erstrecken:
import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';
// Conference spanning 3 days (all-day event)
const multiDayEvent: Event = {
id: '1',
title: 'Tech Conference 2024',
start: Temporal.PlainDate.from('2024-10-20'),
end: Temporal.PlainDate.from('2024-10-22'),
allDay: true,
calendarId: 'conferences',
};
// Meeting spanning across midnight (timed event)
const crossMidnightEvent: Event = {
id: '2',
title: 'Night Shift',
start: Temporal.ZonedDateTime.from('2024-10-15T22:00:00[America/New_York]'), // 10 PM
end: Temporal.ZonedDateTime.from('2024-10-16T06:00:00[America/New_York]'), // 6 AM next day
calendarId: 'shifts',
};Termin-Metadaten
Zusätzliche Informationen legen Sie im Feld meta ab:
import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';
const event: Event = {
id: '1',
title: 'Project Kickoff',
start: Temporal.ZonedDateTime.from('2024-10-15T10:00:00[America/New_York]'),
end: Temporal.ZonedDateTime.from('2024-10-15T11:00:00[America/New_York]'),
meta: {
// Meeting details
location: 'Conference Room A',
meetingUrl: 'https://zoom.us/j/123456',
// Attendees
organizer: 'john@example.com',
attendees: ['jane@example.com', 'bob@example.com'],
// Custom fields
project: 'Project X',
priority: 'high',
tags: ['planning', 'kickoff'],
// Recurring info
recurring: true,
recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO',
// Any other data
customField: 'custom value',
},
};Wiederkehrende Termine
Das eingebaute Termindetail-Panel und der Dialog lesen und schreiben Wiederholungseinstellungen über event.meta. Setzen Sie recurring: true und geben Sie eine recurrenceRule im RRULE-Stil an:
| Regelteil | Bedeutung | Beispiel |
|---|---|---|
FREQ | Erforderliche Häufigkeit: DAILY, WEEKLY, MONTHLY oder YEARLY | FREQ=WEEKLY |
INTERVAL | Wiederholung alle N Häufigkeitseinheiten; Standardwert ist 1 | INTERVAL=2 |
BYDAY | Wochentage für eine benutzerdefinierte wöchentliche Regel | BYDAY=MO,WE,FR |
UNTIL | Einschließlich geltendes Enddatum im Format YYYYMMDD | UNTIL=20261231 |
COUNT | Maximale Anzahl der Vorkommen einschließlich des Serienbeginns | COUNT=10 |
const recurringEvent: Event = {
id: 'team-sync',
title: 'Team-Abstimmung',
start: Temporal.PlainDateTime.from('2026-08-26T09:30'),
end: Temporal.PlainDateTime.from('2026-08-26T10:00'),
meta: {
recurring: true,
recurrenceRule: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE;UNTIL=20261231',
},
};Die Monatsansicht erweitert diese Regeln innerhalb ihres sichtbaren Bereichs. Generierte Vorkommen dienen nur der Darstellung und behalten die Verknüpfung zum Haupttermin der Serie, daher wirken sich Bearbeitungen und Löschungen derzeit auf die gesamte Serie aus. Nicht unterstützte RRULE-Bestandteile bleiben erhalten, soweit der eingebaute Editor dies ermöglicht; für die Erweiterung in der Monatsansicht werden jedoch nur die oben aufgeführten Bestandteile berücksichtigt. Ausnahmen für einzelne Vorkommen werden noch nicht unterstützt.
Bewährte Vorgehensweisen
- Immer eindeutige IDs vergeben – verwenden Sie UUIDs oder die IDs aus Ihrer Datenbank (als String)
- Hilfsfunktionen nutzen –
createEvent()ist der manuellen Temporal-Konstruktion vorzuziehen (deckt 90 % der Fälle ab) - Den passenden Typ wählen:
PlainDatefür ganztägige Termine (Geburtstage, Feiertage)PlainDateTimefür lokale Termine (Meetings, Verabredungen) – empfohlener StandardZonedDateTimenur, wenn die Zeitzone eine Rolle spielt (internationale Calls, Flüge)
- Zeitwerte prüfen – die Hilfsfunktionen validieren Stunde (0–23) und Minute (0–59) automatisch
- Kalendertypen für die Gestaltung nutzen – über
calendarIdkategorisieren und gestalten Sie Termine einheitlich;calendarIdsverwenden Sie, wenn ein Termin zu mehreren Kalendern gehört - Das Feld
metanutzen – eigene Daten ablegen, ohne die Event-Schnittstelle zu ändern - Termindaten prüfen – bei Terminen mit Uhrzeit muss der Beginn vor dem Ende liegen
- Aktualisierungen optimieren – fassen Sie Änderungen nach Möglichkeit zusammen
Typreferenz
Ausführliche Informationen zur Datums- und Zeitverarbeitung finden Sie hier:
- Event-Schnittstelle:
/src/types/event.ts
Verwandte Dokumentation
- Ansichten – die Kalenderansichten verstehen
- Plugins – Terminverwaltung mit Plugins
- Erste Schritte – Beispiele für die grundlegende Verwendung