@actuate-media/cms-admin 0.36.1 → 0.37.1
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/__tests__/layout/sidebar-brand.render.test.d.ts +2 -0
- package/dist/__tests__/layout/sidebar-brand.render.test.d.ts.map +1 -0
- package/dist/__tests__/layout/sidebar-brand.render.test.js +44 -0
- package/dist/__tests__/layout/sidebar-brand.render.test.js.map +1 -0
- package/dist/__tests__/views/appearance-settings.render.test.js +4 -4
- package/dist/__tests__/views/login-branding.render.test.d.ts +2 -0
- package/dist/__tests__/views/login-branding.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/login-branding.render.test.js +80 -0
- package/dist/__tests__/views/login-branding.render.test.js.map +1 -0
- package/dist/__tests__/views/login-mfa.render.test.js +6 -0
- package/dist/__tests__/views/login-mfa.render.test.js.map +1 -1
- package/dist/__tests__/views/updates-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/updates-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/updates-tab.render.test.js +132 -0
- package/dist/__tests__/views/updates-tab.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/LogoPlaceholder.d.ts +17 -0
- package/dist/components/LogoPlaceholder.d.ts.map +1 -0
- package/dist/components/LogoPlaceholder.js +13 -0
- package/dist/components/LogoPlaceholder.js.map +1 -0
- package/dist/components/ui/Badge.d.ts +1 -1
- package/dist/components/ui/Button.d.ts +1 -1
- package/dist/components/ui/Card.d.ts +2 -2
- package/dist/components/ui/Input.d.ts +1 -1
- package/dist/components/ui/Select.d.ts +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +13 -10
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +66 -8
- package/dist/views/Login.js.map +1 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +4 -112
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/settings/AdminThemeCard.js +1 -1
- package/dist/views/settings/BrandPreviewCard.js +1 -1
- package/dist/views/settings/BrandingCard.d.ts.map +1 -1
- package/dist/views/settings/BrandingCard.js +34 -19
- package/dist/views/settings/BrandingCard.js.map +1 -1
- package/dist/views/settings/TypographyMotionCard.js +1 -1
- package/dist/views/settings/UpdatesTab.d.ts +6 -0
- package/dist/views/settings/UpdatesTab.d.ts.map +1 -0
- package/dist/views/settings/UpdatesTab.js +80 -0
- package/dist/views/settings/UpdatesTab.js.map +1 -0
- package/dist/views/settings/components.d.ts +20 -0
- package/dist/views/settings/components.d.ts.map +1 -1
- package/dist/views/settings/components.js +11 -0
- package/dist/views/settings/components.js.map +1 -1
- package/dist/views/settings/useUpdates.d.ts +67 -0
- package/dist/views/settings/useUpdates.d.ts.map +1 -0
- package/dist/views/settings/useUpdates.js +127 -0
- package/dist/views/settings/useUpdates.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/layout/sidebar-brand.render.test.tsx +58 -0
- package/src/__tests__/views/appearance-settings.render.test.tsx +4 -4
- package/src/__tests__/views/login-branding.render.test.tsx +96 -0
- package/src/__tests__/views/login-mfa.render.test.tsx +7 -0
- package/src/__tests__/views/updates-tab.render.test.tsx +161 -0
- package/src/components/LogoPlaceholder.tsx +55 -0
- package/src/layout/Sidebar.tsx +14 -24
- package/src/views/Login.tsx +70 -9
- package/src/views/Settings.tsx +2 -438
- package/src/views/settings/AdminThemeCard.tsx +1 -1
- package/src/views/settings/BrandPreviewCard.tsx +1 -1
- package/src/views/settings/BrandingCard.tsx +179 -180
- package/src/views/settings/TypographyMotionCard.tsx +1 -1
- package/src/views/settings/UpdatesTab.tsx +545 -0
- package/src/views/settings/components.tsx +51 -0
- package/src/views/settings/useUpdates.ts +183 -0
package/src/views/Settings.tsx
CHANGED
|
@@ -3,16 +3,9 @@
|
|
|
3
3
|
import * as Tabs from '@radix-ui/react-tabs'
|
|
4
4
|
import {
|
|
5
5
|
Bot,
|
|
6
|
-
Eye,
|
|
7
|
-
EyeOff,
|
|
8
6
|
Loader2,
|
|
9
7
|
AlertTriangle,
|
|
10
8
|
Download,
|
|
11
|
-
CheckCircle2,
|
|
12
|
-
ArrowUpCircle,
|
|
13
|
-
ExternalLink,
|
|
14
|
-
RefreshCw,
|
|
15
|
-
GitPullRequest,
|
|
16
9
|
Layers,
|
|
17
10
|
Users as UsersIcon,
|
|
18
11
|
KeyRound,
|
|
@@ -28,6 +21,7 @@ import { AISettingsTab } from './settings/AISettingsTab.js'
|
|
|
28
21
|
import { AppearanceTab } from './settings/AppearanceTab.js'
|
|
29
22
|
import { SecurityTab } from './settings/SecurityTab.js'
|
|
30
23
|
import { SettingsCard } from './settings/components.js'
|
|
24
|
+
import { UpdatesTab } from './settings/UpdatesTab.js'
|
|
31
25
|
import { Users as UsersView } from './Users.js'
|
|
32
26
|
import { ApiKeys as ApiKeysView } from './ApiKeys.js'
|
|
33
27
|
|
|
@@ -330,7 +324,7 @@ export function Settings({
|
|
|
330
324
|
</Tabs.Content>
|
|
331
325
|
|
|
332
326
|
<Tabs.Content value="updates" className="space-y-4">
|
|
333
|
-
<
|
|
327
|
+
<UpdatesTab role={session?.user?.role} />
|
|
334
328
|
</Tabs.Content>
|
|
335
329
|
</Tabs.Root>
|
|
336
330
|
|
|
@@ -349,433 +343,3 @@ export function Settings({
|
|
|
349
343
|
</div>
|
|
350
344
|
)
|
|
351
345
|
}
|
|
352
|
-
|
|
353
|
-
// ---------------------------------------------------------------------------
|
|
354
|
-
// Updates Panel
|
|
355
|
-
// ---------------------------------------------------------------------------
|
|
356
|
-
|
|
357
|
-
interface UpdateInfo {
|
|
358
|
-
current: string
|
|
359
|
-
latest: string
|
|
360
|
-
updateAvailable: boolean
|
|
361
|
-
severity?: 'patch' | 'minor' | 'major'
|
|
362
|
-
releaseDate?: string
|
|
363
|
-
changelog?: Array<{ version: string; date: string; summary: string }>
|
|
364
|
-
updateCommand?: string
|
|
365
|
-
hasGithubToken?: boolean
|
|
366
|
-
githubRepo?: string
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function UpdatesPanel() {
|
|
370
|
-
const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null)
|
|
371
|
-
const [checking, setChecking] = useState(false)
|
|
372
|
-
const [checkError, setCheckError] = useState('')
|
|
373
|
-
const [applying, setApplying] = useState(false)
|
|
374
|
-
const [prResult, setPrResult] = useState<{ prUrl: string; prNumber: number } | null>(null)
|
|
375
|
-
const [hasChecked, setHasChecked] = useState(false)
|
|
376
|
-
|
|
377
|
-
// GitHub config
|
|
378
|
-
const [ghToken, setGhToken] = useState('')
|
|
379
|
-
const [ghRepo, setGhRepo] = useState('')
|
|
380
|
-
const [showGhToken, setShowGhToken] = useState(false)
|
|
381
|
-
const [savingConfig, setSavingConfig] = useState(false)
|
|
382
|
-
const [configSaved, setConfigSaved] = useState(false)
|
|
383
|
-
|
|
384
|
-
const checkForUpdates = async () => {
|
|
385
|
-
setChecking(true)
|
|
386
|
-
setCheckError('')
|
|
387
|
-
setPrResult(null)
|
|
388
|
-
|
|
389
|
-
try {
|
|
390
|
-
const res = await cmsApi<UpdateInfo>('/updates/check')
|
|
391
|
-
if (res.error) {
|
|
392
|
-
setCheckError(res.error)
|
|
393
|
-
} else if (res.data) {
|
|
394
|
-
setUpdateInfo(res.data)
|
|
395
|
-
if (res.data.githubRepo) setGhRepo(res.data.githubRepo)
|
|
396
|
-
}
|
|
397
|
-
} catch {
|
|
398
|
-
setCheckError('Unable to check for updates. Please try again.')
|
|
399
|
-
} finally {
|
|
400
|
-
setChecking(false)
|
|
401
|
-
setHasChecked(true)
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const saveGitHubConfig = async () => {
|
|
406
|
-
setSavingConfig(true)
|
|
407
|
-
setConfigSaved(false)
|
|
408
|
-
try {
|
|
409
|
-
const res = await cmsApi('/updates/config', {
|
|
410
|
-
method: 'PUT',
|
|
411
|
-
body: JSON.stringify({
|
|
412
|
-
...(ghToken ? { githubToken: ghToken } : {}),
|
|
413
|
-
...(ghRepo ? { githubRepo: ghRepo } : {}),
|
|
414
|
-
}),
|
|
415
|
-
})
|
|
416
|
-
if (res.error) {
|
|
417
|
-
toast.error(res.error)
|
|
418
|
-
} else {
|
|
419
|
-
toast.success('GitHub configuration saved and encrypted.')
|
|
420
|
-
setConfigSaved(true)
|
|
421
|
-
setGhToken('')
|
|
422
|
-
if (updateInfo) {
|
|
423
|
-
setUpdateInfo({ ...updateInfo, hasGithubToken: true, githubRepo: ghRepo })
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
} catch {
|
|
427
|
-
toast.error('Failed to save configuration.')
|
|
428
|
-
} finally {
|
|
429
|
-
setSavingConfig(false)
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const applyUpdate = async () => {
|
|
434
|
-
if (!updateInfo?.latest) return
|
|
435
|
-
setApplying(true)
|
|
436
|
-
|
|
437
|
-
try {
|
|
438
|
-
const res = await cmsApi<{ prUrl: string; prNumber: number }>('/updates/apply', {
|
|
439
|
-
method: 'POST',
|
|
440
|
-
body: JSON.stringify({ targetVersion: updateInfo.latest }),
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
if (res.error) {
|
|
444
|
-
toast.error(res.error)
|
|
445
|
-
} else if (res.data) {
|
|
446
|
-
setPrResult(res.data)
|
|
447
|
-
toast.success('Update PR created successfully!')
|
|
448
|
-
}
|
|
449
|
-
} catch {
|
|
450
|
-
toast.error('Failed to create update PR. Check your GitHub configuration.')
|
|
451
|
-
} finally {
|
|
452
|
-
setApplying(false)
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
useEffect(() => {
|
|
457
|
-
checkForUpdates()
|
|
458
|
-
}, [])
|
|
459
|
-
|
|
460
|
-
// Semantic severity tokens so update banners match the rest of the admin in
|
|
461
|
-
// both light and dark mode (no raw palette colors). Concrete keys (not a
|
|
462
|
-
// string-index Record) keep the lookup non-nullable.
|
|
463
|
-
const severityColors = {
|
|
464
|
-
patch: { banner: 'border-brand/30 bg-brand/10', text: 'text-brand', label: 'Patch' },
|
|
465
|
-
minor: { banner: 'border-warning/30 bg-warning/10', text: 'text-warning', label: 'Minor' },
|
|
466
|
-
major: {
|
|
467
|
-
banner: 'border-destructive/30 bg-destructive/10',
|
|
468
|
-
text: 'text-destructive',
|
|
469
|
-
label: 'Major',
|
|
470
|
-
},
|
|
471
|
-
}
|
|
472
|
-
const sev = severityColors[updateInfo?.severity ?? 'patch']
|
|
473
|
-
const ghInputClass =
|
|
474
|
-
'w-full rounded-md border border-border bg-input-background px-3 py-2 text-base text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none'
|
|
475
|
-
|
|
476
|
-
return (
|
|
477
|
-
<>
|
|
478
|
-
{/* Current Version Card */}
|
|
479
|
-
<SettingsCard
|
|
480
|
-
title="Actuate CMS"
|
|
481
|
-
description="Keep your CMS up to date with the latest features and security patches."
|
|
482
|
-
actions={
|
|
483
|
-
<button
|
|
484
|
-
type="button"
|
|
485
|
-
onClick={checkForUpdates}
|
|
486
|
-
disabled={checking}
|
|
487
|
-
className="border-border text-foreground hover:bg-accent inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50"
|
|
488
|
-
>
|
|
489
|
-
<RefreshCw size={16} className={checking ? 'animate-spin' : ''} aria-hidden="true" />
|
|
490
|
-
{checking ? 'Checking…' : 'Check for Updates'}
|
|
491
|
-
</button>
|
|
492
|
-
}
|
|
493
|
-
>
|
|
494
|
-
<p className="text-muted-foreground text-sm">
|
|
495
|
-
Current version:{' '}
|
|
496
|
-
<span className="text-foreground font-mono font-medium">
|
|
497
|
-
{updateInfo?.current ?? '…'}
|
|
498
|
-
</span>
|
|
499
|
-
</p>
|
|
500
|
-
</SettingsCard>
|
|
501
|
-
|
|
502
|
-
{/* Error State */}
|
|
503
|
-
{checkError && (
|
|
504
|
-
<div
|
|
505
|
-
role="alert"
|
|
506
|
-
className="border-destructive/30 bg-destructive/10 text-destructive flex items-center gap-3 rounded-lg border p-3 text-sm"
|
|
507
|
-
>
|
|
508
|
-
<AlertTriangle size={20} className="shrink-0" aria-hidden="true" />
|
|
509
|
-
<span className="flex-1">{checkError}</span>
|
|
510
|
-
<button
|
|
511
|
-
onClick={checkForUpdates}
|
|
512
|
-
className="border-destructive/40 rounded-md border px-3 py-1 transition-colors hover:opacity-80"
|
|
513
|
-
>
|
|
514
|
-
Retry
|
|
515
|
-
</button>
|
|
516
|
-
</div>
|
|
517
|
-
)}
|
|
518
|
-
|
|
519
|
-
{/* Up to Date */}
|
|
520
|
-
{hasChecked && updateInfo && !updateInfo.updateAvailable && !checkError && (
|
|
521
|
-
<div className="border-success/30 bg-success/10 text-success flex items-center gap-3 rounded-lg border p-4">
|
|
522
|
-
<CheckCircle2 size={24} className="shrink-0" aria-hidden="true" />
|
|
523
|
-
<div>
|
|
524
|
-
<h3 className="text-foreground font-medium">You're up to date!</h3>
|
|
525
|
-
<p className="text-muted-foreground mt-0.5 text-sm">
|
|
526
|
-
Actuate CMS <span className="font-mono">{updateInfo.current}</span> is the latest
|
|
527
|
-
version.
|
|
528
|
-
</p>
|
|
529
|
-
</div>
|
|
530
|
-
</div>
|
|
531
|
-
)}
|
|
532
|
-
|
|
533
|
-
{/* Update Available */}
|
|
534
|
-
{updateInfo?.updateAvailable && (
|
|
535
|
-
<>
|
|
536
|
-
<div className={`rounded-lg border p-4 ${sev.banner}`}>
|
|
537
|
-
<div className="flex items-start gap-3">
|
|
538
|
-
<ArrowUpCircle
|
|
539
|
-
size={24}
|
|
540
|
-
className={`mt-0.5 shrink-0 ${sev.text}`}
|
|
541
|
-
aria-hidden="true"
|
|
542
|
-
/>
|
|
543
|
-
<div className="flex-1">
|
|
544
|
-
<div className="flex items-center gap-2">
|
|
545
|
-
<h3 className={`font-medium ${sev.text}`}>Update Available</h3>
|
|
546
|
-
<span
|
|
547
|
-
className={`inline-flex items-center rounded border px-2 py-0.5 text-xs font-medium ${sev.banner} ${sev.text}`}
|
|
548
|
-
>
|
|
549
|
-
{sev.label}
|
|
550
|
-
</span>
|
|
551
|
-
</div>
|
|
552
|
-
<p className="text-muted-foreground mt-1 text-sm">
|
|
553
|
-
<span className="font-mono">{updateInfo.current}</span> →{' '}
|
|
554
|
-
<span className="text-foreground font-mono font-medium">{updateInfo.latest}</span>
|
|
555
|
-
{updateInfo.releaseDate && (
|
|
556
|
-
<span className="ml-2 text-xs opacity-70">
|
|
557
|
-
Released {updateInfo.releaseDate}
|
|
558
|
-
</span>
|
|
559
|
-
)}
|
|
560
|
-
</p>
|
|
561
|
-
</div>
|
|
562
|
-
</div>
|
|
563
|
-
|
|
564
|
-
<div className="mt-4 flex items-center gap-3">
|
|
565
|
-
<button
|
|
566
|
-
type="button"
|
|
567
|
-
onClick={applyUpdate}
|
|
568
|
-
disabled={applying}
|
|
569
|
-
className="bg-brand text-brand-foreground inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-opacity hover:opacity-90 disabled:opacity-50"
|
|
570
|
-
>
|
|
571
|
-
{applying ? (
|
|
572
|
-
<>
|
|
573
|
-
<Loader2 size={16} className="animate-spin" aria-hidden="true" />
|
|
574
|
-
Creating PR…
|
|
575
|
-
</>
|
|
576
|
-
) : (
|
|
577
|
-
<>
|
|
578
|
-
<GitPullRequest size={16} aria-hidden="true" />
|
|
579
|
-
Create Update PR
|
|
580
|
-
</>
|
|
581
|
-
)}
|
|
582
|
-
</button>
|
|
583
|
-
<span className="text-muted-foreground text-xs">
|
|
584
|
-
Opens a pull request on your repository
|
|
585
|
-
</span>
|
|
586
|
-
</div>
|
|
587
|
-
|
|
588
|
-
{updateInfo.updateCommand && (
|
|
589
|
-
<div className="border-border bg-card mt-3 rounded-md border p-3">
|
|
590
|
-
<p className="text-muted-foreground mb-1 text-xs">Or update manually:</p>
|
|
591
|
-
<code className="bg-muted text-foreground block rounded px-2 py-1.5 font-mono text-xs select-all">
|
|
592
|
-
{updateInfo.updateCommand}
|
|
593
|
-
</code>
|
|
594
|
-
</div>
|
|
595
|
-
)}
|
|
596
|
-
</div>
|
|
597
|
-
|
|
598
|
-
{/* Changelog */}
|
|
599
|
-
{updateInfo.changelog && updateInfo.changelog.length > 0 && (
|
|
600
|
-
<SettingsCard title="Changelog">
|
|
601
|
-
<div className="max-h-64 space-y-2 overflow-y-auto">
|
|
602
|
-
{updateInfo.changelog.map((entry) => (
|
|
603
|
-
<div key={entry.version} className="flex items-baseline gap-3 text-sm">
|
|
604
|
-
<span className="text-muted-foreground w-14 shrink-0 font-mono text-xs">
|
|
605
|
-
{entry.version}
|
|
606
|
-
</span>
|
|
607
|
-
<span className="text-muted-foreground w-20 shrink-0 text-xs">
|
|
608
|
-
{entry.date}
|
|
609
|
-
</span>
|
|
610
|
-
<span className="text-foreground">{entry.summary}</span>
|
|
611
|
-
</div>
|
|
612
|
-
))}
|
|
613
|
-
</div>
|
|
614
|
-
</SettingsCard>
|
|
615
|
-
)}
|
|
616
|
-
</>
|
|
617
|
-
)}
|
|
618
|
-
|
|
619
|
-
{/* PR Created */}
|
|
620
|
-
{prResult && (
|
|
621
|
-
<div className="border-success/30 bg-success/10 rounded-lg border p-4">
|
|
622
|
-
<div className="flex items-start gap-3">
|
|
623
|
-
<GitPullRequest size={20} className="text-success mt-0.5 shrink-0" aria-hidden="true" />
|
|
624
|
-
<div>
|
|
625
|
-
<h3 className="text-foreground font-medium">Pull Request Created</h3>
|
|
626
|
-
<p className="text-muted-foreground mt-1 text-sm">
|
|
627
|
-
PR #{prResult.prNumber} has been created on your repository. Review and merge it to
|
|
628
|
-
apply the update, then run{' '}
|
|
629
|
-
<code className="bg-muted rounded px-1 font-mono text-xs">
|
|
630
|
-
npx prisma migrate deploy
|
|
631
|
-
</code>
|
|
632
|
-
.
|
|
633
|
-
</p>
|
|
634
|
-
<a
|
|
635
|
-
href={prResult.prUrl}
|
|
636
|
-
target="_blank"
|
|
637
|
-
rel="noopener noreferrer"
|
|
638
|
-
className="text-brand mt-2 inline-flex items-center gap-1.5 text-sm font-medium hover:underline"
|
|
639
|
-
>
|
|
640
|
-
<ExternalLink size={16} aria-hidden="true" />
|
|
641
|
-
View Pull Request
|
|
642
|
-
</a>
|
|
643
|
-
</div>
|
|
644
|
-
</div>
|
|
645
|
-
</div>
|
|
646
|
-
)}
|
|
647
|
-
|
|
648
|
-
{/* GitHub Configuration */}
|
|
649
|
-
<SettingsCard
|
|
650
|
-
title="GitHub Integration"
|
|
651
|
-
description="Connect your repository to enable one-click update PRs. Credentials are encrypted at rest (AES-256-GCM)."
|
|
652
|
-
>
|
|
653
|
-
<div className="space-y-4">
|
|
654
|
-
<div>
|
|
655
|
-
<label
|
|
656
|
-
htmlFor="updates-gh-repo"
|
|
657
|
-
className="text-foreground mb-1 block text-sm font-medium"
|
|
658
|
-
>
|
|
659
|
-
Repository
|
|
660
|
-
</label>
|
|
661
|
-
<input
|
|
662
|
-
id="updates-gh-repo"
|
|
663
|
-
type="text"
|
|
664
|
-
value={ghRepo}
|
|
665
|
-
onChange={(e) => setGhRepo(e.target.value)}
|
|
666
|
-
placeholder="owner/repo"
|
|
667
|
-
className={ghInputClass}
|
|
668
|
-
/>
|
|
669
|
-
<p className="text-muted-foreground mt-1 text-sm">
|
|
670
|
-
e.g.{' '}
|
|
671
|
-
<code className="bg-muted rounded px-1 font-mono">actuate-media/my-client-site</code>
|
|
672
|
-
</p>
|
|
673
|
-
</div>
|
|
674
|
-
<div>
|
|
675
|
-
<label
|
|
676
|
-
htmlFor="updates-gh-token"
|
|
677
|
-
className="text-foreground mb-1 block text-sm font-medium"
|
|
678
|
-
>
|
|
679
|
-
GitHub Token
|
|
680
|
-
{updateInfo?.hasGithubToken && !ghToken && (
|
|
681
|
-
<span className="text-success ml-2 text-xs font-normal">Saved</span>
|
|
682
|
-
)}
|
|
683
|
-
</label>
|
|
684
|
-
<div className="relative">
|
|
685
|
-
<input
|
|
686
|
-
id="updates-gh-token"
|
|
687
|
-
type={showGhToken ? 'text' : 'password'}
|
|
688
|
-
value={ghToken}
|
|
689
|
-
onChange={(e) => setGhToken(e.target.value)}
|
|
690
|
-
placeholder={
|
|
691
|
-
updateInfo?.hasGithubToken ? '••••••••••••••••' : 'ghp_... or github_pat_...'
|
|
692
|
-
}
|
|
693
|
-
className={`${ghInputClass} pr-10`}
|
|
694
|
-
/>
|
|
695
|
-
<button
|
|
696
|
-
type="button"
|
|
697
|
-
onClick={() => setShowGhToken(!showGhToken)}
|
|
698
|
-
aria-label={showGhToken ? 'Hide token' : 'Show token'}
|
|
699
|
-
className="text-muted-foreground hover:text-foreground absolute top-1/2 right-2 -translate-y-1/2 p-1"
|
|
700
|
-
>
|
|
701
|
-
{showGhToken ? (
|
|
702
|
-
<EyeOff size={16} aria-hidden="true" />
|
|
703
|
-
) : (
|
|
704
|
-
<Eye size={16} aria-hidden="true" />
|
|
705
|
-
)}
|
|
706
|
-
</button>
|
|
707
|
-
</div>
|
|
708
|
-
<p className="text-muted-foreground mt-1 text-sm">
|
|
709
|
-
Needs <code className="bg-muted rounded px-1 font-mono">repo</code> scope. Create at{' '}
|
|
710
|
-
<a
|
|
711
|
-
href="https://github.com/settings/tokens"
|
|
712
|
-
target="_blank"
|
|
713
|
-
rel="noopener noreferrer"
|
|
714
|
-
className="text-brand hover:underline"
|
|
715
|
-
>
|
|
716
|
-
github.com/settings/tokens
|
|
717
|
-
</a>
|
|
718
|
-
</p>
|
|
719
|
-
</div>
|
|
720
|
-
|
|
721
|
-
<button
|
|
722
|
-
type="button"
|
|
723
|
-
onClick={saveGitHubConfig}
|
|
724
|
-
disabled={savingConfig || (!ghToken && !ghRepo)}
|
|
725
|
-
className="bg-brand text-brand-foreground inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-opacity hover:opacity-90 disabled:opacity-50"
|
|
726
|
-
>
|
|
727
|
-
{savingConfig ? (
|
|
728
|
-
<>
|
|
729
|
-
<Loader2 size={16} className="animate-spin" aria-hidden="true" />
|
|
730
|
-
Encrypting & Saving…
|
|
731
|
-
</>
|
|
732
|
-
) : (
|
|
733
|
-
'Save Configuration'
|
|
734
|
-
)}
|
|
735
|
-
</button>
|
|
736
|
-
</div>
|
|
737
|
-
</SettingsCard>
|
|
738
|
-
|
|
739
|
-
{/* Help Text */}
|
|
740
|
-
<SettingsCard title="How Updates Work">
|
|
741
|
-
<ul className="text-muted-foreground space-y-1.5 text-sm">
|
|
742
|
-
<li className="flex items-start gap-2">
|
|
743
|
-
<span className="bg-brand/10 text-brand mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[10px] font-medium">
|
|
744
|
-
1
|
|
745
|
-
</span>
|
|
746
|
-
<span>
|
|
747
|
-
Click "Check for Updates" to see if a new version is available on npm.
|
|
748
|
-
</span>
|
|
749
|
-
</li>
|
|
750
|
-
<li className="flex items-start gap-2">
|
|
751
|
-
<span className="bg-brand/10 text-brand mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[10px] font-medium">
|
|
752
|
-
2
|
|
753
|
-
</span>
|
|
754
|
-
<span>
|
|
755
|
-
Add your GitHub token and repository above. They're encrypted at rest — never
|
|
756
|
-
stored in plaintext.
|
|
757
|
-
</span>
|
|
758
|
-
</li>
|
|
759
|
-
<li className="flex items-start gap-2">
|
|
760
|
-
<span className="bg-brand/10 text-brand mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[10px] font-medium">
|
|
761
|
-
3
|
|
762
|
-
</span>
|
|
763
|
-
<span>
|
|
764
|
-
Click "Create Update PR" to open a pull request that bumps your CMS packages
|
|
765
|
-
automatically.
|
|
766
|
-
</span>
|
|
767
|
-
</li>
|
|
768
|
-
<li className="flex items-start gap-2">
|
|
769
|
-
<span className="bg-brand/10 text-brand mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[10px] font-medium">
|
|
770
|
-
4
|
|
771
|
-
</span>
|
|
772
|
-
<span>
|
|
773
|
-
Review and merge the PR, then deploy. Database migrations run automatically via{' '}
|
|
774
|
-
<code className="bg-muted rounded px-1 font-mono">prisma migrate deploy</code>.
|
|
775
|
-
</span>
|
|
776
|
-
</li>
|
|
777
|
-
</ul>
|
|
778
|
-
</SettingsCard>
|
|
779
|
-
</>
|
|
780
|
-
)
|
|
781
|
-
}
|
|
@@ -66,7 +66,7 @@ export function AdminThemeCard({ s, canEdit }: { s: UseAppearanceSettings; canEd
|
|
|
66
66
|
return (
|
|
67
67
|
<section className="border-border bg-card rounded-lg border p-6">
|
|
68
68
|
<header className="mb-4">
|
|
69
|
-
<h3 className="text-foreground text-lg font-medium">Admin
|
|
69
|
+
<h3 className="text-foreground text-lg font-medium">Admin theme</h3>
|
|
70
70
|
<p className="text-muted-foreground mt-0.5 text-sm">
|
|
71
71
|
Controls the default look of the admin. Individual users can override the color mode.
|
|
72
72
|
</p>
|
|
@@ -60,7 +60,7 @@ export function BrandPreviewCard({ s }: { s: UseAppearanceSettings }) {
|
|
|
60
60
|
<section className="border-border bg-card rounded-lg border p-6">
|
|
61
61
|
<header className="mb-4 flex items-center justify-between gap-2">
|
|
62
62
|
<div>
|
|
63
|
-
<h3 className="text-foreground text-lg font-medium">Brand
|
|
63
|
+
<h3 className="text-foreground text-lg font-medium">Brand preview</h3>
|
|
64
64
|
<p className="text-muted-foreground mt-0.5 text-sm">
|
|
65
65
|
How your brand renders across the admin and public site.
|
|
66
66
|
</p>
|