diff --git a/publiccode.yaml b/publiccode.yaml index 9d3c4399e..5739f821f 100644 --- a/publiccode.yaml +++ b/publiccode.yaml @@ -1,6 +1,6 @@ publiccodeYmlVersion: "0.3" name: "OpenCatalogi web-app" -applicationSuite: "opencatalogi" +applicationSuite: "OpenCatalogi Applicatie" url: https://github.com/OpenCatalogi/web-app landingURL: "https://github.com/OpenCatalogi/web-app" isBasedOn: null diff --git a/pwa/src/apiService/resources/search.ts b/pwa/src/apiService/resources/search.ts index 9d647cdbe..a8a6f4cf0 100644 --- a/pwa/src/apiService/resources/search.ts +++ b/pwa/src/apiService/resources/search.ts @@ -10,20 +10,38 @@ export default class Search { this._instance = _instance; } - public getSearch = async (filters: IFiltersContext, currentPage: number, limit: number): Promise => { - let endpoint = `/search?page=${currentPage}&limit=${limit}&extend[]=all${filtersToQueryParams( - filters, - )}&embedded.rating.rating[>%3D]=${filters.rating}`; + public getSearch = async ( + filters: IFiltersContext, + currentPage: number, + limit: number, + ratingFilter: string, + ): Promise => { + let endpoint = `/search?page=${currentPage}&limit=${limit}&extend[]=all${filtersToQueryParams(filters)}`; + + if (ratingFilter === "OpenCatalogi") { + endpoint += `&embedded.rating.rating[>%3D]=${filters.rating}`; + } - if (filters.orderRating === true) { + if (filters.orderRating === true && ratingFilter === "OpenCatalogi") { endpoint += "&order[embedded.rating.rating]=desc"; } + if (ratingFilter === "Commonground") { + endpoint += `&embedded.nl.embedded.commonground.rating[>%3D]=${filters.ratingCommonground}`; + } + + if (filters.orderRating === true && ratingFilter === "Commonground") { + endpoint += "&order[embedded.nl.embedded.commonground.rating]=desc"; + } + if (filters.isForked === true) { endpoint += "&isBasedOn=IS NULL"; } - if (window.sessionStorage.getItem("GITHUB_ORGANIZATION_URL") !== "" && window.sessionStorage.getItem("GITHUB_ORGANIZATION_URL") !== "false") { + if ( + window.sessionStorage.getItem("GITHUB_ORGANIZATION_URL") !== "" && + window.sessionStorage.getItem("GITHUB_ORGANIZATION_URL") !== "false" + ) { endpoint += `&embedded.url.embedded.organisation.github=${window.sessionStorage.getItem( "GITHUB_ORGANIZATION_URL", )}`; diff --git a/pwa/src/components/applicationCard/ApplicationCard.tsx b/pwa/src/components/applicationCard/ApplicationCard.tsx index b3368885c..d513270f0 100644 --- a/pwa/src/components/applicationCard/ApplicationCard.tsx +++ b/pwa/src/components/applicationCard/ApplicationCard.tsx @@ -1,5 +1,6 @@ import * as React from "react"; import * as styles from "./ApplicationCard.module.css"; +import clsx from "clsx"; import { DataBadge, Icon, Link, Paragraph } from "@utrecht/component-library-react/dist/css-module"; import { useTranslation } from "react-i18next"; import { IconArrowRight } from "@tabler/icons-react"; @@ -19,13 +20,17 @@ export interface ApplicationCardProps { organization?: string; githubLink?: string; }; + layoutClassName?: string; } -export const ApplicationCard: React.FC = ({ title, description, tags }) => { +export const ApplicationCard: React.FC = ({ title, description, tags, layoutClassName }) => { const { t } = useTranslation(); return ( - navigate(title.href)}> + navigate(title.href)} + > navigate(title.href)}> diff --git a/pwa/src/components/componentCard/ComponentCard.module.css b/pwa/src/components/componentCard/ComponentCard.module.css index 2345b0d28..ffdafa6b1 100644 --- a/pwa/src/components/componentCard/ComponentCard.module.css +++ b/pwa/src/components/componentCard/ComponentCard.module.css @@ -77,9 +77,14 @@ align-self: center; flex: 1; } + .ratingIndicatorContainer { min-height: 55px; } + + .commongroundRating { + min-height: 55px; + } } @media only screen and (min-width: 576px) { @@ -97,9 +102,40 @@ align-self: center; flex: 1; } + .ratingIndicatorContainer { min-height: 55px; } + + .commongroundRating { + min-height: 55px; + } +} + +.commongroundRating { + border-radius: 50%; + height: 70px; + width: 70px; + justify-content: center; + align-items: center; + display: flex; +} + +/* Colors of commonground rating */ + +.goldRating { + background-color: #d4af37; + color: #ffffff; +} + +.silverRating { + background: #bcc6cc; + color: #ffffff; +} + +.bronzeRating { + background: #a97142; + color: #ffffff; } /* Colors of the 5 layers */ diff --git a/pwa/src/components/componentCard/ComponentCard.tsx b/pwa/src/components/componentCard/ComponentCard.tsx index f01f7ad50..faa2308e7 100644 --- a/pwa/src/components/componentCard/ComponentCard.tsx +++ b/pwa/src/components/componentCard/ComponentCard.tsx @@ -1,7 +1,8 @@ import * as React from "react"; import * as styles from "./ComponentCard.module.css"; -import { DataBadge, Icon, Link, Paragraph } from "@utrecht/component-library-react/dist/css-module"; import _ from "lodash"; +import clsx from "clsx"; +import { DataBadge, Icon, Link, Paragraph } from "@utrecht/component-library-react/dist/css-module"; import { categories as _categories, TCategories } from "../../data/categories"; import { useTranslation } from "react-i18next"; import { IconArrowRight } from "@tabler/icons-react"; @@ -12,6 +13,7 @@ import { TOOLTIP_ID } from "../../layout/Layout"; import { CardHeader, CardHeaderTitle, CardWrapper } from "@conduction/components"; import { navigate } from "gatsby"; import { RatingIndicatorTemplate } from "../../templates/templateParts/ratingIndicator/RatingIndicatorTemplate"; +import { getCommongroundRating } from "../../services/getCommongroundRating"; export interface ComponentCardProps { title: { @@ -26,6 +28,9 @@ export interface ComponentCardProps { rating: number; maxRating: number; }; + ratingCommonground?: { + rating: number; + }; status?: string; installations: string; organization: { @@ -40,6 +45,8 @@ export interface ComponentCardProps { export const ComponentCard: React.FC = ({ title, layer, categories, description, tags }) => { const { t } = useTranslation(); + const ratingFilter = window.sessionStorage.getItem("FILTER_RATING"); + const _layer: TCategories = t(_.upperFirst(layer)); const __categories = @@ -149,13 +156,33 @@ export const ComponentCard: React.FC = ({ title, layer, cate
- {tags.rating && ( - - )} + <> + {ratingFilter === "OpenCatalogi" && ( + <> + {tags.rating && tags.rating?.rating && ( + + )} + + )} + {ratingFilter === "Commonground" && ( + <> + {tags.ratingCommonground && tags.ratingCommonground?.rating && ( +
+ {t(getCommongroundRating(tags.ratingCommonground.rating))} +
+ )} + + )} +
diff --git a/pwa/src/context/filters.ts b/pwa/src/context/filters.ts index c68c1315d..cb6634cdf 100644 --- a/pwa/src/context/filters.ts +++ b/pwa/src/context/filters.ts @@ -6,6 +6,7 @@ export interface IFiltersContext { orderRating: boolean; rating: number; organizationSearch?: string; + ratingCommonground: number; _search?: string; softwareType?: string; @@ -34,6 +35,7 @@ export const defaultFiltersContext: IFiltersContext = { orderRating: true, developmentStatus: "stable", rating: 16, + ratingCommonground: 1, }; export const useFiltersContext = () => { diff --git a/pwa/src/context/queryLimit.ts b/pwa/src/context/queryLimit.ts index 4d3c80490..3f1194909 100644 --- a/pwa/src/context/queryLimit.ts +++ b/pwa/src/context/queryLimit.ts @@ -6,14 +6,18 @@ export const QUERY_LIMIT_DEFAULT = 10; export interface IQueryLimitContext { previousComponentsSearchQueryLimit: number; componentsSearchQueryLimit: number; + previousOrganizationsQueryLimit: number; organizationsQueryLimit: number; + previousApplicationsQueryLimit: number; applicationsQueryLimit: number; } export const defaultQueryLimitContext: IQueryLimitContext = { previousComponentsSearchQueryLimit: QUERY_LIMIT_DEFAULT, componentsSearchQueryLimit: QUERY_LIMIT_DEFAULT, + previousOrganizationsQueryLimit: QUERY_LIMIT_DEFAULT, organizationsQueryLimit: QUERY_LIMIT_DEFAULT, + previousApplicationsQueryLimit: QUERY_LIMIT_DEFAULT, applicationsQueryLimit: QUERY_LIMIT_DEFAULT, }; diff --git a/pwa/src/hooks/search.ts b/pwa/src/hooks/search.ts index a4bbd1430..481824c76 100644 --- a/pwa/src/hooks/search.ts +++ b/pwa/src/hooks/search.ts @@ -7,10 +7,10 @@ import { IFiltersContext } from "../context/filters"; export const useSearch = (_: QueryClient) => { const API: APIService | null = React.useContext(APIContext); - const getSearch = (filters: IFiltersContext, currentPage: number, limit: number) => + const getSearch = (filters: IFiltersContext, currentPage: number, limit: number, ratingFilter: string) => useQuery( ["search", filters, currentPage, limit], - () => API?.Search.getSearch(filters, currentPage, limit), + () => API?.Search.getSearch(filters, currentPage, limit, ratingFilter), { onError: (error) => { throw new Error(error.message); diff --git a/pwa/src/hooks/useEnvironment.ts b/pwa/src/hooks/useEnvironment.ts index bea389afc..e11ccd0f5 100644 --- a/pwa/src/hooks/useEnvironment.ts +++ b/pwa/src/hooks/useEnvironment.ts @@ -51,6 +51,7 @@ export const useEnvironment = () => { window.sessionStorage.setItem("FOOTER_CONTENT", process.env.GATSBY_FOOTER_CONTENT ?? ""); window.sessionStorage.setItem("FOOTER_CONTENT_HEADER", process.env.GATSBY_FOOTER_CONTENT_HEADER ?? ""); window.sessionStorage.setItem("OPTIONAL_START_PAGE", process.env.GATSBY_OPTIONAL_START_PAGE ?? ""); + window.sessionStorage.setItem("FILTER_RATING", process.env.GATSBY_FILTER_RATING ?? "Commonground"); updateSessionStorage(); }; @@ -88,6 +89,7 @@ export const useEnvironment = () => { window.sessionStorage.setItem("FOOTER_CONTENT", config.GATSBY_FOOTER_CONTENT ?? ""); window.sessionStorage.setItem("FOOTER_CONTENT_HEADER", config.GATSBY_FOOTER_CONTENT_HEADER ?? ""); window.sessionStorage.setItem("OPTIONAL_START_PAGE", config.GATSBY_OPTIONAL_START_PAGE ?? ""); + window.sessionStorage.setItem("FILTER_RATING", config.GATSBY_FILTER_RATING ?? "OpenCatalogi"); updateSessionStorage(); }; diff --git a/pwa/src/layout/Head.tsx b/pwa/src/layout/Head.tsx index 5bd7a4bb5..b25860a8d 100644 --- a/pwa/src/layout/Head.tsx +++ b/pwa/src/layout/Head.tsx @@ -14,7 +14,7 @@ export const Head: React.FC = () => { ); diff --git a/pwa/src/services/filtersToQueryParams.ts b/pwa/src/services/filtersToQueryParams.ts index 4d4c10efa..181e497cb 100644 --- a/pwa/src/services/filtersToQueryParams.ts +++ b/pwa/src/services/filtersToQueryParams.ts @@ -21,7 +21,9 @@ export const filtersToQueryParams = (filters: any): string => { params += "&isBasedOn=IS NULL"; break; case "orderRating": - params += "&order[embedded.rating.rating]=desc"; + window.sessionStorage.getItem("FILTER_RATING") === "Commonground" + ? (params += "&order[embedded.nl.embedded.commonground.rating]=desc") + : (params += "&order[embedded.rating.rating]=desc"); break; case "componentsCurrentPage": params += ""; @@ -29,6 +31,9 @@ export const filtersToQueryParams = (filters: any): string => { case "rating": params += ""; break; + case "ratingCommonground": + params += ""; + break; default: params += `&${key}=${value}`; diff --git a/pwa/src/services/getCommongroundRating.ts b/pwa/src/services/getCommongroundRating.ts new file mode 100644 index 000000000..573f8a6ec --- /dev/null +++ b/pwa/src/services/getCommongroundRating.ts @@ -0,0 +1,14 @@ +export const getCommongroundRating = (rating: number): string => { + switch (rating) { + case 0: + return "N.V.T"; + case 1: + return "Bronze"; + case 2: + return "Silver"; + case 3: + return "Gold"; + default: + return "N.V.T"; + } +}; diff --git a/pwa/src/templates/applicationsTemplate/ApplicationsTemplate.tsx b/pwa/src/templates/applicationsTemplate/ApplicationsTemplate.tsx index 5191d087e..776b51a5f 100644 --- a/pwa/src/templates/applicationsTemplate/ApplicationsTemplate.tsx +++ b/pwa/src/templates/applicationsTemplate/ApplicationsTemplate.tsx @@ -18,7 +18,7 @@ import { faExternalLink } from "@fortawesome/free-solid-svg-icons"; export const ApplicationsTemplate: React.FC = () => { const { t } = useTranslation(); const { filters } = useFiltersContext(); - const { queryLimit } = useQueryLimitContext(); + const { queryLimit, setQueryLimit } = useQueryLimitContext(); const { pagination, setPagination } = usePaginationContext(); const queryClient = new QueryClient(); @@ -32,7 +32,10 @@ export const ApplicationsTemplate: React.FC = () => { ); React.useEffect(() => { + if (queryLimit.previousApplicationsQueryLimit === queryLimit.applicationsQueryLimit) return; + setPagination({ ...pagination, applicationCurrentPage: 1 }); + setQueryLimit({ ...queryLimit, previousApplicationsQueryLimit: queryLimit.applicationsQueryLimit }); }, [queryLimit.applicationsQueryLimit]); return ( diff --git a/pwa/src/templates/componentDetail/ComponentsDetailTemplate.module.css b/pwa/src/templates/componentDetail/ComponentsDetailTemplate.module.css index 4755f3f87..7afc433de 100644 --- a/pwa/src/templates/componentDetail/ComponentsDetailTemplate.module.css +++ b/pwa/src/templates/componentDetail/ComponentsDetailTemplate.module.css @@ -39,13 +39,11 @@ .layerAndCategoryContainer { display: flex; + flex-wrap: wrap; + gap: var(--web-app-size-xs); margin-block-end: var(--web-app-size-xs); } -.layerAndCategoryContainer > *:not(:last-child) { - margin-inline-end: var(--web-app-size-xs); -} - .tags { display: flex; flex-wrap: wrap; @@ -74,14 +72,14 @@ } .organizationCardContainer { - flex: 5; + flex: 1; } .noOrganizationCardAvailable { display: flex; justify-content: center; align-items: center; - flex: 5; + flex: 1; background-color: var(--web-app-color-grey); padding-block-end: 50px; padding-block-start: 50px; @@ -90,8 +88,14 @@ } .infoCard { - flex: 2; + flex: 1; +} + +.infoCardCommonground { + display: flex; + justify-content: center; } + .ratingIndicatorContainer { height: var(--web-app-component-rating-indicator-height); } @@ -166,6 +170,10 @@ grid-template-columns: 1fr 1fr; } +.cardsHeading { + flex: 1; +} + .cardsContainer { grid-template-columns: 1fr 1fr; } @@ -174,6 +182,15 @@ margin-block-end: var(--web-app-size-md); } +.cardsHeaderContainer { + grid-template-columns: 1fr 1fr; + margin-block-end: var(--web-app-size-md) !important; +} + +.cardsHeaderContainer > :not(:last-child) { + margin-block-end: var(--web-app-size-md); +} + .badgeLayout { margin-inline-start: var(--web-app-size-xs); } @@ -253,10 +270,19 @@ gap: var(--web-app-size-xl); } + .cardsHeaderContainer { + display: flex; + gap: var(--web-app-size-xl); + } + .cardsContainer > :not(:last-child) { margin-block-end: 0; } + .cardsHeaderContainer > :not(:last-child) { + margin-block-end: 0; + } + .noOrganizationCardAvailable { padding-block-end: unset; padding-block-start: unset; @@ -274,6 +300,35 @@ } } +.commongroundRating { + border-radius: 50%; + height: var(--web-app-component-rating-indicator-height); + width: var(--web-app-component-rating-indicator-height); + justify-content: center; + align-items: center; + display: flex; +} + +/* Colors of commonground rating */ + +.goldRating { + background-color: #d4af37; + color: #ffffff; + font-size: var(--web-app-size-lg); +} + +.silverRating { + background: #bcc6cc; + color: #ffffff; + font-size: var(--web-app-size-lg); +} + +.bronzeRating { + background: #a97142; + color: #ffffff; + font-size: var(--web-app-size-lg); +} + /* Colors of the 5 layers */ .interactionLayer, .interfaceLayer { diff --git a/pwa/src/templates/componentDetail/ComponentsDetailTemplate.tsx b/pwa/src/templates/componentDetail/ComponentsDetailTemplate.tsx index 39a57ae9b..46f4a9c0e 100644 --- a/pwa/src/templates/componentDetail/ComponentsDetailTemplate.tsx +++ b/pwa/src/templates/componentDetail/ComponentsDetailTemplate.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import * as styles from "./ComponentsDetailTemplate.module.css"; import _ from "lodash"; +import clsx from "clsx"; import Skeleton from "react-loading-skeleton"; import componentPlacholderLogo from "../../assets/images/grey.png"; import { @@ -14,6 +15,7 @@ import { TableHeader, TableHeaderCell, StatusBadge, + Heading3, } from "@utrecht/component-library-react/dist/css-module"; import { Container, @@ -55,6 +57,8 @@ import { IDisplaySwitchButton } from "@conduction/components/lib/components/disp import { ExpandableLeadParagraph } from "../../components/expandableLeadParagraph/ExpandableLeadParagraph"; import { TOOLTIP_ID } from "../../layout/Layout"; import { getStatusColor } from "../../services/getStatusColor"; +import { ApplicationCard } from "../../components"; +import { getCommongroundRating } from "../../services/getCommongroundRating"; interface ComponentsDetailTemplateProps { componentId: string; @@ -98,6 +102,7 @@ export const ComponentsDetailTemplate: React.FC = if (_getComponent.isError) return <>Something went wrong...; const organisation = _getComponent?.data?.embedded?.url?.embedded?.organisation; + const application = _getComponent?.data?.embedded?.applicationSuite; const imageHasValidSource = (src: string): boolean => { try { @@ -142,9 +147,11 @@ export const ComponentsDetailTemplate: React.FC = Math.ceil(elementRect.width) % 2 === 0 ? Math.ceil(elementRect.width) : Math.ceil(elementRect.width) + 1; element.style.width = `${newWidth}px`; element.style.maxWidth = newWidth < 1170 ? `${newWidth}px` : `${1170}px`; - }, 210); // Give the modal some time to finish animating + }, 300); // Give the modal some time to finish animating }; + const ratingFilter = window.sessionStorage.getItem("FILTER_RATING"); + return ( = data-tooltip-id={TOOLTIP_ID} data-tooltip-content="Status" status={getStatusColor(_.upperFirst(_getComponent.data.developmentStatus) ?? "Onbekend")} - className={styles.tagWidth} > {t(_.upperFirst(_getComponent.data.developmentStatus))} @@ -295,7 +301,29 @@ export const ComponentsDetailTemplate: React.FC = +
+ {t("Application")} + {t("Organization")} + {t("Rating")} +
+
+ {application && ( + + )} + {!_getComponent?.data?.embedded?.applicationSuite && ( + {t("No application found")} + )} + {organisation && ( = title="" content={ <> - {_getComponent.data.embedded?.rating && ( + {ratingFilter === "OpenCatalogi" && ( <> - - - { - e.preventDefault(), openModal(); - }} - href={`${_getComponent.data.id}/?openratingpopup`} - > - - - - {t("Rating")} - - + {_getComponent.data.embedded?.rating && ( + <> + + + { + e.preventDefault(), openModal(); + }} + href={`${_getComponent.data.id}/?openratingpopup`} + > + + + + {t("Rating")} + + + + )} + {!rating &&
{t("No rating available")}
} )} - {!rating &&
{t("No rating available")}
} + {ratingFilter === "Commonground" && ( +
+ {t(getCommongroundRating(_getComponent.data.embedded?.nl?.embedded?.commonground?.rating))} +
+ )} } - layoutClassName={styles.infoCard} + layoutClassName={clsx(styles.infoCard, ratingFilter === "Commonground" && styles.infoCardCommonground)} /> {isVisible && ( diff --git a/pwa/src/templates/components/ComponentsTemplate.module.css b/pwa/src/templates/components/ComponentsTemplate.module.css index 6d773078a..d042765a0 100644 --- a/pwa/src/templates/components/ComponentsTemplate.module.css +++ b/pwa/src/templates/components/ComponentsTemplate.module.css @@ -24,7 +24,7 @@ .results { flex: 4; - + min-width: 0; --utrecht-heading-4-font-weight: normal; --utrecht-heading-4-font-size: var(--web-app-font-size-md); --utrecht-heading-4-distanced-margin-block-end: var(--web-app-size-2xs); diff --git a/pwa/src/templates/components/ComponentsTemplate.tsx b/pwa/src/templates/components/ComponentsTemplate.tsx index 3045d1210..c0b0fdc0d 100644 --- a/pwa/src/templates/components/ComponentsTemplate.tsx +++ b/pwa/src/templates/components/ComponentsTemplate.tsx @@ -25,6 +25,7 @@ export const ComponentsTemplate: React.FC = () => { const { filters } = useFiltersContext(); const { queryLimit, setQueryLimit } = useQueryLimitContext(); const { pagination, setPagination } = usePaginationContext(); + const [ratingFilter, setRatingFilter] = React.useState(""); const { resultDisplayLayout, setResultDisplayLayout } = useResultDisplayLayoutContext(); const queryClient = new QueryClient(); @@ -33,8 +34,13 @@ export const ComponentsTemplate: React.FC = () => { { ...filters, organizationSearch: "" }, pagination.componentsCurrentPage, queryLimit.componentsSearchQueryLimit, + ratingFilter, ); // Ensure no refetch on resultDisplayLayout change + React.useEffect(() => { + setRatingFilter(window.sessionStorage.getItem("FILTER_RATING") ?? "OpenCatalogi"); + }, []); + React.useEffect(() => { if (queryLimit.previousComponentsSearchQueryLimit === queryLimit.componentsSearchQueryLimit) return; diff --git a/pwa/src/templates/organizationDetail/OrganizationDetailTemplate.module.css b/pwa/src/templates/organizationDetail/OrganizationDetailTemplate.module.css index 77394d303..ed91a91fb 100644 --- a/pwa/src/templates/organizationDetail/OrganizationDetailTemplate.module.css +++ b/pwa/src/templates/organizationDetail/OrganizationDetailTemplate.module.css @@ -7,9 +7,8 @@ } .container > .backButton { - display: none; - margin-block-start: var(--web-app-size-2xl); - margin-block-end: var(--web-app-size-sm); + margin-block-start: var(--web-app-size-sm); + margin-block-end: var(--web-app-size-2xl); } .section > *:not(:last-child) { diff --git a/pwa/src/templates/organizationsTemplate/OrganizationsTemplate.tsx b/pwa/src/templates/organizationsTemplate/OrganizationsTemplate.tsx index a9bfb5c09..8ebff6bcd 100644 --- a/pwa/src/templates/organizationsTemplate/OrganizationsTemplate.tsx +++ b/pwa/src/templates/organizationsTemplate/OrganizationsTemplate.tsx @@ -19,14 +19,14 @@ import { useResultDisplayLayoutContext } from "../../context/resultDisplayLayout export const OrganizationsTemplate: React.FC = () => { const { t } = useTranslation(); const { filters } = useFiltersContext(); - const { queryLimit } = useQueryLimitContext(); + const { queryLimit, setQueryLimit } = useQueryLimitContext(); const { pagination, setPagination } = usePaginationContext(); const { resultDisplayLayout, setResultDisplayLayout } = useResultDisplayLayoutContext(); const queryClient = new QueryClient(); const _useOrganisation = useOrganization(queryClient); const getOrganisations = _useOrganisation.getAll( - { ...filters }, + { ...filters, _search: "" }, pagination.organizationCurrentPage, queryLimit.organizationsQueryLimit, ); @@ -53,7 +53,10 @@ export const OrganizationsTemplate: React.FC = () => { ]; React.useEffect(() => { + if (queryLimit.previousOrganizationsQueryLimit === queryLimit.organizationsQueryLimit) return; + setPagination({ ...pagination, organizationCurrentPage: 1 }); + setQueryLimit({ ...queryLimit, previousOrganizationsQueryLimit: queryLimit.organizationsQueryLimit }); }, [queryLimit.organizationsQueryLimit]); return ( diff --git a/pwa/src/templates/templateParts/componentCardsAccordion/ComponentCardsAccordionTemplate.tsx b/pwa/src/templates/templateParts/componentCardsAccordion/ComponentCardsAccordionTemplate.tsx index e1163c51d..1d866470d 100644 --- a/pwa/src/templates/templateParts/componentCardsAccordion/ComponentCardsAccordionTemplate.tsx +++ b/pwa/src/templates/templateParts/componentCardsAccordion/ComponentCardsAccordionTemplate.tsx @@ -252,6 +252,9 @@ const Components: React.FC = ({ components }) => { rating: component.embedded?.rating?.rating, maxRating: component.embedded?.rating?.maxRating, }, + ratingCommonground: { + rating: component.embedded?.nl?.embedded?.commonground?.rating, + }, status: component.developmentStatus, installations: component.usedBy?.length.toString() ?? "0", organization: { diff --git a/pwa/src/templates/templateParts/filters/organizationSearchFilterTemplate/OrganizationSearchFilterTemplate.tsx b/pwa/src/templates/templateParts/filters/organizationSearchFilterTemplate/OrganizationSearchFilterTemplate.tsx index 94dfd3cae..fe2eb0721 100644 --- a/pwa/src/templates/templateParts/filters/organizationSearchFilterTemplate/OrganizationSearchFilterTemplate.tsx +++ b/pwa/src/templates/templateParts/filters/organizationSearchFilterTemplate/OrganizationSearchFilterTemplate.tsx @@ -2,9 +2,11 @@ import * as React from "react"; import { useForm } from "react-hook-form"; import { useFiltersContext } from "../../../../context/filters"; import { FormField, FormLabel, Textbox } from "@utrecht/component-library-react/dist/css-module"; +import { usePaginationContext } from "../../../../context/pagination"; export const OrganizationSearchFiltersTemplate: React.FC = () => { const { filters, setFilters } = useFiltersContext(); + const { pagination, setPagination } = usePaginationContext(); const searchTimeout = React.useRef(null); const { @@ -22,15 +24,18 @@ export const OrganizationSearchFiltersTemplate: React.FC = () => { const watchName = watch("name"); React.useEffect(() => { + if (watchName === undefined || watchName === filters.organizationSearch) return; + if (searchTimeout.current) clearTimeout(searchTimeout.current); - searchTimeout.current = setTimeout( - () => - setFilters({ - ...filters, - organizationSearch: watchName === undefined ? "" : watchName, //This check is important for the react lifecycle - }), - 500, - ); + + searchTimeout.current = setTimeout(() => { + setFilters({ + ...filters, + organizationSearch: watchName === undefined ? "" : watchName, //This check is important for the react lifecycle + }); + + setPagination({ ...pagination, organizationCurrentPage: 1 }); + }, 500); }, [watchName]); return ( @@ -41,7 +46,12 @@ export const OrganizationSearchFiltersTemplate: React.FC = () => { > Zoek op naam - + ); diff --git a/pwa/src/templates/templateParts/filters/verticalFilters/VerticalFiltersTemplate.tsx b/pwa/src/templates/templateParts/filters/verticalFilters/VerticalFiltersTemplate.tsx index e3128f616..d5bad7851 100644 --- a/pwa/src/templates/templateParts/filters/verticalFilters/VerticalFiltersTemplate.tsx +++ b/pwa/src/templates/templateParts/filters/verticalFilters/VerticalFiltersTemplate.tsx @@ -37,6 +37,7 @@ import { navigate } from "gatsby"; import { filtersToUrlQueryParams } from "../../../../services/filtersToQueryParams"; import { usePaginationContext } from "../../../../context/pagination"; import { useResultDisplayLayoutContext } from "../../../../context/resultDisplayLayout"; +import { getCommongroundRating } from "../../../../services/getCommongroundRating"; interface VerticalFiltersTemplateProps { filterSet: any[]; @@ -57,6 +58,7 @@ export const VerticalFiltersTemplate: React.FC = ( const [softwareTypeRadioFilter, setSoftwareTypeRadioFilter] = React.useState(""); const [ratingFilter, setRatingFilter] = React.useState(16); + const [ratingFilterCommonground, setRatingFilterCommonground] = React.useState(1); const [isOpen, setIsOpen] = React.useState(false); @@ -388,6 +390,19 @@ export const VerticalFiltersTemplate: React.FC = ( }, 500); }; + const changeCommongroundRatingFilter = (e: any) => { + setRatingFilterCommonground(e.target.value); + + if (ratingFilterTimeout.current) clearTimeout(ratingFilterTimeout.current); + + ratingFilterTimeout.current = setTimeout(() => { + setFilters({ + ...filters, + ratingCommonground: e.target.value, + }); + }, 500); + }; + const url = location.search; const [, params] = url.split("?"); const parsedParams = qs.parse(params); @@ -470,30 +485,55 @@ export const VerticalFiltersTemplate: React.FC = (
isOrdered(filters.orderRating)}>
-
- - {t("Rating")}: {ratingFilter} - -
- changeRatingFilter(e)} - min={0} - max={24} - list="ratingDataList" - value={ratingFilter} - id="ratingSlider" - /> - - - - - - - + + {window.sessionStorage.getItem("FILTER_RATING") === "OpenCatalogi" && ( +
+ + {t("Rating")}: {ratingFilter} + +
+ changeRatingFilter(e)} + min={0} + max={24} + list="ratingDataList" + value={ratingFilter} + id="ratingSlider" + /> + + + + + + + +
-
+ )} + {window.sessionStorage.getItem("FILTER_RATING") === "Commonground" && ( +
+
+ changeCommongroundRatingFilter(e)} + min={0} + max={3} + list="ratingDataList" + value={ratingFilterCommonground} + id="ratingSlider" + /> + + + + + + +
+
+ )}
diff --git a/pwa/src/templates/templateParts/header/HeaderContent.json b/pwa/src/templates/templateParts/header/HeaderContent.json index 957db6b75..fecb30696 100644 --- a/pwa/src/templates/templateParts/header/HeaderContent.json +++ b/pwa/src/templates/templateParts/header/HeaderContent.json @@ -137,7 +137,7 @@ "pathname": "/documentation" }, "handleClick": { - "link": "https://documentatie.opencatalogi.nl/" + "link": "https://documentatie.opencatalogi.nl/" } } ] diff --git a/pwa/src/templates/templateParts/header/HeaderTemplate.tsx b/pwa/src/templates/templateParts/header/HeaderTemplate.tsx index b78224f75..2efb8ea50 100644 --- a/pwa/src/templates/templateParts/header/HeaderTemplate.tsx +++ b/pwa/src/templates/templateParts/header/HeaderTemplate.tsx @@ -89,7 +89,8 @@ export const HeaderTemplate: React.FC = ({ layoutClassName
- {window.sessionStorage.getItem("HEADER_LOGO_URL") !== "false" ? ( + {window.sessionStorage.getItem("HEADER_LOGO_URL") && + window.sessionStorage.getItem("HEADER_LOGO_URL") !== "false" ? ( navigate("/")} src={window.sessionStorage.getItem("HEADER_LOGO_URL") ?? LogoRotterdam} diff --git a/pwa/src/templates/templateParts/resultsTemplates/cards/CardsResultTemplate.tsx b/pwa/src/templates/templateParts/resultsTemplates/cards/CardsResultTemplate.tsx index 969d79e2e..abfe40b0c 100644 --- a/pwa/src/templates/templateParts/resultsTemplates/cards/CardsResultTemplate.tsx +++ b/pwa/src/templates/templateParts/resultsTemplates/cards/CardsResultTemplate.tsx @@ -60,6 +60,9 @@ export const CardsResultTemplate: React.FC = ({ compon rating: component.embedded?.rating?.rating, maxRating: component.embedded?.rating?.maxRating, }, + ratingCommonground: { + rating: component.embedded?.nl?.embedded?.commonground?.rating, + }, status: component.developmentStatus, installations: component.usedBy?.length.toString() ?? "0", organization: { diff --git a/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.module.css b/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.module.css index 98ff52bca..089b313cf 100644 --- a/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.module.css +++ b/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.module.css @@ -91,6 +91,21 @@ .icon { margin-inline-end: var(--web-app-size-2xs); } +/* Colors of commonground rating */ +.goldRating { + --utrecht-badge-background-color: #d4af37; + --utrecht-badge-color: #ffffff; +} + +.silverRating { + --utrecht-badge-background-color: #bcc6cc; + --utrecht-badge-color: #ffffff; +} + +.bronzeRating { + --utrecht-badge-background-color: #a97142; + --utrecht-badge-color: #ffffff; +} /* Colors of the 5 layers */ .interactionLayer, diff --git a/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.tsx b/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.tsx index b27597e94..42b25acc8 100644 --- a/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.tsx +++ b/pwa/src/templates/templateParts/resultsTemplates/table/TableResultTemplate.tsx @@ -17,11 +17,12 @@ import clsx from "clsx"; import { getResultsUrl } from "../../../../services/getResultsUrl"; import { getTypeFromSchemaRef } from "../../../../services/getTypeFromSchemaRef"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faInfoCircle, faLayerGroup } from "@fortawesome/free-solid-svg-icons"; +import { faInfoCircle, faLayerGroup, faMedal } from "@fortawesome/free-solid-svg-icons"; import { TOOLTIP_ID } from "../../../../layout/Layout"; import { getStatusColor } from "../../../../services/getStatusColor"; import { HorizontalOverflowWrapper } from "@conduction/components"; import { RatingIndicatorTemplate } from "../../ratingIndicator/RatingIndicatorTemplate"; +import { getCommongroundRating } from "../../../../services/getCommongroundRating"; interface TableResultTemplateProps { components: any[]; @@ -42,7 +43,6 @@ export const TableResultTemplate: React.FC = ({ compon {t("Name")} {t("Type")} {t("Layer")} - {t("Sources")} {t("Software type")} {t("Status")} {t("Rating")} @@ -64,7 +64,9 @@ export const TableResultTemplate: React.FC = ({ compon {component.name} + {t(_.upperFirst(getTypeFromSchemaRef(component._self?.schema.ref)))} +
= ({ compon
- - - {_.upperFirst( - component._self?.synchronizations - ? component._self?.synchronizations?.length - ? component._self?.synchronizations?.at(-1)?.source.name - : "Onbekend" - : "N.V.T.", - )} - - - {_.upperFirst( @@ -144,21 +130,47 @@ export const TableResultTemplate: React.FC = ({ compon - - {component._self.schema.ref.includes("component.schema.json") ? ( - component.embedded?.rating?.rating ? ( - + {window.sessionStorage.getItem("FILTER_RATING") === "OpenCatalogi" && ( + + {component._self.schema.ref.includes("component.schema.json") ? ( + component.embedded?.rating?.rating ? ( + + ) : ( + t("No rating available") + ) ) : ( - t("No rating available") - ) - ) : ( - "N.V.T." - )} - + "N.V.T." + )} + + )} + + {window.sessionStorage.getItem("FILTER_RATING") === "Commonground" && ( + + + + {t(getCommongroundRating(component.embedded?.nl?.embedded?.commonground?.rating))} + + + )} + navigate(`/${getResultsUrl(component._self?.schema?.ref)}/${component.id}`)} @@ -182,6 +194,7 @@ export const TableResultTemplate: React.FC = ({ compon + )} diff --git a/pwa/src/translations/en.ts b/pwa/src/translations/en.ts index efebd9f76..ae4aa595f 100644 --- a/pwa/src/translations/en.ts +++ b/pwa/src/translations/en.ts @@ -73,6 +73,9 @@ export const en = { Unavailable: "Unavailable", Rating: "Rating", Description: "Description", + Gold: "Gold", + Silver: "Silver", + Bronze: "Bronze", "Open Catalogs": "Open Catalogs", "Reusable components within the government": "Reusable components within the government", "Information Models": "Information Models", @@ -154,4 +157,7 @@ export const en = { "Order by rating": "Order by rating", "Left scroll button": "Left scroll button", "Right scroll button": "Right scroll button", + "No application found": "No application found", + "Commonground rating": "Commonground rating", + "No rating": "No rating", }; diff --git a/pwa/src/translations/nl.ts b/pwa/src/translations/nl.ts index 5de3e08bf..6d6019ed9 100644 --- a/pwa/src/translations/nl.ts +++ b/pwa/src/translations/nl.ts @@ -75,6 +75,9 @@ export const nl = { Rating: "Beoordeling", Points: "Punten", Description: "Beschrijving", + Gold: "Goud", + Silver: "Zilver", + Bronze: "Brons", "Open Catalogs": "OpenCatalogi", "Reusable components within the government": "Herbruikbare componenten binnen de overheid", "Information Models": "Informatiemodellen", @@ -157,4 +160,7 @@ export const nl = { "Order by rating": "Sorteren op beoordeling", "Left scroll button": "Linker scrollknop", "Right scroll button": "Rechter scrollknop", + "No application found": "Geen applicatie gevonden", + "Commonground rating": "Commonground beoordeling", + "No rating": "Geen beoordeling", }; diff --git a/pwa/static/.env.development b/pwa/static/.env.development index ffe59c200..b878234c6 100644 --- a/pwa/static/.env.development +++ b/pwa/static/.env.development @@ -42,6 +42,9 @@ GATSBY_FOOTER_CONTENT="https://raw.githubusercontent.com/OpenCatalogi/web-app/25 # options: "heading-1" | "heading-2" | "heading-3" | "heading-4" | "heading-5" GATSBY_FOOTER_CONTENT_HEADER="" +# Filters +# options: "OpenCatalogi" | "Commonground" +GATSBY_FILTER_RATING="OpenCatalogi" # Home GATSBY_OPTIONAL_START_PAGE= diff --git a/pwa/static/.env.production b/pwa/static/.env.production index 5ac4375c5..e9851fec5 100644 --- a/pwa/static/.env.production +++ b/pwa/static/.env.production @@ -43,6 +43,10 @@ GATSBY_FOOTER_CONTENT="https://raw.githubusercontent.com/OpenCatalogi/web-app/25 # options: "heading-1" | "heading-2" | "heading-3" | "heading-4" | "heading-5" GATSBY_FOOTER_CONTENT_HEADER="" +# Filters +# options: "OpenCatalogi" | "Commonground" +GATSBY_FILTER_RATING="OpenCatalogi" + # Home GATSBY_OPTIONAL_START_PAGE=