refactor(webui): use central scheduling controls
This commit is contained in:
@@ -52,7 +52,7 @@ assert.match(page, /<DataGridEmptyAction/);
|
|||||||
assert.doesNotMatch(page, /EmailAddressInput|MailboxAddress|addressSuggestions|addressLookupQuery/);
|
assert.doesNotMatch(page, /EmailAddressInput|MailboxAddress|addressSuggestions|addressLookupQuery/);
|
||||||
assert.match(page, /type="email"[\s\S]*aria-label=\{I18N\.participantEmail\}/);
|
assert.match(page, /type="email"[\s\S]*aria-label=\{I18N\.participantEmail\}/);
|
||||||
assert.doesNotMatch(page, /header: I18N\.required|<table|scheduling-table|scheduling-card(?:\s|"|`)/);
|
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, /<TableActionGroup[\s\S]*label: i18nMessage\("i18n:govoplan-scheduling\.decide_on_value/);
|
||||||
assert.match(page, /submitSchedulingAvailability\(settings, selected\.id/);
|
assert.match(page, /submitSchedulingAvailability\(settings, selected\.id/);
|
||||||
assert.match(page, /option_revision: slot\.revision/);
|
assert.match(page, /option_revision: slot\.revision/);
|
||||||
assert.match(page, /getSchedulingAvailabilityResponse\(settings, selected\.id\)/);
|
assert.match(page, /getSchedulingAvailabilityResponse\(settings, selected\.id\)/);
|
||||||
|
|||||||
@@ -11,12 +11,15 @@ import {
|
|||||||
XCircle
|
XCircle
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
|
AdminIconButton,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
DataGrid,
|
DataGrid,
|
||||||
DataGridEmptyAction,
|
DataGridEmptyAction,
|
||||||
DataGridRowActions,
|
DataGridRowActions,
|
||||||
|
DateTimeField,
|
||||||
ModuleSubnav,
|
ModuleSubnav,
|
||||||
|
TableActionGroup,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
hasScope,
|
hasScope,
|
||||||
i18nMessage,
|
i18nMessage,
|
||||||
@@ -578,9 +581,7 @@ export default function SchedulingPage({ settings, auth }: { settings: ApiSettin
|
|||||||
<h1>{I18N.requests}</h1>
|
<h1>{I18N.requests}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="scheduling-page-actions">
|
<div className="scheduling-page-actions">
|
||||||
<Button type="button" className="scheduling-icon-button" onClick={() => void loadRequests()} title={I18N.refresh} aria-label={I18N.refresh}>
|
<AdminIconButton label={I18N.refresh} icon={<RefreshCw aria-hidden="true" size={16} />} onClick={() => void loadRequests()} disabled={loading} />
|
||||||
<RefreshCw aria-hidden="true" size={16} />
|
|
||||||
</Button>
|
|
||||||
{canWrite ? (
|
{canWrite ? (
|
||||||
<Button type="button" variant="primary" onClick={beginCreate}>
|
<Button type="button" variant="primary" onClick={beginCreate}>
|
||||||
<Plus aria-hidden="true" size={16} /> {I18N.add}
|
<Plus aria-hidden="true" size={16} /> {I18N.add}
|
||||||
@@ -883,33 +884,31 @@ function EditableSlots({
|
|||||||
{
|
{
|
||||||
id: "start",
|
id: "start",
|
||||||
header: I18N.start,
|
header: I18N.start,
|
||||||
minWidth: 200,
|
minWidth: 280,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
value: (slot) => slot.start_at,
|
value: (slot) => slot.start_at,
|
||||||
render: (slot, index) => (
|
render: (slot, index) => (
|
||||||
<input
|
<DateTimeField
|
||||||
className="scheduling-grid-input"
|
className="scheduling-grid-date-time"
|
||||||
required
|
required
|
||||||
type="datetime-local"
|
|
||||||
aria-label={I18N.start}
|
aria-label={I18N.start}
|
||||||
value={slot.start_at}
|
value={slot.start_at}
|
||||||
onChange={(event) => onChange(index, "start_at", event.target.value)} />
|
onChange={(value) => onChange(index, "start_at", value)} />
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "end",
|
id: "end",
|
||||||
header: I18N.end,
|
header: I18N.end,
|
||||||
minWidth: 200,
|
minWidth: 280,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
value: (slot) => slot.end_at,
|
value: (slot) => slot.end_at,
|
||||||
render: (slot, index) => (
|
render: (slot, index) => (
|
||||||
<input
|
<DateTimeField
|
||||||
className="scheduling-grid-input"
|
className="scheduling-grid-date-time"
|
||||||
required
|
required
|
||||||
type="datetime-local"
|
|
||||||
aria-label={I18N.end}
|
aria-label={I18N.end}
|
||||||
value={slot.end_at}
|
value={slot.end_at}
|
||||||
onChange={(event) => onChange(index, "end_at", event.target.value)} />
|
onChange={(value) => onChange(index, "end_at", value)} />
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1087,17 +1086,14 @@ function CandidateSlotsGrid({
|
|||||||
header: I18N.actions,
|
header: I18N.actions,
|
||||||
width: 72,
|
width: 72,
|
||||||
sticky: "end",
|
sticky: "end",
|
||||||
render: (slot) => canDecide ? (
|
render: (slot) => <TableActionGroup actions={[{
|
||||||
<Button
|
id: "decide",
|
||||||
className="scheduling-icon-button"
|
label: i18nMessage("i18n:govoplan-scheduling.decide_on_value.196409cd", { value0: slot.label }),
|
||||||
type="button"
|
icon: <Check aria-hidden="true" size={16} />,
|
||||||
title={i18nMessage("i18n:govoplan-scheduling.decide_on_value.196409cd", { value0: slot.label })}
|
applicable: canDecide,
|
||||||
aria-label={i18nMessage("i18n:govoplan-scheduling.decide_on_value.196409cd", { value0: slot.label })}
|
disabled: saving,
|
||||||
disabled={saving}
|
onClick: () => onDecide(slot)
|
||||||
onClick={() => onDecide(slot)}>
|
}]} />
|
||||||
<Check aria-hidden="true" size={16} />
|
|
||||||
</Button>
|
|
||||||
) : null
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -89,15 +89,6 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scheduling-icon-button.btn {
|
|
||||||
width: 34px;
|
|
||||||
min-width: 34px;
|
|
||||||
height: 34px;
|
|
||||||
min-height: 34px;
|
|
||||||
padding: 0;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scheduling-alert,
|
.scheduling-alert,
|
||||||
.scheduling-success {
|
.scheduling-success {
|
||||||
margin: 10px 14px 0;
|
margin: 10px 14px 0;
|
||||||
@@ -107,8 +98,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scheduling-alert {
|
.scheduling-alert {
|
||||||
border-color: var(--danger);
|
border-color: var(--danger-border-strong);
|
||||||
color: var(--danger);
|
color: var(--danger-text-strong);
|
||||||
background: var(--danger-soft);
|
background: var(--danger-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +154,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scheduling-list-item:hover {
|
.scheduling-list-item:hover {
|
||||||
background: var(--hover-bg);
|
background: var(--hover-tint-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scheduling-list-item.is-selected {
|
.scheduling-list-item.is-selected {
|
||||||
@@ -247,7 +238,7 @@
|
|||||||
|
|
||||||
.scheduling-freebusy.busy,
|
.scheduling-freebusy.busy,
|
||||||
.scheduling-freebusy.error {
|
.scheduling-freebusy.error {
|
||||||
color: var(--danger);
|
color: var(--danger-text-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scheduling-columns {
|
.scheduling-columns {
|
||||||
@@ -317,7 +308,7 @@
|
|||||||
border: var(--border-line);
|
border: var(--border-line);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background: var(--input-bg);
|
background: var(--control-bg);
|
||||||
font: inherit;
|
font: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user