feat(scheduling): compose request editor from core UI

This commit is contained in:
2026-07-20 18:10:48 +02:00
parent 887e064ae9
commit ec538f524d
4 changed files with 702 additions and 675 deletions

View File

@@ -11,6 +11,8 @@ assert.match(page, /usePlatformUiCapability<CalendarPickerUiCapability>\("calend
assert.match(page, /hasScope\(auth, "calendar:calendar:read"\)/); assert.match(page, /hasScope\(auth, "calendar:calendar:read"\)/);
assert.match(page, /Boolean\(calendarPickerCapability\) && canReadCalendars && canReadAvailability && canWriteCalendarEvent/); assert.match(page, /Boolean\(calendarPickerCapability\) && canReadCalendars && canReadAvailability && canWriteCalendarEvent/);
assert.doesNotMatch(page, /@govoplan\/calendar-webui|govoplan-calendar\/webui/); assert.doesNotMatch(page, /@govoplan\/calendar-webui|govoplan-calendar\/webui/);
assert.match(page, /Card,[\s\S]*DataGrid,[\s\S]*DataGridEmptyAction,[\s\S]*DataGridRowActions,[\s\S]*ModuleSubnav,[\s\S]*ToggleSwitch,[\s\S]*from "@govoplan\/core-webui"/);
assert.doesNotMatch(page, /@govoplan\/core-webui\/src\//);
assert.match(page, /className="scheduling-full-editor"/); assert.match(page, /className="scheduling-full-editor"/);
assert.match(page, /className="scheduling-page-actions"/); assert.match(page, /className="scheduling-page-actions"/);
@@ -19,10 +21,35 @@ const editorActions = page.slice(
page.indexOf('</div>', 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.ok(editorActions.indexOf("I18N.discard") < editorActions.indexOf("I18N.save"));
assert.match(page, /<Plus aria-hidden="true" size=\{16\} \/> \{I18N\.addRequest\}/); assert.match(editorActions, /form="scheduling-create-form"/);
const createBranchStart = page.indexOf(" if (creating) {");
const createReturnStart = page.indexOf("\n return (", createBranchStart);
const browseBranchStart = page.indexOf("\n return (", createReturnStart + 1);
const createBranch = page.slice(createBranchStart, browseBranchStart);
const browseBranch = page.slice(browseBranchStart);
assert.match(createBranch, /<ModuleSubnav/);
assert.match(createBranch, /className="scheduling-create-subnav"/);
assert.doesNotMatch(browseBranch, /scheduling-sidebar|<aside/);
assert.match(browseBranch, /<h1>\{I18N\.requests\}<\/h1>/);
assert.match(browseBranch, /<Plus aria-hidden="true" size=\{16\} \/> \{I18N\.add\}/);
assert.match(page, /title=\{I18N\.myRequests\}/); assert.match(page, /title=\{I18N\.myRequests\}/);
assert.match(page, /title=\{I18N\.invitedRequests\}/); assert.match(page, /title=\{I18N\.invitedRequests\}/);
assert.match(page, /className="scheduling-table-actions"/); assert.match(createBranch, /<Card title=\{I18N\.basicInformation\}>/);
assert.match(createBranch, /<Card title=\{I18N\.calendarIntegration\}>/);
assert.match(page, /<Card title=\{I18N\.candidateSlots\}>/);
assert.match(page, /<Card title=\{I18N\.participants\}>/);
assert.match(createBranch, /<ToggleSwitch[\s\S]*checked=\{calendarEnabled\}/);
assert.match(page, /id="scheduling-create-candidate-slots-grid"/);
assert.match(page, /id="scheduling-create-participants-grid"/);
assert.match(page, /id="scheduling-candidate-slots-grid"/);
assert.match(page, /id="scheduling-participants-grid"/);
assert.match(page, /<DataGridRowActions/);
assert.match(page, /<DataGridEmptyAction/);
assert.doesNotMatch(page, /EmailAddressInput|MailboxAddress|addressSuggestions|addressLookupQuery/);
assert.match(page, /type="email"[\s\S]*aria-label=\{I18N\.participantEmail\}/);
assert.doesNotMatch(page, /header: I18N\.required|<table|scheduling-table|scheduling-card(?:\s|"|`)/);
assert.match(page, /aria-label=\{i18nMessage\("i18n:govoplan-scheduling\.decide_on_value/); assert.match(page, /aria-label=\{i18nMessage\("i18n:govoplan-scheduling\.decide_on_value/);
assert.match(page, /submitSchedulingAvailability\(settings, selected\.id/); assert.match(page, /submitSchedulingAvailability\(settings, selected\.id/);
assert.doesNotMatch(page, /<p>\{selected\.calendar_id\}<\/p>/); assert.doesNotMatch(page, /<p>\{selected\.calendar_id\}<\/p>/);

File diff suppressed because it is too large Load Diff

View File

@@ -88,6 +88,7 @@ export const generatedTranslations = {
"i18n:govoplan-scheduling.sent.35f49dcf": "Sent", "i18n:govoplan-scheduling.sent.35f49dcf": "Sent",
"i18n:govoplan-scheduling.skipped.5a000ad7": "Skipped", "i18n:govoplan-scheduling.skipped.5a000ad7": "Skipped",
"i18n:govoplan-scheduling.start.952f3754": "Start", "i18n:govoplan-scheduling.start.952f3754": "Start",
"i18n:govoplan-scheduling.status.bae7d5be": "Status",
"i18n:govoplan-scheduling.submit_response.a5f0c053": "Submit response", "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.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_final_event_is_created_only_for_the_selected_slot.e3621252": "The final event is created only for the selected slot.",
@@ -192,6 +193,7 @@ export const generatedTranslations = {
"i18n:govoplan-scheduling.sent.35f49dcf": "Gesendet", "i18n:govoplan-scheduling.sent.35f49dcf": "Gesendet",
"i18n:govoplan-scheduling.skipped.5a000ad7": "Übersprungen", "i18n:govoplan-scheduling.skipped.5a000ad7": "Übersprungen",
"i18n:govoplan-scheduling.start.952f3754": "Beginn", "i18n:govoplan-scheduling.start.952f3754": "Beginn",
"i18n:govoplan-scheduling.status.bae7d5be": "Status",
"i18n:govoplan-scheduling.submit_response.a5f0c053": "Antwort senden", "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.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_final_event_is_created_only_for_the_selected_slot.e3621252": "Der endgültige Kalendereintrag wird nur für den ausgewählten Termin erstellt.",

View File

@@ -13,64 +13,61 @@
box-sizing: border-box; box-sizing: border-box;
} }
.scheduling-shell {
display: grid;
grid-template-columns: minmax(300px, 360px) minmax(0, 1fr);
width: 100%;
height: 100%;
min-height: 0;
overflow: hidden;
border: var(--border-line);
background: var(--panel);
}
.scheduling-sidebar,
.scheduling-workspace, .scheduling-workspace,
.scheduling-full-editor { .scheduling-full-editor {
width: 100%;
height: 100%;
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
border: var(--border-line);
background: var(--bg);
} }
.scheduling-sidebar {
border-right: var(--border-line);
background: var(--panel-soft);
}
.scheduling-sidebar-bar,
.scheduling-topbar,
.scheduling-page-header { .scheduling-page-header {
min-height: 58px; min-height: 58px;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 14px; padding: 10px 14px;
border-bottom: var(--border-line); border-bottom: var(--border-line);
background: var(--panel-header); background: var(--panel-header);
} }
.scheduling-sidebar-bar, .scheduling-page-title,
.scheduling-topbar,
.scheduling-page-header,
.scheduling-section-heading,
.scheduling-sidebar-actions,
.scheduling-page-actions, .scheduling-page-actions,
.scheduling-card-actions, .scheduling-card-actions,
.scheduling-actions, .scheduling-actions,
.scheduling-title-line,
.scheduling-page-title,
.scheduling-status-row { .scheduling-status-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
} }
.scheduling-sidebar-bar, .scheduling-page-title {
.scheduling-topbar, min-width: 0;
.scheduling-page-header, }
.scheduling-section-heading {
justify-content: space-between; .scheduling-page-title > div {
min-width: 0;
}
.scheduling-page-title h1 {
margin: 0;
color: var(--text-strong);
font-size: 19px;
}
.scheduling-page-title p {
margin: 3px 0 0;
color: var(--muted);
font-size: 12px;
} }
.scheduling-sidebar-actions,
.scheduling-page-actions, .scheduling-page-actions,
.scheduling-card-actions, .scheduling-card-actions,
.scheduling-actions { .scheduling-actions {
@@ -78,12 +75,14 @@
justify-content: flex-end; justify-content: flex-end;
} }
.scheduling-sidebar-actions .btn, .scheduling-page-actions {
margin-left: auto;
}
.scheduling-page-actions .btn, .scheduling-page-actions .btn,
.scheduling-card-actions .btn, .scheduling-card-actions .btn,
.scheduling-actions .btn, .scheduling-actions .btn,
.scheduling-section-heading .btn, .scheduling-response-card + .btn {
.scheduling-response-card .btn {
min-height: 34px; min-height: 34px;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -99,25 +98,51 @@
justify-content: center; justify-content: center;
} }
.scheduling-list { .scheduling-alert,
.scheduling-success {
margin: 10px 14px 0;
padding: 9px 11px;
border: var(--border-line);
border-radius: 7px;
}
.scheduling-alert {
border-color: var(--danger);
color: var(--danger);
background: var(--danger-soft);
}
.scheduling-success {
border-color: var(--success);
color: var(--success);
background: color-mix(in srgb, var(--success) 10%, var(--panel));
}
.scheduling-detail,
.scheduling-editor-scroll {
min-width: 0;
min-height: 0; min-height: 0;
overflow: auto; overflow: auto;
padding: 8px; padding: 14px;
} }
.scheduling-list-group + .scheduling-list-group { .scheduling-detail,
margin-top: 14px; .scheduling-editor-scroll {
padding-top: 12px; display: grid;
border-top: var(--border-line); align-content: start;
gap: 12px;
} }
.scheduling-list-group h2 { .scheduling-request-groups {
margin: 0 8px 6px; display: grid;
color: var(--muted); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
font-size: 12px; gap: 12px;
font-weight: 800; }
letter-spacing: .025em;
text-transform: uppercase; .scheduling-list-group {
min-height: 52px;
max-height: 270px;
overflow: auto;
} }
.scheduling-list-item { .scheduling-list-item {
@@ -146,14 +171,8 @@
background: color-mix(in srgb, var(--accent) 10%, var(--panel)); 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 { .scheduling-list-item > span {
min-width: 0;
display: grid; display: grid;
gap: 3px; gap: 3px;
} }
@@ -168,9 +187,6 @@
} }
.scheduling-list-item small, .scheduling-list-item small,
.scheduling-title-line small,
.scheduling-page-title p,
.scheduling-section-heading p,
.scheduling-note, .scheduling-note,
.scheduling-list-empty, .scheduling-list-empty,
.scheduling-compact-row small { .scheduling-compact-row small {
@@ -179,95 +195,15 @@
} }
.scheduling-list-status { .scheduling-list-status {
max-width: 110px; max-width: 120px;
padding: 3px 7px; padding: 3px 7px;
border-radius: 999px; border-radius: 999px;
background: var(--panel); background: var(--panel-soft);
font-weight: 700; font-weight: 700;
} }
.scheduling-list-empty { .scheduling-list-empty {
margin: 3px 8px 8px; margin: 3px 0 8px;
}
.scheduling-workspace {
overflow: hidden;
}
.scheduling-title-line {
min-width: 0;
}
.scheduling-title-line > div {
display: grid;
gap: 2px;
}
.scheduling-alert,
.scheduling-success {
margin: 10px 14px 0;
padding: 9px 11px;
border: var(--border-line);
border-radius: 7px;
}
.scheduling-alert {
border-color: var(--danger);
color: var(--danger);
background: var(--danger-soft);
}
.scheduling-success {
border-color: var(--success);
color: var(--success);
background: color-mix(in srgb, var(--success) 10%, var(--panel));
}
.scheduling-detail,
.scheduling-editor-scroll {
min-height: 0;
overflow: auto;
padding: 14px;
}
.scheduling-detail {
display: grid;
align-content: start;
gap: 12px;
}
.scheduling-card {
width: 100%;
min-width: 0;
padding: 14px;
border: var(--border-line);
border-radius: 8px;
background: var(--panel);
}
.scheduling-card h2,
.scheduling-page-header h1,
.scheduling-section-heading h2 {
margin: 0;
color: var(--text-strong);
}
.scheduling-card h2,
.scheduling-section-heading h2 {
font-size: 15px;
}
.scheduling-page-header h1 {
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 {
@@ -288,65 +224,10 @@
background: color-mix(in srgb, var(--accent) 18%, transparent); background: color-mix(in srgb, var(--accent) 18%, transparent);
} }
.scheduling-table-card { .scheduling-slot-title {
padding: 0; min-width: 0;
overflow: hidden; display: grid;
} justify-items: start;
.scheduling-table-card > .scheduling-section-heading {
min-height: 52px;
padding: 9px 14px;
border-bottom: var(--border-line);
background: var(--panel-header);
}
.scheduling-table-scroll {
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 {
@@ -393,6 +274,10 @@
gap: 12px; gap: 12px;
} }
.scheduling-response-card > p {
margin: 0;
}
.scheduling-response-grid { .scheduling-response-grid {
display: grid; display: grid;
gap: 6px; gap: 6px;
@@ -425,7 +310,7 @@
.scheduling-response-grid select, .scheduling-response-grid select,
.scheduling-field input, .scheduling-field input,
.scheduling-field textarea, .scheduling-field textarea,
.scheduling-edit-table input:not([type="checkbox"]) { .scheduling-grid-input {
width: 100%; width: 100%;
min-height: 36px; min-height: 36px;
padding: 7px 9px; padding: 7px 9px;
@@ -436,13 +321,8 @@
font: inherit; font: inherit;
} }
.scheduling-action-help summary { .scheduling-action-help {
cursor: pointer; margin: 0;
font-weight: 700;
}
.scheduling-action-help ul {
margin-bottom: 0;
} }
.scheduling-empty { .scheduling-empty {
@@ -455,26 +335,22 @@
background: var(--panel); background: var(--panel);
} }
.scheduling-full-editor { .scheduling-editor-layout {
width: 100%; min-width: 0;
height: 100%; min-height: 0;
overflow: hidden; flex: 1 1 auto;
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; display: grid;
align-content: start; grid-template-columns: 198px minmax(0, 1fr);
gap: 12px; overflow: hidden;
}
.scheduling-create-subnav {
min-height: 0;
}
.scheduling-create-section {
min-width: 0;
scroll-margin-top: 14px;
} }
.scheduling-form-grid { .scheduling-form-grid {
@@ -483,7 +359,6 @@
gap: 12px; gap: 12px;
} }
.scheduling-form-grid h2,
.scheduling-field-wide { .scheduling-field-wide {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
@@ -499,35 +374,19 @@
font-weight: 800; font-weight: 800;
} }
.scheduling-toggle {
display: inline-flex;
align-items: center;
gap: 7px;
font-weight: 700;
}
.scheduling-capability-note { .scheduling-capability-note {
margin: 10px 0 0; margin: 10px 0 0;
color: var(--muted); color: var(--muted);
font-size: 12px; font-size: 12px;
} }
.scheduling-edit-table input[type="checkbox"] { .scheduling-selected-calendar {
width: 18px; max-width: 520px;
height: 18px;
} }
@media (max-width: 1050px) { @media (max-width: 900px) {
.scheduling-shell { .scheduling-editor-layout {
grid-template-columns: minmax(260px, 310px) minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
}
.scheduling-sidebar-bar {
align-items: flex-start;
}
.scheduling-sidebar-actions {
align-items: flex-end;
} }
} }
@@ -538,25 +397,12 @@
overflow: auto; overflow: auto;
} }
.scheduling-shell {
height: auto;
grid-template-columns: minmax(0, 1fr);
overflow: visible;
}
.scheduling-sidebar {
max-height: 46vh;
border-right: 0;
border-bottom: var(--border-line);
}
.scheduling-workspace, .scheduling-workspace,
.scheduling-full-editor { .scheduling-full-editor,
overflow: visible; .scheduling-editor-layout,
}
.scheduling-detail, .scheduling-detail,
.scheduling-editor-scroll { .scheduling-editor-scroll {
height: auto;
overflow: visible; overflow: visible;
} }
@@ -565,9 +411,7 @@
grid-template-columns: minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
} }
.scheduling-page-header, .scheduling-page-header {
.scheduling-topbar,
.scheduling-section-heading {
align-items: flex-start; align-items: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
} }