Skip to content

Commit

Permalink
Merge branch 'main' into dynamic-spinner
Browse files Browse the repository at this point in the history
  • Loading branch information
ibolton336 authored Dec 15, 2023
2 parents 553ea3e + 21e3e9f commit 56bdfdb
Show file tree
Hide file tree
Showing 17 changed files with 310 additions and 189 deletions.
13 changes: 6 additions & 7 deletions .github/workflows/ci-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,25 @@ on:
push:
branches:
- main
- "v[0-9]+.[0-9]+.[0-9]+"
- "release-*"
pull_request:
branches:
- main
- "v[0-9]+.[0-9]+.[0-9]+"
- "release-*"

jobs:
unit-test:
runs-on: ubuntu-latest
strategy:
matrix:
# Note: This should match the node version(s) used in the base Dockerfile
node-version: [18.x]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

Expand All @@ -43,21 +42,21 @@ jobs:
run: npm run test -- --coverage --watchAll=false

- name: Upload to codecov (client)
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v3
with:
flags: client
directory: ./*/coverage

- name: Upload to codecov (server)
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v3
with:
flags: server
directory: ./*/coverage

build-and-upload-for-global-ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: save tackle2-ui image
run: |
Expand Down
6 changes: 6 additions & 0 deletions client/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@
"duplicateWave": "The migration wave could not be created due to a conflict with an existing wave. Make sure the name and start/end dates are unique and try again.",
"importErrorCheckDocumentation": "For status Error imports, check the documentation to ensure your file is structured correctly.",
"insecureTracker": "Insecure mode deactivates certificate verification. Use insecure mode for instances that have self-signed certificates.",
"inheritedReviewTooltip": "This application is inheriting a review from an archetype.",
"inheritedReviewTooltip_plural": "This application is inheriting reviews from {{count}} archetypes.",
"inheritedAssessmentTooltip": "This application is inheriting an assessment from an archetype.",
"inheritedAssessmentTooltip_plural": "This application is inheriting assessments from {{count}} archetypes.",
"jiraInstanceNotConnected": "Jira instance {{name}} is not connected.",
"manageDependenciesInstructions": "Add northbound and southbound dependencies for the selected application here. Note that any selections made will be saved automatically. To undo any changes, you must manually delete the applications from the dropdowns.",
"noDataAvailableBody": "No data available to be shown here.",
Expand Down Expand Up @@ -237,6 +241,7 @@
"associatedApplications": "Associated applications",
"associatedArchetypes": "Associated archetypes",
"archetypesReviewed": "Archetypes reviewed",
"archetypesAssessed": "Archetypes assessed",
"add": "Add",
"additionalNotesOrComments": "Additional notes or comments",
"adoptionCandidateDistribution": "Application confidence and risk",
Expand Down Expand Up @@ -327,6 +332,7 @@
"inProgress": "In-progress",
"instanceType": "Instance type",
"instance": "Instance",
"inherited": "Inherited",
"issueType": "Issue type",
"jiraConfig": "Jira configuration",
"issue": "Issue",
Expand Down
6 changes: 6 additions & 0 deletions client/src/app/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export const isRWXSupported = ENV.RWX_SUPPORTED === "true";

export const DEFAULT_SELECT_MAX_HEIGHT = 200;

/**
* The name of the client generated id field inserted in a object marked with mixin type
* `WithUiId`.
*/
export const UI_UNIQUE_ID = "_ui_unique_id";

// Colors

// t('colors.red')
Expand Down
8 changes: 7 additions & 1 deletion client/src/app/api/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,14 @@ export interface BaseAnalysisIssueReport extends AnalysisIssuesCommonFields {
files: number;
}

// After fetching from the hub, we inject a unique id composed of ruleset+rule for convenience
/**
* Mark an object as having a unique client generated id field. Use this type if
* an objects from hub does not have a single field with a unique key AND the object
* is to be used in a table. Our table handlers assume a single field with a unique
* value across all objects in a set to properly handle row selections.
*/
export type WithUiId<T> = T & { _ui_unique_id: string };

export type AnalysisRuleReport = WithUiId<BaseAnalysisRuleReport>;
export type AnalysisIssueReport = WithUiId<BaseAnalysisIssueReport>;

Expand Down
40 changes: 36 additions & 4 deletions client/src/app/components/IconedStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import React from "react";
import { Flex, FlexItem, Icon } from "@patternfly/react-core";
import { Flex, FlexItem, Icon, Tooltip } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import CheckCircleIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
import TimesCircleIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon";
import InProgressIcon from "@patternfly/react-icons/dist/esm/icons/in-progress-icon";
import ExclamationCircleIcon from "@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon";
import UnknownIcon from "@patternfly/react-icons/dist/esm/icons/unknown-icon";
import QuestionCircleIcon from "@patternfly/react-icons/dist/esm/icons/question-circle-icon";

