@byline/admin 5.1.1 → 5.1.3
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/exports-parity.test.node.js +37 -0
- package/dist/fields/array/array-field.jsx +209 -0
- package/dist/fields/blocks/blocks-field.jsx +248 -0
- package/dist/fields/checkbox/checkbox-field.jsx +24 -0
- package/dist/fields/code/code-editor.jsx +211 -0
- package/dist/fields/code/code-field.jsx +109 -0
- package/dist/fields/column-formatter.jsx +25 -0
- package/dist/fields/date-time-formatter.jsx +18 -0
- package/dist/fields/datetime/datetime-field.jsx +22 -0
- package/dist/fields/draggable-context-menu.jsx +50 -0
- package/dist/fields/field-admin.test.node.js +40 -0
- package/dist/fields/field-renderer.jsx +136 -0
- package/dist/fields/field-services-context.jsx +17 -0
- package/dist/fields/file/file-field.jsx +176 -0
- package/dist/fields/file/file-upload-field.jsx +89 -0
- package/dist/fields/group/group-field.jsx +46 -0
- package/dist/fields/image/image-field.jsx +183 -0
- package/dist/fields/image/image-upload-field.jsx +157 -0
- package/dist/fields/local-date-time.jsx +69 -0
- package/dist/fields/locale-badge.jsx +19 -0
- package/dist/fields/numerical/numerical-field.jsx +93 -0
- package/dist/fields/relation/relation-column-formatter.jsx +74 -0
- package/dist/fields/relation/relation-display.jsx +118 -0
- package/dist/fields/relation/relation-field.jsx +108 -0
- package/dist/fields/relation/relation-many-field.jsx +142 -0
- package/dist/fields/relation/relation-picker.jsx +240 -0
- package/dist/fields/relation/relation-summary.jsx +45 -0
- package/dist/fields/select/select-field.jsx +27 -0
- package/dist/fields/sortable-item.jsx +54 -0
- package/dist/fields/text/text-field.jsx +79 -0
- package/dist/fields/text-area/text-area-field.jsx +79 -0
- package/dist/forms/available-locales-reconcile.test.node.js +43 -0
- package/dist/forms/available-locales-widget.jsx +63 -0
- package/dist/forms/document-actions.jsx +486 -0
- package/dist/forms/form-context.jsx +598 -0
- package/dist/forms/form-modals.jsx +128 -0
- package/dist/forms/form-renderer.jsx +532 -0
- package/dist/forms/form-status-display.jsx +69 -0
- package/dist/forms/navigation-guard.jsx +68 -0
- package/dist/forms/nested-path.test.node.js +115 -0
- package/dist/forms/path-widget.jsx +125 -0
- package/dist/forms/pending-uploads.test.node.js +19 -0
- package/dist/forms/repeating-items.test.node.js +29 -0
- package/dist/forms/scheduled-publication-control.jsx +389 -0
- package/dist/forms/scheduled-publication-state.test.node.js +121 -0
- package/dist/forms/scheduled-publication-time.test.node.js +70 -0
- package/dist/forms/status-transitions.test.node.js +76 -0
- package/dist/forms/tree-placement-widget.jsx +160 -0
- package/dist/forms/upload-executor.test.node.js +368 -0
- package/dist/forms/use-form-layout.test.node.js +69 -0
- package/dist/modules/admin-account/components/change-password.jsx +169 -0
- package/dist/modules/admin-account/components/container.jsx +164 -0
- package/dist/modules/admin-account/components/preferences.jsx +148 -0
- package/dist/modules/admin-account/components/theme-switch.jsx +52 -0
- package/dist/modules/admin-account/components/update.jsx +186 -0
- package/dist/modules/admin-permissions/components/inspector.jsx +200 -0
- package/dist/modules/admin-preferences/schemas.test.node.js +48 -0
- package/dist/modules/admin-roles/components/create.jsx +155 -0
- package/dist/modules/admin-roles/components/permissions.jsx +239 -0
- package/dist/modules/admin-roles/components/update.jsx +148 -0
- package/dist/modules/admin-users/components/create.jsx +206 -0
- package/dist/modules/admin-users/components/roles.jsx +126 -0
- package/dist/modules/admin-users/components/set-password.jsx +133 -0
- package/dist/modules/admin-users/components/update.jsx +215 -0
- package/dist/modules/analytics/components/dashboard.jsx +208 -0
- package/dist/modules/analytics/components/dashboard.test.node.js +162 -0
- package/dist/modules/analytics/components/timeseries.jsx +193 -0
- package/dist/modules/auth/components/sign-in-form.jsx +89 -0
- package/dist/modules/auth/safe-redirect.test.node.js +40 -0
- package/dist/modules/auth/sign-in-form-props.test.node.js +7 -0
- package/dist/presentation/group.jsx +26 -0
- package/dist/presentation/row.jsx +23 -0
- package/dist/presentation/tabs.jsx +39 -0
- package/dist/services/admin-services-context.jsx +17 -0
- package/dist/widgets/diff-viewer/diff-modal.jsx +110 -0
- package/dist/widgets/source-locale-badge/source-locale-badge.jsx +28 -0
- package/dist/widgets/status-badge/status-badge.jsx +46 -0
- package/package.json +7 -7
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { normalizeRootRelativeRedirect, resolveSignInFormRedirect } from './safe-redirect.js';
|
|
3
|
+
describe('normalizeRootRelativeRedirect', () => {
|
|
4
|
+
it.each([
|
|
5
|
+
['/cms', '/cms'],
|
|
6
|
+
['/cms/account?tab=profile#name', '/cms/account?tab=profile#name'],
|
|
7
|
+
])('accepts %j', (value, expected) => {
|
|
8
|
+
expect(normalizeRootRelativeRedirect(value)).toBe(expected);
|
|
9
|
+
});
|
|
10
|
+
it.each([
|
|
11
|
+
'',
|
|
12
|
+
' /cms',
|
|
13
|
+
'https://evil.test',
|
|
14
|
+
'//evil.test',
|
|
15
|
+
'/\\evil.test',
|
|
16
|
+
'/cms\\account',
|
|
17
|
+
'/cms\naccount',
|
|
18
|
+
'%2Fcms',
|
|
19
|
+
'/%2F%2Fevil.test',
|
|
20
|
+
'/cms/%5cevil.test',
|
|
21
|
+
'/cms/%2e/account',
|
|
22
|
+
'/cms/../account',
|
|
23
|
+
'/cms/./account',
|
|
24
|
+
'/cms\u0085account',
|
|
25
|
+
])('rejects %j', (value) => {
|
|
26
|
+
expect(normalizeRootRelativeRedirect(value)).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('resolveSignInFormRedirect', () => {
|
|
30
|
+
it('uses a safe redirectTo without evaluating the fallback', () => {
|
|
31
|
+
expect(resolveSignInFormRedirect('/cms/account', '/cms')).toBe('/cms/account');
|
|
32
|
+
expect(resolveSignInFormRedirect('/cms/account', () => {
|
|
33
|
+
throw new Error('fallback must be lazy');
|
|
34
|
+
})).toBe('/cms/account');
|
|
35
|
+
});
|
|
36
|
+
it('uses the safe configured fallback without permitting an open redirect', () => {
|
|
37
|
+
expect(resolveSignInFormRedirect('//evil.test', '/cms')).toBe('/cms');
|
|
38
|
+
expect(resolveSignInFormRedirect(undefined, 'https://evil.test')).toBe('/');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
import cx from 'clsx';
|
|
9
|
+
import styles from './group.module.css';
|
|
10
|
+
/**
|
|
11
|
+
* Labelled fieldset clustering related fields together.
|
|
12
|
+
*
|
|
13
|
+
* Used by `FormRenderer` when a `CollectionAdminConfig` declares a `groups`
|
|
14
|
+
* primitive. Renders a bordered, padded `<fieldset>` with an optional
|
|
15
|
+
* `<legend>` for the label.
|
|
16
|
+
*
|
|
17
|
+
* Stable override handles: `.byline-admin-group` on the fieldset and
|
|
18
|
+
* `.byline-admin-group-legend` on the legend (alongside the hashed
|
|
19
|
+
* CSS-modules locals).
|
|
20
|
+
*/
|
|
21
|
+
export const AdminGroup = ({ label, children, className }) => {
|
|
22
|
+
return (<fieldset className={cx('byline-admin-group', styles.group, className)}>
|
|
23
|
+
{label && <legend className={cx('byline-admin-group-legend', styles.legend)}>{label}</legend>}
|
|
24
|
+
{children}
|
|
25
|
+
</fieldset>);
|
|
26
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
import cx from 'clsx';
|
|
9
|
+
import styles from './row.module.css';
|
|
10
|
+
/**
|
|
11
|
+
* Horizontal flex-row layout for admin form fields.
|
|
12
|
+
*
|
|
13
|
+
* Used by `FormRenderer` when a `CollectionAdminConfig` declares a `rows`
|
|
14
|
+
* primitive. Members are rendered side-by-side above the `sm` breakpoint
|
|
15
|
+
* and stack vertically below it. `flex-1` + `min-width: 0` lets two text
|
|
16
|
+
* inputs share the row evenly without overflowing.
|
|
17
|
+
*
|
|
18
|
+
* The element carries `.byline-admin-row` as a stable global class for
|
|
19
|
+
* host overrides (alongside the hashed CSS-modules local).
|
|
20
|
+
*/
|
|
21
|
+
export const AdminRow = ({ children, className }) => {
|
|
22
|
+
return <div className={cx('byline-admin-row', styles.row, className)}>{children}</div>;
|
|
23
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
4
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
5
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) Infonomic Company Limited
|
|
8
|
+
*/
|
|
9
|
+
import { useTranslation } from '@byline/i18n/react';
|
|
10
|
+
import { Badge } from '@byline/ui/react';
|
|
11
|
+
import cx from 'clsx';
|
|
12
|
+
import styles from './tabs.module.css';
|
|
13
|
+
/**
|
|
14
|
+
* Tabs navigation bar for admin form layouts.
|
|
15
|
+
*
|
|
16
|
+
* Used by FormRenderer when a CollectionAdminConfig declares a `tabs` array.
|
|
17
|
+
* Each tab is a simple button with a bottom-border active indicator.
|
|
18
|
+
* Inactive tabs show a subtle hover state. Fully dark-mode aware.
|
|
19
|
+
*
|
|
20
|
+
* Stable override handles: `.byline-admin-tabs`, `.byline-admin-tab`,
|
|
21
|
+
* `.byline-admin-tab-active`, `.byline-admin-tab-label`,
|
|
22
|
+
* `.byline-admin-tab-badge`.
|
|
23
|
+
*/
|
|
24
|
+
export const AdminTabs = ({ tabs, activeTab, onChange, errorCounts, className, }) => {
|
|
25
|
+
const { t } = useTranslation('byline-admin');
|
|
26
|
+
return (<div role="tablist" aria-label={t('presentation.formTabsAriaLabel')} className={cx('byline-admin-tabs', styles.tabs, className)}>
|
|
27
|
+
{tabs.map((tab) => {
|
|
28
|
+
const isActive = tab.name === activeTab;
|
|
29
|
+
return (<button key={tab.name} type="button" role="tab" aria-selected={isActive} onClick={() => onChange(tab.name)} className={cx('byline-admin-tab', styles.tab, isActive && ['byline-admin-tab-active', styles['tab-active']])}>
|
|
30
|
+
<span className={cx('byline-admin-tab-label', styles.label)}>
|
|
31
|
+
{tab.label}
|
|
32
|
+
{(errorCounts?.[tab.name] ?? 0) > 0 && (<Badge intent="danger" className={cx('byline-admin-tab-badge', styles.badge)}>
|
|
33
|
+
{errorCounts?.[tab.name]}
|
|
34
|
+
</Badge>)}
|
|
35
|
+
</span>
|
|
36
|
+
</button>);
|
|
37
|
+
})}
|
|
38
|
+
</div>);
|
|
39
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
import { createContext, useContext } from 'react';
|
|
9
|
+
const AdminServicesContext = createContext(null);
|
|
10
|
+
export const BylineAdminServicesProvider = ({ services, children, }) => (<AdminServicesContext.Provider value={services}>{children}</AdminServicesContext.Provider>);
|
|
11
|
+
export const useBylineAdminServices = () => {
|
|
12
|
+
const ctx = useContext(AdminServicesContext);
|
|
13
|
+
if (!ctx) {
|
|
14
|
+
throw new Error('@byline/admin: BylineAdminServicesProvider missing. Wrap your admin tree with <BylineAdminServicesProvider services={…} />.');
|
|
15
|
+
}
|
|
16
|
+
return ctx;
|
|
17
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
4
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
5
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) Infonomic Company Limited
|
|
8
|
+
*/
|
|
9
|
+
import { useEffect, useState } from 'react';
|
|
10
|
+
import { useTranslation } from '@byline/i18n/react';
|
|
11
|
+
import { CloseIcon, IconButton, LoaderRing, Modal } from '@byline/ui/react';
|
|
12
|
+
import cx from 'clsx';
|
|
13
|
+
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued';
|
|
14
|
+
import styles from './diff-modal.module.css';
|
|
15
|
+
// Keys that are per-version metadata rather than content — strip before diffing
|
|
16
|
+
// so the diff focuses on meaningful content changes. ClientDocument-shape
|
|
17
|
+
// metadata keys after the Phase 7 admin migration.
|
|
18
|
+
const STRIP_KEYS = new Set([
|
|
19
|
+
'id',
|
|
20
|
+
'versionId',
|
|
21
|
+
'path',
|
|
22
|
+
'status',
|
|
23
|
+
'createdAt',
|
|
24
|
+
'updatedAt',
|
|
25
|
+
'hasPublishedVersion',
|
|
26
|
+
'_publishedVersion',
|
|
27
|
+
]);
|
|
28
|
+
function stripMeta(doc) {
|
|
29
|
+
// With the nested document shape, extract just the fields for diffing.
|
|
30
|
+
if (doc.fields && typeof doc.fields === 'object') {
|
|
31
|
+
return doc.fields;
|
|
32
|
+
}
|
|
33
|
+
return Object.fromEntries(Object.entries(doc).filter(([k]) => !STRIP_KEYS.has(k)));
|
|
34
|
+
}
|
|
35
|
+
export function DiffModal({ isOpen, onDismiss, collection, documentId, versionId, versionLabel, currentDocument, locale, loadHistoricalVersion, }) {
|
|
36
|
+
const { t } = useTranslation('byline-admin');
|
|
37
|
+
const [historicalDoc, setHistoricalDoc] = useState(null);
|
|
38
|
+
const [loading, setLoading] = useState(false);
|
|
39
|
+
const [error, setError] = useState(null);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!isOpen || !versionId)
|
|
42
|
+
return;
|
|
43
|
+
let cancelled = false;
|
|
44
|
+
setLoading(true);
|
|
45
|
+
setError(null);
|
|
46
|
+
setHistoricalDoc(null);
|
|
47
|
+
loadHistoricalVersion(collection, documentId, versionId, locale)
|
|
48
|
+
.then((doc) => {
|
|
49
|
+
if (cancelled)
|
|
50
|
+
return;
|
|
51
|
+
setHistoricalDoc(doc);
|
|
52
|
+
})
|
|
53
|
+
.catch((err) => {
|
|
54
|
+
if (cancelled)
|
|
55
|
+
return;
|
|
56
|
+
setError(err instanceof Error ? err.message : t('diffModal.loadFailed'));
|
|
57
|
+
})
|
|
58
|
+
.finally(() => {
|
|
59
|
+
if (!cancelled)
|
|
60
|
+
setLoading(false);
|
|
61
|
+
});
|
|
62
|
+
return () => {
|
|
63
|
+
cancelled = true;
|
|
64
|
+
};
|
|
65
|
+
}, [isOpen, collection, documentId, versionId, locale, loadHistoricalVersion, t]);
|
|
66
|
+
const currentStr = currentDocument ? JSON.stringify(stripMeta(currentDocument), null, 2) : '';
|
|
67
|
+
const historicalStr = historicalDoc ? JSON.stringify(stripMeta(historicalDoc), null, 2) : '';
|
|
68
|
+
return (<Modal isOpen={isOpen} closeOnOverlayClick={true} onDismiss={onDismiss}>
|
|
69
|
+
<Modal.Container style={{
|
|
70
|
+
width: '96vw',
|
|
71
|
+
maxWidth: '96vw',
|
|
72
|
+
height: '90vh',
|
|
73
|
+
maxHeight: '90vh',
|
|
74
|
+
display: 'flex',
|
|
75
|
+
flexDirection: 'column',
|
|
76
|
+
overflow: 'hidden',
|
|
77
|
+
}}>
|
|
78
|
+
<Modal.Header className={cx('byline-diff-modal-header', styles.header)}>
|
|
79
|
+
<div className={cx('byline-diff-modal-title-stack', styles['title-stack'])}>
|
|
80
|
+
<h3 className={cx('byline-diff-modal-title', styles.title)}>{t('diffModal.title')}</h3>
|
|
81
|
+
<p className={cx('byline-diff-modal-subtitle', styles.subtitle)}>
|
|
82
|
+
{t('diffModal.subtitleBefore')}{' '}
|
|
83
|
+
<span className={cx('byline-diff-modal-version', styles.version)}>
|
|
84
|
+
{versionLabel}
|
|
85
|
+
</span>{' '}
|
|
86
|
+
{t('diffModal.subtitleAfter')}
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
<IconButton onClick={onDismiss} size="xs" aria-label={t('diffModal.closeAriaLabel')}>
|
|
90
|
+
<CloseIcon width="15px" height="15px"/>
|
|
91
|
+
</IconButton>
|
|
92
|
+
</Modal.Header>
|
|
93
|
+
|
|
94
|
+
<Modal.Content className={cx('byline-diff-modal-content', styles.content)} style={{ minHeight: 0 }}>
|
|
95
|
+
{loading && (<div className={cx('byline-diff-modal-state', styles.state)}>
|
|
96
|
+
<LoaderRing size={28} color="#666666"/>
|
|
97
|
+
<span>{t('diffModal.loading')}</span>
|
|
98
|
+
</div>)}
|
|
99
|
+
|
|
100
|
+
{error && (<div className={cx('byline-diff-modal-state', 'byline-diff-modal-error', styles.state, styles.error)}>
|
|
101
|
+
{error}
|
|
102
|
+
</div>)}
|
|
103
|
+
|
|
104
|
+
{!loading && !error && historicalDoc && (<div className={cx('byline-diff-modal-viewer', styles.viewer)}>
|
|
105
|
+
<ReactDiffViewer oldValue={historicalStr} newValue={currentStr} splitView={true} compareMethod={DiffMethod.LINES} useDarkTheme={true} leftTitle={versionLabel} rightTitle={t('diffModal.currentVersion')} hideLineNumbers={false}/>
|
|
106
|
+
</div>)}
|
|
107
|
+
</Modal.Content>
|
|
108
|
+
</Modal.Container>
|
|
109
|
+
</Modal>);
|
|
110
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
import { Badge } from '@byline/ui/react';
|
|
9
|
+
import cx from 'clsx';
|
|
10
|
+
import styles from './source-locale-badge.module.css';
|
|
11
|
+
/**
|
|
12
|
+
* Small neutral badge indicating a document's content **source locale** — the
|
|
13
|
+
* locale it was authored in (its anchor: fallback floor, path locale,
|
|
14
|
+
* completeness yardstick). Rendered next to the document title in the edit and
|
|
15
|
+
* list views. Mirrors the localized-field {@link LocaleBadge} in spirit but uses
|
|
16
|
+
* the shared {@link Badge} with the `noeffect` (neutral) intent.
|
|
17
|
+
*
|
|
18
|
+
* NOTE: currently rendered for *every* document so the anchor is visible during
|
|
19
|
+
* development. The intended end state is to show it only when `locale` differs
|
|
20
|
+
* from the system's current default content locale (a normal single-default
|
|
21
|
+
* install then shows nothing). See docs/08-internationalization/index.md.
|
|
22
|
+
*
|
|
23
|
+
* Stable override handle: `.byline-source-locale-badge`.
|
|
24
|
+
*/
|
|
25
|
+
export const SourceLocaleBadge = ({ locale, className }) => (<Badge intent="noeffect" render={<span />} title={`Primary content language: ${locale.toUpperCase()}`} className={cx('byline-source-locale-badge', styles.badge, className)}>
|
|
26
|
+
{locale.toUpperCase()}
|
|
27
|
+
</Badge>);
|
|
28
|
+
SourceLocaleBadge.displayName = 'SourceLocaleBadge';
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
import { WORKFLOW_STATUS_ARCHIVED, WORKFLOW_STATUS_DRAFT, WORKFLOW_STATUS_PUBLISHED, } from '@byline/core';
|
|
9
|
+
import { useTranslation } from '@byline/i18n/react';
|
|
10
|
+
import { Badge } from '@byline/ui/react';
|
|
11
|
+
import cx from 'clsx';
|
|
12
|
+
import styles from './status-badge.module.css';
|
|
13
|
+
function statusIntent(status) {
|
|
14
|
+
switch (status) {
|
|
15
|
+
case WORKFLOW_STATUS_PUBLISHED:
|
|
16
|
+
return 'success';
|
|
17
|
+
case WORKFLOW_STATUS_DRAFT:
|
|
18
|
+
return 'warning';
|
|
19
|
+
case WORKFLOW_STATUS_ARCHIVED:
|
|
20
|
+
return 'info';
|
|
21
|
+
default:
|
|
22
|
+
return 'noeffect';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Compact badge for workflow status values. Maps the three built-in
|
|
27
|
+
* statuses (draft, published, archived) to semantic intents and falls
|
|
28
|
+
* back to `noeffect` for any custom workflow statuses.
|
|
29
|
+
*
|
|
30
|
+
* When `hasPublishedVersion` is true and the current status is not
|
|
31
|
+
* `published`, a small green dot is rendered before the badge to
|
|
32
|
+
* indicate that a published version is live.
|
|
33
|
+
*
|
|
34
|
+
* Stable override handles: `.byline-status-badge-wrap`,
|
|
35
|
+
* `.byline-status-badge-dot`, `.byline-status-badge`.
|
|
36
|
+
*/
|
|
37
|
+
export const StatusBadge = ({ status, workflowStatuses, hasPublishedVersion, }) => {
|
|
38
|
+
const { t } = useTranslation('byline-admin');
|
|
39
|
+
const label = workflowStatuses.find((s) => s.name === status)?.label ?? String(status ?? '');
|
|
40
|
+
return (<span className={cx('byline-status-badge-wrap', styles.wrap)}>
|
|
41
|
+
{hasPublishedVersion === true && status !== 'published' && (<span title={t('statusBadge.publishedVersionLive')} className={cx('byline-status-badge-dot', styles.dot)}/>)}
|
|
42
|
+
<Badge intent={statusIntent(status)} className={cx('byline-status-badge', styles.badge)}>
|
|
43
|
+
{label}
|
|
44
|
+
</Badge>
|
|
45
|
+
</span>);
|
|
46
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@byline/admin",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "5.1.
|
|
5
|
+
"version": "5.1.3",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -183,12 +183,12 @@
|
|
|
183
183
|
"react-diff-viewer-continued": "^4.4.0",
|
|
184
184
|
"uuid": "^14.0.2",
|
|
185
185
|
"zod": "^4.5.4",
|
|
186
|
-
"@byline/analytics": "5.1.
|
|
187
|
-
"@byline/analytics-agent": "5.1.
|
|
188
|
-
"@byline/
|
|
189
|
-
"@byline/core": "5.1.
|
|
190
|
-
"@byline/i18n": "5.1.
|
|
191
|
-
"@byline/
|
|
186
|
+
"@byline/analytics": "5.1.3",
|
|
187
|
+
"@byline/analytics-agent": "5.1.3",
|
|
188
|
+
"@byline/ui": "5.1.3",
|
|
189
|
+
"@byline/core": "5.1.3",
|
|
190
|
+
"@byline/i18n": "5.1.3",
|
|
191
|
+
"@byline/auth": "5.1.3"
|
|
192
192
|
},
|
|
193
193
|
"peerDependencies": {
|
|
194
194
|
"react": "^19.0.0",
|