@byline/admin 2.5.2 → 2.6.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/fields/array/array-field.d.ts +14 -0
- package/dist/fields/array/array-field.js +177 -0
- package/dist/fields/array/array-field.module.js +11 -0
- package/dist/fields/array/array-field_module.css +32 -0
- package/dist/fields/blocks/blocks-field.d.ts +13 -0
- package/dist/fields/blocks/blocks-field.js +245 -0
- package/dist/fields/blocks/blocks-field.module.js +26 -0
- package/dist/fields/blocks/blocks-field_module.css +107 -0
- package/dist/fields/checkbox/checkbox-field.d.ts +16 -0
- package/dist/fields/checkbox/checkbox-field.js +28 -0
- package/dist/fields/checkbox/checkbox-field.module.js +6 -0
- package/dist/fields/checkbox/checkbox-field_module.css +4 -0
- package/dist/fields/column-formatter.d.ts +20 -0
- package/dist/fields/column-formatter.js +15 -0
- package/dist/fields/date-time-formatter.d.ts +16 -0
- package/dist/fields/date-time-formatter.js +8 -0
- package/dist/fields/datetime/datetime-field.d.ts +16 -0
- package/dist/fields/datetime/datetime-field.js +37 -0
- package/dist/fields/datetime/datetime-field.module.js +5 -0
- package/dist/fields/datetime/datetime-field_module.css +4 -0
- package/dist/fields/draggable-context-menu.d.ts +6 -0
- package/dist/fields/draggable-context-menu.js +85 -0
- package/dist/fields/draggable-context-menu.module.js +15 -0
- package/dist/fields/draggable-context-menu_module.css +91 -0
- package/dist/fields/field-helpers.d.ts +26 -0
- package/dist/fields/field-helpers.js +50 -0
- package/dist/fields/field-renderer.d.ts +37 -0
- package/dist/fields/field-renderer.js +206 -0
- package/dist/fields/field-renderer.module.js +8 -0
- package/dist/fields/field-renderer_module.css +11 -0
- package/dist/fields/field-services-context.d.ts +16 -0
- package/dist/fields/field-services-context.js +13 -0
- package/dist/fields/field-services-types.d.ts +63 -0
- package/dist/fields/field-services-types.js +1 -0
- package/dist/fields/file/file-field.d.ts +19 -0
- package/dist/fields/file/file-field.js +225 -0
- package/dist/fields/file/file-field.module.js +18 -0
- package/dist/fields/file/file-field_module.css +131 -0
- package/dist/fields/file/file-upload-field.d.ts +21 -0
- package/dist/fields/file/file-upload-field.js +130 -0
- package/dist/fields/file/file-upload-field.module.js +15 -0
- package/dist/fields/file/file-upload-field_module.css +74 -0
- package/dist/fields/group/group-field.d.ts +15 -0
- package/dist/fields/group/group-field.js +59 -0
- package/dist/fields/group/group-field.module.js +9 -0
- package/dist/fields/group/group-field_module.css +27 -0
- package/dist/fields/image/image-field.d.ts +19 -0
- package/dist/fields/image/image-field.js +241 -0
- package/dist/fields/image/image-field.module.js +22 -0
- package/dist/fields/image/image-field_module.css +121 -0
- package/dist/fields/image/image-upload-field.d.ts +21 -0
- package/dist/fields/image/image-upload-field.js +190 -0
- package/dist/fields/image/image-upload-field.module.js +19 -0
- package/dist/fields/image/image-upload-field_module.css +92 -0
- package/dist/fields/local-date-time.d.ts +27 -0
- package/dist/fields/local-date-time.js +49 -0
- package/dist/fields/locale-badge.d.ts +18 -0
- package/dist/fields/locale-badge.js +10 -0
- package/dist/fields/locale-badge.module.js +5 -0
- package/dist/fields/locale-badge_module.css +27 -0
- package/dist/fields/numerical/numerical-field.d.ts +18 -0
- package/dist/fields/numerical/numerical-field.js +74 -0
- package/dist/fields/relation/relation-display.d.ts +40 -0
- package/dist/fields/relation/relation-display.js +58 -0
- package/dist/fields/relation/relation-display.module.js +9 -0
- package/dist/fields/relation/relation-display_module.css +21 -0
- package/dist/fields/relation/relation-field.d.ts +18 -0
- package/dist/fields/relation/relation-field.js +138 -0
- package/dist/fields/relation/relation-field.module.js +13 -0
- package/dist/fields/relation/relation-field_module.css +62 -0
- package/dist/fields/relation/relation-picker.d.ts +49 -0
- package/dist/fields/relation/relation-picker.js +236 -0
- package/dist/fields/relation/relation-picker.module.js +26 -0
- package/dist/fields/relation/relation-picker_module.css +124 -0
- package/dist/fields/relation/relation-summary.d.ts +31 -0
- package/dist/fields/relation/relation-summary.js +50 -0
- package/dist/fields/relation/relation-summary.module.js +11 -0
- package/dist/fields/relation/relation-summary_module.css +37 -0
- package/dist/fields/select/select-field.d.ts +16 -0
- package/dist/fields/select/select-field.js +50 -0
- package/dist/fields/select/select-field.module.js +5 -0
- package/dist/fields/select/select-field_module.css +4 -0
- package/dist/fields/sortable-item.d.ts +15 -0
- package/dist/fields/sortable-item.js +81 -0
- package/dist/fields/sortable-item.module.js +22 -0
- package/dist/fields/sortable-item_module.css +124 -0
- package/dist/fields/text/text-field.d.ts +20 -0
- package/dist/fields/text/text-field.js +104 -0
- package/dist/fields/text/text-field.module.js +6 -0
- package/dist/fields/text/text-field_module.css +5 -0
- package/dist/fields/text-area/text-area-field.d.ts +20 -0
- package/dist/fields/text-area/text-area-field.js +105 -0
- package/dist/fields/text-area/text-area-field.module.js +6 -0
- package/dist/fields/text-area/text-area-field_module.css +5 -0
- package/dist/fields/use-field-change-handler.d.ts +23 -0
- package/dist/fields/use-field-change-handler.js +52 -0
- package/dist/forms/document-actions.d.ts +48 -0
- package/dist/forms/document-actions.js +475 -0
- package/dist/forms/document-actions.module.js +34 -0
- package/dist/forms/document-actions_module.css +118 -0
- package/dist/forms/form-context.d.ts +89 -0
- package/dist/forms/form-context.js +466 -0
- package/dist/forms/form-renderer.d.ts +98 -0
- package/dist/forms/form-renderer.js +597 -0
- package/dist/forms/form-renderer.module.js +46 -0
- package/dist/forms/form-renderer_module.css +245 -0
- package/dist/forms/navigation-guard.d.ts +54 -0
- package/dist/forms/navigation-guard.js +22 -0
- package/dist/forms/path-widget.d.ts +36 -0
- package/dist/forms/path-widget.js +116 -0
- package/dist/forms/path-widget.module.js +8 -0
- package/dist/forms/path-widget_module.css +29 -0
- package/dist/forms/upload-executor.d.ts +57 -0
- package/dist/forms/upload-executor.js +94 -0
- package/dist/lib/translate-validation-error.d.ts +36 -0
- package/dist/lib/translate-validation-error.js +11 -0
- package/dist/modules/admin-account/commands.d.ts +2 -1
- package/dist/modules/admin-account/commands.js +13 -2
- package/dist/modules/admin-account/components/change-password.js +45 -36
- package/dist/modules/admin-account/components/container.js +185 -134
- package/dist/modules/admin-account/components/preferences.d.ts +8 -0
- package/dist/modules/admin-account/components/preferences.js +152 -0
- package/dist/modules/admin-account/components/preferences.module.js +11 -0
- package/dist/modules/admin-account/components/preferences_module.css +41 -0
- package/dist/modules/admin-account/components/update.js +50 -31
- package/dist/modules/admin-account/index.d.ts +3 -3
- package/dist/modules/admin-account/index.js +2 -2
- package/dist/modules/admin-account/schemas.d.ts +4 -0
- package/dist/modules/admin-account/schemas.js +4 -1
- package/dist/modules/admin-account/service.d.ts +1 -0
- package/dist/modules/admin-account/service.js +8 -0
- package/dist/modules/admin-permissions/components/inspector.js +31 -41
- package/dist/modules/admin-roles/components/create.js +43 -26
- package/dist/modules/admin-roles/components/permissions.js +26 -35
- package/dist/modules/admin-roles/components/update.js +26 -16
- package/dist/modules/admin-users/components/create.js +60 -40
- package/dist/modules/admin-users/components/roles.js +9 -15
- package/dist/modules/admin-users/components/set-password.js +30 -31
- package/dist/modules/admin-users/components/update.js +58 -39
- package/dist/modules/admin-users/dto.js +1 -0
- package/dist/modules/admin-users/repository.d.ts +17 -0
- package/dist/modules/admin-users/schemas.d.ts +4 -0
- package/dist/modules/admin-users/schemas.js +6 -2
- package/dist/modules/auth/components/sign-in-form.js +10 -8
- package/dist/presentation/group.d.ts +27 -0
- package/dist/presentation/group.js +14 -0
- package/dist/presentation/group.module.js +6 -0
- package/dist/presentation/group_module.css +19 -0
- package/dist/presentation/row.d.ts +25 -0
- package/dist/presentation/row.js +8 -0
- package/dist/presentation/row.module.js +5 -0
- package/dist/presentation/row_module.css +18 -0
- package/dist/presentation/tabs.d.ts +25 -0
- package/dist/presentation/tabs.js +39 -0
- package/dist/presentation/tabs.module.js +10 -0
- package/dist/presentation/tabs_module.css +68 -0
- package/dist/react.d.ts +66 -0
- package/dist/react.js +36 -0
- package/dist/services/admin-services-types.d.ts +16 -0
- package/dist/widgets/diff-viewer/diff-modal.d.ts +22 -0
- package/dist/widgets/diff-viewer/diff-modal.js +149 -0
- package/dist/widgets/diff-viewer/diff-modal.module.js +14 -0
- package/dist/widgets/diff-viewer/diff-modal_module.css +56 -0
- package/dist/widgets/status-badge/status-badge.d.ts +25 -0
- package/dist/widgets/status-badge/status-badge.js +37 -0
- package/dist/widgets/status-badge/status-badge.module.js +7 -0
- package/dist/widgets/status-badge/status-badge_module.css +20 -0
- package/package.json +14 -4
- package/src/fields/array/array-field.module.css +48 -0
- package/src/fields/array/array-field.tsx +267 -0
- package/src/fields/blocks/blocks-field.module.css +148 -0
- package/src/fields/blocks/blocks-field.tsx +323 -0
- package/src/fields/checkbox/checkbox-field.module.css +4 -0
- package/src/fields/checkbox/checkbox-field.tsx +54 -0
- package/src/fields/column-formatter.tsx +31 -0
- package/src/fields/date-time-formatter.tsx +22 -0
- package/src/fields/datetime/datetime-field.module.css +13 -0
- package/src/fields/datetime/datetime-field.tsx +54 -0
- package/src/fields/draggable-context-menu.module.css +127 -0
- package/src/fields/draggable-context-menu.tsx +87 -0
- package/src/fields/field-helpers.ts +69 -0
- package/src/fields/field-renderer.module.css +22 -0
- package/src/fields/field-renderer.tsx +288 -0
- package/src/fields/field-services-context.tsx +35 -0
- package/src/fields/field-services-types.ts +68 -0
- package/src/fields/file/file-field.module.css +153 -0
- package/src/fields/file/file-field.tsx +286 -0
- package/src/fields/file/file-upload-field.module.css +101 -0
- package/src/fields/file/file-upload-field.tsx +187 -0
- package/src/fields/group/group-field.module.css +43 -0
- package/src/fields/group/group-field.tsx +84 -0
- package/src/fields/image/image-field.module.css +155 -0
- package/src/fields/image/image-field.tsx +306 -0
- package/src/fields/image/image-upload-field.module.css +123 -0
- package/src/fields/image/image-upload-field.tsx +276 -0
- package/src/fields/local-date-time.tsx +88 -0
- package/src/fields/locale-badge.module.css +37 -0
- package/src/fields/locale-badge.tsx +32 -0
- package/src/fields/numerical/numerical-field.tsx +114 -0
- package/src/fields/relation/relation-display.module.css +36 -0
- package/src/fields/relation/relation-display.tsx +130 -0
- package/src/fields/relation/relation-field.module.css +83 -0
- package/src/fields/relation/relation-field.tsx +211 -0
- package/src/fields/relation/relation-picker.module.css +168 -0
- package/src/fields/relation/relation-picker.tsx +326 -0
- package/src/fields/relation/relation-summary.module.css +55 -0
- package/src/fields/relation/relation-summary.tsx +123 -0
- package/src/fields/select/select-field.module.css +13 -0
- package/src/fields/select/select-field.tsx +61 -0
- package/src/fields/sortable-item.module.css +167 -0
- package/src/fields/sortable-item.tsx +106 -0
- package/src/fields/text/text-field.module.css +13 -0
- package/src/fields/text/text-field.tsx +146 -0
- package/src/fields/text-area/text-area-field.module.css +13 -0
- package/src/fields/text-area/text-area-field.tsx +147 -0
- package/src/fields/use-field-change-handler.ts +112 -0
- package/src/forms/document-actions.module.css +160 -0
- package/src/forms/document-actions.tsx +482 -0
- package/src/forms/form-context.tsx +704 -0
- package/src/forms/form-renderer.module.css +321 -0
- package/src/forms/form-renderer.tsx +891 -0
- package/src/forms/navigation-guard.tsx +98 -0
- package/src/forms/path-widget.module.css +41 -0
- package/src/forms/path-widget.test.tsx +217 -0
- package/src/forms/path-widget.tsx +183 -0
- package/src/forms/upload-executor.ts +192 -0
- package/src/lib/translate-validation-error.ts +56 -0
- package/src/modules/admin-account/commands.ts +13 -0
- package/src/modules/admin-account/components/change-password.tsx +46 -31
- package/src/modules/admin-account/components/container.tsx +83 -38
- package/src/modules/admin-account/components/preferences.module.css +60 -0
- package/src/modules/admin-account/components/preferences.tsx +203 -0
- package/src/modules/admin-account/components/update.tsx +53 -27
- package/src/modules/admin-account/index.ts +3 -0
- package/src/modules/admin-account/schemas.ts +13 -0
- package/src/modules/admin-account/service.ts +12 -0
- package/src/modules/admin-permissions/components/inspector.tsx +22 -14
- package/src/modules/admin-roles/components/create.tsx +51 -23
- package/src/modules/admin-roles/components/permissions.tsx +25 -21
- package/src/modules/admin-roles/components/update.tsx +37 -19
- package/src/modules/admin-users/components/create.tsx +63 -34
- package/src/modules/admin-users/components/roles.tsx +9 -8
- package/src/modules/admin-users/components/set-password.tsx +34 -28
- package/src/modules/admin-users/components/update.tsx +58 -36
- package/src/modules/admin-users/dto.ts +1 -0
- package/src/modules/admin-users/repository.ts +17 -0
- package/src/modules/admin-users/schemas.ts +12 -0
- package/src/modules/auth/components/sign-in-form.tsx +14 -8
- package/src/presentation/group.module.css +41 -0
- package/src/presentation/group.tsx +40 -0
- package/src/presentation/row.module.css +32 -0
- package/src/presentation/row.tsx +33 -0
- package/src/presentation/tabs.module.css +107 -0
- package/src/presentation/tabs.tsx +84 -0
- package/src/react.ts +84 -0
- package/src/services/admin-services-types.ts +18 -0
- package/src/widgets/diff-viewer/diff-modal.module.css +79 -0
- package/src/widgets/diff-viewer/diff-modal.tsx +186 -0
- package/src/widgets/status-badge/status-badge.module.css +31 -0
- package/src/widgets/status-badge/status-badge.tsx +71 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
5
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) Infonomic Company Limited
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
12
|
+
import { Badge } from '@byline/ui/react'
|
|
13
|
+
import cx from 'classnames'
|
|
14
|
+
|
|
15
|
+
import styles from './tabs.module.css'
|
|
16
|
+
|
|
17
|
+
export interface AdminTabItem {
|
|
18
|
+
name: string
|
|
19
|
+
label: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface AdminTabsProps {
|
|
23
|
+
tabs: AdminTabItem[]
|
|
24
|
+
activeTab: string
|
|
25
|
+
onChange: (name: string) => void
|
|
26
|
+
/** Error counts keyed by tab name — shows a danger badge when > 0. */
|
|
27
|
+
errorCounts?: Record<string, number>
|
|
28
|
+
className?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Tabs navigation bar for admin form layouts.
|
|
33
|
+
*
|
|
34
|
+
* Used by FormRenderer when a CollectionAdminConfig declares a `tabs` array.
|
|
35
|
+
* Each tab is a simple button with a bottom-border active indicator.
|
|
36
|
+
* Inactive tabs show a subtle hover state. Fully dark-mode aware.
|
|
37
|
+
*
|
|
38
|
+
* Stable override handles: `.byline-admin-tabs`, `.byline-admin-tab`,
|
|
39
|
+
* `.byline-admin-tab-active`, `.byline-admin-tab-label`,
|
|
40
|
+
* `.byline-admin-tab-badge`.
|
|
41
|
+
*/
|
|
42
|
+
export const AdminTabs = ({
|
|
43
|
+
tabs,
|
|
44
|
+
activeTab,
|
|
45
|
+
onChange,
|
|
46
|
+
errorCounts,
|
|
47
|
+
className,
|
|
48
|
+
}: AdminTabsProps) => {
|
|
49
|
+
const { t } = useTranslation('byline-admin')
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
role="tablist"
|
|
53
|
+
aria-label={t('presentation.formTabsAriaLabel')}
|
|
54
|
+
className={cx('byline-admin-tabs', styles.tabs, className)}
|
|
55
|
+
>
|
|
56
|
+
{tabs.map((tab) => {
|
|
57
|
+
const isActive = tab.name === activeTab
|
|
58
|
+
return (
|
|
59
|
+
<button
|
|
60
|
+
key={tab.name}
|
|
61
|
+
type="button"
|
|
62
|
+
role="tab"
|
|
63
|
+
aria-selected={isActive}
|
|
64
|
+
onClick={() => onChange(tab.name)}
|
|
65
|
+
className={cx(
|
|
66
|
+
'byline-admin-tab',
|
|
67
|
+
styles.tab,
|
|
68
|
+
isActive && ['byline-admin-tab-active', styles['tab-active']]
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
<span className={cx('byline-admin-tab-label', styles.label)}>
|
|
72
|
+
{tab.label}
|
|
73
|
+
{(errorCounts?.[tab.name] ?? 0) > 0 && (
|
|
74
|
+
<Badge intent="danger" className={cx('byline-admin-tab-badge', styles.badge)}>
|
|
75
|
+
{errorCounts?.[tab.name]}
|
|
76
|
+
</Badge>
|
|
77
|
+
)}
|
|
78
|
+
</span>
|
|
79
|
+
</button>
|
|
80
|
+
)
|
|
81
|
+
})}
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
package/src/react.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Single React barrel for `@byline/admin`. Hosts every client-side
|
|
11
|
+
* surface the admin shell needs to render the document editor —
|
|
12
|
+
* presentational layout primitives, field widgets, the form runtime,
|
|
13
|
+
* the editor-shared widgets (status badge, diff modal), and the
|
|
14
|
+
* field-side services Context. Generic drag-and-drop helpers
|
|
15
|
+
* (`DraggableSortable`, `useSortable`, `moveItem`) live in
|
|
16
|
+
* `@byline/ui/react` since they embed no CMS concepts.
|
|
17
|
+
*
|
|
18
|
+
* Why one barrel: per-area subpath exports break React Context
|
|
19
|
+
* identity under bundlers that pre-bundle subpaths individually
|
|
20
|
+
* (e.g. Vite's `optimizeDeps.include`) — a provider mounted on one
|
|
21
|
+
* Context identity and a hook reading another. A single specifier
|
|
22
|
+
* eliminates the trap structurally. Tree-shaking inside the ESM
|
|
23
|
+
* bundle still drops anything unused by the host.
|
|
24
|
+
*
|
|
25
|
+
* Sibling subpaths in this package — `@byline/admin/admin-users`,
|
|
26
|
+
* `@byline/admin/admin-roles/components/*`, `@byline/admin/auth`,
|
|
27
|
+
* etc. — host per-vertical components and server-side modules;
|
|
28
|
+
* those are intentionally separate so server-only imports don't pull
|
|
29
|
+
* React.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
// Field widgets.
|
|
33
|
+
export * from './fields/array/array-field.js'
|
|
34
|
+
export * from './fields/blocks/blocks-field.js'
|
|
35
|
+
export * from './fields/checkbox/checkbox-field.js'
|
|
36
|
+
export * from './fields/column-formatter.js'
|
|
37
|
+
export * from './fields/date-time-formatter.js'
|
|
38
|
+
export * from './fields/datetime/datetime-field.js'
|
|
39
|
+
export * from './fields/draggable-context-menu.js'
|
|
40
|
+
export * from './fields/field-helpers.js'
|
|
41
|
+
export * from './fields/field-renderer.js'
|
|
42
|
+
export * from './fields/field-services-context.js'
|
|
43
|
+
export * from './fields/file/file-field.js'
|
|
44
|
+
export * from './fields/file/file-upload-field.js'
|
|
45
|
+
export * from './fields/group/group-field.js'
|
|
46
|
+
export * from './fields/image/image-field.js'
|
|
47
|
+
export * from './fields/image/image-upload-field.js'
|
|
48
|
+
export * from './fields/local-date-time.js'
|
|
49
|
+
export * from './fields/locale-badge.js'
|
|
50
|
+
export * from './fields/numerical/numerical-field.js'
|
|
51
|
+
export * from './fields/relation/relation-field.js'
|
|
52
|
+
export * from './fields/relation/relation-picker.js'
|
|
53
|
+
export * from './fields/select/select-field.js'
|
|
54
|
+
export * from './fields/sortable-item.js'
|
|
55
|
+
export * from './fields/text/text-field.js'
|
|
56
|
+
export * from './fields/text-area/text-area-field.js'
|
|
57
|
+
export * from './fields/use-field-change-handler.js'
|
|
58
|
+
// Form runtime.
|
|
59
|
+
export * from './forms/document-actions.js'
|
|
60
|
+
export * from './forms/form-context.js'
|
|
61
|
+
export * from './forms/form-renderer.js'
|
|
62
|
+
export * from './forms/navigation-guard.js'
|
|
63
|
+
export * from './forms/path-widget.js'
|
|
64
|
+
// Validation error code → translation helper. Sibling to schemas in
|
|
65
|
+
// `@byline/core/validation`, which emit codes; this helper maps the
|
|
66
|
+
// codes onto the active locale for form-input errorText slots.
|
|
67
|
+
export * from './lib/translate-validation-error.js'
|
|
68
|
+
// Presentational admin layout primitives.
|
|
69
|
+
export * from './presentation/group.js'
|
|
70
|
+
export * from './presentation/row.js'
|
|
71
|
+
export * from './presentation/tabs.js'
|
|
72
|
+
// Collection-editor-shared widgets.
|
|
73
|
+
export * from './widgets/diff-viewer/diff-modal.js'
|
|
74
|
+
export * from './widgets/status-badge/status-badge.js'
|
|
75
|
+
// Field-side service contract types.
|
|
76
|
+
export type {
|
|
77
|
+
BylineFieldServices,
|
|
78
|
+
CollectionListDoc,
|
|
79
|
+
CollectionListParams,
|
|
80
|
+
CollectionListResponse,
|
|
81
|
+
GetCollectionDocumentsFn,
|
|
82
|
+
UploadedFileResult,
|
|
83
|
+
UploadFieldFn,
|
|
84
|
+
} from './fields/field-services-types.js'
|
|
@@ -67,6 +67,23 @@ export type UpdateAccountInput = UpdateAccountRequest
|
|
|
67
67
|
/** Same shape as `ChangeAccountPasswordRequest` from `@byline/admin/admin-account`. */
|
|
68
68
|
export type ChangeAccountPasswordInput = ChangeAccountPasswordRequest
|
|
69
69
|
|
|
70
|
+
export interface SetInterfaceLocaleInput {
|
|
71
|
+
/** BCP 47 tag, or `null` to clear the preference and re-engage detection. */
|
|
72
|
+
locale: string | null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Return shape for the locale-switcher service. `account` is populated
|
|
77
|
+
* when the request resolved an authenticated admin actor (the form on
|
|
78
|
+
* the account page); `null` on pre-auth surfaces (the sign-in page
|
|
79
|
+
* locale dropdown) where the cookie write is the only effect.
|
|
80
|
+
*/
|
|
81
|
+
export interface SetInterfaceLocaleResult {
|
|
82
|
+
ok: true
|
|
83
|
+
locale: string | null
|
|
84
|
+
account: AccountResponse | null
|
|
85
|
+
}
|
|
86
|
+
|
|
70
87
|
// --- Admin users ----------------------------------------------------------
|
|
71
88
|
|
|
72
89
|
export interface CreateAdminUserInput {
|
|
@@ -142,6 +159,7 @@ export interface BylineAdminServices {
|
|
|
142
159
|
// Account self-service
|
|
143
160
|
updateAccount: AdminServiceCall<UpdateAccountInput, AccountResponse>
|
|
144
161
|
changeAccountPassword: AdminServiceCall<ChangeAccountPasswordInput, AccountResponse>
|
|
162
|
+
setInterfaceLocale: AdminServiceCall<SetInterfaceLocaleInput, SetInterfaceLocaleResult>
|
|
145
163
|
|
|
146
164
|
// Admin user writes (page-container reads stay in the host for now)
|
|
147
165
|
createAdminUser: AdminServiceCall<CreateAdminUserInput, AdminUserResponse>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiffModal — side-by-side document version comparison modal.
|
|
3
|
+
*
|
|
4
|
+
* Override handles:
|
|
5
|
+
* .byline-diff-modal-header — modal header padding
|
|
6
|
+
* .byline-diff-modal-title-stack — heading + subtitle stack
|
|
7
|
+
* .byline-diff-modal-title — heading
|
|
8
|
+
* .byline-diff-modal-subtitle — subtitle line
|
|
9
|
+
* .byline-diff-modal-version — inline pill identifying the historical version
|
|
10
|
+
* .byline-diff-modal-content — scrollable content
|
|
11
|
+
* .byline-diff-modal-state — loading / error placeholder
|
|
12
|
+
* .byline-diff-modal-error — error placeholder colour modifier
|
|
13
|
+
* .byline-diff-modal-viewer — diff viewer wrapper
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
.header,
|
|
17
|
+
:global(.byline-diff-modal-header) {
|
|
18
|
+
padding-top: var(--spacing-16);
|
|
19
|
+
margin-bottom: var(--spacing-8);
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.title-stack,
|
|
24
|
+
:global(.byline-diff-modal-title-stack) {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.title,
|
|
30
|
+
:global(.byline-diff-modal-title) {
|
|
31
|
+
margin: 0;
|
|
32
|
+
font-size: var(--font-size-xl);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.subtitle,
|
|
36
|
+
:global(.byline-diff-modal-subtitle) {
|
|
37
|
+
margin: 0;
|
|
38
|
+
font-size: var(--font-size-sm);
|
|
39
|
+
color: var(--gray-400);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.version,
|
|
43
|
+
:global(.byline-diff-modal-version) {
|
|
44
|
+
font-family: var(--font-family-mono);
|
|
45
|
+
font-size: var(--font-size-xs);
|
|
46
|
+
background-color: var(--canvas-700);
|
|
47
|
+
padding: 0 var(--spacing-4);
|
|
48
|
+
border-radius: var(--border-radius-sm);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.content,
|
|
52
|
+
:global(.byline-diff-modal-content) {
|
|
53
|
+
flex: 1 1 0;
|
|
54
|
+
overflow: auto;
|
|
55
|
+
padding: 0;
|
|
56
|
+
min-height: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.state,
|
|
60
|
+
:global(.byline-diff-modal-state) {
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
height: 100%;
|
|
65
|
+
gap: var(--spacing-12);
|
|
66
|
+
color: var(--gray-400);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.error,
|
|
70
|
+
:global(.byline-diff-modal-error) {
|
|
71
|
+
color: var(--red-400);
|
|
72
|
+
gap: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.viewer,
|
|
76
|
+
:global(.byline-diff-modal-viewer) {
|
|
77
|
+
font-family: var(--font-family-mono);
|
|
78
|
+
font-size: var(--font-size-sm);
|
|
79
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
5
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) Infonomic Company Limited
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useEffect, useState } from 'react'
|
|
12
|
+
|
|
13
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
14
|
+
import { CloseIcon, IconButton, LoaderRing, Modal } from '@byline/ui/react'
|
|
15
|
+
import cx from 'classnames'
|
|
16
|
+
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued'
|
|
17
|
+
|
|
18
|
+
import styles from './diff-modal.module.css'
|
|
19
|
+
|
|
20
|
+
// Keys that are per-version metadata rather than content — strip before diffing
|
|
21
|
+
// so the diff focuses on meaningful content changes. ClientDocument-shape
|
|
22
|
+
// metadata keys after the Phase 7 admin migration.
|
|
23
|
+
const STRIP_KEYS = new Set([
|
|
24
|
+
'id',
|
|
25
|
+
'versionId',
|
|
26
|
+
'path',
|
|
27
|
+
'status',
|
|
28
|
+
'createdAt',
|
|
29
|
+
'updatedAt',
|
|
30
|
+
'hasPublishedVersion',
|
|
31
|
+
'_publishedVersion',
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
function stripMeta(doc: Record<string, unknown>): Record<string, unknown> {
|
|
35
|
+
// With the nested document shape, extract just the fields for diffing.
|
|
36
|
+
if (doc.fields && typeof doc.fields === 'object') {
|
|
37
|
+
return doc.fields as Record<string, unknown>
|
|
38
|
+
}
|
|
39
|
+
return Object.fromEntries(Object.entries(doc).filter(([k]) => !STRIP_KEYS.has(k)))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface DiffModalProps {
|
|
43
|
+
isOpen: boolean
|
|
44
|
+
onDismiss: () => void
|
|
45
|
+
collection: string
|
|
46
|
+
documentId: string
|
|
47
|
+
/** The `versionId` of the historical version to compare. */
|
|
48
|
+
versionId: string
|
|
49
|
+
/** A human-readable label for the historical version (e.g. a date string). */
|
|
50
|
+
versionLabel: string
|
|
51
|
+
/** The already-loaded current (latest) version of the document. */
|
|
52
|
+
currentDocument: Record<string, unknown>
|
|
53
|
+
/** Content locale to compare — undefined / 'all' shows all locales. */
|
|
54
|
+
locale?: string
|
|
55
|
+
/**
|
|
56
|
+
* Host-provided loader for a historical document version. The diff
|
|
57
|
+
* modal is framework-neutral; callers wire this to whatever transport
|
|
58
|
+
* they use (e.g. `BylineAdminServices.getCollectionDocumentVersion` in
|
|
59
|
+
* the admin shell).
|
|
60
|
+
*/
|
|
61
|
+
loadHistoricalVersion: (
|
|
62
|
+
collection: string,
|
|
63
|
+
documentId: string,
|
|
64
|
+
versionId: string,
|
|
65
|
+
locale: string | undefined
|
|
66
|
+
) => Promise<Record<string, unknown>>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function DiffModal({
|
|
70
|
+
isOpen,
|
|
71
|
+
onDismiss,
|
|
72
|
+
collection,
|
|
73
|
+
documentId,
|
|
74
|
+
versionId,
|
|
75
|
+
versionLabel,
|
|
76
|
+
currentDocument,
|
|
77
|
+
locale,
|
|
78
|
+
loadHistoricalVersion,
|
|
79
|
+
}: DiffModalProps) {
|
|
80
|
+
const { t } = useTranslation('byline-admin')
|
|
81
|
+
const [historicalDoc, setHistoricalDoc] = useState<Record<string, unknown> | null>(null)
|
|
82
|
+
const [loading, setLoading] = useState(false)
|
|
83
|
+
const [error, setError] = useState<string | null>(null)
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (!isOpen || !versionId) return
|
|
87
|
+
|
|
88
|
+
let cancelled = false
|
|
89
|
+
setLoading(true)
|
|
90
|
+
setError(null)
|
|
91
|
+
setHistoricalDoc(null)
|
|
92
|
+
|
|
93
|
+
loadHistoricalVersion(collection, documentId, versionId, locale)
|
|
94
|
+
.then((doc) => {
|
|
95
|
+
if (cancelled) return
|
|
96
|
+
setHistoricalDoc(doc)
|
|
97
|
+
})
|
|
98
|
+
.catch((err) => {
|
|
99
|
+
if (cancelled) return
|
|
100
|
+
setError(err instanceof Error ? err.message : t('diffModal.loadFailed'))
|
|
101
|
+
})
|
|
102
|
+
.finally(() => {
|
|
103
|
+
if (!cancelled) setLoading(false)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
cancelled = true
|
|
108
|
+
}
|
|
109
|
+
}, [isOpen, collection, documentId, versionId, locale, loadHistoricalVersion, t])
|
|
110
|
+
|
|
111
|
+
const currentStr = currentDocument ? JSON.stringify(stripMeta(currentDocument), null, 2) : ''
|
|
112
|
+
|
|
113
|
+
const historicalStr = historicalDoc ? JSON.stringify(stripMeta(historicalDoc), null, 2) : ''
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Modal isOpen={isOpen} closeOnOverlayClick={true} onDismiss={onDismiss}>
|
|
117
|
+
<Modal.Container
|
|
118
|
+
style={{
|
|
119
|
+
width: '96vw',
|
|
120
|
+
maxWidth: '96vw',
|
|
121
|
+
height: '90vh',
|
|
122
|
+
maxHeight: '90vh',
|
|
123
|
+
display: 'flex',
|
|
124
|
+
flexDirection: 'column',
|
|
125
|
+
overflow: 'hidden',
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
<Modal.Header className={cx('byline-diff-modal-header', styles.header)}>
|
|
129
|
+
<div className={cx('byline-diff-modal-title-stack', styles['title-stack'])}>
|
|
130
|
+
<h3 className={cx('byline-diff-modal-title', styles.title)}>{t('diffModal.title')}</h3>
|
|
131
|
+
<p className={cx('byline-diff-modal-subtitle', styles.subtitle)}>
|
|
132
|
+
{t('diffModal.subtitleBefore')}{' '}
|
|
133
|
+
<span className={cx('byline-diff-modal-version', styles.version)}>
|
|
134
|
+
{versionLabel}
|
|
135
|
+
</span>{' '}
|
|
136
|
+
{t('diffModal.subtitleAfter')}
|
|
137
|
+
</p>
|
|
138
|
+
</div>
|
|
139
|
+
<IconButton onClick={onDismiss} size="xs" aria-label={t('diffModal.closeAriaLabel')}>
|
|
140
|
+
<CloseIcon width="15px" height="15px" />
|
|
141
|
+
</IconButton>
|
|
142
|
+
</Modal.Header>
|
|
143
|
+
|
|
144
|
+
<Modal.Content
|
|
145
|
+
className={cx('byline-diff-modal-content', styles.content)}
|
|
146
|
+
style={{ minHeight: 0 }}
|
|
147
|
+
>
|
|
148
|
+
{loading && (
|
|
149
|
+
<div className={cx('byline-diff-modal-state', styles.state)}>
|
|
150
|
+
<LoaderRing size={28} color="#666666" />
|
|
151
|
+
<span>{t('diffModal.loading')}</span>
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
|
|
155
|
+
{error && (
|
|
156
|
+
<div
|
|
157
|
+
className={cx(
|
|
158
|
+
'byline-diff-modal-state',
|
|
159
|
+
'byline-diff-modal-error',
|
|
160
|
+
styles.state,
|
|
161
|
+
styles.error
|
|
162
|
+
)}
|
|
163
|
+
>
|
|
164
|
+
{error}
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
{!loading && !error && historicalDoc && (
|
|
169
|
+
<div className={cx('byline-diff-modal-viewer', styles.viewer)}>
|
|
170
|
+
<ReactDiffViewer
|
|
171
|
+
oldValue={historicalStr}
|
|
172
|
+
newValue={currentStr}
|
|
173
|
+
splitView={true}
|
|
174
|
+
compareMethod={DiffMethod.LINES}
|
|
175
|
+
useDarkTheme={true}
|
|
176
|
+
leftTitle={versionLabel}
|
|
177
|
+
rightTitle={t('diffModal.currentVersion')}
|
|
178
|
+
hideLineNumbers={false}
|
|
179
|
+
/>
|
|
180
|
+
</div>
|
|
181
|
+
)}
|
|
182
|
+
</Modal.Content>
|
|
183
|
+
</Modal.Container>
|
|
184
|
+
</Modal>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBadge — workflow-status pill, optionally preceded by a "live" dot.
|
|
3
|
+
*
|
|
4
|
+
* Override handles:
|
|
5
|
+
* .byline-status-badge-wrap — the outer flex container
|
|
6
|
+
* .byline-status-badge-dot — the green "published version live" dot
|
|
7
|
+
* .byline-status-badge — the badge itself
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.wrap,
|
|
11
|
+
:global(.byline-status-badge-wrap) {
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: var(--spacing-4);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.dot,
|
|
18
|
+
:global(.byline-status-badge-dot) {
|
|
19
|
+
display: inline-block;
|
|
20
|
+
width: 0.5rem;
|
|
21
|
+
height: 0.5rem;
|
|
22
|
+
border-radius: var(--border-radius-full);
|
|
23
|
+
background-color: var(--green-500);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.badge,
|
|
27
|
+
:global(.byline-status-badge) {
|
|
28
|
+
padding: 0 0.375rem;
|
|
29
|
+
font-size: 0.65rem;
|
|
30
|
+
line-height: 1.5;
|
|
31
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { WorkflowStatus } from '@byline/core'
|
|
10
|
+
import {
|
|
11
|
+
WORKFLOW_STATUS_ARCHIVED,
|
|
12
|
+
WORKFLOW_STATUS_DRAFT,
|
|
13
|
+
WORKFLOW_STATUS_PUBLISHED,
|
|
14
|
+
} from '@byline/core'
|
|
15
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
16
|
+
import { Badge } from '@byline/ui/react'
|
|
17
|
+
import cx from 'classnames'
|
|
18
|
+
|
|
19
|
+
import styles from './status-badge.module.css'
|
|
20
|
+
|
|
21
|
+
function statusIntent(status: string): 'success' | 'warning' | 'info' | 'noeffect' {
|
|
22
|
+
switch (status) {
|
|
23
|
+
case WORKFLOW_STATUS_PUBLISHED:
|
|
24
|
+
return 'success'
|
|
25
|
+
case WORKFLOW_STATUS_DRAFT:
|
|
26
|
+
return 'warning'
|
|
27
|
+
case WORKFLOW_STATUS_ARCHIVED:
|
|
28
|
+
return 'info'
|
|
29
|
+
default:
|
|
30
|
+
return 'noeffect'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Compact badge for workflow status values. Maps the three built-in
|
|
36
|
+
* statuses (draft, published, archived) to semantic intents and falls
|
|
37
|
+
* back to `noeffect` for any custom workflow statuses.
|
|
38
|
+
*
|
|
39
|
+
* When `hasPublishedVersion` is true and the current status is not
|
|
40
|
+
* `published`, a small green dot is rendered before the badge to
|
|
41
|
+
* indicate that a published version is live.
|
|
42
|
+
*
|
|
43
|
+
* Stable override handles: `.byline-status-badge-wrap`,
|
|
44
|
+
* `.byline-status-badge-dot`, `.byline-status-badge`.
|
|
45
|
+
*/
|
|
46
|
+
export const StatusBadge = ({
|
|
47
|
+
status,
|
|
48
|
+
workflowStatuses,
|
|
49
|
+
hasPublishedVersion,
|
|
50
|
+
}: {
|
|
51
|
+
status: string
|
|
52
|
+
workflowStatuses: WorkflowStatus[]
|
|
53
|
+
hasPublishedVersion?: boolean
|
|
54
|
+
}) => {
|
|
55
|
+
const { t } = useTranslation('byline-admin')
|
|
56
|
+
const label = workflowStatuses.find((s) => s.name === status)?.label ?? String(status ?? '')
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<span className={cx('byline-status-badge-wrap', styles.wrap)}>
|
|
60
|
+
{hasPublishedVersion === true && status !== 'published' && (
|
|
61
|
+
<span
|
|
62
|
+
title={t('statusBadge.publishedVersionLive')}
|
|
63
|
+
className={cx('byline-status-badge-dot', styles.dot)}
|
|
64
|
+
/>
|
|
65
|
+
)}
|
|
66
|
+
<Badge intent={statusIntent(status)} className={cx('byline-status-badge', styles.badge)}>
|
|
67
|
+
{label}
|
|
68
|
+
</Badge>
|
|
69
|
+
</span>
|
|
70
|
+
)
|
|
71
|
+
}
|