diff --git a/client/public/locales/en/translation.json b/client/public/locales/en/translation.json index f97c91fc29..7f25c7fa23 100644 --- a/client/public/locales/en/translation.json +++ b/client/public/locales/en/translation.json @@ -265,6 +265,8 @@ "applications": "Applications", "archetype": "Archetype", "archetypes": "Archetypes", + "archetypes_singular": "Archetype", + "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 6219a3e0f4..def5112708 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 @@ -58,6 +58,8 @@ import { RiskLabel } from "@app/components/RiskLabel"; import { ApplicationDetailFields } from "./application-detail-fields"; import { useFetchArchetypes } from "@app/queries/archetypes"; import { AssessedArchetypes } from "./components/assessed-archetypes"; +import { serializeFilterUrlParams } from "@app/hooks/table-controls"; +import { Paths } from "@app/Paths"; export interface IApplicationDetailDrawerProps extends Pick { @@ -190,15 +192,25 @@ export const ApplicationDetailDrawer: React.FC< {t("terms.associatedArchetypes")} - {application?.archetypes?.length ?? 0 > 0 ? ( - + {application?.archetypes?.length ? ( + <> + + {application.archetypes.length}{" "} + {t("terms.archetypes", { + count: application.archetypes.length, + context: + application.archetypes.length > 1 + ? "plural" + : "singular", + }).toLocaleLowerCase()}{" "} + + ) : ( )} + {t("terms.archetypesAssessed")} @@ -441,3 +453,16 @@ const ArchetypeLabels: React.FC<{ archetypeRefs?: Ref[] }> = ({ const ArchetypeItem: React.FC<{ archetype: Archetype }> = ({ archetype }) => { return ; }; + +const getArchetypesUrl = (applicationName: string) => { + const filterValues = { + "application.name": [applicationName], + }; + + const serializedParams = serializeFilterUrlParams(filterValues); + + const queryString = serializedParams.filters + ? `filters=${serializedParams.filters}` + : ""; + return `${Paths.archetypes}?${queryString}`; +}; 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)", }} > - +