@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,331 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useId, useRef, useState, type KeyboardEvent } from 'react'
|
|
4
|
+
import { toast } from 'sonner'
|
|
5
|
+
import type { ChromeSyncStatus } from '@prenta/core'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
CODE_SAMPLES,
|
|
9
|
+
LOCAL_DEV_SAMPLE,
|
|
10
|
+
highlight,
|
|
11
|
+
type CodeSampleId,
|
|
12
|
+
type CodeTokenKind,
|
|
13
|
+
} from './code-samples.js'
|
|
14
|
+
import { shortCommit, timeAgo } from './chrome-form.js'
|
|
15
|
+
import { CARD, CardHead, FieldLabel, Help, INPUT, KeyChip, MONO } from './controls.js'
|
|
16
|
+
|
|
17
|
+
const TOKEN_CLASS: Record<CodeTokenKind, string | undefined> = {
|
|
18
|
+
comment: 'text-code-cmt',
|
|
19
|
+
string: 'text-code-str',
|
|
20
|
+
keyword: 'text-code-kw',
|
|
21
|
+
plain: undefined,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** `.hf-code`: the dark code block, 12px DM Mono at 1.65. */
|
|
25
|
+
const CODE_BLOCK =
|
|
26
|
+
'bg-code-bg text-code-txt relative m-0 overflow-auto rounded-b-12 px-4 py-3.5 font-mono text-[12px] leading-[1.65] whitespace-pre'
|
|
27
|
+
|
|
28
|
+
function Highlighted({ src }: { src: string }) {
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
{highlight(src).map((token, index) =>
|
|
32
|
+
TOKEN_CLASS[token.kind] ? (
|
|
33
|
+
<span key={index} className={TOKEN_CLASS[token.kind]}>
|
|
34
|
+
{token.text}
|
|
35
|
+
</span>
|
|
36
|
+
) : (
|
|
37
|
+
token.text
|
|
38
|
+
),
|
|
39
|
+
)}
|
|
40
|
+
</>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Put text on the clipboard and say so. */
|
|
45
|
+
async function copy(text: string, done = 'Copied') {
|
|
46
|
+
try {
|
|
47
|
+
if (!navigator.clipboard?.writeText) throw new Error('No clipboard')
|
|
48
|
+
await navigator.clipboard.writeText(text)
|
|
49
|
+
toast(done)
|
|
50
|
+
} catch {
|
|
51
|
+
toast.error('Couldn’t copy. Select the text and copy it instead.')
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function HowItsWired() {
|
|
56
|
+
const id = useId()
|
|
57
|
+
const [current, setCurrent] = useState<CodeSampleId>('config')
|
|
58
|
+
const refs = useRef<Array<HTMLButtonElement | null>>([])
|
|
59
|
+
const index = Math.max(
|
|
60
|
+
0,
|
|
61
|
+
CODE_SAMPLES.findIndex((sample) => sample.id === current),
|
|
62
|
+
)
|
|
63
|
+
const sample = CODE_SAMPLES[index]!
|
|
64
|
+
|
|
65
|
+
function onKeyDown(event: KeyboardEvent<HTMLDivElement>) {
|
|
66
|
+
const last = CODE_SAMPLES.length - 1
|
|
67
|
+
let next: number
|
|
68
|
+
if (event.key === 'ArrowRight') next = index === last ? 0 : index + 1
|
|
69
|
+
else if (event.key === 'ArrowLeft') next = index === 0 ? last : index - 1
|
|
70
|
+
else if (event.key === 'Home') next = 0
|
|
71
|
+
else if (event.key === 'End') next = last
|
|
72
|
+
else return
|
|
73
|
+
event.preventDefault()
|
|
74
|
+
setCurrent(CODE_SAMPLES[next]!.id)
|
|
75
|
+
refs.current[next]?.focus()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<section aria-labelledby={`${id}-title`} className={`${CARD} overflow-hidden`}>
|
|
80
|
+
<CardHead id={`${id}-title`} title="How it’s wired">
|
|
81
|
+
<div
|
|
82
|
+
role="tablist"
|
|
83
|
+
aria-label="Code sample"
|
|
84
|
+
onKeyDown={onKeyDown}
|
|
85
|
+
className="rounded-8 border-bdr bg-bg ml-auto flex gap-0.5 border p-0.5"
|
|
86
|
+
>
|
|
87
|
+
{CODE_SAMPLES.map((item, i) => {
|
|
88
|
+
const on = item.id === current
|
|
89
|
+
return (
|
|
90
|
+
<button
|
|
91
|
+
key={item.id}
|
|
92
|
+
ref={(el) => {
|
|
93
|
+
refs.current[i] = el
|
|
94
|
+
}}
|
|
95
|
+
id={`${id}-tab-${item.id}`}
|
|
96
|
+
type="button"
|
|
97
|
+
role="tab"
|
|
98
|
+
aria-selected={on}
|
|
99
|
+
aria-controls={`${id}-panel`}
|
|
100
|
+
tabIndex={on ? 0 : -1}
|
|
101
|
+
onClick={() => setCurrent(item.id)}
|
|
102
|
+
className={`rounded-6 cursor-pointer border-0 px-[9px] py-[3px] text-[12px] leading-[normal] font-semibold ${
|
|
103
|
+
on ? 'bg-card text-txt shadow-shd' : 'text-sub bg-transparent'
|
|
104
|
+
}`}
|
|
105
|
+
>
|
|
106
|
+
{item.label}
|
|
107
|
+
</button>
|
|
108
|
+
)
|
|
109
|
+
})}
|
|
110
|
+
</div>
|
|
111
|
+
</CardHead>
|
|
112
|
+
<pre
|
|
113
|
+
id={`${id}-panel`}
|
|
114
|
+
role="tabpanel"
|
|
115
|
+
aria-labelledby={`${id}-tab-${sample.id}`}
|
|
116
|
+
// Scrolls, so it takes focus for keyboard scrolling.
|
|
117
|
+
tabIndex={0}
|
|
118
|
+
className={`${CODE_BLOCK} max-h-[520px]`}
|
|
119
|
+
>
|
|
120
|
+
<button
|
|
121
|
+
type="button"
|
|
122
|
+
onClick={() => void copy(sample.src)}
|
|
123
|
+
aria-label={`Copy ${sample.file}`}
|
|
124
|
+
className="rounded-6 border-code-btn-bdr bg-code-btn-bg text-code-btn-txt absolute top-2.5 right-2.5 cursor-pointer border px-2 py-[3px] font-mono text-[11px] leading-[1.65] font-medium"
|
|
125
|
+
>
|
|
126
|
+
Copy
|
|
127
|
+
</button>
|
|
128
|
+
<span className="text-code-file mb-2 block text-[11px]">{sample.file}</span>
|
|
129
|
+
<Highlighted src={sample.src} />
|
|
130
|
+
</pre>
|
|
131
|
+
</section>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function SchemaCard({ sync, now }: { sync: ChromeSyncStatus | null; now: number }) {
|
|
136
|
+
const id = useId()
|
|
137
|
+
const synced = sync?.status === 'synced' && sync.source
|
|
138
|
+
const source = sync?.source ?? null
|
|
139
|
+
const regions = [...(sync?.regions ?? []), 'announcement'].join(' · ')
|
|
140
|
+
const rows: Array<[string, string, boolean]> = [
|
|
141
|
+
[
|
|
142
|
+
'Source',
|
|
143
|
+
source ? `${source.repo || 'unknown repo'}@${source.branch || 'unknown'}` : '—',
|
|
144
|
+
true,
|
|
145
|
+
],
|
|
146
|
+
[
|
|
147
|
+
'Last read',
|
|
148
|
+
source
|
|
149
|
+
? `${shortCommit(source.commit) || 'unknown'} · ${timeAgo(source.readAt, now)}`
|
|
150
|
+
: 'never',
|
|
151
|
+
true,
|
|
152
|
+
],
|
|
153
|
+
['Regions', regions, false],
|
|
154
|
+
['Fields', String(sync?.fieldCount ?? 0), false],
|
|
155
|
+
]
|
|
156
|
+
return (
|
|
157
|
+
<section aria-labelledby={`${id}-title`} className={CARD}>
|
|
158
|
+
<CardHead id={`${id}-title`} title="Schema">
|
|
159
|
+
{synced ? (
|
|
160
|
+
<span className="rounded-pill bg-suc-l text-suc inline-flex items-center gap-[5px] px-2 py-px text-[11px] leading-[normal] font-bold">
|
|
161
|
+
● In sync
|
|
162
|
+
</span>
|
|
163
|
+
) : (
|
|
164
|
+
<span className="rounded-pill bg-warn-l text-warn inline-flex items-center gap-[5px] px-2 py-px text-[11px] leading-[normal] font-bold">
|
|
165
|
+
● Not read yet
|
|
166
|
+
</span>
|
|
167
|
+
)}
|
|
168
|
+
</CardHead>
|
|
169
|
+
<dl className="m-0 flex flex-col">
|
|
170
|
+
{rows.map(([label, value, mono]) => (
|
|
171
|
+
<div
|
|
172
|
+
key={label}
|
|
173
|
+
className="border-bdr text-sm2 text-sub flex items-center gap-2.5 border-t px-4 py-[9px] leading-[normal] first:border-t-0"
|
|
174
|
+
>
|
|
175
|
+
<dt>{label}</dt>
|
|
176
|
+
<dd
|
|
177
|
+
className={`text-txt m-0 ml-auto min-w-0 truncate text-right font-semibold ${
|
|
178
|
+
mono ? 'font-mono text-[11.5px]' : ''
|
|
179
|
+
}`}
|
|
180
|
+
>
|
|
181
|
+
{value}
|
|
182
|
+
</dd>
|
|
183
|
+
</div>
|
|
184
|
+
))}
|
|
185
|
+
</dl>
|
|
186
|
+
<div className="text-hint text-faint px-4 pb-3 leading-[1.45]">
|
|
187
|
+
{synced ? null : (
|
|
188
|
+
<>
|
|
189
|
+
Add <KeyChip>prenta push chrome</KeyChip> to your deploy to send the header and
|
|
190
|
+
footer.{' '}
|
|
191
|
+
</>
|
|
192
|
+
)}
|
|
193
|
+
Read on every deploy. Removed fields keep their values for 30 days; renamed fields need{' '}
|
|
194
|
+
<KeyChip>renamedFrom</KeyChip>.
|
|
195
|
+
</div>
|
|
196
|
+
</section>
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface ChromeSettingsForm {
|
|
201
|
+
revalidateUrl: string
|
|
202
|
+
previewUrl: string
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function OnSaveCard({
|
|
206
|
+
form,
|
|
207
|
+
onChange,
|
|
208
|
+
canEdit,
|
|
209
|
+
secret,
|
|
210
|
+
problem,
|
|
211
|
+
}: {
|
|
212
|
+
form: ChromeSettingsForm
|
|
213
|
+
onChange: (form: ChromeSettingsForm) => void
|
|
214
|
+
/** Only admins see the revalidate URL and can change these. */
|
|
215
|
+
canEdit: boolean
|
|
216
|
+
/** Shown once, after a save creates the revalidate webhook. */
|
|
217
|
+
secret: string | null
|
|
218
|
+
problem: string | null
|
|
219
|
+
}) {
|
|
220
|
+
const id = useId()
|
|
221
|
+
return (
|
|
222
|
+
<section aria-labelledby={`${id}-title`} className={CARD}>
|
|
223
|
+
<CardHead id={`${id}-title`} title="On save" />
|
|
224
|
+
<div className="flex flex-col gap-3.5 px-4 py-3.5">
|
|
225
|
+
<div>
|
|
226
|
+
<FieldLabel htmlFor={`${id}-revalidate`}>Revalidate URL</FieldLabel>
|
|
227
|
+
<input
|
|
228
|
+
id={`${id}-revalidate`}
|
|
229
|
+
className={`${INPUT} ${MONO}`}
|
|
230
|
+
value={form.revalidateUrl}
|
|
231
|
+
placeholder={canEdit ? 'https://' : 'Only admins can see this'}
|
|
232
|
+
disabled={!canEdit}
|
|
233
|
+
spellCheck={false}
|
|
234
|
+
onChange={(event) => onChange({ ...form, revalidateUrl: event.target.value })}
|
|
235
|
+
/>
|
|
236
|
+
<Help>
|
|
237
|
+
POSTs <KeyChip>chrome.updated</KeyChip> with the changed region. Also fires when a
|
|
238
|
+
scheduled announcement starts or ends.
|
|
239
|
+
</Help>
|
|
240
|
+
{secret ? (
|
|
241
|
+
<div
|
|
242
|
+
role="status"
|
|
243
|
+
className="rounded-9 border-bdr bg-bg text-hint text-sub mt-2 flex flex-wrap items-center gap-2 border px-3 py-2 leading-[1.45]"
|
|
244
|
+
>
|
|
245
|
+
<span className="min-w-0 flex-1">
|
|
246
|
+
Signing secret <KeyChip>{secret}</KeyChip>. Copy it now; it won’t be shown again.
|
|
247
|
+
</span>
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
onClick={() => void copy(secret, 'Secret copied')}
|
|
251
|
+
className="rounded-8 border-bdr bg-card text-meta text-txt hover:border-acc-m hover:text-acc inline-flex h-7 cursor-pointer items-center border px-[9px] font-[550]"
|
|
252
|
+
>
|
|
253
|
+
Copy
|
|
254
|
+
</button>
|
|
255
|
+
</div>
|
|
256
|
+
) : null}
|
|
257
|
+
</div>
|
|
258
|
+
<div>
|
|
259
|
+
<FieldLabel htmlFor={`${id}-preview`}>Preview URL</FieldLabel>
|
|
260
|
+
<input
|
|
261
|
+
id={`${id}-preview`}
|
|
262
|
+
className={`${INPUT} ${MONO}`}
|
|
263
|
+
value={form.previewUrl}
|
|
264
|
+
placeholder={canEdit ? 'https://' : undefined}
|
|
265
|
+
disabled={!canEdit}
|
|
266
|
+
spellCheck={false}
|
|
267
|
+
onChange={(event) => onChange({ ...form, previewUrl: event.target.value })}
|
|
268
|
+
/>
|
|
269
|
+
<Help>
|
|
270
|
+
Renders your real components with draft values. Leave blank to use the built-in mock.
|
|
271
|
+
</Help>
|
|
272
|
+
</div>
|
|
273
|
+
{problem ? (
|
|
274
|
+
<Help tone="error">
|
|
275
|
+
<span role="alert">{problem}</span>
|
|
276
|
+
</Help>
|
|
277
|
+
) : null}
|
|
278
|
+
</div>
|
|
279
|
+
</section>
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function LocalDevCard() {
|
|
284
|
+
const id = useId()
|
|
285
|
+
return (
|
|
286
|
+
<section aria-labelledby={`${id}-title`} className={`${CARD} overflow-hidden`}>
|
|
287
|
+
<CardHead id={`${id}-title`} title="Local dev" />
|
|
288
|
+
<pre tabIndex={0} aria-label="Local dev commands" className={CODE_BLOCK}>
|
|
289
|
+
<Highlighted src={LOCAL_DEV_SAMPLE} />
|
|
290
|
+
</pre>
|
|
291
|
+
</section>
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface DeveloperTabProps {
|
|
296
|
+
sync: ChromeSyncStatus | null
|
|
297
|
+
settings: ChromeSettingsForm
|
|
298
|
+
onSettingsChange: (form: ChromeSettingsForm) => void
|
|
299
|
+
canEditSettings: boolean
|
|
300
|
+
secret: string | null
|
|
301
|
+
settingsProblem: string | null
|
|
302
|
+
now: number
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** The Developer tab: the code samples, the live schema status, the On save settings and local dev. */
|
|
306
|
+
export function DeveloperTab({
|
|
307
|
+
sync,
|
|
308
|
+
settings,
|
|
309
|
+
onSettingsChange,
|
|
310
|
+
canEditSettings,
|
|
311
|
+
secret,
|
|
312
|
+
settingsProblem,
|
|
313
|
+
now,
|
|
314
|
+
}: DeveloperTabProps) {
|
|
315
|
+
return (
|
|
316
|
+
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,340px)] items-start gap-[18px] [@media(width<=1100px)]:grid-cols-[minmax(0,1fr)]">
|
|
317
|
+
<HowItsWired />
|
|
318
|
+
<div className="flex min-w-0 flex-col gap-3.5">
|
|
319
|
+
<SchemaCard sync={sync} now={now} />
|
|
320
|
+
<OnSaveCard
|
|
321
|
+
form={settings}
|
|
322
|
+
onChange={onSettingsChange}
|
|
323
|
+
canEdit={canEditSettings}
|
|
324
|
+
secret={secret}
|
|
325
|
+
problem={settingsProblem}
|
|
326
|
+
/>
|
|
327
|
+
<LocalDevCard />
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
)
|
|
331
|
+
}
|