feat(scheduling): add a task-focused poll workspace

This commit is contained in:
2026-07-20 17:34:36 +02:00
parent fc418e63ac
commit 887e064ae9
6 changed files with 1658 additions and 494 deletions

View File

@@ -13,6 +13,10 @@
}, },
"./styles/scheduling.css": "./src/styles/scheduling.css" "./styles/scheduling.css": "./src/styles/scheduling.css"
}, },
"scripts": {
"test:view-model": "node --experimental-strip-types --test tests/scheduling-view-model.test.ts",
"test:ui-structure": "node scripts/test-scheduling-page-structure.mjs"
},
"peerDependencies": { "peerDependencies": {
"@govoplan/core-webui": "^0.1.8", "@govoplan/core-webui": "^0.1.8",
"lucide-react": "^1.23.0", "lucide-react": "^1.23.0",

View File

@@ -0,0 +1,34 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
const pagePath = fileURLToPath(new URL("../src/features/scheduling/SchedulingPage.tsx", import.meta.url));
const apiPath = fileURLToPath(new URL("../src/api/scheduling.ts", import.meta.url));
const page = readFileSync(pagePath, "utf8");
const api = readFileSync(apiPath, "utf8");
assert.match(page, /usePlatformUiCapability<CalendarPickerUiCapability>\("calendar\.picker"\)/);
assert.match(page, /hasScope\(auth, "calendar:calendar:read"\)/);
assert.match(page, /Boolean\(calendarPickerCapability\) && canReadCalendars && canReadAvailability && canWriteCalendarEvent/);
assert.doesNotMatch(page, /@govoplan\/calendar-webui|govoplan-calendar\/webui/);
assert.match(page, /className="scheduling-full-editor"/);
assert.match(page, /className="scheduling-page-actions"/);
const editorActions = page.slice(
page.indexOf('<div className="scheduling-page-actions">'),
page.indexOf('</div>', page.indexOf('<div className="scheduling-page-actions">'))
);
assert.ok(editorActions.indexOf("I18N.discard") < editorActions.indexOf("I18N.save"));
assert.match(page, /<Plus aria-hidden="true" size=\{16\} \/> \{I18N\.addRequest\}/);
assert.match(page, /title=\{I18N\.myRequests\}/);
assert.match(page, /title=\{I18N\.invitedRequests\}/);
assert.match(page, /className="scheduling-table-actions"/);
assert.match(page, /aria-label=\{i18nMessage\("i18n:govoplan-scheduling\.decide_on_value/);
assert.match(page, /submitSchedulingAvailability\(settings, selected\.id/);
assert.doesNotMatch(page, /<p>\{selected\.calendar_id\}<\/p>/);
assert.match(page, /className="scheduling-selected-calendar"/);
assert.match(page, /showPlanningCalendarActions/);
assert.match(page, /title=\{!canReadAvailability \? I18N\.requiresAvailabilityRead/);
assert.match(api, /\/api\/v1\/scheduling\/requests\/\$\{requestId\}\/responses/);
console.log("Scheduling page structure satisfies the focused list/create/respond contract.");

View File

@@ -115,6 +115,15 @@ export type SchedulingDecisionPayload = {
handoff_to_calendar?: boolean | null; handoff_to_calendar?: boolean | null;
}; };
export type SchedulingAvailabilityValue = "available" | "maybe" | "unavailable";
export type SchedulingAvailabilityPayload = {
answers: Array<{
slot_id: string;
value: SchedulingAvailabilityValue;
}>;
};
export type SchedulingCalendarActionResponse = { export type SchedulingCalendarActionResponse = {
request: SchedulingRequest; request: SchedulingRequest;
created_event_ids: string[]; created_event_ids: string[];
@@ -197,6 +206,18 @@ export function createSchedulingRequest(settings: ApiSettings, payload: Scheduli
return apiFetch<SchedulingRequest>(settings, "/api/v1/scheduling/requests", json(payload)); return apiFetch<SchedulingRequest>(settings, "/api/v1/scheduling/requests", json(payload));
} }
export function submitSchedulingAvailability(
settings: ApiSettings,
requestId: string,
payload: SchedulingAvailabilityPayload
): Promise<SchedulingStatusResponse> {
return apiFetch<SchedulingStatusResponse>(
settings,
`/api/v1/scheduling/requests/${requestId}/responses`,
json(payload)
);
}
export function openSchedulingRequest(settings: ApiSettings, requestId: string): Promise<SchedulingStatusResponse> { export function openSchedulingRequest(settings: ApiSettings, requestId: string): Promise<SchedulingStatusResponse> {
return apiFetch<SchedulingStatusResponse>(settings, `/api/v1/scheduling/requests/${requestId}/open`, json({})); return apiFetch<SchedulingStatusResponse>(settings, `/api/v1/scheduling/requests/${requestId}/open`, json({}));
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,210 @@
export const generatedTranslations = { export const generatedTranslations = {
en: {}, en: {
de: {} "i18n:govoplan-scheduling.add_participant.6cff957d": "Add participant",
"i18n:govoplan-scheduling.add_scheduling_request.3c71be15": "Add scheduling request",
"i18n:govoplan-scheduling.add_slot.9fcff3ed": "Add slot",
"i18n:govoplan-scheduling.answered.e0aafffa": "Answered",
"i18n:govoplan-scheduling.available.7c62a142": "Available",
"i18n:govoplan-scheduling.awaiting_response.ee646ea9": "Awaiting response",
"i18n:govoplan-scheduling.basic_information.d8bc7383": "Basic information",
"i18n:govoplan-scheduling.busy.592a9d16": "Busy",
"i18n:govoplan-scheduling.calendar.adab5090": "Calendar",
"i18n:govoplan-scheduling.calendar_coordination.291ab00a": "Calendar coordination",
"i18n:govoplan-scheduling.configured_calendar.e2e8ebd5": "Configured calendar",
"i18n:govoplan-scheduling.calendar_integration.181ad18b": "Calendar integration",
"i18n:govoplan-scheduling.calendar_integration_requires_the_calendar_module_plus_c.f892cb1e": "Calendar integration requires the Calendar module plus calendar-read, availability-read, and event-write access.",
"i18n:govoplan-scheduling.candidate_availability.9541c4b5": "Candidate availability",
"i18n:govoplan-scheduling.candidate_slots.c414946b": "Candidate slots",
"i18n:govoplan-scheduling.check_free_busy.e9700e00": "Check free/busy",
"i18n:govoplan-scheduling.choose_availability.ac95b8f6": "Choose availability",
"i18n:govoplan-scheduling.choose_availability_for_at_least_one_candidate_slot.28d2111f": "Choose availability for at least one candidate slot.",
"i18n:govoplan-scheduling.close_poll.a6a18916": "Close poll",
"i18n:govoplan-scheduling.close_stops_accepting_new_availability_responses.a1d57519": "Close stops accepting new availability responses.",
"i18n:govoplan-scheduling.closed.88d86b77": "Closed",
"i18n:govoplan-scheduling.create_and_open_scheduling_request.1dbc9345": "Create and open scheduling request",
"i18n:govoplan-scheduling.create_calendar_event.0b87cfcf": "Create calendar event",
"i18n:govoplan-scheduling.create_tentative_holds.51c4744e": "Create tentative holds",
"i18n:govoplan-scheduling.cancellation.319aaae4": "Cancellation",
"i18n:govoplan-scheduling.decision.7f59a1f1": "Decision",
"i18n:govoplan-scheduling.declined.ff59b80f": "Declined",
"i18n:govoplan-scheduling.decide_on_value.196409cd": "Decide on {value0}",
"i18n:govoplan-scheduling.description.55f8ebc8": "Description",
"i18n:govoplan-scheduling.determined.9f23293d": "Determined",
"i18n:govoplan-scheduling.discard.36fff63c": "Discard",
"i18n:govoplan-scheduling.discard_this_unsaved_scheduling_request.4a956be2": "Discard this unsaved scheduling request?",
"i18n:govoplan-scheduling.draft.23d33e22": "Draft",
"i18n:govoplan-scheduling.end.a2bb9d34": "End",
"i18n:govoplan-scheduling.error.7f2f6a15": "Error",
"i18n:govoplan-scheduling.every_candidate_slot_must_end_after_it_starts.47836010": "Every candidate slot must end after it starts.",
"i18n:govoplan-scheduling.failed.09fef5d8": "Failed",
"i18n:govoplan-scheduling.free.75f52718": "Free",
"i18n:govoplan-scheduling.free_busy_checks_each_candidate_slot_against_the_selecte.50f0371a": "Free/busy checks each candidate slot against the selected calendar.",
"i18n:govoplan-scheduling.loading_scheduling_requests.f42be95d": "Loading scheduling requests…",
"i18n:govoplan-scheduling.invitation.6306ef74": "Invitation",
"i18n:govoplan-scheduling.invited.53469df1": "Invited",
"i18n:govoplan-scheduling.location.d219c681": "Location",
"i18n:govoplan-scheduling.managed_scheduling_requests.7a45972f": "Managed scheduling requests",
"i18n:govoplan-scheduling.maybe.56dd8d0b": "Maybe",
"i18n:govoplan-scheduling.my_scheduling_requests.d28ef235": "My scheduling requests",
"i18n:govoplan-scheduling.name.709a2322": "Name",
"i18n:govoplan-scheduling.new_scheduling_request.2080f675": "New scheduling request",
"i18n:govoplan-scheduling.no_notifications_have_been_created.c8d43ca3": "No notifications have been created.",
"i18n:govoplan-scheduling.no_participants.73a89101": "No participants",
"i18n:govoplan-scheduling.no_requests_in_this_group.a63c1b40": "No requests in this group",
"i18n:govoplan-scheduling.no_scheduling_request_selected.ac940664": "No scheduling request selected",
"i18n:govoplan-scheduling.notifications.753a22b2": "Notifications",
"i18n:govoplan-scheduling.open.cf9b7706": "Open",
"i18n:govoplan-scheduling.open_poll.2beac9a7": "Open poll",
"i18n:govoplan-scheduling.open_starts_collecting_availability_responses.c8ec34e5": "Open starts collecting availability responses.",
"i18n:govoplan-scheduling.option_value.3f643dc0": "Option {value0}",
"i18n:govoplan-scheduling.other_scheduling_requests.5cb6eb30": "Other scheduling requests",
"i18n:govoplan-scheduling.participant_email.2cadfd9e": "Participant email",
"i18n:govoplan-scheduling.participants.cd56e083": "Participants",
"i18n:govoplan-scheduling.past.405c12fb": "Past",
"i18n:govoplan-scheduling.pending.96f608c1": "Pending",
"i18n:govoplan-scheduling.queued.6a599877": "Queued",
"i18n:govoplan-scheduling.refresh_requests.0a3ed7a1": "Refresh requests",
"i18n:govoplan-scheduling.reminder_creates_a_notification_job_for_every_active_par.7ec68797": "Reminder creates a notification job for every active participant.",
"i18n:govoplan-scheduling.remove.e963907d": "Remove",
"i18n:govoplan-scheduling.remove_participant_value.e55f2b70": "Remove participant {value0}",
"i18n:govoplan-scheduling.remove_slot_value.3adc7576": "Remove slot {value0}",
"i18n:govoplan-scheduling.removed.b5e77c5c": "Removed",
"i18n:govoplan-scheduling.reminder.b87a1929": "Reminder",
"i18n:govoplan-scheduling.request_failed.9fcda32c": "Request failed",
"i18n:govoplan-scheduling.required.eed6bfb4": "Required",
"i18n:govoplan-scheduling.requires_calendar_availability_read_access.b48ed91b": "Requires Calendar availability-read access.",
"i18n:govoplan-scheduling.requires_calendar_event_write_access.887b0763": "Requires Calendar event-write access.",
"i18n:govoplan-scheduling.responses_may_be_updated_while_this_request_remains_open.4faecbbe": "Responses may be updated while this request remains open.",
"i18n:govoplan-scheduling.responded.4f218211": "Responded",
"i18n:govoplan-scheduling.save.efc007a3": "Save",
"i18n:govoplan-scheduling.saving.56a2285c": "Saving…",
"i18n:govoplan-scheduling.scheduling_requests.b3c12f4d": "Scheduling requests",
"i18n:govoplan-scheduling.scheduling_request_title_is_required.a27338be": "Scheduling request title is required.",
"i18n:govoplan-scheduling.scheduling_requests_for_me.1d521aba": "Scheduling requests for me",
"i18n:govoplan-scheduling.select_a_calendar.f6af95bb": "Select a calendar",
"i18n:govoplan-scheduling.select_a_calendar_or_turn_off_calendar_integration.cc3652a9": "Select a calendar or turn off Calendar integration.",
"i18n:govoplan-scheduling.send_reminder.cf5eb3bf": "Send reminder",
"i18n:govoplan-scheduling.sending.ceafde86": "Sending",
"i18n:govoplan-scheduling.sent.35f49dcf": "Sent",
"i18n:govoplan-scheduling.skipped.5a000ad7": "Skipped",
"i18n:govoplan-scheduling.start.952f3754": "Start",
"i18n:govoplan-scheduling.submit_response.a5f0c053": "Submit response",
"i18n:govoplan-scheduling.tentative_holds_create_one_provisional_calendar_event_pe.ff3f1884": "Tentative holds create one provisional calendar event per candidate slot.",
"i18n:govoplan-scheduling.the_final_event_is_created_only_for_the_selected_slot.e3621252": "The final event is created only for the selected slot.",
"i18n:govoplan-scheduling.the_response_replaces_your_previous_availability_choices.74c16d53": "The response replaces your previous availability choices.",
"i18n:govoplan-scheduling.title.768e0c1c": "Title",
"i18n:govoplan-scheduling.unavailable.2c9c1f79": "Unavailable",
"i18n:govoplan-scheduling.unchecked.1b927dec": "Unchecked",
"i18n:govoplan-scheduling.update_response.346233cf": "Update response",
"i18n:govoplan-scheduling.use_a_calendar_for_availability_checks_tentative_holds_a.20ccc1fa": "Use a calendar for availability checks, tentative holds, and the final event.",
"i18n:govoplan-scheduling.value_participants.e776b092": "{value0} participants",
"i18n:govoplan-scheduling.value_responses.ba17af9a": "{value0} responses",
"i18n:govoplan-scheduling.what_do_these_actions_do.9a9aee0e": "What do these actions do?",
"i18n:govoplan-scheduling.you_can_respond_here_or_use_the_invitation_link_you_rece.1a25fd53": "You can respond here or use the invitation link you received.",
"i18n:govoplan-scheduling.your_response_has_been_recorded.b855088d": "Your response has been recorded."
},
de: {
"i18n:govoplan-scheduling.add_participant.6cff957d": "Teilnehmende Person hinzufügen",
"i18n:govoplan-scheduling.add_scheduling_request.3c71be15": "Terminanfrage hinzufügen",
"i18n:govoplan-scheduling.add_slot.9fcff3ed": "Terminvorschlag hinzufügen",
"i18n:govoplan-scheduling.answered.e0aafffa": "Beantwortet",
"i18n:govoplan-scheduling.available.7c62a142": "Verfügbar",
"i18n:govoplan-scheduling.awaiting_response.ee646ea9": "Antwort ausstehend",
"i18n:govoplan-scheduling.basic_information.d8bc7383": "Grunddaten",
"i18n:govoplan-scheduling.busy.592a9d16": "Belegt",
"i18n:govoplan-scheduling.calendar.adab5090": "Kalender",
"i18n:govoplan-scheduling.calendar_coordination.291ab00a": "Kalenderabgleich",
"i18n:govoplan-scheduling.configured_calendar.e2e8ebd5": "Ausgewählter Kalender",
"i18n:govoplan-scheduling.calendar_integration.181ad18b": "Kalenderintegration",
"i18n:govoplan-scheduling.calendar_integration_requires_the_calendar_module_plus_c.f892cb1e": "Die Kalenderintegration benötigt das Kalendermodul sowie Leserechte für Kalender und Verfügbarkeiten und Schreibrechte für Termine.",
"i18n:govoplan-scheduling.candidate_availability.9541c4b5": "Verfügbarkeit zu den Vorschlägen",
"i18n:govoplan-scheduling.candidate_slots.c414946b": "Terminvorschläge",
"i18n:govoplan-scheduling.check_free_busy.e9700e00": "Frei/Belegt prüfen",
"i18n:govoplan-scheduling.choose_availability.ac95b8f6": "Verfügbarkeit auswählen",
"i18n:govoplan-scheduling.choose_availability_for_at_least_one_candidate_slot.28d2111f": "Wählen Sie für mindestens einen Terminvorschlag eine Verfügbarkeit aus.",
"i18n:govoplan-scheduling.close_poll.a6a18916": "Abstimmung schließen",
"i18n:govoplan-scheduling.close_stops_accepting_new_availability_responses.a1d57519": "Schließen beendet die Annahme neuer Verfügbarkeitsantworten.",
"i18n:govoplan-scheduling.closed.88d86b77": "Geschlossen",
"i18n:govoplan-scheduling.create_and_open_scheduling_request.1dbc9345": "Terminanfrage erstellen und öffnen",
"i18n:govoplan-scheduling.create_calendar_event.0b87cfcf": "Kalendertermin erstellen",
"i18n:govoplan-scheduling.create_tentative_holds.51c4744e": "Vorläufige Reservierungen erstellen",
"i18n:govoplan-scheduling.cancellation.319aaae4": "Stornierung",
"i18n:govoplan-scheduling.decision.7f59a1f1": "Entscheidung",
"i18n:govoplan-scheduling.declined.ff59b80f": "Abgelehnt",
"i18n:govoplan-scheduling.decide_on_value.196409cd": "{value0} als Termin festlegen",
"i18n:govoplan-scheduling.description.55f8ebc8": "Beschreibung",
"i18n:govoplan-scheduling.determined.9f23293d": "Festgelegt",
"i18n:govoplan-scheduling.discard.36fff63c": "Verwerfen",
"i18n:govoplan-scheduling.discard_this_unsaved_scheduling_request.4a956be2": "Diese ungespeicherte Terminanfrage verwerfen?",
"i18n:govoplan-scheduling.draft.23d33e22": "Entwurf",
"i18n:govoplan-scheduling.end.a2bb9d34": "Ende",
"i18n:govoplan-scheduling.error.7f2f6a15": "Fehler",
"i18n:govoplan-scheduling.every_candidate_slot_must_end_after_it_starts.47836010": "Jeder Terminvorschlag muss nach seinem Beginn enden.",
"i18n:govoplan-scheduling.failed.09fef5d8": "Fehlgeschlagen",
"i18n:govoplan-scheduling.free.75f52718": "Frei",
"i18n:govoplan-scheduling.free_busy_checks_each_candidate_slot_against_the_selecte.50f0371a": "Frei/Belegt prüft jeden Terminvorschlag gegen den ausgewählten Kalender.",
"i18n:govoplan-scheduling.loading_scheduling_requests.f42be95d": "Terminanfragen werden geladen …",
"i18n:govoplan-scheduling.invitation.6306ef74": "Einladung",
"i18n:govoplan-scheduling.invited.53469df1": "Eingeladen",
"i18n:govoplan-scheduling.location.d219c681": "Ort",
"i18n:govoplan-scheduling.managed_scheduling_requests.7a45972f": "Verwaltete Terminanfragen",
"i18n:govoplan-scheduling.maybe.56dd8d0b": "Vielleicht",
"i18n:govoplan-scheduling.my_scheduling_requests.d28ef235": "Meine Terminanfragen",
"i18n:govoplan-scheduling.name.709a2322": "Name",
"i18n:govoplan-scheduling.new_scheduling_request.2080f675": "Neue Terminanfrage",
"i18n:govoplan-scheduling.no_notifications_have_been_created.c8d43ca3": "Es wurden noch keine Benachrichtigungen erstellt.",
"i18n:govoplan-scheduling.no_participants.73a89101": "Keine Teilnehmenden",
"i18n:govoplan-scheduling.no_requests_in_this_group.a63c1b40": "Keine Anfragen in dieser Gruppe",
"i18n:govoplan-scheduling.no_scheduling_request_selected.ac940664": "Keine Terminanfrage ausgewählt",
"i18n:govoplan-scheduling.notifications.753a22b2": "Benachrichtigungen",
"i18n:govoplan-scheduling.open.cf9b7706": "Offen",
"i18n:govoplan-scheduling.open_poll.2beac9a7": "Abstimmung öffnen",
"i18n:govoplan-scheduling.open_starts_collecting_availability_responses.c8ec34e5": "Öffnen startet die Erfassung von Verfügbarkeitsantworten.",
"i18n:govoplan-scheduling.option_value.3f643dc0": "Vorschlag {value0}",
"i18n:govoplan-scheduling.other_scheduling_requests.5cb6eb30": "Andere Terminanfragen",
"i18n:govoplan-scheduling.participant_email.2cadfd9e": "E-Mail der teilnehmenden Person",
"i18n:govoplan-scheduling.participants.cd56e083": "Teilnehmende",
"i18n:govoplan-scheduling.past.405c12fb": "Vergangen",
"i18n:govoplan-scheduling.pending.96f608c1": "Ausstehend",
"i18n:govoplan-scheduling.queued.6a599877": "Eingereiht",
"i18n:govoplan-scheduling.refresh_requests.0a3ed7a1": "Anfragen aktualisieren",
"i18n:govoplan-scheduling.reminder_creates_a_notification_job_for_every_active_par.7ec68797": "Erinnern erstellt für jede aktive teilnehmende Person einen Benachrichtigungsauftrag.",
"i18n:govoplan-scheduling.remove.e963907d": "Entfernen",
"i18n:govoplan-scheduling.remove_participant_value.e55f2b70": "Teilnehmende Person {value0} entfernen",
"i18n:govoplan-scheduling.remove_slot_value.3adc7576": "Terminvorschlag {value0} entfernen",
"i18n:govoplan-scheduling.removed.b5e77c5c": "Entfernt",
"i18n:govoplan-scheduling.reminder.b87a1929": "Erinnerung",
"i18n:govoplan-scheduling.request_failed.9fcda32c": "Anfrage fehlgeschlagen",
"i18n:govoplan-scheduling.required.eed6bfb4": "Erforderlich",
"i18n:govoplan-scheduling.requires_calendar_availability_read_access.b48ed91b": "Erfordert Leserechte für Kalenderverfügbarkeiten.",
"i18n:govoplan-scheduling.requires_calendar_event_write_access.887b0763": "Erfordert Schreibrechte für Kalendertermine.",
"i18n:govoplan-scheduling.responses_may_be_updated_while_this_request_remains_open.4faecbbe": "Antworten können aktualisiert werden, solange diese Anfrage offen ist.",
"i18n:govoplan-scheduling.responded.4f218211": "Geantwortet",
"i18n:govoplan-scheduling.save.efc007a3": "Speichern",
"i18n:govoplan-scheduling.saving.56a2285c": "Wird gespeichert …",
"i18n:govoplan-scheduling.scheduling_requests.b3c12f4d": "Terminanfragen",
"i18n:govoplan-scheduling.scheduling_request_title_is_required.a27338be": "Für die Terminanfrage ist ein Titel erforderlich.",
"i18n:govoplan-scheduling.scheduling_requests_for_me.1d521aba": "Terminanfragen an mich",
"i18n:govoplan-scheduling.select_a_calendar.f6af95bb": "Kalender auswählen",
"i18n:govoplan-scheduling.select_a_calendar_or_turn_off_calendar_integration.cc3652a9": "Wählen Sie einen Kalender aus oder schalten Sie die Kalenderintegration aus.",
"i18n:govoplan-scheduling.send_reminder.cf5eb3bf": "Erinnerung senden",
"i18n:govoplan-scheduling.sending.ceafde86": "Wird gesendet",
"i18n:govoplan-scheduling.sent.35f49dcf": "Gesendet",
"i18n:govoplan-scheduling.skipped.5a000ad7": "Übersprungen",
"i18n:govoplan-scheduling.start.952f3754": "Beginn",
"i18n:govoplan-scheduling.submit_response.a5f0c053": "Antwort senden",
"i18n:govoplan-scheduling.tentative_holds_create_one_provisional_calendar_event_pe.ff3f1884": "Vorläufige Reservierungen erstellen je Terminvorschlag einen provisorischen Kalendereintrag.",
"i18n:govoplan-scheduling.the_final_event_is_created_only_for_the_selected_slot.e3621252": "Der endgültige Kalendereintrag wird nur für den ausgewählten Termin erstellt.",
"i18n:govoplan-scheduling.the_response_replaces_your_previous_availability_choices.74c16d53": "Die Antwort ersetzt Ihre vorherige Verfügbarkeitsauswahl.",
"i18n:govoplan-scheduling.title.768e0c1c": "Titel",
"i18n:govoplan-scheduling.unavailable.2c9c1f79": "Nicht verfügbar",
"i18n:govoplan-scheduling.unchecked.1b927dec": "Nicht geprüft",
"i18n:govoplan-scheduling.update_response.346233cf": "Antwort aktualisieren",
"i18n:govoplan-scheduling.use_a_calendar_for_availability_checks_tentative_holds_a.20ccc1fa": "Einen Kalender für Verfügbarkeitsprüfungen, vorläufige Reservierungen und den endgültigen Termin verwenden.",
"i18n:govoplan-scheduling.value_participants.e776b092": "{value0} Teilnehmende",
"i18n:govoplan-scheduling.value_responses.ba17af9a": "{value0} Antworten",
"i18n:govoplan-scheduling.what_do_these_actions_do.9a9aee0e": "Was bewirken diese Aktionen?",
"i18n:govoplan-scheduling.you_can_respond_here_or_use_the_invitation_link_you_rece.1a25fd53": "Sie können hier oder über den erhaltenen Einladungslink antworten.",
"i18n:govoplan-scheduling.your_response_has_been_recorded.b855088d": "Ihre Antwort wurde gespeichert."
}
}; };

View File

@@ -2,7 +2,6 @@
box-sizing: border-box; box-sizing: border-box;
height: calc(100vh - 115px); height: calc(100vh - 115px);
min-height: 0; min-height: 0;
padding: 0;
overflow: hidden; overflow: hidden;
color: var(--text); color: var(--text);
background: var(--bg); background: var(--bg);
@@ -15,48 +14,87 @@
} }
.scheduling-shell { .scheduling-shell {
min-height: 0;
height: 100%;
display: grid; display: grid;
grid-template-columns: minmax(250px, 310px) minmax(0, 1fr); grid-template-columns: minmax(300px, 360px) minmax(0, 1fr);
width: 100%;
height: 100%;
min-height: 0;
overflow: hidden;
border: var(--border-line); border: var(--border-line);
background: var(--panel); background: var(--panel);
overflow: hidden;
} }
.scheduling-sidebar { .scheduling-sidebar,
.scheduling-workspace,
.scheduling-full-editor {
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
}
.scheduling-sidebar {
border-right: var(--border-line); border-right: var(--border-line);
background: var(--panel-soft); background: var(--panel-soft);
} }
.scheduling-sidebar-bar, .scheduling-sidebar-bar,
.scheduling-topbar, .scheduling-topbar,
.scheduling-panel-heading, .scheduling-page-header {
.scheduling-mini-heading, min-height: 58px;
.scheduling-status-row, padding: 10px 14px;
border-bottom: var(--border-line);
background: var(--panel-header);
}
.scheduling-sidebar-bar,
.scheduling-topbar,
.scheduling-page-header,
.scheduling-section-heading,
.scheduling-sidebar-actions,
.scheduling-page-actions,
.scheduling-card-actions,
.scheduling-actions, .scheduling-actions,
.scheduling-title-line { .scheduling-title-line,
.scheduling-page-title,
.scheduling-status-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
} }
.scheduling-sidebar-bar { .scheduling-sidebar-bar,
.scheduling-topbar,
.scheduling-page-header,
.scheduling-section-heading {
justify-content: space-between; justify-content: space-between;
min-height: 54px; }
padding: 10px 12px;
border-bottom: var(--border-line); .scheduling-sidebar-actions,
background: var(--panel-header); .scheduling-page-actions,
.scheduling-card-actions,
.scheduling-actions {
flex-wrap: wrap;
justify-content: flex-end;
}
.scheduling-sidebar-actions .btn,
.scheduling-page-actions .btn,
.scheduling-card-actions .btn,
.scheduling-actions .btn,
.scheduling-section-heading .btn,
.scheduling-response-card .btn {
min-height: 34px;
display: inline-flex;
align-items: center;
gap: 6px;
} }
.scheduling-icon-button.btn { .scheduling-icon-button.btn {
width: 34px; width: 34px;
min-width: 34px; min-width: 34px;
height: 34px; height: 34px;
min-height: 34px;
padding: 0; padding: 0;
justify-content: center; justify-content: center;
} }
@@ -67,30 +105,61 @@
padding: 8px; padding: 8px;
} }
.scheduling-list-group + .scheduling-list-group {
margin-top: 14px;
padding-top: 12px;
border-top: var(--border-line);
}
.scheduling-list-group h2 {
margin: 0 8px 6px;
color: var(--muted);
font-size: 12px;
font-weight: 800;
letter-spacing: .025em;
text-transform: uppercase;
}
.scheduling-list-item { .scheduling-list-item {
width: 100%; width: 100%;
min-height: 50px;
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr) auto;
gap: 8px; gap: 10px;
align-items: center; align-items: center;
min-height: 40px;
margin: 0 0 4px; margin: 0 0 4px;
padding: 8px 9px; padding: 8px 10px;
border: 0; border: 1px solid transparent;
border-radius: 6px; border-radius: 7px;
color: var(--text); color: var(--text);
background: transparent; background: transparent;
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
} }
.scheduling-list-item:hover, .scheduling-list-item:hover {
.scheduling-list-item.is-selected {
background: var(--hover-bg); background: var(--hover-bg);
} }
.scheduling-list-item span, .scheduling-list-item.is-selected {
.scheduling-slot-row strong, border-color: color-mix(in srgb, var(--accent) 45%, transparent);
background: color-mix(in srgb, var(--accent) 10%, var(--panel));
}
.scheduling-list-item > span,
.scheduling-title-line > div,
.scheduling-page-title > div,
.scheduling-section-heading > div {
min-width: 0;
}
.scheduling-list-item > span {
display: grid;
gap: 3px;
}
.scheduling-list-item strong,
.scheduling-list-item small,
.scheduling-compact-row span { .scheduling-compact-row span {
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
@@ -99,121 +168,110 @@
} }
.scheduling-list-item small, .scheduling-list-item small,
.scheduling-slot-row small, .scheduling-title-line small,
.scheduling-compact-row small, .scheduling-page-title p,
.scheduling-note { .scheduling-section-heading p,
.scheduling-note,
.scheduling-list-empty,
.scheduling-compact-row small {
color: var(--muted); color: var(--muted);
font-size: 12px; font-size: 12px;
} }
.scheduling-list-status {
max-width: 110px;
padding: 3px 7px;
border-radius: 999px;
background: var(--panel);
font-weight: 700;
}
.scheduling-list-empty {
margin: 3px 8px 8px;
}
.scheduling-workspace { .scheduling-workspace {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden; overflow: hidden;
} }
.scheduling-topbar {
justify-content: space-between;
min-height: 54px;
padding: 9px 12px;
border-bottom: var(--border-line);
background: var(--panel-header);
}
.scheduling-title-line { .scheduling-title-line {
min-width: 180px; min-width: 0;
} }
.scheduling-actions { .scheduling-title-line > div {
flex-wrap: wrap; display: grid;
justify-content: flex-end; gap: 2px;
} }
.scheduling-actions .btn, .scheduling-alert,
.scheduling-create-panel .btn, .scheduling-success {
.scheduling-slot-row .btn { margin: 10px 14px 0;
min-height: 32px; padding: 9px 11px;
display: inline-flex; border: var(--border-line);
align-items: center; border-radius: 7px;
gap: 6px;
} }
.scheduling-alert { .scheduling-alert {
margin: 8px 12px 0;
padding: 8px 10px;
border: var(--border-line);
border-color: var(--danger); border-color: var(--danger);
border-radius: 6px;
color: var(--danger); color: var(--danger);
background: var(--danger-soft); background: var(--danger-soft);
} }
.scheduling-content { .scheduling-success {
min-height: 0; border-color: var(--success);
display: grid; color: var(--success);
grid-template-columns: minmax(280px, 360px) minmax(0, 1fr); background: color-mix(in srgb, var(--success) 10%, var(--panel));
gap: 0;
overflow: hidden;
} }
.scheduling-create-panel, .scheduling-detail,
.scheduling-detail { .scheduling-editor-scroll {
min-height: 0; min-height: 0;
overflow: auto; overflow: auto;
padding: 12px; padding: 14px;
} }
.scheduling-create-panel { .scheduling-detail {
display: grid; display: grid;
align-content: start; align-content: start;
gap: 10px; gap: 12px;
border-right: var(--border-line);
background: var(--panel-soft);
} }
.scheduling-create-panel label, .scheduling-card {
.scheduling-form-section {
display: grid;
gap: 5px;
}
.scheduling-create-panel label span,
.scheduling-mini-heading span {
color: var(--muted);
font-size: 12px;
font-weight: 700;
}
.scheduling-create-panel input {
width: 100%; width: 100%;
min-height: 34px; min-width: 0;
padding: 6px 8px; padding: 14px;
border: var(--border-line); border: var(--border-line);
border-radius: 6px; border-radius: 8px;
color: var(--text); background: var(--panel);
background: var(--input-bg);
} }
.scheduling-checkbox { .scheduling-card h2,
grid-template-columns: auto minmax(0, 1fr); .scheduling-page-header h1,
align-items: center; .scheduling-section-heading h2 {
margin: 0;
color: var(--text-strong);
} }
.scheduling-slot-draft, .scheduling-card h2,
.scheduling-participant-draft { .scheduling-section-heading h2 {
display: grid; font-size: 15px;
gap: 6px;
} }
.scheduling-slot-draft { .scheduling-page-header h1 {
grid-template-columns: minmax(0, 1fr); font-size: 19px;
}
.scheduling-page-title p,
.scheduling-section-heading p {
margin: 3px 0 0;
}
.scheduling-summary-card > p:last-child {
margin-bottom: 0;
} }
.scheduling-status-row { .scheduling-status-row {
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: 10px;
} }
.scheduling-status { .scheduling-status {
@@ -222,7 +280,7 @@
color: var(--text-strong); color: var(--text-strong);
background: var(--panel-soft); background: var(--panel-soft);
font-size: 12px; font-size: 12px;
font-weight: 700; font-weight: 800;
} }
.scheduling-status-collecting, .scheduling-status-collecting,
@@ -230,33 +288,76 @@
background: color-mix(in srgb, var(--accent) 18%, transparent); background: color-mix(in srgb, var(--accent) 18%, transparent);
} }
.scheduling-slot-table { .scheduling-table-card {
display: grid; padding: 0;
gap: 6px; overflow: hidden;
} }
.scheduling-slot-row { .scheduling-table-card > .scheduling-section-heading {
display: grid; min-height: 52px;
grid-template-columns: minmax(180px, 1fr) auto auto auto auto auto; padding: 9px 14px;
gap: 8px;
align-items: center;
min-height: 46px;
padding: 8px;
border-bottom: var(--border-line); border-bottom: var(--border-line);
background: var(--panel-header);
} }
.scheduling-slot-row:hover { .scheduling-table-scroll {
background: var(--hover-bg); width: 100%;
overflow: auto;
}
.scheduling-table {
width: 100%;
border-collapse: collapse;
table-layout: auto;
}
.scheduling-table th,
.scheduling-table td {
padding: 9px 10px;
border-bottom: var(--border-line);
text-align: left;
vertical-align: middle;
}
.scheduling-table tr:last-child td {
border-bottom: 0;
}
.scheduling-table th {
color: var(--muted);
background: var(--panel-soft);
font-size: 12px;
font-weight: 800;
white-space: nowrap;
}
.scheduling-table td {
font-size: 13px;
}
.scheduling-table td:first-child {
min-width: 190px;
}
.scheduling-table td > strong {
display: block;
}
.scheduling-table-actions {
width: 1%;
text-align: right !important;
white-space: nowrap;
} }
.scheduling-freebusy { .scheduling-freebusy {
min-width: 74px; display: inline-block;
padding: 3px 7px; margin-top: 3px;
border-radius: 6px; padding: 2px 6px;
text-align: center; border-radius: 5px;
font-size: 12px; color: var(--muted);
font-weight: 700;
background: var(--panel-soft); background: var(--panel-soft);
font-size: 11px;
font-weight: 700;
} }
.scheduling-freebusy.free { .scheduling-freebusy.free {
@@ -272,37 +373,218 @@
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px; gap: 12px;
margin-top: 16px;
} }
.scheduling-compact-row { .scheduling-compact-row {
min-height: 36px;
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr) auto;
gap: 8px; gap: 8px;
min-height: 34px;
align-items: center; align-items: center;
border-bottom: var(--border-line); border-bottom: var(--border-line);
} }
.scheduling-empty { .scheduling-compact-row:last-child {
padding: 20px; border-bottom: 0;
}
.scheduling-response-card {
display: grid;
gap: 12px;
}
.scheduling-response-grid {
display: grid;
gap: 6px;
}
.scheduling-response-grid label {
min-width: 0;
display: grid;
grid-template-columns: minmax(220px, 1fr) minmax(160px, 240px);
gap: 12px;
align-items: center;
padding: 8px 0;
border-bottom: var(--border-line);
}
.scheduling-response-grid label:last-child {
border-bottom: 0;
}
.scheduling-response-grid label > span {
min-width: 0;
display: grid;
gap: 3px;
}
.scheduling-response-grid small {
color: var(--muted); color: var(--muted);
} }
@media (max-width: 980px) { .scheduling-response-grid select,
.scheduling-shell, .scheduling-field input,
.scheduling-content, .scheduling-field textarea,
.scheduling-columns { .scheduling-edit-table input:not([type="checkbox"]) {
grid-template-columns: minmax(0, 1fr); width: 100%;
min-height: 36px;
padding: 7px 9px;
border: var(--border-line);
border-radius: 6px;
color: var(--text);
background: var(--input-bg);
font: inherit;
}
.scheduling-action-help summary {
cursor: pointer;
font-weight: 700;
}
.scheduling-action-help ul {
margin-bottom: 0;
}
.scheduling-empty {
padding: 28px;
color: var(--muted);
text-align: center;
}
.scheduling-editor-page {
background: var(--panel);
}
.scheduling-full-editor {
width: 100%;
height: 100%;
overflow: hidden;
border: var(--border-line);
background: var(--bg);
}
.scheduling-page-header {
flex: 0 0 auto;
}
.scheduling-page-actions {
margin-left: auto;
}
.scheduling-editor-scroll {
display: grid;
align-content: start;
gap: 12px;
}
.scheduling-form-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.scheduling-form-grid h2,
.scheduling-field-wide {
grid-column: 1 / -1;
}
.scheduling-field {
display: grid;
gap: 5px;
}
.scheduling-field > span {
color: var(--muted);
font-size: 12px;
font-weight: 800;
}
.scheduling-toggle {
display: inline-flex;
align-items: center;
gap: 7px;
font-weight: 700;
}
.scheduling-capability-note {
margin: 10px 0 0;
color: var(--muted);
font-size: 12px;
}
.scheduling-edit-table input[type="checkbox"] {
width: 18px;
height: 18px;
}
@media (max-width: 1050px) {
.scheduling-shell {
grid-template-columns: minmax(260px, 310px) minmax(0, 1fr);
} }
.scheduling-sidebar, .scheduling-sidebar-bar {
.scheduling-create-panel { align-items: flex-start;
}
.scheduling-sidebar-actions {
align-items: flex-end;
}
}
@media (max-width: 820px) {
.scheduling-page {
height: auto;
min-height: calc(100vh - 115px);
overflow: auto;
}
.scheduling-shell {
height: auto;
grid-template-columns: minmax(0, 1fr);
overflow: visible;
}
.scheduling-sidebar {
max-height: 46vh;
border-right: 0; border-right: 0;
border-bottom: var(--border-line); border-bottom: var(--border-line);
} }
.scheduling-slot-row { .scheduling-workspace,
grid-template-columns: minmax(0, 1fr) auto; .scheduling-full-editor {
overflow: visible;
}
.scheduling-detail,
.scheduling-editor-scroll {
overflow: visible;
}
.scheduling-columns,
.scheduling-form-grid {
grid-template-columns: minmax(0, 1fr);
}
.scheduling-page-header,
.scheduling-topbar,
.scheduling-section-heading {
align-items: flex-start;
flex-wrap: wrap;
}
.scheduling-page-actions,
.scheduling-card-actions,
.scheduling-actions {
width: 100%;
}
.scheduling-page-actions .btn:last-child,
.scheduling-card-actions .btn:last-child,
.scheduling-actions .btn:last-child {
margin-left: auto;
}
.scheduling-response-grid label {
grid-template-columns: minmax(0, 1fr);
} }
} }