Skip to content

Commit 7f03c25

Browse files
ElayAharonisjd78
authored andcommitted
✨ Enhance application inventory (konveyor#2468)
https://issues.redhat.com/browse/MTA-5245 Resolves: konveyor#2287 Update the inventory table, detail drawer, and create/edit modal to support new data and actions needed to implement ## Summary by CodeRabbit * **New Features** * Introduced a detailed application drawer with tabbed views for Details, Tags, Reports, Reviews, Tasks, and Platform information. * Added support for managing source platform and asset repository data in the application form. * Enhanced bulk and per-application actions in the applications table, including new actions for changing source platform, retrieving configurations, and generating assets. * Added schema-driven platform coordinates support and related UI components. * Introduced new hooks for fetching schemas and platform-specific schema data. * Added a multi-section application form with dynamic validation and platform-specific coordinates support. * Added new translation keys for actions and terms related to configurations, source platform, and assets. * **Improvements** * Expanded application metadata and UI to display and edit additional platform, repository, and business service details. * Refined component structure and naming for clarity and maintainability. * Improved handling of empty states and localization throughout the UI. * Disabled automatic refetch and retries for application manifest fetching by default to improve performance. * Enhanced form validation and submission logic with improved data mapping and error handling. * Improved bulk action dropdown styling and conditional enabling based on selection. * Updated notification handling for application create and update actions. * **Bug Fixes** * Ensured platform lists are always returned as arrays to prevent undefined errors. * **Refactor** * Reorganized and consolidated UI components, removing deprecated or redundant files and updating import paths. * Converted schema-defined field components to controlled components with improved accessibility IDs. * Replaced local repository details component with a shared version for consistency. * Split application actions column into primary and secondary actions for better UX. * Renamed several status components for consistency. * Replaced single form hook with separate hooks for form data and form logic. * Centralized exports for detail drawer components. * **Tests** * Updated application form tests to account for new platform data and improved validation synchronization. * **Style** * Added new styles for the drawer tab container and its content. --------- Signed-off-by: Elay Aharoni <[email protected]> Signed-off-by: Scott J Dickerson <[email protected]> Co-authored-by: Scott J Dickerson <[email protected]>
1 parent 5632dcc commit 7f03c25

File tree

54 files changed

+2632
-1928
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2632
-1928
lines changed

client/public/locales/en/translation.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@
6868
"unlink": "Unlink",
6969
"view": "View",
7070
"viewErrorReport": "View error report",
71-
"viewArchetypes": "View archetypes"
71+
"viewArchetypes": "View archetypes",
72+
"retrieveConfigurations": "Retrieve configurations",
73+
"changeSourcePlatform": "Change source platform",
74+
"generateAssets": "Generate assets"
7275
},
7376
"colors": {
7477
"black": "Black",
@@ -316,6 +319,7 @@
316319
"applicationImports": "Application imports",
317320
"applicationName": "Application name",
318321
"archetypeName": "Archetype name",
322+
"applicationDiscoveryManifest": "Application discovery manifest",
319323
"applicationInformation": "Application information",
320324
"applicationFile": "Application file",
321325
"archetype": "Archetype",
@@ -328,6 +332,7 @@
328332
"assessmentQuestionnaires": "Assessment questionnaires",
329333
"assessmentNotes": "Assessment notes",
330334
"assessmentSummary": "Assessment summary",
335+
"assetRepository": "Asset repository",
331336
"attachments": "Attachments",
332337
"autoTagging": "Automated Tagging",
333338
"binary": "Binary (Java)",
@@ -422,7 +427,6 @@
422427
"label": "Label",
423428
"loading": "Loading",
424429
"lowRisk": "Low risk",
425-
"manifest": "Manifest",
426430
"manualTags": "Manual Tags",
427431
"maintainers": "Maintainers",
428432
"mavenConfig": "Maven configuration",
@@ -482,6 +486,8 @@
482486
"sourceBranch": "Branch",
483487
"sourceCode": "Source code",
484488
"sourcePlatforms": "Source platforms",
489+
"sourcePlatform": "Source platform",
490+
"sourcePlatformCoordinates": "Source platform coordinates",
485491
"sourceRepo": "Source Repository",
486492
"sourceRootPath": "Root path",
487493
"southboundDependencies": "Southbound dependencies",

client/src/app/api/models.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,27 +125,36 @@ export interface Repository {
125125
path?: string;
126126
}
127127

128+
/** A JSON document with its schema */
129+
export interface Document {
130+
content: JsonDocument;
131+
/** name of the schema */ schema: string;
132+
}
133+
128134
export interface Application {
129135
id: number;
130136
name: string;
131137
description?: string;
138+
bucket?: Ref;
139+
repository?: Repository;
140+
assets?: Repository;
141+
binary?: string;
142+
coordinates?: Document;
143+
review?: Ref;
132144
comments?: string;
133-
businessService?: Ref;
145+
identities?: Ref[];
134146
tags?: TagRef[];
147+
businessService?: Ref;
135148
owner?: Ref;
136149
contributors?: Ref[];
137-
review?: Ref;
138-
identities?: Ref[];
139-
repository?: Repository;
140-
binary?: string;
141150
migrationWave: Ref | null;
151+
platform?: Ref;
152+
archetypes?: Ref[];
142153
assessments?: Ref[];
143154
assessed?: boolean;
144-
archetypes?: Ref[];
145155
risk?: Risk;
146156
confidence?: number;
147157
effort?: number;
148-
platform?: Ref;
149158
}
150159

151160
export interface Review {
@@ -958,6 +967,7 @@ export interface Manifest {
958967

959968
// Could use https://www.npmjs.com/package/@types/json-schema in future if needed
960969
export interface JsonSchemaObject {
970+
$schema?: string;
961971
type: "string" | "integer" | "number" | "boolean" | "object" | "array";
962972
title?: string;
963973
description?: string;
@@ -992,3 +1002,19 @@ export interface JsonSchemaObject {
9921002
/** For type object, whether additional properties are allowed */
9931003
additionalProperties?: boolean;
9941004
}
1005+
1006+
export interface Schema {
1007+
name: string;
1008+
domain: string;
1009+
variant: string;
1010+
subject: string;
1011+
versions: Array<{
1012+
id: number;
1013+
definition: JsonSchemaObject;
1014+
}>;
1015+
}
1016+
1017+
export interface TargetedSchema {
1018+
name: string;
1019+
definition: JsonSchemaObject;
1020+
}

client/src/app/api/rest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ export const HEADERS: Record<string, RawAxiosRequestHeaders> = {
122122

123123
export * from "./rest/analysis";
124124
export * from "./rest/files";
125+
export * from "./rest/schemas";
126+
export * from "./rest/generators";
125127

126128
/**
127129
* Provide consistent fetch and processing for server side filtering and sorting with

client/src/app/api/rest/schemas.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import axios from "axios";
2+
3+
import { Schema, TargetedSchema } from "../models";
4+
import { hub, template } from "../rest";
5+
6+
const SCHEMAS = hub`/schemas`;
7+
const TARGETED_SCHEMA = hub`/schema/jsd/{{domain}}/{{variant}}/{{subject}}`;
8+
9+
export const getSchemas = () =>
10+
axios.get<Schema[]>(SCHEMAS).then(({ data }) => data);
11+
12+
export const getSchemaByName = (name: string) =>
13+
axios.get<Schema>(`${SCHEMAS}/${name}`).then(({ data }) => data);
14+
15+
export const getPlatformCoordinatesSchema = (platformKind: string) =>
16+
axios
17+
.get<TargetedSchema>(
18+
template(TARGETED_SCHEMA, {
19+
domain: "platform",
20+
variant: platformKind,
21+
subject: "coordinates",
22+
})
23+
)
24+
.then(({ data }) => data);
25+
26+
export const getPlatformDiscoveryFiltersSchema = (platformKind: string) =>
27+
axios
28+
.get<TargetedSchema>(
29+
template(TARGETED_SCHEMA, {
30+
domain: "platform",
31+
variant: platformKind,
32+
subject: "filters",
33+
})
34+
)
35+
.then(({ data }) => data);
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import React from "react";
22
import { Text } from "@patternfly/react-core";
3+
import { useTranslation } from "react-i18next";
34

45
export interface EmptyTextMessageProps {
5-
message: string;
6+
message?: string;
67
}
78

89
export const EmptyTextMessage: React.FC<EmptyTextMessageProps> = ({
910
message,
1011
}) => {
12+
const { t } = useTranslation();
13+
1114
return (
1215
<Text className="pf-v5-u-color-200 pf-v5-u-font-weight-light">
13-
<i>{message}</i>
16+
<i>{message || t("terms.notAvailable")}</i>
1417
</Text>
1518
);
1619
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
.drawer-tabs-container {
2+
flex-grow: 1;
3+
position: relative;
4+
min-height: 0;
5+
6+
display: flex;
7+
flex-direction: column;
8+
}
9+
10+
.drawer-tabs-container .pf-v5-c-tabs {
11+
--pf-v5-c-tabs__scroll-button--Width: 2.5rem;
12+
flex-grow: 0;
13+
flex-shrink: 0;
14+
}
15+
16+
.drawer-tabs-container > :not(.pf-v5-c-tabs) {
17+
flex-grow: 1;
18+
position: relative;
19+
min-height: 0; /* keep the content from growing beyond the .drawer-tabs-container */
20+
}
21+
22+
.drawer-tab-content {
23+
height: 100%;
24+
overflow-y: auto;
25+
}
26+
27+
.drawer-tab-content__section-label {
28+
padding-block-start: var(--pf-v5-global--spacer--md);
29+
margin-block-end: var(--pf-v5-global--spacer--xs);
30+
}
31+
32+
.drawer-tab-content__section-content {
33+
padding-left: var(--pf-v5-global--spacer--xs);
34+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from "react";
2+
import { Panel, Title, PanelMain } from "@patternfly/react-core";
3+
import "./drawer-tabs-container.css";
4+
5+
export const DrawerTabsContainer: React.FC<{ children: React.ReactNode }> = ({
6+
children,
7+
}) => {
8+
return <div className="drawer-tabs-container">{children}</div>;
9+
};
10+
11+
export const DrawerTabContent: React.FC<{ children: React.ReactNode }> = ({
12+
children,
13+
}) => {
14+
return (
15+
<Panel className={`drawer-tab-content`}>
16+
<PanelMain>{children}</PanelMain>
17+
</Panel>
18+
);
19+
};
20+
21+
export const DrawerTabContentSection: React.FC<{
22+
label?: string;
23+
children: React.ReactNode;
24+
}> = ({ label, children }) => {
25+
return (
26+
<div className="drawer-tab-content__section">
27+
{label && (
28+
<Title
29+
headingLevel="h4"
30+
size="md"
31+
className="drawer-tab-content__section-label"
32+
>
33+
{label}
34+
</Title>
35+
)}
36+
<div className="drawer-tab-content__section-content">{children}</div>
37+
</div>
38+
);
39+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from "./drawer-tabs-container";
2+
export * from "./repository-details";
3+
export * from "./review-fields";
4+
export * from "./review-label";
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React from "react";
2+
import { useTranslation } from "react-i18next";
3+
import {
4+
DescriptionList,
5+
DescriptionListGroup,
6+
DescriptionListTerm,
7+
DescriptionListDescription,
8+
} from "@patternfly/react-core";
9+
10+
import { Repository } from "@app/api/models";
11+
12+
export const RepositoryDetails: React.FC<{ repository: Repository }> = ({
13+
repository,
14+
}) => {
15+
const { t } = useTranslation();
16+
17+
return (
18+
<DescriptionList isHorizontal isCompact>
19+
{repository.kind && (
20+
<DescriptionListGroup>
21+
<DescriptionListTerm>{t("terms.repositoryType")}</DescriptionListTerm>
22+
<DescriptionListDescription>
23+
{repository.kind || ""}
24+
</DescriptionListDescription>
25+
</DescriptionListGroup>
26+
)}
27+
{repository.url && (
28+
<DescriptionListGroup>
29+
<DescriptionListTerm>{t("terms.url")}</DescriptionListTerm>
30+
<DescriptionListDescription>
31+
{repository.url || ""}
32+
</DescriptionListDescription>
33+
</DescriptionListGroup>
34+
)}
35+
{repository.branch && (
36+
<DescriptionListGroup>
37+
<DescriptionListTerm>{t("terms.branch")}</DescriptionListTerm>
38+
<DescriptionListDescription>
39+
{repository.branch || ""}
40+
</DescriptionListDescription>
41+
</DescriptionListGroup>
42+
)}
43+
{repository.path && (
44+
<DescriptionListGroup>
45+
<DescriptionListTerm>{t("terms.path")}</DescriptionListTerm>
46+
<DescriptionListDescription>
47+
{repository.path || ""}
48+
</DescriptionListDescription>
49+
</DescriptionListGroup>
50+
)}
51+
{/* TODO: Add credentials, show the Identity name, the default, or "Not available" */}
52+
</DescriptionList>
53+
);
54+
};

client/src/app/components/schema-defined-fields/SchemaAsCodeEditor.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type ControlledEditor = {
2222
};
2323

2424
export interface ISchemaAsCodeEditorProps {
25+
id: string;
2526
jsonDocument: object;
2627
jsonSchema?: JsonSchemaObject;
2728
onDocumentSaved?: (newSchemaContent: object) => void;
@@ -30,6 +31,7 @@ export interface ISchemaAsCodeEditorProps {
3031
}
3132

3233
export const SchemaAsCodeEditor = ({
34+
id,
3335
jsonDocument,
3436
jsonSchema,
3537
onDocumentSaved,
@@ -38,10 +40,11 @@ export const SchemaAsCodeEditor = ({
3840
}: ISchemaAsCodeEditorProps) => {
3941
const editorRef = React.useRef<ControlledEditor>();
4042

41-
const initialCode = JSON.stringify(jsonDocument, null, 2);
42-
43-
const [currentCode, setCurrentCode] = React.useState(initialCode);
43+
const [currentCode, setCurrentCode] = React.useState(
44+
JSON.stringify(jsonDocument, null, 2)
45+
);
4446
const [okToSave, setOkToSave] = React.useState(true);
47+
4548
const focusMovedOnSelectedDocumentChange = React.useRef<boolean>(false);
4649
React.useEffect(() => {
4750
if (currentCode && !focusMovedOnSelectedDocumentChange.current) {
@@ -84,13 +87,14 @@ export const SchemaAsCodeEditor = ({
8487
}
8588
} catch (error) {
8689
console.error("Invalid JSON:", error);
87-
// TODO: Use useNotify() to toast the error to the user
90+
// TODO: Use useNotify() to toast the save error to the user
8891
}
8992
}
9093
};
9194

9295
return (
9396
<CodeEditor
97+
id={id}
9498
className="schema-defined-field-viewer-code-editor"
9599
isCopyEnabled
96100
isDarkTheme

0 commit comments

Comments
 (0)