diff --git a/client/public/locales/en/translation.json b/client/public/locales/en/translation.json index 4209d445de..95593f0e53 100644 --- a/client/public/locales/en/translation.json +++ b/client/public/locales/en/translation.json @@ -259,15 +259,17 @@ "analysis": "Analysis", "answer": "Answer", "application": "Application", + "application_plural": "Applications", "applicationReview": "Application review", "application(s)": "Application(s)", + "applications": "Applications", "applicationImports": "Application imports", "applicationName": "Application name", "archetypeName": "Archetype name", "applicationInformation": "Application information", - "applications": "Applications", "archetype": "Archetype", "archetypes": "Archetypes", + "archetypes_plural": "Archetypes", "artifact": "Artifact", "artifactAssociated": "Associated artifact", "artifactNotAssociated": "No associated artifact", diff --git a/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx b/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx index e67153c3c1..b721a02478 100644 --- a/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx +++ b/client/src/app/pages/applications/components/application-detail-drawer/application-detail-drawer.tsx @@ -192,15 +192,24 @@ export const ApplicationDetailDrawer: React.FC< {t("terms.associatedArchetypes")} - {application?.archetypes?.length ?? 0 > 0 ? ( - + {application?.archetypes?.length ? ( + <> + + {application.archetypes.length ?? 0 > 0 ? ( + + ) : ( + + )} + + ) : ( )} + {t("terms.archetypesAssessed")} diff --git a/client/src/app/pages/archetypes/archetypes-page.tsx b/client/src/app/pages/archetypes/archetypes-page.tsx index b4c6941e54..0f44e8f1ca 100644 --- a/client/src/app/pages/archetypes/archetypes-page.tsx +++ b/client/src/app/pages/archetypes/archetypes-page.tsx @@ -38,7 +38,10 @@ import { TableHeaderContentWithControls, TableRowContentWithControls, } from "@app/components/TableControls"; -import { useLocalTableControls } from "@app/hooks/table-controls"; +import { + deserializeFilterUrlParams, + useLocalTableControls, +} from "@app/hooks/table-controls"; import { useDeleteArchetypeMutation, useFetchArchetypes, @@ -171,6 +174,10 @@ const Archetypes: React.FC = () => { }); } }; + const urlParams = new URLSearchParams(window.location.search); + const filters = urlParams.get("filters"); + + const deserializedFilterValues = deserializeFilterUrlParams({ filters }); const tableControls = useLocalTableControls({ persistTo: "urlParams", @@ -206,15 +213,47 @@ const Archetypes: React.FC = () => { return archetype?.name ?? ""; }, }, + { + key: "application.name", + title: t("terms.applicationName"), + type: FilterType.multiselect, + logicOperator: "OR", + selectOptions: [ + ...new Set( + archetypes.flatMap( + (archetype) => + archetype?.applications + ?.map((app) => app.name) + .filter(Boolean) || [] + ) + ), + ].map((applicationName) => ({ + key: applicationName, + value: applicationName, + })), + placeholderText: + t("actions.filterBy", { + what: t("terms.application").toLowerCase(), + }) + "...", + getItemValue: (archetype) => { + const appNames = archetype.applications + ?.map((app) => app.name) + .join(""); + return appNames || ""; + }, + }, + // TODO: Add filter for archetype tags ], sortableColumns: ["name"], + initialFilterValues: deserializedFilterValues, getSortValues: (archetype) => ({ name: archetype.name ?? "", }), initialSort: { columnKey: "name", direction: "asc" }, }); + const { currentPageItems, numRenderedColumns, @@ -285,6 +324,14 @@ const Archetypes: React.FC = () => { assessmentWriteAccess = checkAccess(userScopes, assessmentWriteScopes), reviewsWriteAccess = checkAccess(userScopes, reviewsWriteScopes); + const clearFilters = () => { + const currentPath = history.location.pathname; + const newSearch = new URLSearchParams(history.location.search); + newSearch.delete("filters"); + history.push(`${currentPath}`); + filterToolbarProps.setFilterValues({}); + }; + return ( <> @@ -302,7 +349,7 @@ const Archetypes: React.FC = () => { backgroundColor: "var(--pf-v5-global--BackgroundColor--100)", }} > - + diff --git a/client/src/app/pages/archetypes/components/archetype-detail-drawer.tsx b/client/src/app/pages/archetypes/components/archetype-detail-drawer.tsx index 648fdd01f3..90b85d0574 100644 --- a/client/src/app/pages/archetypes/components/archetype-detail-drawer.tsx +++ b/client/src/app/pages/archetypes/components/archetype-detail-drawer.tsx @@ -27,6 +27,9 @@ import { LabelsFromItems } from "@app/components/labels/labels-from-items/labels import { ReviewFields } from "@app/pages/applications/components/application-detail-drawer/review-fields"; import { RiskLabel } from "@app/components/RiskLabel"; import { LabelsFromTags } from "@app/components/labels/labels-from-tags/labels-from-tags"; +import { serializeFilterUrlParams } from "@app/hooks/table-controls"; +import { Paths } from "@app/Paths"; +import { Link } from "react-router-dom"; export interface IArchetypeDetailDrawerProps { onCloseClick: () => void; @@ -103,10 +106,19 @@ const ArchetypeDetailDrawer: React.FC = ({ {t("terms.applications")} - {archetype?.applications?.length ?? 0 > 0 ? ( - + {archetype?.applications?.length ? ( + <> + + {archetype.applications.length}{" "} + {t("terms.application", { + count: archetype.applications.length, + context: + archetype.applications.length > 1 + ? "plural" + : "singular", + }).toLocaleLowerCase()}{" "} + + ) : ( )} @@ -223,10 +235,6 @@ const ArchetypeDetailDrawer: React.FC = ({ ); }; -const ApplicationLabels: React.FC<{ applicationRefs?: Ref[] }> = ({ - applicationRefs, -}) => ; - const TagLabels: React.FC<{ tags?: Tag[] }> = ({ tags }) => ( ); @@ -240,3 +248,16 @@ const StakeholderGroupsLabels: React.FC<{ archetype: Archetype }> = ({ }) => ; export default ArchetypeDetailDrawer; + +const getApplicationsUrl = (archetypeName: string) => { + const filterValues = { + archetypes: [archetypeName], + }; + + const serializedParams = serializeFilterUrlParams(filterValues); + + const queryString = serializedParams.filters + ? `filters=${serializedParams.filters}` + : ""; + return `${Paths.applications}?${queryString}`; +};