From b563cd03fb17d71a7df1cee9a1a95486bc9c3736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriele=20Vigan=C3=B2?= Date: Wed, 28 Oct 2026 17:50:11 +0000 Subject: [PATCH 1/2] feat(guides): align the guides hub and pages, drop the lorem placeholders - /guides uses the shared container, rhythm and Hero, with Figma's subtitle ("Una raccolta di guide realizzate dai membri del Network"); the two entry cards sit in an equal-height grid and react to hover, since they are links. - /guides/generali and the course result page drop the lorem ipsum guides that were served in production and show their existing empty state instead. GENERAL_GUIDES / COURSE_GUIDES are empty until the guide model and content are decided. - The back/close header and column of the wizard's end pages move from groups/page-header to layout/flow-header (FlowHeader, FLOW_MAIN), shared by groups and guides; the top padding on phones grows from 144 to 176px so the title no longer sits 16px under the fixed header. - EMPTY_STATE (the dashed placeholder) moves to layout/page and is used by projects, associations, faqs, groups and guides. Co-Authored-By: Claude Opus 5.5 --- src/app/guides/page.tsx | 22 +++++---- .../associations/associations-list.tsx | 7 +-- src/components/faqs/faqs-tabs.tsx | 4 +- src/components/groups/extra-groups.tsx | 9 ++-- src/components/groups/groups-result.tsx | 13 ++--- src/components/guides/constants.ts | 36 ++++---------- src/components/guides/general-guides.tsx | 37 ++++---------- src/components/guides/guides-result.tsx | 48 +++++++------------ .../flow-header.tsx} | 9 ++-- src/components/layout/page.ts | 4 ++ src/components/projects/project-list.tsx | 7 +-- 11 files changed, 71 insertions(+), 125 deletions(-) rename src/components/{groups/page-header.tsx => layout/flow-header.tsx} (76%) diff --git a/src/app/guides/page.tsx b/src/app/guides/page.tsx index e9d039f3..20742968 100644 --- a/src/app/guides/page.tsx +++ b/src/app/guides/page.tsx @@ -1,6 +1,8 @@ import type { Metadata } from "next" import { FiBook, FiFileText } from "react-icons/fi" import { CardIcon } from "@/components/card-icon" +import { CONTAINER, PAGE_HERO, PAGE_MAIN } from "@/components/layout/page" +import { Hero } from "@/components/ui/hero" const guides = [ { @@ -26,15 +28,14 @@ export const metadata: Metadata = { export default function GuidesHome() { return ( -
-
-
-

- Guide -

-
+
+
+ +
-
+ {/* Two equal paths: same height, side by side from sm, and the whole card is the link */} +
+
{guides.map((guide) => ( ))}
-
+
) } diff --git a/src/components/associations/associations-list.tsx b/src/components/associations/associations-list.tsx index 65dd312a..d22670e6 100644 --- a/src/components/associations/associations-list.tsx +++ b/src/components/associations/associations-list.tsx @@ -12,6 +12,7 @@ import { } from "react-icons/fi" import AccordionAssociation from "@/components/accordion-association" import type { GradientIconType } from "@/components/gradient-icon" +import { EMPTY_STATE } from "@/components/layout/page" import type { ApiOutput } from "@/types" type Association = ApiOutput["web"]["associations"]["getAllAssociations"][number] @@ -33,11 +34,7 @@ const LINK_CONFIG: { key: keyof AssociationLinks; label: string; icon: GradientI export function AssociationsList({ associations }: { associations: Association[] }) { if (associations.length === 0) { // The query returns an empty list on backend errors too, so the message covers both cases - return ( -

- Le associazioni non sono disponibili al momento. Riprova più tardi. -

- ) + return

Le associazioni non sono disponibili al momento. Riprova più tardi.

} const accordionItems = associations.map((association) => ({ diff --git a/src/components/faqs/faqs-tabs.tsx b/src/components/faqs/faqs-tabs.tsx index c1080bd6..632cd4a8 100644 --- a/src/components/faqs/faqs-tabs.tsx +++ b/src/components/faqs/faqs-tabs.tsx @@ -4,6 +4,7 @@ import type { IconType } from "react-icons" import { FiBookOpen, FiDollarSign, FiEdit, FiFileText, FiHelpCircle, FiSend } from "react-icons/fi" import AccordionList from "@/components/accordion-list" import CalloutItem from "@/components/callout-item" +import { EMPTY_STATE } from "@/components/layout/page" import TabsNavigation from "@/components/tabs" import { Tabs } from "@/components/tabs/tabs" import { TabsContent } from "@/components/tabs/tabs-content" @@ -19,9 +20,6 @@ const ICONS: Record = { send: FiSend, } -const EMPTY_STATE = - "typo-body-large rounded-rectangles border border-slate-300 border-dashed px-6 py-10 text-center text-slate-600" - export function FaqsTabs({ categories }: { categories: FaqCategory[] }) { const tabs = categories.map((category) => ({ value: String(category.categoryId), diff --git a/src/components/groups/extra-groups.tsx b/src/components/groups/extra-groups.tsx index f0b25c96..a381cb5c 100644 --- a/src/components/groups/extra-groups.tsx +++ b/src/components/groups/extra-groups.tsx @@ -1,5 +1,6 @@ import { CardCourseGroup } from "@/components/card-course-group" -import { GROUPS_COLUMN, GroupsPageHeader } from "@/components/groups/page-header" +import { FLOW_MAIN, FlowHeader } from "@/components/layout/flow-header" +import { EMPTY_STATE } from "@/components/layout/page" import { getVisibleGroups } from "@/queries/groups" import { EXTRA_LABEL, matchesLabelBranch } from "@/utils/labels" import { mergeGroupsByTitle } from "@/utils/merge-groups" @@ -11,8 +12,8 @@ export async function ExtraGroups() { ) return ( -
- +
+ {extraGroups.length > 0 ? (
@@ -28,7 +29,7 @@ export async function ExtraGroups() { ))}
) : ( -

Nessun gruppo extra disponibile al momento.

+

Nessun gruppo extra disponibile al momento.

)}
) diff --git a/src/components/groups/groups-result.tsx b/src/components/groups/groups-result.tsx index b0ff9c99..591b4ddd 100644 --- a/src/components/groups/groups-result.tsx +++ b/src/components/groups/groups-result.tsx @@ -1,6 +1,7 @@ import { notFound } from "next/navigation" import { CardCourseGroup } from "@/components/card-course-group" -import { GROUPS_COLUMN, GroupsPageHeader } from "@/components/groups/page-header" +import { FLOW_MAIN, FlowHeader } from "@/components/layout/flow-header" +import { EMPTY_STATE } from "@/components/layout/page" import { getLevel, getSchool } from "@/components/wizard/constants" import { getVisibleGroups } from "@/queries/groups" import type { VisibleGroup } from "@/queries/types" @@ -107,8 +108,8 @@ export async function GroupsResult({ ] return ( -
- + ))} - {!hasAnyGroup && ( -

- Nessun gruppo disponibile al momento per questo corso. -

- )} + {!hasAnyGroup &&

Nessun gruppo disponibile al momento per questo corso.

}
) } diff --git a/src/components/guides/constants.ts b/src/components/guides/constants.ts index 742951ef..75df72c8 100644 --- a/src/components/guides/constants.ts +++ b/src/components/guides/constants.ts @@ -1,26 +1,10 @@ -export const LOREM_GUIDES = [ - { - value: "lorem-ipsum-dolor-sit-amet", - trigger: "Lorem ipsum dolor sit amet", - content: - "Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.", - }, - { - value: "ut-enim-ad-minim-veniam", - trigger: "Ut enim ad minim veniam", - content: - "Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.", - }, - { - value: "duis-aute-irure-dolor", - trigger: "Duis aute irure dolor", - content: - "In reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.", - }, - { - value: "excepteur-sint-occaecat", - trigger: "Excepteur sint occaecat", - content: - "Cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt.", - }, -] +import type { AccordionListItem } from "@/components/accordion-list/types" + +/* + The guides have no content yet, and how they'll be written and organised (per course, per exam, by whom) is still + open. Until then the pages show their empty state: the lorem ipsum placeholders used to be served in production. +*/ +export const GENERAL_GUIDES: AccordionListItem[] = [] + +/** Guides for a given course, by course slug. */ +export const COURSE_GUIDES: Record = {} diff --git a/src/components/guides/general-guides.tsx b/src/components/guides/general-guides.tsx index 48b62904..321f7169 100644 --- a/src/components/guides/general-guides.tsx +++ b/src/components/guides/general-guides.tsx @@ -1,38 +1,17 @@ -import Link from "next/link" -import { FiArrowLeft, FiX } from "react-icons/fi" import AccordionList from "@/components/accordion-list" -import { LOREM_GUIDES } from "@/components/guides/constants" +import { GENERAL_GUIDES } from "@/components/guides/constants" +import { FLOW_MAIN, FlowHeader } from "@/components/layout/flow-header" +import { EMPTY_STATE } from "@/components/layout/page" export function GeneralGuides() { return ( -
-
- - - -
-

Guide Generali

-
- - - -
+
+ - {LOREM_GUIDES.length > 0 ? ( - + {GENERAL_GUIDES.length > 0 ? ( + ) : ( -

- Nessuna guida disponibile al momento. -

+

Nessuna guida disponibile al momento.

)}
) diff --git a/src/components/guides/guides-result.tsx b/src/components/guides/guides-result.tsx index bf98f20f..32f2b5ee 100644 --- a/src/components/guides/guides-result.tsx +++ b/src/components/guides/guides-result.tsx @@ -1,8 +1,8 @@ -import Link from "next/link" import { notFound } from "next/navigation" -import { FiArrowLeft, FiX } from "react-icons/fi" import AccordionList from "@/components/accordion-list" -import { LOREM_GUIDES } from "@/components/guides/constants" +import { COURSE_GUIDES } from "@/components/guides/constants" +import { FLOW_MAIN, FlowHeader } from "@/components/layout/flow-header" +import { EMPTY_STATE } from "@/components/layout/page" import { getLevel, getSchool } from "@/components/wizard/constants" import { getVisibleGroups } from "@/queries/groups" import { courseFacetsForLevel, humanizeSlug } from "@/utils/labels" @@ -28,38 +28,22 @@ export async function GuidesResult({ const courseName = humanizeSlug(course) + const guides = COURSE_GUIDES[course] ?? [] + return ( -
-
- - - -
-

{courseName}

-

- Laurea {currentLevel.name} -

-
- - - -
+
+ - {LOREM_GUIDES.length > 0 ? ( - + {guides.length > 0 ? ( + ) : ( -

- Nessuna guida disponibile al momento per questo corso. -

+

Nessuna guida disponibile al momento per questo corso.

)}
) diff --git a/src/components/groups/page-header.tsx b/src/components/layout/flow-header.tsx similarity index 76% rename from src/components/groups/page-header.tsx rename to src/components/layout/flow-header.tsx index ef06eb8c..7bb97a8d 100644 --- a/src/components/groups/page-header.tsx +++ b/src/components/layout/flow-header.tsx @@ -1,8 +1,11 @@ import Link from "next/link" import { FiArrowLeft, FiX } from "react-icons/fi" -/** Figma's list column for group results (~1040px at 1728), with the shared 24/40px gutters. */ -export const GROUPS_COLUMN = "mx-auto w-full min-w-0 max-w-5xl px-6 sm:px-10" +/** Figma's column for the pages a wizard leads to (group results, guides: ~1040px at 1728), with the shared 24/40px gutters. */ +export const FLOW_COLUMN = "mx-auto w-full min-w-0 max-w-5xl px-6 sm:px-10" + +/** Page shell for those pages: the top padding leaves the fixed header ~48px of air on phones (it was 16px). */ +export const FLOW_MAIN = `${FLOW_COLUMN} flex min-h-svh flex-col gap-12 pt-44 pb-32 md:gap-16 md:pt-52` const iconLinkClass = "grid size-10 shrink-0 place-items-center rounded-full bg-white/60 outline-none transition-colors hover:bg-white focus-visible:ring-2 focus-visible:ring-blue-secondary" @@ -11,7 +14,7 @@ const iconLinkClass = * Back button, centred title and optional close button. A three-column grid keeps a long course name from running * under the buttons on phones. */ -export function GroupsPageHeader({ +export function FlowHeader({ title, subtitle, backHref, diff --git a/src/components/layout/page.ts b/src/components/layout/page.ts index 495b9d2a..fcde2497 100644 --- a/src/components/layout/page.ts +++ b/src/components/layout/page.ts @@ -27,3 +27,7 @@ export const PAGE_HERO = `${PAGE_HERO_TOP} pb-8 md:pb-12 xl:pb-24` /** A section made of a heading block followed by its content. */ export const SECTION_STACK = `${CONTAINER} flex flex-col gap-10 md:gap-14` + +/** Placeholder for a list with nothing to show yet: dashed, so it reads as "empty" rather than as a card. */ +export const EMPTY_STATE = + "typo-body-large wrap-anywhere rounded-rectangles border border-slate-300 border-dashed px-6 py-10 text-center text-slate-600" diff --git a/src/components/projects/project-list.tsx b/src/components/projects/project-list.tsx index 72ee5c4a..2da6afee 100644 --- a/src/components/projects/project-list.tsx +++ b/src/components/projects/project-list.tsx @@ -3,6 +3,7 @@ import { type ReactNode, useId, useState } from "react" import { FiArrowDown, FiArrowUp } from "react-icons/fi" import { PeekCarousel } from "../peek-carousel" +import { EMPTY_STATE } from "@/components/layout/page" import { Button } from "../ui/button" import { ProjectCard } from "./project-card" import type { Project } from "./resources" @@ -66,9 +67,5 @@ export function ProjectList({ /** Shown in place of an empty list: says so plainly and points to the one thing the reader can do about it. */ export function ProjectsEmpty({ children }: { children: ReactNode }) { - return ( -
- {children} -
- ) + return
{children}
} From b0306784d1ea81814f4ace09869e13e1fbd089a8 Mon Sep 17 00:00:00 2001 From: Bianca Roberta Ianosel Date: Mon, 2 Nov 2026 13:44:29 +0000 Subject: [PATCH 2/2] fix(guides): site gutters on wizard pages, shared empty state for courses - FLOW_COLUMN builds on CONTAINER, so wizard result pages get the 64px gutter from lg like every other page instead of 40px. - The course step's empty list uses EMPTY_STATE, like the other lists this PR moved to it. - Comments trimmed; project-list imports sorted. --- src/components/guides/constants.ts | 5 +---- src/components/layout/flow-header.tsx | 8 +++++--- src/components/projects/project-list.tsx | 2 +- src/components/wizard/course-step.tsx | 5 ++--- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/components/guides/constants.ts b/src/components/guides/constants.ts index 75df72c8..e53f618c 100644 --- a/src/components/guides/constants.ts +++ b/src/components/guides/constants.ts @@ -1,9 +1,6 @@ import type { AccordionListItem } from "@/components/accordion-list/types" -/* - The guides have no content yet, and how they'll be written and organised (per course, per exam, by whom) is still - open. Until then the pages show their empty state: the lorem ipsum placeholders used to be served in production. -*/ +// The guides have no content yet, and how they will be organised is still open: until then the pages show their empty state. export const GENERAL_GUIDES: AccordionListItem[] = [] /** Guides for a given course, by course slug. */ diff --git a/src/components/layout/flow-header.tsx b/src/components/layout/flow-header.tsx index 7bb97a8d..6f86ccc6 100644 --- a/src/components/layout/flow-header.tsx +++ b/src/components/layout/flow-header.tsx @@ -1,10 +1,12 @@ import Link from "next/link" import { FiArrowLeft, FiX } from "react-icons/fi" +import { CONTAINER } from "@/components/layout/page" +import { cn } from "@/lib/utils" -/** Figma's column for the pages a wizard leads to (group results, guides: ~1040px at 1728), with the shared 24/40px gutters. */ -export const FLOW_COLUMN = "mx-auto w-full min-w-0 max-w-5xl px-6 sm:px-10" +/** The narrower column of the pages a wizard leads to (group results, guides), with the site's gutters. */ +export const FLOW_COLUMN = cn(CONTAINER, "min-w-0 max-w-5xl") -/** Page shell for those pages: the top padding leaves the fixed header ~48px of air on phones (it was 16px). */ +/** Page shell for those pages: the top padding leaves the fixed header ~48px of air on phones. */ export const FLOW_MAIN = `${FLOW_COLUMN} flex min-h-svh flex-col gap-12 pt-44 pb-32 md:gap-16 md:pt-52` const iconLinkClass = diff --git a/src/components/projects/project-list.tsx b/src/components/projects/project-list.tsx index 2da6afee..6429d5f8 100644 --- a/src/components/projects/project-list.tsx +++ b/src/components/projects/project-list.tsx @@ -2,8 +2,8 @@ import { type ReactNode, useId, useState } from "react" import { FiArrowDown, FiArrowUp } from "react-icons/fi" -import { PeekCarousel } from "../peek-carousel" import { EMPTY_STATE } from "@/components/layout/page" +import { PeekCarousel } from "../peek-carousel" import { Button } from "../ui/button" import { ProjectCard } from "./project-card" import type { Project } from "./resources" diff --git a/src/components/wizard/course-step.tsx b/src/components/wizard/course-step.tsx index a3b570ac..c2872829 100644 --- a/src/components/wizard/course-step.tsx +++ b/src/components/wizard/course-step.tsx @@ -2,6 +2,7 @@ import Link from "next/link" import { notFound } from "next/navigation" import { FiChevronRight } from "react-icons/fi" import { CardCourse } from "@/components/card-course" +import { EMPTY_STATE } from "@/components/layout/page" import { getLevel, getSchool } from "@/components/wizard/constants" import { CourseFilters, type FilterOption } from "@/components/wizard/course-filters" import { WizardShell } from "@/components/wizard/wizard-shell" @@ -76,9 +77,7 @@ export async function CourseStep({ } >
- {courseSlugs.length === 0 && ( -

Nessun corso disponibile al momento.

- )} + {courseSlugs.length === 0 &&

Nessun corso disponibile al momento.

} {courseSlugs.map((course) => { const facets = facetsByCourse.get(course) ?? [] const courseCampus = campusFacet(facets)