@prenta/admin 2.7.0 → 2.8.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/CHANGELOG.md +34 -0
- package/design-system/tokens.css +17 -0
- package/design-system/tokens.json +35 -1
- package/design-system/tokens.scss +15 -0
- package/design-system/tokens.tailwind.cjs +14 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +2 -2
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/lib/batch-wizard.test.d.ts +2 -0
- package/dist/__tests__/lib/batch-wizard.test.d.ts.map +1 -0
- package/dist/__tests__/lib/batch-wizard.test.js +144 -0
- package/dist/__tests__/lib/batch-wizard.test.js.map +1 -0
- package/dist/__tests__/lib/job-drawer.test.js +2 -0
- package/dist/__tests__/lib/job-drawer.test.js.map +1 -1
- package/dist/__tests__/lib/job-view.test.js +100 -3
- package/dist/__tests__/lib/job-view.test.js.map +1 -1
- package/dist/__tests__/lib/jobs-service.test.js +56 -2
- package/dist/__tests__/lib/jobs-service.test.js.map +1 -1
- package/dist/__tests__/views/appearance-chrome.render.test.d.ts +2 -0
- package/dist/__tests__/views/appearance-chrome.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/appearance-chrome.render.test.js +688 -0
- package/dist/__tests__/views/appearance-chrome.render.test.js.map +1 -0
- package/dist/__tests__/views/header-footer-form.test.d.ts +2 -0
- package/dist/__tests__/views/header-footer-form.test.d.ts.map +1 -0
- package/dist/__tests__/views/header-footer-form.test.js +266 -0
- package/dist/__tests__/views/header-footer-form.test.js.map +1 -0
- package/dist/__tests__/views/jobs-drawer.render.test.js +442 -2
- package/dist/__tests__/views/jobs-drawer.render.test.js.map +1 -1
- package/dist/__tests__/views/jobs-list.render.test.js +7 -5
- package/dist/__tests__/views/jobs-list.render.test.js.map +1 -1
- package/dist/__tests__/views/jobs-wizard.render.test.d.ts +2 -0
- package/dist/__tests__/views/jobs-wizard.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/jobs-wizard.render.test.js +588 -0
- package/dist/__tests__/views/jobs-wizard.render.test.js.map +1 -0
- package/dist/lib/api.d.ts +2 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +8 -1
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/batch-wizard.d.ts +84 -0
- package/dist/lib/batch-wizard.d.ts.map +1 -0
- package/dist/lib/batch-wizard.js +122 -0
- package/dist/lib/batch-wizard.js.map +1 -0
- package/dist/lib/chrome-service.d.ts +69 -0
- package/dist/lib/chrome-service.d.ts.map +1 -0
- package/dist/lib/chrome-service.js +108 -0
- package/dist/lib/chrome-service.js.map +1 -0
- package/dist/lib/job-view.d.ts +43 -0
- package/dist/lib/job-view.d.ts.map +1 -1
- package/dist/lib/job-view.js +57 -1
- package/dist/lib/job-view.js.map +1 -1
- package/dist/lib/jobs-service.d.ts +19 -1
- package/dist/lib/jobs-service.d.ts.map +1 -1
- package/dist/lib/jobs-service.js +41 -0
- package/dist/lib/jobs-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/AppearanceChrome.d.ts +22 -4
- package/dist/views/AppearanceChrome.d.ts.map +1 -1
- package/dist/views/AppearanceChrome.js +451 -15
- package/dist/views/AppearanceChrome.js.map +1 -1
- package/dist/views/Jobs/CancelJobDialog.d.ts +7 -1
- package/dist/views/Jobs/CancelJobDialog.d.ts.map +1 -1
- package/dist/views/Jobs/CancelJobDialog.js +13 -3
- package/dist/views/Jobs/CancelJobDialog.js.map +1 -1
- package/dist/views/Jobs/DrawerReview.d.ts +7 -0
- package/dist/views/Jobs/DrawerReview.d.ts.map +1 -1
- package/dist/views/Jobs/DrawerReview.js +1 -1
- package/dist/views/Jobs/DrawerReview.js.map +1 -1
- package/dist/views/Jobs/JobDrawer.d.ts +23 -3
- package/dist/views/Jobs/JobDrawer.d.ts.map +1 -1
- package/dist/views/Jobs/JobDrawer.js +30 -5
- package/dist/views/Jobs/JobDrawer.js.map +1 -1
- package/dist/views/Jobs/JobsTable.d.ts +4 -2
- package/dist/views/Jobs/JobsTable.d.ts.map +1 -1
- package/dist/views/Jobs/JobsTable.js +6 -5
- package/dist/views/Jobs/JobsTable.js.map +1 -1
- package/dist/views/Jobs/NewBatchJob.d.ts +34 -0
- package/dist/views/Jobs/NewBatchJob.d.ts.map +1 -0
- package/dist/views/Jobs/NewBatchJob.js +282 -0
- package/dist/views/Jobs/NewBatchJob.js.map +1 -0
- package/dist/views/Jobs/WorkerStrip.d.ts +3 -1
- package/dist/views/Jobs/WorkerStrip.d.ts.map +1 -1
- package/dist/views/Jobs/WorkerStrip.js +2 -2
- package/dist/views/Jobs/WorkerStrip.js.map +1 -1
- package/dist/views/Jobs/focus.d.ts +6 -0
- package/dist/views/Jobs/focus.d.ts.map +1 -1
- package/dist/views/Jobs/focus.js +10 -0
- package/dist/views/Jobs/focus.js.map +1 -1
- package/dist/views/Jobs.d.ts +12 -3
- package/dist/views/Jobs.d.ts.map +1 -1
- package/dist/views/Jobs.js +109 -31
- package/dist/views/Jobs.js.map +1 -1
- package/dist/views/header-footer/AnnouncementTab.d.ts +15 -0
- package/dist/views/header-footer/AnnouncementTab.d.ts.map +1 -0
- package/dist/views/header-footer/AnnouncementTab.js +43 -0
- package/dist/views/header-footer/AnnouncementTab.js.map +1 -0
- package/dist/views/header-footer/ChromeField.d.ts +25 -0
- package/dist/views/header-footer/ChromeField.d.ts.map +1 -0
- package/dist/views/header-footer/ChromeField.js +99 -0
- package/dist/views/header-footer/ChromeField.js.map +1 -0
- package/dist/views/header-footer/ChromePreview.d.ts +31 -0
- package/dist/views/header-footer/ChromePreview.d.ts.map +1 -0
- package/dist/views/header-footer/ChromePreview.js +195 -0
- package/dist/views/header-footer/ChromePreview.js.map +1 -0
- package/dist/views/header-footer/DeveloperTab.d.ts +17 -0
- package/dist/views/header-footer/DeveloperTab.d.ts.map +1 -0
- package/dist/views/header-footer/DeveloperTab.js +98 -0
- package/dist/views/header-footer/DeveloperTab.js.map +1 -0
- package/dist/views/header-footer/RegionTab.d.ts +27 -0
- package/dist/views/header-footer/RegionTab.d.ts.map +1 -0
- package/dist/views/header-footer/RegionTab.js +80 -0
- package/dist/views/header-footer/RegionTab.js.map +1 -0
- package/dist/views/header-footer/chrome-form.d.ts +137 -0
- package/dist/views/header-footer/chrome-form.d.ts.map +1 -0
- package/dist/views/header-footer/chrome-form.js +518 -0
- package/dist/views/header-footer/chrome-form.js.map +1 -0
- package/dist/views/header-footer/code-samples.d.ts +30 -0
- package/dist/views/header-footer/code-samples.d.ts.map +1 -0
- package/dist/views/header-footer/code-samples.js +127 -0
- package/dist/views/header-footer/code-samples.js.map +1 -0
- package/dist/views/header-footer/controls.d.ts +57 -0
- package/dist/views/header-footer/controls.d.ts.map +1 -0
- package/dist/views/header-footer/controls.js +43 -0
- package/dist/views/header-footer/controls.js.map +1 -0
- package/package.json +3 -3
- package/src/AdminRoot.tsx +16 -2
- package/src/__tests__/lib/batch-wizard.test.ts +199 -0
- package/src/__tests__/lib/job-drawer.test.ts +2 -0
- package/src/__tests__/lib/job-view.test.ts +123 -2
- package/src/__tests__/lib/jobs-service.test.ts +72 -3
- package/src/__tests__/views/appearance-chrome.render.test.tsx +818 -0
- package/src/__tests__/views/header-footer-form.test.ts +333 -0
- package/src/__tests__/views/jobs-drawer.render.test.tsx +508 -2
- package/src/__tests__/views/jobs-list.render.test.tsx +8 -5
- package/src/__tests__/views/jobs-wizard.render.test.tsx +707 -0
- package/src/lib/api.ts +12 -1
- package/src/lib/batch-wizard.ts +188 -0
- package/src/lib/chrome-service.ts +200 -0
- package/src/lib/job-view.ts +95 -1
- package/src/lib/jobs-service.ts +56 -1
- package/src/styles/prenta-tokens.css +17 -0
- package/src/styles/theme.css +15 -0
- package/src/views/AppearanceChrome.tsx +704 -46
- package/src/views/Jobs/CancelJobDialog.tsx +20 -2
- package/src/views/Jobs/DrawerReview.tsx +1 -1
- package/src/views/Jobs/JobDrawer.tsx +83 -3
- package/src/views/Jobs/JobsTable.tsx +11 -4
- package/src/views/Jobs/NewBatchJob.tsx +857 -0
- package/src/views/Jobs/WorkerStrip.tsx +16 -11
- package/src/views/Jobs/focus.ts +11 -0
- package/src/views/Jobs.tsx +163 -49
- package/src/views/header-footer/AnnouncementTab.tsx +231 -0
- package/src/views/header-footer/ChromeField.tsx +358 -0
- package/src/views/header-footer/ChromePreview.tsx +497 -0
- package/src/views/header-footer/DeveloperTab.tsx +331 -0
- package/src/views/header-footer/RegionTab.tsx +398 -0
- package/src/views/header-footer/chrome-form.ts +603 -0
- package/src/views/header-footer/code-samples.ts +146 -0
- package/src/views/header-footer/controls.tsx +166 -0
- package/dist/views/Jobs/placeholders.d.ts +0 -6
- package/dist/views/Jobs/placeholders.d.ts.map +0 -1
- package/dist/views/Jobs/placeholders.js +0 -20
- package/dist/views/Jobs/placeholders.js.map +0 -1
- package/src/views/Jobs/placeholders.tsx +0 -66
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { AlertTriangle, Code } from 'lucide-react'
|
|
4
|
+
import { useRef, type KeyboardEvent, type MouseEvent } from 'react'
|
|
5
|
+
import type {
|
|
6
|
+
ChromeOrphan,
|
|
7
|
+
ChromeOverridesList,
|
|
8
|
+
ChromeRegionName,
|
|
9
|
+
RegionSchema,
|
|
10
|
+
} from '@prenta/core'
|
|
11
|
+
|
|
12
|
+
import { Button } from '../../components/ui/Button.js'
|
|
13
|
+
import type { ChromeMenuOption } from '../../lib/chrome-service.js'
|
|
14
|
+
import { ChromeField } from './ChromeField.js'
|
|
15
|
+
import {
|
|
16
|
+
componentPath,
|
|
17
|
+
fieldEntries,
|
|
18
|
+
overrideChips,
|
|
19
|
+
repoFileUrl,
|
|
20
|
+
shortCommit,
|
|
21
|
+
timeAgo,
|
|
22
|
+
type RegionForm,
|
|
23
|
+
} from './chrome-form.js'
|
|
24
|
+
import { CARD, CardHead, KeyChip, LockPill } from './controls.js'
|
|
25
|
+
|
|
26
|
+
const ORPHAN_TTL_MS = 30 * 86_400_000
|
|
27
|
+
|
|
28
|
+
/** The kit's variant thumbnails, by variant id; other ids draw the default. */
|
|
29
|
+
function VariantThumb({ region, id }: { region: ChromeRegionName; id: string }) {
|
|
30
|
+
const bar = 'block h-[5px] rounded-[3px] bg-bdr'
|
|
31
|
+
const logo = 'block h-[9px] w-4 rounded-[2px] bg-sub'
|
|
32
|
+
const cta = 'block h-[9px] w-3.5 rounded-[2px] bg-acc'
|
|
33
|
+
if (region === 'footer') {
|
|
34
|
+
const blocks = id === 'minimal' ? 0 : id === 'compact' ? 2 : 4
|
|
35
|
+
return (
|
|
36
|
+
<span
|
|
37
|
+
aria-hidden
|
|
38
|
+
className="rounded-6 bg-site-thumb flex h-[30px] items-center gap-1 px-[7px]"
|
|
39
|
+
>
|
|
40
|
+
{blocks === 0 ? (
|
|
41
|
+
<i className="bg-site-thumb-bar block h-[5px] w-full rounded-[3px]" />
|
|
42
|
+
) : (
|
|
43
|
+
Array.from({ length: blocks }, (_, index) => (
|
|
44
|
+
<i key={index} className="bg-site-thumb-bar block h-3.5 flex-1 rounded-[3px]" />
|
|
45
|
+
))
|
|
46
|
+
)}
|
|
47
|
+
</span>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
const centered = /center/.test(id)
|
|
51
|
+
const minimal = id === 'minimal'
|
|
52
|
+
return (
|
|
53
|
+
<span
|
|
54
|
+
aria-hidden
|
|
55
|
+
className={`rounded-6 bg-bg flex h-[30px] items-center gap-1 px-[7px] ${
|
|
56
|
+
centered ? 'justify-center' : 'justify-between'
|
|
57
|
+
}`}
|
|
58
|
+
>
|
|
59
|
+
<i className={logo} />
|
|
60
|
+
{centered ? null : minimal ? (
|
|
61
|
+
<i className={cta} />
|
|
62
|
+
) : (
|
|
63
|
+
<span className="flex gap-[3px]">
|
|
64
|
+
<i className={`${bar} w-2.5`} />
|
|
65
|
+
<i className={`${bar} w-2.5`} />
|
|
66
|
+
<i className={cta} />
|
|
67
|
+
</span>
|
|
68
|
+
)}
|
|
69
|
+
</span>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function VariantCards({
|
|
74
|
+
region,
|
|
75
|
+
schema,
|
|
76
|
+
value,
|
|
77
|
+
onChange,
|
|
78
|
+
disabled,
|
|
79
|
+
}: {
|
|
80
|
+
region: ChromeRegionName
|
|
81
|
+
schema: RegionSchema
|
|
82
|
+
value: string
|
|
83
|
+
onChange: (variant: string) => void
|
|
84
|
+
disabled: boolean
|
|
85
|
+
}) {
|
|
86
|
+
const ids = Object.keys(schema.variants).filter((id) => Object.hasOwn(schema.variants, id))
|
|
87
|
+
const refs = useRef<Array<HTMLButtonElement | null>>([])
|
|
88
|
+
const selected = Math.max(0, ids.indexOf(value))
|
|
89
|
+
|
|
90
|
+
function onKeyDown(event: KeyboardEvent<HTMLDivElement>) {
|
|
91
|
+
const last = ids.length - 1
|
|
92
|
+
let next: number
|
|
93
|
+
if (event.key === 'ArrowRight' || event.key === 'ArrowDown')
|
|
94
|
+
next = selected === last ? 0 : selected + 1
|
|
95
|
+
else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp')
|
|
96
|
+
next = selected === 0 ? last : selected - 1
|
|
97
|
+
else return
|
|
98
|
+
event.preventDefault()
|
|
99
|
+
if (disabled) return
|
|
100
|
+
onChange(ids[next]!)
|
|
101
|
+
refs.current[next]?.focus()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
role="radiogroup"
|
|
107
|
+
aria-label={`${region} variant`}
|
|
108
|
+
onKeyDown={onKeyDown}
|
|
109
|
+
className="grid grid-cols-3 gap-2 [@media(width<=560px)]:grid-cols-2"
|
|
110
|
+
>
|
|
111
|
+
{ids.map((id, index) => {
|
|
112
|
+
const on = id === value
|
|
113
|
+
return (
|
|
114
|
+
<button
|
|
115
|
+
key={id}
|
|
116
|
+
ref={(el) => {
|
|
117
|
+
refs.current[index] = el
|
|
118
|
+
}}
|
|
119
|
+
type="button"
|
|
120
|
+
role="radio"
|
|
121
|
+
aria-checked={on}
|
|
122
|
+
tabIndex={index === selected ? 0 : -1}
|
|
123
|
+
disabled={disabled}
|
|
124
|
+
onClick={() => onChange(id)}
|
|
125
|
+
className={`rounded-10 bg-card flex min-w-0 cursor-pointer flex-col gap-1.5 border p-2 text-left leading-[normal] disabled:cursor-not-allowed ${
|
|
126
|
+
on ? 'border-acc shadow-[0_0_0_3px_var(--acc-l)]' : 'border-bdr'
|
|
127
|
+
}`}
|
|
128
|
+
>
|
|
129
|
+
<VariantThumb region={region} id={id} />
|
|
130
|
+
<b className="text-txt text-[12px] leading-[normal] font-semibold">
|
|
131
|
+
{schema.variants[id]}
|
|
132
|
+
</b>
|
|
133
|
+
<KeyChip bare size={10.5}>
|
|
134
|
+
{id}
|
|
135
|
+
</KeyChip>
|
|
136
|
+
</button>
|
|
137
|
+
)
|
|
138
|
+
})}
|
|
139
|
+
</div>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function OrphanRow({
|
|
144
|
+
region,
|
|
145
|
+
orphans,
|
|
146
|
+
now,
|
|
147
|
+
removing,
|
|
148
|
+
disabled,
|
|
149
|
+
onRemove,
|
|
150
|
+
}: {
|
|
151
|
+
region: ChromeRegionName
|
|
152
|
+
orphans: ChromeOrphan[]
|
|
153
|
+
now: number
|
|
154
|
+
removing: boolean
|
|
155
|
+
disabled: boolean
|
|
156
|
+
onRemove: () => void
|
|
157
|
+
}) {
|
|
158
|
+
const expires = Math.min(...orphans.map((o) => Date.parse(o.orphanedAt) + ORPHAN_TTL_MS))
|
|
159
|
+
const days = Math.max(0, Math.ceil((expires - now) / 86_400_000))
|
|
160
|
+
const when = days <= 0 ? 'today' : `in ${days} ${days === 1 ? 'day' : 'days'}`
|
|
161
|
+
const one = orphans.length === 1
|
|
162
|
+
return (
|
|
163
|
+
<div className="border-bdr bg-bg text-sub flex items-start gap-2.5 border-t px-4 py-2.5 text-[12px] leading-[normal]">
|
|
164
|
+
<span className="text-warn mt-px shrink-0">
|
|
165
|
+
<AlertTriangle size={14} strokeWidth={1.8} aria-hidden />
|
|
166
|
+
</span>
|
|
167
|
+
<span className="min-w-0">
|
|
168
|
+
<b className="text-txt font-semibold">
|
|
169
|
+
{one
|
|
170
|
+
? '1 saved value isn’t in the schema anymore:'
|
|
171
|
+
: `${orphans.length} saved values aren’t in the schema anymore:`}
|
|
172
|
+
</b>{' '}
|
|
173
|
+
{orphans.map((orphan, index) => (
|
|
174
|
+
<span key={orphan.key}>
|
|
175
|
+
{index > 0 ? ', ' : null}
|
|
176
|
+
<KeyChip>{`${region}.${orphan.key}`}</KeyChip>
|
|
177
|
+
</span>
|
|
178
|
+
))}
|
|
179
|
+
.{' '}
|
|
180
|
+
{one
|
|
181
|
+
? `It’s no longer sent to the site and will be removed ${when}.`
|
|
182
|
+
: `They’re no longer sent to the site and will be removed ${when}.`}
|
|
183
|
+
</span>
|
|
184
|
+
<Button
|
|
185
|
+
variant="ghost"
|
|
186
|
+
size="sm"
|
|
187
|
+
className="ml-auto"
|
|
188
|
+
loading={removing}
|
|
189
|
+
disabled={disabled}
|
|
190
|
+
onClick={onRemove}
|
|
191
|
+
>
|
|
192
|
+
Remove now
|
|
193
|
+
</Button>
|
|
194
|
+
</div>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface RegionTabProps {
|
|
199
|
+
region: ChromeRegionName
|
|
200
|
+
schema: RegionSchema
|
|
201
|
+
form: RegionForm
|
|
202
|
+
onChange: (form: RegionForm) => void
|
|
203
|
+
problems: Record<string, string>
|
|
204
|
+
orphans: ChromeOrphan[]
|
|
205
|
+
removingOrphans: boolean
|
|
206
|
+
onRemoveOrphans: () => void
|
|
207
|
+
overrides: ChromeOverridesList | 'unavailable' | 'error'
|
|
208
|
+
menus: ChromeMenuOption[]
|
|
209
|
+
siteTitle: string
|
|
210
|
+
readOnly: boolean
|
|
211
|
+
/** A save is out: Remove now waits, so the save's If-Match still holds. */
|
|
212
|
+
saving: boolean
|
|
213
|
+
now: number
|
|
214
|
+
href: (path: string) => string
|
|
215
|
+
onNavigate: (path: string) => void
|
|
216
|
+
/** The admin route that edits a page, by collection and id. */
|
|
217
|
+
editPath: (collection: string, id: string) => string
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** One region's cards, top to bottom: Source, Variant, Fields (with orphans), Page overrides. */
|
|
221
|
+
export function RegionTab({
|
|
222
|
+
region,
|
|
223
|
+
schema,
|
|
224
|
+
form,
|
|
225
|
+
onChange,
|
|
226
|
+
problems,
|
|
227
|
+
orphans,
|
|
228
|
+
removingOrphans,
|
|
229
|
+
onRemoveOrphans,
|
|
230
|
+
overrides,
|
|
231
|
+
menus,
|
|
232
|
+
siteTitle,
|
|
233
|
+
readOnly,
|
|
234
|
+
saving,
|
|
235
|
+
now,
|
|
236
|
+
href,
|
|
237
|
+
onNavigate,
|
|
238
|
+
editPath,
|
|
239
|
+
}: RegionTabProps) {
|
|
240
|
+
const { source } = schema
|
|
241
|
+
const repoUrl = repoFileUrl(source, schema.component)
|
|
242
|
+
const fields = fieldEntries(schema)
|
|
243
|
+
const pages =
|
|
244
|
+
typeof overrides === 'object'
|
|
245
|
+
? overrides.pages.filter((page) => page[region] !== undefined)
|
|
246
|
+
: []
|
|
247
|
+
const brandPath = '/settings?tab=general'
|
|
248
|
+
|
|
249
|
+
function go(event: MouseEvent<HTMLAnchorElement>, path: string) {
|
|
250
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.button !== 0) return
|
|
251
|
+
event.preventDefault()
|
|
252
|
+
onNavigate(path)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<>
|
|
257
|
+
<section aria-label="Source" className={CARD}>
|
|
258
|
+
<div className="flex flex-wrap items-center gap-3 px-4 py-3">
|
|
259
|
+
<Code size={15} strokeWidth={1.8} aria-hidden className="text-txt shrink-0" />
|
|
260
|
+
{/* The kit sets the path on its 16px body line: 20px tall. */}
|
|
261
|
+
<div className="min-w-0 leading-5">
|
|
262
|
+
<code className="text-txt font-mono text-[12.5px] font-medium break-all">
|
|
263
|
+
{componentPath(schema.component)}
|
|
264
|
+
</code>
|
|
265
|
+
<div className="text-hint text-faint flex flex-wrap items-center gap-2 leading-[normal]">
|
|
266
|
+
<i aria-hidden className="bg-suc block h-[7px] w-[7px] rounded-full" />
|
|
267
|
+
<span>{`Schema in sync · ${source.repo || 'unknown repo'}@${shortCommit(source.commit) || 'unknown'} · ${timeAgo(source.readAt, now)}`}</span>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
{repoUrl ? (
|
|
271
|
+
<a
|
|
272
|
+
href={repoUrl}
|
|
273
|
+
target="_blank"
|
|
274
|
+
rel="noopener noreferrer"
|
|
275
|
+
className="text-sm2 text-acc ml-auto font-semibold whitespace-nowrap no-underline"
|
|
276
|
+
>
|
|
277
|
+
View in repo ↗
|
|
278
|
+
</a>
|
|
279
|
+
) : null}
|
|
280
|
+
</div>
|
|
281
|
+
</section>
|
|
282
|
+
|
|
283
|
+
<section aria-labelledby={`hf-${region}-variant`} className={CARD}>
|
|
284
|
+
<CardHead
|
|
285
|
+
id={`hf-${region}-variant`}
|
|
286
|
+
title="Variant"
|
|
287
|
+
description="Site default. Pages can pick a different one."
|
|
288
|
+
>
|
|
289
|
+
<LockPill>Defined in code</LockPill>
|
|
290
|
+
</CardHead>
|
|
291
|
+
<div className="flex flex-col gap-3.5 px-4 py-3.5">
|
|
292
|
+
<VariantCards
|
|
293
|
+
region={region}
|
|
294
|
+
schema={schema}
|
|
295
|
+
value={form.variant}
|
|
296
|
+
disabled={readOnly}
|
|
297
|
+
onChange={(variant) => onChange({ ...form, variant })}
|
|
298
|
+
/>
|
|
299
|
+
</div>
|
|
300
|
+
</section>
|
|
301
|
+
|
|
302
|
+
<section aria-labelledby={`hf-${region}-fields`} className={CARD}>
|
|
303
|
+
<CardHead
|
|
304
|
+
id={`hf-${region}-fields`}
|
|
305
|
+
title="Fields"
|
|
306
|
+
description="Your developer chose what’s editable here."
|
|
307
|
+
/>
|
|
308
|
+
<div className="flex flex-col gap-3.5 px-4 py-3.5">
|
|
309
|
+
{fields.length === 0 ? (
|
|
310
|
+
<p className="text-meta text-sub m-0">This region has no editable fields.</p>
|
|
311
|
+
) : (
|
|
312
|
+
fields.map(([key, def]) => (
|
|
313
|
+
<ChromeField
|
|
314
|
+
key={key}
|
|
315
|
+
region={region}
|
|
316
|
+
fieldKey={key}
|
|
317
|
+
def={def}
|
|
318
|
+
value={form.fields[key]}
|
|
319
|
+
menus={menus}
|
|
320
|
+
siteTitle={siteTitle}
|
|
321
|
+
disabled={readOnly}
|
|
322
|
+
problem={problems[key]}
|
|
323
|
+
brandHref={href(brandPath)}
|
|
324
|
+
onOpenBrand={() => onNavigate(brandPath)}
|
|
325
|
+
onChange={(value) =>
|
|
326
|
+
onChange({ ...form, fields: { ...form.fields, [key]: value } })
|
|
327
|
+
}
|
|
328
|
+
/>
|
|
329
|
+
))
|
|
330
|
+
)}
|
|
331
|
+
</div>
|
|
332
|
+
{orphans.length > 0 ? (
|
|
333
|
+
<OrphanRow
|
|
334
|
+
region={region}
|
|
335
|
+
orphans={orphans}
|
|
336
|
+
now={now}
|
|
337
|
+
removing={removingOrphans}
|
|
338
|
+
disabled={readOnly || saving}
|
|
339
|
+
onRemove={onRemoveOrphans}
|
|
340
|
+
/>
|
|
341
|
+
) : null}
|
|
342
|
+
</section>
|
|
343
|
+
|
|
344
|
+
<section aria-labelledby={`hf-${region}-overrides`} className={CARD}>
|
|
345
|
+
<CardHead
|
|
346
|
+
id={`hf-${region}-overrides`}
|
|
347
|
+
title="Page overrides"
|
|
348
|
+
description="Set in each page’s settings. Child pages inherit."
|
|
349
|
+
/>
|
|
350
|
+
<div className="flex flex-col">
|
|
351
|
+
{overrides === 'error' ? (
|
|
352
|
+
<p className="text-meta text-faint m-0 px-4 py-2.5" role="alert">
|
|
353
|
+
Couldn’t load the pages that override the {region}.
|
|
354
|
+
</p>
|
|
355
|
+
) : overrides === 'unavailable' ? (
|
|
356
|
+
<p className="text-meta text-faint m-0 px-4 py-2.5">
|
|
357
|
+
Update the CMS to list the pages that override the {region}.
|
|
358
|
+
</p>
|
|
359
|
+
) : pages.length === 0 ? (
|
|
360
|
+
<p className="text-meta text-faint m-0 px-4 py-2.5">
|
|
361
|
+
No page overrides the {region}. Every page uses the site default.
|
|
362
|
+
</p>
|
|
363
|
+
) : (
|
|
364
|
+
pages.map((page) => {
|
|
365
|
+
const path = editPath(page.collection, page.id)
|
|
366
|
+
return (
|
|
367
|
+
<a
|
|
368
|
+
key={page.id}
|
|
369
|
+
href={href(path)}
|
|
370
|
+
onClick={(event) => go(event, path)}
|
|
371
|
+
className="border-bdr text-sm2 text-txt hover:bg-bg flex items-center gap-2.5 border-t px-4 py-[9px] no-underline first:border-t-0"
|
|
372
|
+
>
|
|
373
|
+
<span className="min-w-0 truncate">{page.title || page.path}</span>
|
|
374
|
+
{overrideChips(page, region).map((chip) => (
|
|
375
|
+
<span
|
|
376
|
+
key={chip}
|
|
377
|
+
className="rounded-5 border-bdr bg-bg text-sub border px-1.5 font-mono text-[11px] font-medium whitespace-nowrap"
|
|
378
|
+
>
|
|
379
|
+
{chip}
|
|
380
|
+
</span>
|
|
381
|
+
))}
|
|
382
|
+
<code className="text-faint ml-auto font-mono text-[11.5px] whitespace-nowrap">
|
|
383
|
+
{page.path}
|
|
384
|
+
</code>
|
|
385
|
+
</a>
|
|
386
|
+
)
|
|
387
|
+
})
|
|
388
|
+
)}
|
|
389
|
+
{typeof overrides === 'object' && overrides.truncated ? (
|
|
390
|
+
<p className="border-bdr text-hint text-faint m-0 border-t px-4 py-2">
|
|
391
|
+
Showing the 200 most recently changed pages with an override.
|
|
392
|
+
</p>
|
|
393
|
+
) : null}
|
|
394
|
+
</div>
|
|
395
|
+
</section>
|
|
396
|
+
</>
|
|
397
|
+
)
|
|
398
|
+
}
|