Skip to content

Commit 5394da1

Browse files
authored
Implement Data-Center UI (#2599)
1 parent 2ea4a4e commit 5394da1

65 files changed

Lines changed: 4606 additions & 29 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-dcm': patch
3+
---
4+
5+
UI code-quality improvements: replace all inline styles with makeStyles classes, use theme palette tokens (status.ok, error, text) instead of hardcoded colours, merge duplicate style files into a single useDcmStyles hook, add destructive-action styling to delete dialogs, and move RhdhLogoFull/RhdhLogoIcon into the plugin so they can be wired in RHDH without relying on the dev-only app shell.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-dcm': patch
3+
---
4+
5+
Refresh DCM plugin API reports and align UI internals around shared route/tab constants and reusable detail components.

workspaces/dcm/app-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ permission:
6060

6161
scaffolder: {}
6262

63+
dcm:
64+
policyPacks:
65+
- security-baseline
66+
- compliance-pci
67+
- audit-logging
68+
6369
catalog:
6470
import:
6571
entityFilename: catalog-info.yaml

workspaces/dcm/packages/app/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ const routes = (
122122
</Route>
123123
<Route path="/settings" element={<UserSettingsPage />} />
124124
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
125-
<Route path="/dcm" element={<DcmPage />} />
125+
<Route path="/dcm/*" element={<DcmPage />} />
126126
<Route path="/rbac" element={<RbacPage />} />
127127
</FlatRoutes>
128128
);

workspaces/dcm/packages/app/src/components/Root/Root.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import ChevronRightIcon from '@material-ui/icons/ChevronRight';
4747
import LogoFull from './LogoFull';
4848
import LogoIcon from './LogoIcon';
4949
import { useRhdhTheme } from '../../hooks/useRhdhTheme';
50+
import { usePatternFlyTheme } from '../../hooks/usePatternFlyTheme';
5051

5152
/** Returns true when the theme palette is in dark mode (supports both MUI v4 type and v5 mode). */
5253
function isPaletteDark(palette: { type?: string; mode?: string }): boolean {
@@ -232,6 +233,7 @@ const SidebarLogo = () => {
232233
export const Root = ({ children }: PropsWithChildren<{}>) => {
233234
const classes = useSidebarItemStyles();
234235
const location = useLocation();
236+
usePatternFlyTheme();
235237
const isDcmActive = location.pathname.startsWith('/dcm');
236238
const isRbacActive = location.pathname.startsWith('/rbac');
237239

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { useEffect } from 'react';
18+
import { useIsDarkMode } from '@red-hat-developer-hub/backstage-plugin-dcm';
19+
20+
/**
21+
* Hook to automatically sync Backstage theme with PatternFly dark theme.
22+
* Applies 'pf-v6-theme-dark' class to HTML element when Backstage is in dark mode.
23+
*/
24+
export const usePatternFlyTheme = () => {
25+
const isDark = useIsDarkMode();
26+
27+
useEffect(() => {
28+
// Apply PatternFly dark theme class to HTML when Backstage is in dark mode
29+
const html = document.documentElement;
30+
if (isDark) {
31+
html.classList.add('pf-v6-theme-dark');
32+
} else {
33+
html.classList.remove('pf-v6-theme-dark');
34+
}
35+
}, [isDark]);
36+
};

workspaces/dcm/packages/app/src/hooks/useRhdhTheme.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
*/
1616

1717
import { getThemes } from '@red-hat-developer-hub/backstage-plugin-theme';
18-
import LogoFull from '../components/rhdh-logo/RhdhLogoFull';
19-
import RhdhLogoIcon from '../components/rhdh-logo/RhdhLogoIcon';
18+
import {
19+
RhdhLogoFull,
20+
RhdhLogoIcon,
21+
} from '@red-hat-developer-hub/backstage-plugin-dcm';
2022

2123
/**
2224
* Change this value to `true` if you want to use the RHDH theme.
@@ -26,8 +28,8 @@ const ENABLE_RHDH_THEME = true;
2628
export function useRhdhTheme() {
2729
return ENABLE_RHDH_THEME
2830
? ({
29-
RhdhLogoFull: LogoFull,
30-
RhdhLogoIcon: RhdhLogoIcon,
31+
RhdhLogoFull,
32+
RhdhLogoIcon,
3133
get themes() {
3234
return getThemes();
3335
},

workspaces/dcm/plugins/dcm-common/report.api.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,30 @@ import { BasicPermission } from '@backstage/plugin-permission-common';
88
// @public
99
export const DCM_COMMON_PLUGIN_ID: 'dcm';
1010

11+
// @public
12+
export const DCM_ENTITY_STATUS: {
13+
readonly success: 'success';
14+
readonly running: 'running';
15+
};
16+
17+
// @public
18+
export const DCM_ENTITY_STATUS_VALUES: readonly DcmEntityStatus[];
19+
20+
// @public
21+
export type DcmEntityStatus = 'success' | 'running';
22+
1123
// @public
1224
export const dcmPluginPermissions: BasicPermission[];
1325

1426
// @public
1527
export const dcmPluginReadPermission: BasicPermission;
28+
29+
// @public
30+
export function displayDcmEntityStatus(status: DcmEntityStatus): string;
31+
32+
// @public
33+
export function displayDcmEntityStatusLoose(raw: string): string;
34+
35+
// @public
36+
export function parseDcmEntityStatus(raw: string): DcmEntityStatus | undefined;
1637
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {
18+
DCM_ENTITY_STATUS,
19+
DCM_ENTITY_STATUS_VALUES,
20+
displayDcmEntityStatus,
21+
displayDcmEntityStatusLoose,
22+
parseDcmEntityStatus,
23+
} from './entityStatus';
24+
25+
describe('entityStatus', () => {
26+
it('maps every known status to a non-empty display label', () => {
27+
for (const status of DCM_ENTITY_STATUS_VALUES) {
28+
expect(displayDcmEntityStatus(status).length).toBeGreaterThan(0);
29+
}
30+
});
31+
32+
it('displays success and running with expected UI labels', () => {
33+
expect(displayDcmEntityStatus(DCM_ENTITY_STATUS.success)).toBe('Success');
34+
expect(displayDcmEntityStatus(DCM_ENTITY_STATUS.running)).toBe('Running');
35+
});
36+
37+
it('parses known lowercase values', () => {
38+
expect(parseDcmEntityStatus('success')).toBe(DCM_ENTITY_STATUS.success);
39+
expect(parseDcmEntityStatus('running')).toBe(DCM_ENTITY_STATUS.running);
40+
});
41+
42+
it('rejects legacy / wrong casing', () => {
43+
expect(parseDcmEntityStatus('Success')).toBeUndefined();
44+
});
45+
46+
it('displayDcmEntityStatusLoose falls back for unknown', () => {
47+
expect(displayDcmEntityStatusLoose('success')).toBe('Success');
48+
expect(displayDcmEntityStatusLoose('unknown-state')).toBe('unknown-state');
49+
});
50+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Wire / storage form of entity status (lowercase).
19+
*
20+
* @public
21+
*/
22+
export type DcmEntityStatus = 'success' | 'running';
23+
24+
/**
25+
* Canonical lowercase entity status values for DCM data (mock, API, DB).
26+
* Use {@link displayDcmEntityStatus} for user-visible labels in the UI.
27+
*
28+
* When adding a status: extend {@link DcmEntityStatus}, add an entry here, and add a branch in
29+
* {@link displayDcmEntityStatus} (`assertNever` catches missing switch cases).
30+
*
31+
* @public
32+
*/
33+
export const DCM_ENTITY_STATUS = {
34+
success: 'success',
35+
running: 'running',
36+
} as const satisfies Record<DcmEntityStatus, DcmEntityStatus>;
37+
38+
/** All known {@link DcmEntityStatus} values (for guards / tests). @public */
39+
export const DCM_ENTITY_STATUS_VALUES: readonly DcmEntityStatus[] =
40+
Object.freeze(Object.values(DCM_ENTITY_STATUS) as DcmEntityStatus[]);
41+
42+
function assertNever(x: never): never {
43+
throw new Error(`Unexpected entity status: ${String(x)}`);
44+
}
45+
46+
/**
47+
* Human-readable label for UI (e.g. table cells).
48+
* @public
49+
*/
50+
export function displayDcmEntityStatus(status: DcmEntityStatus): string {
51+
switch (status) {
52+
case DCM_ENTITY_STATUS.success:
53+
return 'Success';
54+
case DCM_ENTITY_STATUS.running:
55+
return 'Running';
56+
default:
57+
return assertNever(status);
58+
}
59+
}
60+
61+
/**
62+
* Parse wire / JSON values; returns `undefined` if not a known status.
63+
* @public
64+
*/
65+
export function parseDcmEntityStatus(raw: string): DcmEntityStatus | undefined {
66+
return (DCM_ENTITY_STATUS_VALUES as readonly string[]).includes(raw)
67+
? (raw as DcmEntityStatus)
68+
: undefined;
69+
}
70+
71+
/**
72+
* Display for possibly unknown wire values — falls back to the raw string.
73+
* @public
74+
*/
75+
export function displayDcmEntityStatusLoose(raw: string): string {
76+
const parsed = parseDcmEntityStatus(raw);
77+
return parsed === undefined ? raw : displayDcmEntityStatus(parsed);
78+
}

0 commit comments

Comments
 (0)