diff --git a/.changeset/swift-experts-camp.md b/.changeset/swift-experts-camp.md new file mode 100644 index 0000000000..1b3814269b --- /dev/null +++ b/.changeset/swift-experts-camp.md @@ -0,0 +1,5 @@ +--- +"nextjs-website": minor +--- + +Generalise filtered grid component diff --git a/apps/nextjs-website/src/app/[productSlug]/tutorials/page.tsx b/apps/nextjs-website/src/app/[productSlug]/tutorials/page.tsx index 7a6de18ca1..fb4035bf58 100644 --- a/apps/nextjs-website/src/app/[productSlug]/tutorials/page.tsx +++ b/apps/nextjs-website/src/app/[productSlug]/tutorials/page.tsx @@ -18,7 +18,7 @@ import { breadcrumbItemByProduct, productToBreadcrumb, } from '@/helpers/structuredData.helpers'; -import { TutorialsList } from '@/components/organisms/TutorialsList/TutorialsList'; +import { FilteredGridLayout } from '@/components/organisms/FilteredGridLayout/FilteredGridLayout'; export type TutorialsPageProps = { readonly product: Product; @@ -75,6 +75,24 @@ const TutorialsPage = async ({ params }: ProductParams) => { ], seo: seo, }); + const mappedTutorials = tutorials.map((tutorial) => { + return { + tags: tutorial.tags || [], + title: tutorial.title, + date: { + date: tutorial.publishedAt, + }, + href: { + label: 'shared.readTutorial', + link: tutorial.path, + translate: true, + }, + img: { + alt: tutorial.image?.alternativeText || '', + src: tutorial.image?.url || '/images/news.png', + }, + }; + }); return ( { /> )} {tutorials && ( - )} diff --git a/apps/nextjs-website/src/app/[productSlug]/use-cases/page.tsx b/apps/nextjs-website/src/app/[productSlug]/use-cases/page.tsx index a856f8a8e5..8f3410bed5 100644 --- a/apps/nextjs-website/src/app/[productSlug]/use-cases/page.tsx +++ b/apps/nextjs-website/src/app/[productSlug]/use-cases/page.tsx @@ -18,7 +18,7 @@ import { } from '@/helpers/structuredData.helpers'; import { UseCase } from '@/lib/types/useCaseData'; import { getUseCaseListPageProps } from '@/lib/api'; -import { UseCaseList } from '../../../components/organisms/UseCaseList/UseCaseList'; +import { FilteredGridLayout } from '@/components/organisms/FilteredGridLayout/FilteredGridLayout'; export type UseCasesPageProps = { readonly product: Product; @@ -69,6 +69,25 @@ const UseCasesPage = async ({ params }: ProductParams) => { seo: seo, }); + const mappedUseCases = useCases.map((useCase) => { + return { + tags: useCase.tags || [], + title: useCase.title, + date: { + date: useCase.publishedAt, + }, + href: { + label: 'shared.readUseCase', + link: useCase.path, + translate: true, + }, + img: { + alt: useCase.coverImage?.alternativeText || '', + src: useCase.coverImage?.url || '/images/news.png', + }, + }; + }); + return ( { /> )} {useCases && ( - )} diff --git a/apps/nextjs-website/src/components/molecules/DesktopFilterSelector/DesktopFilterSelector.tsx b/apps/nextjs-website/src/components/molecules/DesktopFilterSelector/DesktopFilterSelector.tsx index 0d730d21b2..3e6b730b77 100644 --- a/apps/nextjs-website/src/components/molecules/DesktopFilterSelector/DesktopFilterSelector.tsx +++ b/apps/nextjs-website/src/components/molecules/DesktopFilterSelector/DesktopFilterSelector.tsx @@ -23,6 +23,7 @@ const DesktopFilterSelector = ({ }: DesktopFilterSelectorProps) => { return ( ; return ( { + noItemsMessageKey = '', +}: FilteredGridLayoutProps) => { const t = useTranslations(); const updatedTags = [ { - name: t('overview.useCases.all'), + name: t('overview.all'), icon: { data: { attributes: { @@ -52,19 +55,17 @@ export const UseCaseList = ({ ); const [selectedTag, setSelectedTag] = useState(tagValue); - const filteredUseCases = useCases.filter((useCase) => { + const filteredItems = items.filter((item) => { return ( selectedTag === 0 || - useCase.tags?.some((tag) => tag.name === updatedTags[selectedTag].name) + item.tags?.some((tag) => tag.name === updatedTags[selectedTag].name) ); }); // eslint-disable-next-line functional/no-return-void const setSelectedTagFilter = (newTag: number): void => { if (newTag === selectedTag) return; addQueryParam('tag', `${newTag}`); - document - .getElementById('chatbot-page-content') - ?.scrollIntoView({ behavior: 'smooth' }); + document.getElementById('filters')?.scrollIntoView({ behavior: 'smooth' }); setSelectedTag(newTag); }; @@ -76,7 +77,7 @@ export const UseCaseList = ({ const isSmallScreen = useMediaQuery('(max-width: 1000px)'); return ( - 0 ? '24px' : 0 }}> + 0 ? '24px' : 0 }}> {enableFilters && tags.length > 0 && (isSmallScreen ? ( @@ -93,7 +94,7 @@ export const UseCaseList = ({ /> ))} - {filteredUseCases.length <= 0 ? ( + {filteredItems.length <= 0 ? ( - + ) : ( ({ - title: useCase.title, - date: { - date: useCase.publishedAt, - }, - href: { - label: 'shared.readUseCase', - link: useCase.path, - translate: true, - }, - img: { - alt: useCase.coverImage?.alternativeText || '', - src: useCase.coverImage?.url || '/images/news.png', - }, + items={filteredItems.map((item) => ({ + ...item, }))} /> )} diff --git a/apps/nextjs-website/src/components/organisms/TutorialsList/TutorialsList.tsx b/apps/nextjs-website/src/components/organisms/TutorialsList/TutorialsList.tsx deleted file mode 100644 index 9c8aaf732e..0000000000 --- a/apps/nextjs-website/src/components/organisms/TutorialsList/TutorialsList.tsx +++ /dev/null @@ -1,131 +0,0 @@ -'use client'; - -import { Tag } from '@/lib/types/tag'; -import Newsroom from '@/editorialComponents/Newsroom/Newsroom'; -import { Box, useMediaQuery } from '@mui/material'; -import React, { useState } from 'react'; -import { Tutorial } from '@/lib/types/tutorialData'; -import { useTranslations } from 'next-intl'; -import { useSearchParams } from 'next/navigation'; -import MobileFilterSelector from '@/components/molecules/MobileFilterSelector/MobileFilterSelector'; -import DesktopFilterSelector from '@/components/molecules/DesktopFilterSelector/DesktopFilterSelector'; -import SectionTitle from '@/components/molecules/SectionTitle/SectionTitle'; - -type TutorialsListProps = { - readonly tutorials: readonly Tutorial[]; - readonly tags: readonly Tag[]; - readonly enableFilters?: boolean; -}; - -export const TutorialsList = ({ - tags, - tutorials, - enableFilters, -}: TutorialsListProps) => { - const t = useTranslations(); - const updatedTags = [ - { - name: t('overview.tutorial.all'), - icon: { - data: { - attributes: { - name: 'all.svg', - alternativeText: '', - caption: '', - width: 32, - height: 32, - size: 32, - ext: '.svg', - mime: 'image/svg', - url: '/icons/all.svg', - }, - }, - }, - }, - ...tags, - ]; - const searchParams = useSearchParams(); - const parsedTag = parseInt(searchParams.get('tag') || '0'); - const tagValue = Math.max( - 0, - Math.min(isNaN(parsedTag) ? 0 : parsedTag, updatedTags.length - 1) - ); - const [selectedTag, setSelectedTag] = useState(tagValue); - - const filteredTutorials = tutorials.filter((tutorial) => { - return ( - selectedTag === 0 || - tutorial.tags?.some((tag) => tag.name === updatedTags[selectedTag].name) - ); - }); - // eslint-disable-next-line functional/no-return-void - const setSelectedTagFilter = (newTag: number): void => { - if (newTag === selectedTag) return; - addQueryParam('tag', `${newTag}`); - // eslint-disable-next-line functional/immutable-data - //window.location.href = '#webinarsHeader'; - document - .getElementById('chatbot-page-content') - ?.scrollIntoView({ behavior: 'smooth' }); - setSelectedTag(newTag); - }; - - const addQueryParam = (key: string, value: string) => { - const url = new URL(window.location.href); - url.searchParams.set(key, value); - window.history.pushState({}, '', url.toString()); - }; - const isSmallScreen = useMediaQuery('(max-width: 1000px)'); - return ( - - 0 ? '24px' : 0 }}> - {enableFilters && - tags.length > 0 && - (isSmallScreen ? ( - - ) : ( - - ))} - - {filteredTutorials.length <= 0 ? ( - - - - ) : ( - ({ - title: tutorial.title, - date: { - date: tutorial.publishedAt, - }, - href: { - label: 'shared.readTutorial', - link: tutorial.path, - translate: true, - }, - img: { - alt: tutorial.image?.alternativeText || '', - src: tutorial.image?.url || '/images/news.png', - }, - }))} - /> - )} - - ); -}; diff --git a/apps/nextjs-website/src/components/organisms/WebinarsTemplate/WebinarsTemplate.tsx b/apps/nextjs-website/src/components/organisms/WebinarsTemplate/WebinarsTemplate.tsx index 423bd9f329..b4a84a085c 100644 --- a/apps/nextjs-website/src/components/organisms/WebinarsTemplate/WebinarsTemplate.tsx +++ b/apps/nextjs-website/src/components/organisms/WebinarsTemplate/WebinarsTemplate.tsx @@ -2,27 +2,22 @@ import React, { useEffect, useState, Suspense } from 'react'; import Hero from '@/editorialComponents/Hero/Hero'; import { useTranslations } from 'next-intl'; -import { Box, Grid, useMediaQuery, useTheme } from '@mui/material'; +import { useTheme } from '@mui/material'; import { Webinar } from '@/lib/types/webinar'; -import EContainer from '@/editorialComponents/EContainer/EContainer'; -import SectionTitle from '@/components/molecules/SectionTitle/SectionTitle'; -import WebinarListItem from '@/components/molecules/WebinarListItem/WebinarListItem'; import { getFutureWebinars, getPastWebinars } from '@/helpers/webinars.helpers'; import FutureWebinarsShowcase from '../FutureWebinarsShowcase/FutureWebinarsShowcase'; import { baseUrl } from '@/config'; import { generateStructuredDataScripts } from '@/helpers/generateStructuredDataScripts.helpers'; import { getItemFromPaths } from '@/helpers/structuredData.helpers'; -import MobileFilterSelector from '@/components/molecules/MobileFilterSelector/MobileFilterSelector'; -import DesktopFilterSelector from '@/components/molecules/DesktopFilterSelector/DesktopFilterSelector'; -import { WebinarCategory } from '@/lib/types/webinarCategory'; -import { useSearchParams } from 'next/navigation'; import Spinner from '@/components/atoms/Spinner/Spinner'; +import { FilteredGridLayout } from '@/components/organisms/FilteredGridLayout/FilteredGridLayout'; +import { Tag } from '@/lib/types/tag'; const CHECK_WEBINARS_INTERVAL_MS = 60 * 1000; type WebinarsTemplateProps = { webinars: readonly Webinar[]; - categories: readonly WebinarCategory[]; + categories: readonly Tag[]; }; const WebinarsTemplateContent = ({ @@ -30,64 +25,9 @@ const WebinarsTemplateContent = ({ categories, }: WebinarsTemplateProps) => { const t = useTranslations(); - const updatedCategories = [ - { - name: t('webinars.all'), - icon: { - data: { - attributes: { - name: 'all.svg', - alternativeText: '', - caption: '', - width: 32, - height: 32, - size: 32, - ext: '.svg', - mime: 'image/svg', - url: ' icons/all.svg', - }, - }, - }, - }, - ...categories, - ]; const { palette } = useTheme(); const [futureWebinars, setFutureWebinars] = useState([]); const [pastWebinars, setPastWebinars] = useState([]); - const searchParams = useSearchParams(); - const parsedCategory = parseInt(searchParams.get('category') || '0'); - const categoryValue = Math.max( - 0, - Math.min( - isNaN(parsedCategory) ? 0 : parsedCategory, - updatedCategories.length - 1 - ) - ); - const [selectedCategory, setSelectedCategory] = useState(categoryValue); - - const filteredWebinars = pastWebinars.filter((cat) => { - return ( - selectedCategory === 0 || - cat.webinarCategory?.name === updatedCategories[selectedCategory].name - ); - }); - // eslint-disable-next-line functional/no-return-void - const setSelectedWebinarCategory = (newCategory: number): void => { - if (newCategory === selectedCategory) return; - addQueryParam('category', `${newCategory}`); - // eslint-disable-next-line functional/immutable-data - //window.location.href = '#webinarsHeader'; - document - .getElementById('webinarsHeader') - ?.scrollIntoView({ behavior: 'smooth' }); - setSelectedCategory(newCategory); - }; - - const addQueryParam = (key: string, value: string) => { - const url = new URL(window.location.href); - url.searchParams.set(key, value); - window.history.pushState({}, '', url.toString()); - }; const webinarsListPageSEO = { metaTitle: t('webinars.title'), @@ -116,7 +56,21 @@ const WebinarsTemplateContent = ({ // Cleanup the interval when the component is unmounted return () => clearInterval(intervalId); }, [webinars]); - const isSmallScreen = useMediaQuery('(max-width: 1000px)'); + const mappedWebinars = webinars.map((webinar) => ({ + tags: webinar.tag ? [webinar.tag] : [], + title: webinar.title, + date: { + date: new Date(Date.parse(webinar.startDateTime || '')), + }, + href: { + label: t('webinar.goToWebinar'), + link: `/webinars/${webinar.slug}`, + }, + img: { + alt: webinar.title, + src: webinar.imagePath || '/images/news.png', + }, + })); return ( <> @@ -134,59 +88,12 @@ const WebinarsTemplateContent = ({ )} {pastWebinars.length > 0 && ( - <> - - - - {categories.length <= 0 ? null : isSmallScreen ? ( - - ) : ( - - )} - {filteredWebinars.length <= 0 ? ( - - - - ) : ( - - - - {filteredWebinars.map((webinar, i) => ( - - ))} - - - - )} - + )} ); diff --git a/apps/nextjs-website/src/lib/strapi/__tests__/fixtures/webinars.ts b/apps/nextjs-website/src/lib/strapi/__tests__/fixtures/webinars.ts index 62cf64b904..506fbf2780 100644 --- a/apps/nextjs-website/src/lib/strapi/__tests__/fixtures/webinars.ts +++ b/apps/nextjs-website/src/lib/strapi/__tests__/fixtures/webinars.ts @@ -187,7 +187,7 @@ export const webinarProps = { subscribeCtaLabel: 'Subscribe Now', imagePath: 'https://example.com/example.jpg', seo: { metaTitle: 'SEO Webinar', metaDescription: 'SEO Description' }, - webinarCategory: { + tag: { name: 'Category 1', icon: { data: { diff --git a/apps/nextjs-website/src/lib/strapi/makeProps/makeWebinars.ts b/apps/nextjs-website/src/lib/strapi/makeProps/makeWebinars.ts index 57fd13404e..af1f5a6225 100644 --- a/apps/nextjs-website/src/lib/strapi/makeProps/makeWebinars.ts +++ b/apps/nextjs-website/src/lib/strapi/makeProps/makeWebinars.ts @@ -56,8 +56,7 @@ export const makeWebinarProps = ( subscribeCtaLabel: strapiWebinar.attributes.subscribeParagraphLabel, imagePath: strapiWebinar.attributes.coverImage.data.attributes.url, seo: strapiWebinar.attributes.seo, - webinarCategory: - strapiWebinar.attributes.webinarCategory?.data?.attributes, + tag: strapiWebinar.attributes.webinarCategory?.data?.attributes, headerImage: strapiWebinar.attributes.headerImage?.data?.attributes, updatedAt: strapiWebinar.attributes.updatedAt, } satisfies Webinar; diff --git a/apps/nextjs-website/src/lib/types/webinar.ts b/apps/nextjs-website/src/lib/types/webinar.ts index 3bb0961369..331a0563f3 100644 --- a/apps/nextjs-website/src/lib/types/webinar.ts +++ b/apps/nextjs-website/src/lib/types/webinar.ts @@ -3,6 +3,7 @@ import { BlocksContent } from '@strapi/blocks-react-renderer'; import { SEO } from './seo'; import { WebinarCategory } from '@/lib/types/webinarCategory'; import { Media } from '@/lib/types/media'; +import { Tag } from '@/lib/types/tag'; export type QuestionsAndAnswer = { readonly question: string; @@ -47,7 +48,7 @@ export type Webinar = { readonly imagePath: string; readonly questionsAndAnswers?: readonly QuestionsAndAnswer[]; readonly seo?: SEO; - readonly webinarCategory?: WebinarCategory; + readonly tag?: Tag; readonly headerImage?: Media; readonly updatedAt: string; }; diff --git a/apps/nextjs-website/src/messages/it.json b/apps/nextjs-website/src/messages/it.json index 01b69bb61f..149732ff14 100644 --- a/apps/nextjs-website/src/messages/it.json +++ b/apps/nextjs-website/src/messages/it.json @@ -439,7 +439,8 @@ } }, "overview": { - "startInfo": { + "all": "Tutti", + "startInfo": { "title": "Si comincia da qui" }, "tutorialsList": { @@ -448,7 +449,6 @@ "tutorial": { "title": "Esplora i tutorial", "ctaLabel": "Vedi tutti i tutorial", - "all": "Tutti", "noTutorialMessage": "Non sono presenti tutorial della categoria selezionata" }, "postIntegration": { @@ -460,7 +460,6 @@ "useCases": { "title": "Casi d'uso", "ctaLabel": "Vedi tutti i casi d'uso", - "all": "Tutti", "noUseCaseMessage": "Non sono presenti casi d'uso della categoria selezionata" } },