@byline/host-tanstack-start 4.14.0 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin-shell/chrome/admin-layout_module.css +1 -1
- package/dist/admin-shell/chrome/admin-menu-drawer.d.ts +3 -1
- package/dist/admin-shell/chrome/admin-menu-drawer.js +14 -2
- package/dist/admin-shell/chrome/admin-menu-drawer_module.css +7 -6
- package/dist/admin-shell/chrome/dashboard.js +2 -2
- package/dist/admin-shell/collections/edit.js +142 -2
- package/dist/admin-shell/scheduled-publications/list.d.ts +4 -0
- package/dist/admin-shell/scheduled-publications/list.js +295 -0
- package/dist/admin-shell/scheduled-publications/list.module.js +25 -0
- package/dist/admin-shell/scheduled-publications/list_module.css +97 -0
- package/dist/routes/create-admin-layout-route.js +8 -3
- package/dist/routes/create-admin-scheduled-publications-route.d.ts +8 -0
- package/dist/routes/create-admin-scheduled-publications-route.js +66 -0
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.js +1 -0
- package/dist/server-fns/collections/get.d.ts +5 -0
- package/dist/server-fns/collections/get.js +19 -3
- package/dist/server-fns/collections/index.d.ts +1 -0
- package/dist/server-fns/collections/index.js +1 -0
- package/dist/server-fns/collections/scheduled-publication-response.d.ts +11 -0
- package/dist/server-fns/collections/scheduled-publication-response.js +5 -0
- package/dist/server-fns/collections/scheduled-publication.d.ts +111 -0
- package/dist/server-fns/collections/scheduled-publication.js +157 -0
- package/package.json +8 -8
- package/src/admin-shell/chrome/admin-layout.module.css +6 -1
- package/src/admin-shell/chrome/admin-menu-drawer.module.css +19 -6
- package/src/admin-shell/chrome/admin-menu-drawer.tsx +23 -1
- package/src/admin-shell/chrome/dashboard.tsx +11 -4
- package/src/admin-shell/collections/edit.tsx +140 -1
- package/src/admin-shell/scheduled-publications/list.module.css +105 -0
- package/src/admin-shell/scheduled-publications/list.tsx +292 -0
- package/src/routes/create-admin-layout-route.tsx +8 -3
- package/src/routes/create-admin-scheduled-publications-route.tsx +83 -0
- package/src/routes/index.ts +1 -0
- package/src/server-fns/collections/get.ts +30 -1
- package/src/server-fns/collections/index.ts +1 -0
- package/src/server-fns/collections/scheduled-publication-response.test.node.ts +45 -0
- package/src/server-fns/collections/scheduled-publication-response.ts +22 -0
- package/src/server-fns/collections/scheduled-publication.ts +270 -0
|
@@ -47,10 +47,16 @@
|
|
|
47
47
|
overflow: hidden;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/* Pinned under the fixed app bar (45px) for the whole scroll of the page.
|
|
51
|
+
`align-self: flex-start` keeps the sticky box from being stretched to the
|
|
52
|
+
full height of the flex row — a stretched sticky item has no room to move
|
|
53
|
+
and never pins. */
|
|
50
54
|
.asideDesktop,
|
|
51
55
|
:global(.byline-admin-menu-drawer-aside-desktop) {
|
|
52
|
-
position:
|
|
53
|
-
|
|
56
|
+
position: sticky;
|
|
57
|
+
top: 45px;
|
|
58
|
+
align-self: flex-start;
|
|
59
|
+
height: calc(100vh - 45px);
|
|
54
60
|
flex-shrink: 0;
|
|
55
61
|
}
|
|
56
62
|
|
|
@@ -64,12 +70,13 @@
|
|
|
64
70
|
width: 60px;
|
|
65
71
|
}
|
|
66
72
|
|
|
73
|
+
/* The aside owns the viewport-height box; the nav just fills it and scrolls
|
|
74
|
+
internally when the menu is longer than the drawer. */
|
|
67
75
|
.nav,
|
|
68
76
|
:global(.byline-admin-menu-drawer) {
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
flex: 1 1 auto;
|
|
78
|
+
min-height: 0;
|
|
71
79
|
margin: 0;
|
|
72
|
-
max-height: calc(100vh - 60px);
|
|
73
80
|
overflow-y: auto;
|
|
74
81
|
}
|
|
75
82
|
|
|
@@ -96,9 +103,15 @@
|
|
|
96
103
|
width: 24px;
|
|
97
104
|
}
|
|
98
105
|
|
|
106
|
+
/* A real line-height, not 0. Zero leaves every line box with no height, which
|
|
107
|
+
a single-line label survives — the flex row centres it anyway — but a label
|
|
108
|
+
long enough to wrap does not: both lines land in the same place and render
|
|
109
|
+
on top of each other. "Scheduled Publications" is the first label in this
|
|
110
|
+
menu long enough to reach that. Non-compact rows have no fixed height, so
|
|
111
|
+
the row simply grows to fit a second line. */
|
|
99
112
|
.nav ul :global(li.menu-item .label),
|
|
100
113
|
:global(.byline-admin-menu-drawer) ul :global(li.menu-item .label) {
|
|
101
|
-
line-height:
|
|
114
|
+
line-height: 1.2;
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
.nav ul :global(li.menu-item.compact .label),
|
|
@@ -13,9 +13,11 @@ import { ADMIN_ACTIVITY_ABILITIES } from '@byline/admin/admin-activity'
|
|
|
13
13
|
import { ADMIN_PERMISSIONS_ABILITIES } from '@byline/admin/admin-permissions'
|
|
14
14
|
import { ADMIN_ROLES_ABILITIES } from '@byline/admin/admin-roles'
|
|
15
15
|
import { ADMIN_USERS_ABILITIES } from '@byline/admin/admin-users'
|
|
16
|
+
import { collectionAbilityKey, getAdminConfig } from '@byline/core'
|
|
16
17
|
import { useTranslation } from '@byline/i18n/react'
|
|
17
18
|
import {
|
|
18
19
|
ActivityIcon,
|
|
20
|
+
CalendarIcon,
|
|
19
21
|
HomeIcon,
|
|
20
22
|
RolesIcon,
|
|
21
23
|
SettingsSlidersIcon,
|
|
@@ -75,7 +77,11 @@ function MenuItem({ to, label, icon, pathname, compact }: MenuItemProps) {
|
|
|
75
77
|
)
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
export function AdminMenuDrawer(
|
|
80
|
+
export function AdminMenuDrawer({
|
|
81
|
+
scheduledPublicationEnabled = false,
|
|
82
|
+
}: {
|
|
83
|
+
scheduledPublicationEnabled?: boolean
|
|
84
|
+
}): React.JSX.Element | null {
|
|
79
85
|
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
|
80
86
|
const { mobile, drawerOpen, closeDrawer } = useAdminMenu()
|
|
81
87
|
const { t } = useTranslation('byline-admin')
|
|
@@ -90,6 +96,13 @@ export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
|
90
96
|
const canReadRoles = has(ADMIN_ROLES_ABILITIES.read)
|
|
91
97
|
const canReadPermissions = has(ADMIN_PERMISSIONS_ABILITIES.read)
|
|
92
98
|
const canReadActivity = has(ADMIN_ACTIVITY_ABILITIES.read)
|
|
99
|
+
const canSeeScheduledPublications =
|
|
100
|
+
scheduledPublicationEnabled &&
|
|
101
|
+
getAdminConfig().collections.some(
|
|
102
|
+
(collection) =>
|
|
103
|
+
has(collectionAbilityKey(collection.path, 'changeStatus')) &&
|
|
104
|
+
has(collectionAbilityKey(collection.path, 'publish'))
|
|
105
|
+
)
|
|
93
106
|
const showAdminSection = canReadUsers || canReadRoles || canReadPermissions || canReadActivity
|
|
94
107
|
|
|
95
108
|
const handlers = useSwipeable({
|
|
@@ -130,6 +143,15 @@ export function AdminMenuDrawer(): React.JSX.Element | null {
|
|
|
130
143
|
pathname={pathname}
|
|
131
144
|
compact={compact}
|
|
132
145
|
/>
|
|
146
|
+
{canSeeScheduledPublications && (
|
|
147
|
+
<MenuItem
|
|
148
|
+
to={getAdminRoutePath('scheduled-publications')}
|
|
149
|
+
label={t('chrome.menu.scheduledPublications')}
|
|
150
|
+
icon={<CalendarIcon width="18px" height="18px" />}
|
|
151
|
+
pathname={pathname}
|
|
152
|
+
compact={compact}
|
|
153
|
+
/>
|
|
154
|
+
)}
|
|
133
155
|
{showAdminSection && (
|
|
134
156
|
<>
|
|
135
157
|
<li className="menu-separator" />
|
|
@@ -72,7 +72,12 @@ function CollectionCard({
|
|
|
72
72
|
stats: CollectionStatusCount[] | undefined
|
|
73
73
|
}) {
|
|
74
74
|
const { t } = useTranslation('byline-admin')
|
|
75
|
-
|
|
75
|
+
// `stats` is undefined when the collection opted out of counts (no
|
|
76
|
+
// `showStats: true` on the definition) — the dashboard loader only fetches
|
|
77
|
+
// them for collections that opted in. Falling back to a total of zero there
|
|
78
|
+
// would read as "this collection is empty", so the count is omitted along
|
|
79
|
+
// with the status tiles.
|
|
80
|
+
const total = stats !== undefined ? stats.reduce((sum, s) => sum + s.count, 0) : undefined
|
|
76
81
|
const workflowStatuses = getWorkflowStatuses(collection)
|
|
77
82
|
|
|
78
83
|
return (
|
|
@@ -88,9 +93,11 @@ function CollectionCard({
|
|
|
88
93
|
<span className={cx('byline-dashboard-title-text', styles.titleText)}>
|
|
89
94
|
{collection.labels.plural}
|
|
90
95
|
</span>
|
|
91
|
-
|
|
92
|
-
{
|
|
93
|
-
|
|
96
|
+
{total !== undefined && (
|
|
97
|
+
<span className={cx('muted byline-dashboard-title-meta', styles.titleMeta)}>
|
|
98
|
+
{t('dashboard.totalCount', { count: total })}
|
|
99
|
+
</span>
|
|
100
|
+
)}
|
|
94
101
|
</Card.Title>
|
|
95
102
|
<Card.Description className="muted">
|
|
96
103
|
{t('dashboard.collectionDescription', { label: collection.labels.plural })}
|
|
@@ -10,7 +10,12 @@ import { useState } from 'react'
|
|
|
10
10
|
|
|
11
11
|
import { FormRenderer } from '@byline/admin/react'
|
|
12
12
|
import type { CollectionAdminConfig, CollectionDefinition } from '@byline/core'
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getDefaultStatus,
|
|
15
|
+
getWorkflow,
|
|
16
|
+
getWorkflowStatuses,
|
|
17
|
+
validateStatusTransition,
|
|
18
|
+
} from '@byline/core'
|
|
14
19
|
import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas'
|
|
15
20
|
import { useTranslation } from '@byline/i18n/react'
|
|
16
21
|
import { Container, Section, useToastManager } from '@byline/ui/react'
|
|
@@ -18,11 +23,14 @@ import { Container, Section, useToastManager } from '@byline/ui/react'
|
|
|
18
23
|
import { getAdminRoutePath } from '../../routes/admin-path.js'
|
|
19
24
|
import { clearListReturnState } from '../../routes/list-return-storage.js'
|
|
20
25
|
import {
|
|
26
|
+
cancelCollectionDocumentScheduledPublish,
|
|
27
|
+
confirmCollectionDocumentScheduledPublish,
|
|
21
28
|
copyDocumentToLocale,
|
|
22
29
|
deleteDocument,
|
|
23
30
|
deleteDocumentLocale,
|
|
24
31
|
duplicateCollectionDocument,
|
|
25
32
|
hasDeleteSideEffectFailures,
|
|
33
|
+
scheduleCollectionDocumentPublish,
|
|
26
34
|
unpublishDocument,
|
|
27
35
|
updateCollectionDocumentSystemFields,
|
|
28
36
|
updateCollectionDocumentWithPatches,
|
|
@@ -31,6 +39,7 @@ import {
|
|
|
31
39
|
import { useNavigate } from '../chrome/loose-router.js'
|
|
32
40
|
import { useTanStackNavigationGuard } from './tanstack-navigation-guard.js'
|
|
33
41
|
import { ViewMenu } from './view-menu.js'
|
|
42
|
+
import type { SerializedDocumentPublishSchedule } from '../../server-fns/collections/index.js'
|
|
34
43
|
import type { ContentLocaleOption } from './view-menu.js'
|
|
35
44
|
|
|
36
45
|
type EditState = {
|
|
@@ -76,6 +85,30 @@ export const EditView = ({
|
|
|
76
85
|
currentIndex !== -1 && currentIndex < workflowStatuses.length - 1
|
|
77
86
|
? workflowStatuses[currentIndex + 1]
|
|
78
87
|
: undefined
|
|
88
|
+
const scheduledPublicationEnabled =
|
|
89
|
+
// biome-ignore lint/suspicious/noExplicitAny: host-only loader metadata
|
|
90
|
+
(initialData as any)?._scheduledPublicationEnabled === true
|
|
91
|
+
const canSchedulePublication =
|
|
92
|
+
// biome-ignore lint/suspicious/noExplicitAny: host-only loader metadata
|
|
93
|
+
(initialData as any)?._canSchedulePublication === true
|
|
94
|
+
// biome-ignore lint/suspicious/noExplicitAny: host-only loader metadata
|
|
95
|
+
const scheduledPublication = ((initialData as any)?._scheduledPublish ??
|
|
96
|
+
null) as SerializedDocumentPublishSchedule | null
|
|
97
|
+
const publishTransitionValid =
|
|
98
|
+
currentStatus !== 'published' &&
|
|
99
|
+
getWorkflowStatuses(collectionDefinition).length > 1 &&
|
|
100
|
+
validateStatusTransition(getWorkflow(collectionDefinition), currentStatus, 'published').valid
|
|
101
|
+
const canArmSchedule =
|
|
102
|
+
scheduledPublicationEnabled && canSchedulePublication && publishTransitionValid
|
|
103
|
+
|
|
104
|
+
const notifyScheduleSuspended = () => {
|
|
105
|
+
if (scheduledPublication?.state !== 'armed') return
|
|
106
|
+
toastManager.add({
|
|
107
|
+
title: t('scheduledPublication.toast.suspendedTitle'),
|
|
108
|
+
description: t('scheduledPublication.toast.suspendedDescription'),
|
|
109
|
+
data: { intent: 'warning', iconType: 'warning', icon: true, close: true },
|
|
110
|
+
})
|
|
111
|
+
}
|
|
79
112
|
|
|
80
113
|
const handleLocaleChange = (newLocale: string) => {
|
|
81
114
|
navigate({
|
|
@@ -127,6 +160,96 @@ export const EditView = ({
|
|
|
127
160
|
}
|
|
128
161
|
}
|
|
129
162
|
|
|
163
|
+
const reloadDocument = () => {
|
|
164
|
+
navigate({
|
|
165
|
+
to: getAdminRoutePath('collections', '$collection', '$id'),
|
|
166
|
+
params: { collection: path, id: String(initialData.id) },
|
|
167
|
+
search: (prev: Record<string, unknown>) => ({ ...prev }),
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const handleSchedulePublication = async ({ publishAt }: { publishAt: string }) => {
|
|
172
|
+
try {
|
|
173
|
+
await scheduleCollectionDocumentPublish({
|
|
174
|
+
data: {
|
|
175
|
+
collection: path,
|
|
176
|
+
id: String(initialData.id),
|
|
177
|
+
publishAt,
|
|
178
|
+
expectedVersionId: String(initialData.versionId),
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
toastManager.add({
|
|
182
|
+
title: t('scheduledPublication.toast.scheduledTitle'),
|
|
183
|
+
description: t('scheduledPublication.toast.scheduledDescription'),
|
|
184
|
+
data: { intent: 'success', iconType: 'success', icon: true, close: true },
|
|
185
|
+
})
|
|
186
|
+
reloadDocument()
|
|
187
|
+
} catch (err) {
|
|
188
|
+
toastManager.add({
|
|
189
|
+
title: t('scheduledPublication.toast.failedTitle'),
|
|
190
|
+
description: (err as Error).message,
|
|
191
|
+
data: { intent: 'danger', iconType: 'danger', icon: true, close: true },
|
|
192
|
+
})
|
|
193
|
+
throw err
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const handleConfirmScheduledPublication = async () => {
|
|
198
|
+
try {
|
|
199
|
+
await confirmCollectionDocumentScheduledPublish({
|
|
200
|
+
data: {
|
|
201
|
+
collection: path,
|
|
202
|
+
id: String(initialData.id),
|
|
203
|
+
expectedVersionId: String(initialData.versionId),
|
|
204
|
+
},
|
|
205
|
+
})
|
|
206
|
+
toastManager.add({
|
|
207
|
+
title: t('scheduledPublication.toast.confirmedTitle'),
|
|
208
|
+
description: t('scheduledPublication.toast.confirmedDescription'),
|
|
209
|
+
data: { intent: 'success', iconType: 'success', icon: true, close: true },
|
|
210
|
+
})
|
|
211
|
+
reloadDocument()
|
|
212
|
+
} catch (err) {
|
|
213
|
+
toastManager.add({
|
|
214
|
+
title: t('scheduledPublication.toast.failedTitle'),
|
|
215
|
+
description: (err as Error).message,
|
|
216
|
+
data: { intent: 'danger', iconType: 'danger', icon: true, close: true },
|
|
217
|
+
})
|
|
218
|
+
throw err
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const handleCancelScheduledPublication = async () => {
|
|
223
|
+
try {
|
|
224
|
+
const result = await cancelCollectionDocumentScheduledPublish({
|
|
225
|
+
data: { collection: path, id: String(initialData.id) },
|
|
226
|
+
})
|
|
227
|
+
const cancelled = result.status === 'cancelled'
|
|
228
|
+
toastManager.add({
|
|
229
|
+
title: cancelled
|
|
230
|
+
? t('scheduledPublication.toast.cancelledTitle')
|
|
231
|
+
: t('scheduledPublication.toast.cancelLostTitle'),
|
|
232
|
+
description: cancelled
|
|
233
|
+
? t('scheduledPublication.toast.cancelledDescription')
|
|
234
|
+
: t('scheduledPublication.toast.cancelLostDescription'),
|
|
235
|
+
data: {
|
|
236
|
+
intent: cancelled ? 'success' : 'warning',
|
|
237
|
+
iconType: cancelled ? 'success' : 'warning',
|
|
238
|
+
icon: true,
|
|
239
|
+
close: true,
|
|
240
|
+
},
|
|
241
|
+
})
|
|
242
|
+
reloadDocument()
|
|
243
|
+
} catch (err) {
|
|
244
|
+
toastManager.add({
|
|
245
|
+
title: t('scheduledPublication.toast.failedTitle'),
|
|
246
|
+
description: (err as Error).message,
|
|
247
|
+
data: { intent: 'danger', iconType: 'danger', icon: true, close: true },
|
|
248
|
+
})
|
|
249
|
+
throw err
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
130
253
|
// Published version metadata — attached by getCollectionDocument when a
|
|
131
254
|
// published version exists behind the current draft.
|
|
132
255
|
// biome-ignore lint/suspicious/noExplicitAny: storage shape
|
|
@@ -269,6 +392,7 @@ export const EditView = ({
|
|
|
269
392
|
close: true,
|
|
270
393
|
},
|
|
271
394
|
})
|
|
395
|
+
notifyScheduleSuspended()
|
|
272
396
|
setEditState({
|
|
273
397
|
status: 'success',
|
|
274
398
|
message: t('collections.edit.copiedSuccessMessage', {
|
|
@@ -327,6 +451,7 @@ export const EditView = ({
|
|
|
327
451
|
close: true,
|
|
328
452
|
},
|
|
329
453
|
})
|
|
454
|
+
notifyScheduleSuspended()
|
|
330
455
|
setEditState({ status: 'success', message: description })
|
|
331
456
|
// The deleted locale may be the one being viewed — land on the default
|
|
332
457
|
// locale (which always survives) so the loader re-fetches a valid view.
|
|
@@ -457,6 +582,10 @@ export const EditView = ({
|
|
|
457
582
|
})
|
|
458
583
|
}
|
|
459
584
|
|
|
585
|
+
if (contentDirty && scheduledPublication?.state === 'armed') {
|
|
586
|
+
notifyScheduleSuspended()
|
|
587
|
+
}
|
|
588
|
+
|
|
460
589
|
const description = t('collections.edit.updatedDescription', { label: singularLower })
|
|
461
590
|
toastManager.add({
|
|
462
591
|
title: t('collections.edit.updateTitle', { label: singular }),
|
|
@@ -531,6 +660,16 @@ export const EditView = ({
|
|
|
531
660
|
}
|
|
532
661
|
onStatusChange={handleStatusChange}
|
|
533
662
|
onUnpublish={publishedVersion ? handleUnpublish : undefined}
|
|
663
|
+
scheduledPublication={scheduledPublication}
|
|
664
|
+
onSchedulePublication={canArmSchedule ? handleSchedulePublication : undefined}
|
|
665
|
+
onConfirmScheduledPublication={
|
|
666
|
+
canArmSchedule && scheduledPublication?.state === 'needs_reconfirm'
|
|
667
|
+
? handleConfirmScheduledPublication
|
|
668
|
+
: undefined
|
|
669
|
+
}
|
|
670
|
+
onCancelScheduledPublication={
|
|
671
|
+
scheduledPublication != null ? handleCancelScheduledPublication : undefined
|
|
672
|
+
}
|
|
534
673
|
onDelete={handleDelete}
|
|
535
674
|
onDuplicate={handleDuplicate}
|
|
536
675
|
onCopyToLocale={handleCopyToLocale}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
.head {
|
|
2
|
+
margin-bottom: var(--spacing-16);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.title {
|
|
6
|
+
margin-bottom: var(--spacing-4);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.intro {
|
|
10
|
+
margin: 0;
|
|
11
|
+
color: var(--gray-500);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.health {
|
|
15
|
+
margin-bottom: var(--spacing-16);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Filters and pagination are separate jobs and used at different moments, so
|
|
19
|
+
they sit at opposite ends of one row rather than running together — the
|
|
20
|
+
pager previously read as a third filter control. */
|
|
21
|
+
.controls {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-wrap: wrap;
|
|
24
|
+
align-items: flex-end;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
gap: var(--spacing-16);
|
|
27
|
+
margin-bottom: var(--spacing-4);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.filters-help {
|
|
31
|
+
margin: 0 0 var(--spacing-16);
|
|
32
|
+
color: var(--gray-500);
|
|
33
|
+
font-size: var(--font-size-sm);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.filters {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-wrap: wrap;
|
|
39
|
+
align-items: flex-end;
|
|
40
|
+
gap: var(--spacing-12);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.filter {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: var(--spacing-4);
|
|
47
|
+
min-width: 12rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.filter-authorizer :global(.byline-input-wrapper) {
|
|
51
|
+
min-width: 22rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Sits on the control baseline, not the label baseline, so the buttons line up
|
|
55
|
+
with the inputs rather than floating against their labels. */
|
|
56
|
+
.filter-actions {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: var(--spacing-8);
|
|
60
|
+
padding-bottom: 0.1rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.tableWrap {
|
|
64
|
+
margin-top: 0.75rem;
|
|
65
|
+
overflow-x: auto;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.collection,
|
|
69
|
+
.zone,
|
|
70
|
+
.error {
|
|
71
|
+
display: block;
|
|
72
|
+
margin-top: var(--spacing-4);
|
|
73
|
+
color: var(--gray-500);
|
|
74
|
+
font-size: var(--font-size-xs);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.instant {
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* The whole point of this column is offboarding — an operator pastes an
|
|
82
|
+
account id in and compares. Tabular figures make mismatches visible. */
|
|
83
|
+
.authorizer {
|
|
84
|
+
font-family: var(--font-family-mono, monospace);
|
|
85
|
+
font-size: var(--font-size-xs);
|
|
86
|
+
overflow-wrap: anywhere;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.error {
|
|
90
|
+
max-width: 24rem;
|
|
91
|
+
color: var(--red-600);
|
|
92
|
+
white-space: normal;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.empty {
|
|
96
|
+
padding: var(--spacing-32) 0;
|
|
97
|
+
color: var(--gray-500);
|
|
98
|
+
text-align: center;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.pagerBottom {
|
|
102
|
+
display: flex;
|
|
103
|
+
justify-content: flex-end;
|
|
104
|
+
margin-top: var(--spacing-16);
|
|
105
|
+
}
|