export type IconedStatusPreset =
| "InheritedReviews"
| "InheritedAssessments"
| "Canceled"
| "Completed"
| "Error"
Expand Down Expand Up @@ -35,6 +38,8 @@ export interface IIconedStatusProps {
icon?: React.ReactNode;
className?: string;
label?: React.ReactNode | string;
tooltipMessage?: string;
tooltipCount?: number;
}

export const IconedStatus: React.FC<IIconedStatusProps> = ({
Expand All @@ -43,9 +48,26 @@ export const IconedStatus: React.FC<IIconedStatusProps> = ({
icon,
className = "",
label,
tooltipCount = 0,
}: IIconedStatusProps) => {
const { t } = useTranslation();
const presets: IconedStatusPresetType = {
InheritedReviews: {
icon: <QuestionCircleIcon />,
status: "info",
label: t("terms.inherited"),
tooltipMessage: t("message.inheritedReviewTooltip", {
count: tooltipCount,
}),
},
InheritedAssessments: {
icon: <QuestionCircleIcon />,
status: "info",
label: t("terms.inherited"),
tooltipMessage: t("message.inheritedAssessmentTooltip", {
count: tooltipCount,
}),
},
Canceled: {
icon: <TimesCircleIcon />,
status: "info",
Expand Down Expand Up @@ -89,16 +111,26 @@ export const IconedStatus: React.FC<IIconedStatusProps> = ({
},
};
const presetProps = preset && presets[preset];
const IconWithOptionalTooltip: React.FC<{ children: React.ReactElement }> = ({
children,
}) =>
presetProps?.tooltipMessage ? (
<Tooltip content={presetProps?.tooltipMessage}>{children}</Tooltip>
) : (
<>{children}</>
);

return (
<Flex
flexWrap={{ default: "nowrap" }}
spaceItems={{ default: "spaceItemsSm" }}
>
<FlexItem>
<Icon status={status || presetProps?.status} className={className}>
{icon || presetProps?.icon || <UnknownIcon />}
</Icon>
<IconWithOptionalTooltip>
<Icon status={status || presetProps?.status} className={className}>
{icon || presetProps?.icon || <UnknownIcon />}
</Icon>
</IconWithOptionalTooltip>
</FlexItem>
<FlexItem>{label || presetProps?.label}</FlexItem>
</Flex>
Expand Down
4 changes: 3 additions & 1 deletion client/src/app/hooks/table-controls/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,9 @@ Table columns are identified by unique keys which are statically inferred from t

#### Item IDs

Item objects must contain some unique identifier which is either a string or number. The property key of this identifier is a required config argument called `idProperty`, which will usually be `"id"`. If no unique identifier is present in the API data, an artificial one can be injected before passing the data into these hooks, which can be done in the useQuery `select` callback (see instances where we have used `"_ui_unique_id"`). Any state which keeps track of something by item (i.e. by row) makes use of `item[idProperty]` as an identifier. Examples of this include selected rows, expanded rows and active rows. Valid `idProperty` values are also enforced by TypeScript generics; if an `idProperty` is provided that is not a property on the `TItem` type, you should get a type error.
Item objects must contain some unique identifier which is either a string or number. The property key of this identifier is a required config argument called `idProperty`, which will usually be `"id"`. If no unique identifier is present in the API data, an artificial one can be injected before passing the data into these hooks. This can be done in the useQuery `select` callback (see instances where we have used `"_ui_unique_id"`). Another option is to use the query hook `useWithUiId()` on the react-query fetched data. Since `select` modified data is not part of the query cache, it does not matter if transforms are done in react-query, `useWithUiId` hook, or other means.

Any state which keeps track of something by item (i.e. by row) makes use of `item[idProperty]` as an identifier. Examples of this include selected rows, expanded rows and active rows. Valid `idProperty` values are also enforced by TypeScript generics. If an `idProperty` is provided that is not a property on the `TItem` type, you should get a type error.

> ⚠️ TECH DEBT NOTE: Things specific to `useQuery` and `_ui_unique_id` here are Konveyor-specific notes that should be removed after moving this to table-batteries.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import {
MenuToggle,
MenuToggleElement,
Modal,
Flex,
FlexItem,
} from "@patternfly/react-core";
import { PencilAltIcon, TagIcon, EllipsisVIcon } from "@patternfly/react-icons";
import {
Expand All @@ -30,7 +28,6 @@ import {
ActionsColumn,
Tbody,
} from "@patternfly/react-table";
import { QuestionCircleIcon } from "@patternfly/react-icons/dist/esm/icons/question-circle-icon";

// @app components and utilities
import { AppPlaceholder } from "@app/components/AppPlaceholder";
Expand All @@ -44,7 +41,6 @@ import {
ConditionalTableBody,
TableRowContentWithControls,
} from "@app/components/TableControls";
import { IconedStatus } from "@app/components/IconedStatus";
import { ToolbarBulkSelector } from "@app/components/ToolbarBulkSelector";
import { ConfirmDialog } from "@app/components/ConfirmDialog";
import { NotificationsContext } from "@app/components/NotificationsContext";
Expand Down Expand Up @@ -108,14 +104,14 @@ import {
getTaskById,
} from "@app/api/rest";
import { ApplicationDependenciesForm } from "@app/components/ApplicationDependenciesFormContainer/ApplicationDependenciesForm";
import { useFetchArchetypes } from "@app/queries/archetypes";
import { useState } from "react";
import { ApplicationAnalysisStatus } from "../components/application-analysis-status";
import { ApplicationDetailDrawer } from "../components/application-detail-drawer/application-detail-drawer";
import { SimpleDocumentViewerModal } from "@app/components/SimpleDocumentViewer";
import { AnalysisWizard } from "../analysis-wizard/analysis-wizard";
import { TaskGroupProvider } from "../analysis-wizard/components/TaskGroupContext";
import { ApplicationIdentityForm } from "../components/application-identity-form/application-identity-form";
import { ApplicationReviewStatus } from "../components/application-review-status/application-review-status";

export const ApplicationsTable: React.FC = () => {
const { t } = useTranslation();
Expand Down Expand Up @@ -221,8 +217,6 @@ export const ApplicationsTable: React.FC = () => {
refetch: fetchApplications,
} = useFetchApplications();

const { archetypes } = useFetchArchetypes();

const onDeleteApplicationSuccess = (appIDCount: number) => {
pushNotification({
title: t("toastr.success.applicationDeleted", {
Expand Down Expand Up @@ -870,23 +864,6 @@ export const ApplicationsTable: React.FC = () => {
>
<Tbody>
{currentPageItems?.map((application, rowIndex) => {
const isAppReviewed = !!application.review;
const applicationArchetypes = application.archetypes?.map(
(archetypeRef) => {
return archetypes.find(
(archetype) => archetype.id === archetypeRef.id
);
}
);

const hasReviewedArchetype = applicationArchetypes?.some(
(archetype) => !!archetype?.review
);

const hasAssessedArchetype = applicationArchetypes?.some(
(archetype) => !!archetype?.assessments?.length
);

return (
<Tr
key={application.name}
Expand Down Expand Up @@ -920,50 +897,20 @@ export const ApplicationsTable: React.FC = () => {
modifier="truncate"
{...getTdProps({ columnKey: "assessment" })}
>
<Flex alignItems={{ default: "alignItemsCenter" }}>
<FlexItem>
<ApplicationAssessmentStatus
application={application}
/>
</FlexItem>
<FlexItem>
{hasAssessedArchetype ? (
<ConditionalTooltip
isTooltipEnabled={hasAssessedArchetype || false}
content={t("message.archetypeAlreadyAssessed")}
>
<QuestionCircleIcon />
</ConditionalTooltip>
) : null}
</FlexItem>
</Flex>
<ApplicationAssessmentStatus
application={application}
key={`${application?.id}-assessment-status`}
/>
</Td>
<Td
width={15}
modifier="truncate"
{...getTdProps({ columnKey: "review" })}
>
<Flex alignItems={{ default: "alignItemsCenter" }}>
<FlexItem>
<IconedStatus
preset={
isAppReviewed || hasReviewedArchetype
? "Completed"
: "NotStarted"
}
/>
</FlexItem>
<FlexItem>
{hasReviewedArchetype ? (
<ConditionalTooltip
isTooltipEnabled={hasReviewedArchetype || false}
content={t("message.archetypeAlreadyReviewed")}
>
<QuestionCircleIcon />
</ConditionalTooltip>
) : null}
</FlexItem>
</Flex>
<ApplicationReviewStatus
application={application}
key={`${application?.id}-review-status`}
/>
</Td>
<Td
width={10}
Expand Down
Loading

0 comments on commit 56bdfdb

Please sign in to comment.