Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions src/app/guides/page.tsx
Original file line number Diff line number Diff line change
@@ -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 = [
{
Expand All @@ -26,15 +28,14 @@ export const metadata: Metadata = {

export default function GuidesHome() {
return (
<main className="w-full">
<div className="flex min-h-svh flex-col items-center gap-16 px-6 py-64 md:gap-20 md:py-49">
<div className="flex w-full flex-col items-center gap-5 text-center">
<h2 className="typo-display-large md:typo-display-extralarge w-fit bg-linear-to-b from-text-primary to-text-secondary bg-clip-text py-4 text-transparent md:py-14">
Guide
</h2>
</div>
<main className={PAGE_MAIN}>
<section className={`${CONTAINER} ${PAGE_HERO}`}>
<Hero title="Guide" description="Una raccolta di guide realizzate dai membri del Network" />
</section>

<div className="flex w-full flex-col items-center justify-center gap-4 md:flex-row md:gap-13">
{/* Two equal paths: same height, side by side from sm, and the whole card is the link */}
<section className={CONTAINER}>
<div className="mx-auto grid max-w-4xl gap-4 sm:grid-cols-2 sm:gap-6">
{guides.map((guide) => (
<CardIcon
key={guide.title}
Expand All @@ -44,12 +45,13 @@ export default function GuidesHome() {
icon={guide.icon}
cta={guide.cta}
align="start"
className="w-full max-w-108"
size="compact"
hoverEffect
className="h-full"
/>
))}
</div>
</div>
</section>
</main>
)
}
7 changes: 2 additions & 5 deletions src/components/associations/associations-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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 (
<p className="typo-body-large rounded-rectangles border border-slate-300 border-dashed px-6 py-10 text-center text-slate-600">
Le associazioni non sono disponibili al momento. Riprova più tardi.
</p>
)
return <p className={EMPTY_STATE}>Le associazioni non sono disponibili al momento. Riprova più tardi.</p>
}

const accordionItems = associations.map((association) => ({
Expand Down
4 changes: 1 addition & 3 deletions src/components/faqs/faqs-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -19,9 +20,6 @@ const ICONS: Record<string, IconType> = {
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),
Expand Down
9 changes: 5 additions & 4 deletions src/components/groups/extra-groups.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -11,8 +12,8 @@ export async function ExtraGroups() {
)

return (
<main className={`${GROUPS_COLUMN} flex min-h-svh flex-col gap-12 pt-36 pb-32 md:gap-16 md:pt-52`}>
<GroupsPageHeader title="Gruppi Extra" backHref="/groups" />
<main className={FLOW_MAIN}>
<FlowHeader title="Gruppi Extra" backHref="/groups" />

{extraGroups.length > 0 ? (
<div className="flex flex-col gap-3">
Expand All @@ -28,7 +29,7 @@ export async function ExtraGroups() {
))}
</div>
) : (
<p className="typo-body-medium text-center text-text-secondary">Nessun gruppo extra disponibile al momento.</p>
<p className={EMPTY_STATE}>Nessun gruppo extra disponibile al momento.</p>
)}
</main>
)
Expand Down
13 changes: 5 additions & 8 deletions src/components/groups/groups-result.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -107,8 +108,8 @@ export async function GroupsResult({
]

return (
<main className={`${GROUPS_COLUMN} flex min-h-svh flex-col gap-12 pt-36 pb-32 md:gap-16 md:pt-52`}>
<GroupsPageHeader
<main className={FLOW_MAIN}>
<FlowHeader
title={courseName}
subtitle={`Laurea ${currentLevel.name}`}
backHref={stepHref({ school: schoolSlug, level })}
Expand Down Expand Up @@ -136,11 +137,7 @@ export async function GroupsResult({
/>
))}

{!hasAnyGroup && (
<p className="typo-body-medium text-center text-text-secondary">
Nessun gruppo disponibile al momento per questo corso.
</p>
)}
{!hasAnyGroup && <p className={EMPTY_STATE}>Nessun gruppo disponibile al momento per questo corso.</p>}
</main>
)
}
33 changes: 7 additions & 26 deletions src/components/guides/constants.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
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 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. */
export const COURSE_GUIDES: Record<string, AccordionListItem[]> = {}
37 changes: 8 additions & 29 deletions src/components/guides/general-guides.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<main className="mx-auto flex min-h-svh w-full min-w-0 max-w-7xl flex-col gap-8 px-6 py-52 md:gap-0">
<header className="relative flex flex-col items-center gap-1 md:static md:flex-row md:items-center md:gap-4">
<Link
href="/guides"
replace
aria-label="Indietro"
className="absolute top-0 left-0 grid size-10 shrink-0 place-items-center rounded-full bg-white/60 md:static"
>
<FiArrowLeft className="size-5" />
</Link>
<div className="flex min-w-0 flex-col items-center gap-1 text-center md:flex-1">
<h1 className="typo-title-large md:typo-display-medium text-text-primary">Guide Generali</h1>
</div>
<Link
href="/guides"
aria-label="Chiudi"
className="absolute top-0 right-0 grid size-10 shrink-0 place-items-center rounded-full bg-white/60 md:static"
>
<FiX className="size-5" />
</Link>
</header>
<main className={FLOW_MAIN}>
<FlowHeader title="Guide Generali" backHref="/guides" replaceBack />

{LOREM_GUIDES.length > 0 ? (
<AccordionList items={LOREM_GUIDES} className="md:mt-25.75" />
{GENERAL_GUIDES.length > 0 ? (
<AccordionList items={GENERAL_GUIDES} />
) : (
<p className="typo-body-medium text-center text-text-secondary md:mt-16.25">
Nessuna guida disponibile al momento.
</p>
<p className={EMPTY_STATE}>Nessuna guida disponibile al momento.</p>
)}
</main>
)
Expand Down
48 changes: 16 additions & 32 deletions src/components/guides/guides-result.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -28,38 +28,22 @@ export async function GuidesResult({

const courseName = humanizeSlug(course)

const guides = COURSE_GUIDES[course] ?? []

return (
<main className="mx-auto flex min-h-svh w-full min-w-0 max-w-7xl flex-col gap-8 px-6 py-52 md:gap-0">
<header className="relative flex flex-col items-center gap-1 md:static md:flex-row md:items-center md:gap-4">
<Link
href={stepHref({ school: schoolSlug, level })}
replace
aria-label="Indietro"
className="absolute top-0 left-0 grid size-10 shrink-0 place-items-center rounded-full bg-white/60 md:static"
>
<FiArrowLeft className="size-5" />
</Link>
<div className="flex min-w-0 flex-col items-center gap-1 text-center md:flex-1">
<h1 className="typo-title-large md:typo-display-medium text-text-primary">{courseName}</h1>
<p className="typo-label-large md:typo-headline-small text-text-secondary md:text-text-primary">
Laurea {currentLevel.name}
</p>
</div>
<Link
href="/guides"
aria-label="Chiudi"
className="absolute top-0 right-0 grid size-10 shrink-0 place-items-center rounded-full bg-white/60 md:static"
>
<FiX className="size-5" />
</Link>
</header>
<main className={FLOW_MAIN}>
<FlowHeader
title={courseName}
subtitle={`Laurea ${currentLevel.name}`}
backHref={stepHref({ school: schoolSlug, level })}
replaceBack
closeHref="/guides"
/>

{LOREM_GUIDES.length > 0 ? (
<AccordionList items={LOREM_GUIDES} className="md:mt-25.75" />
{guides.length > 0 ? (
<AccordionList items={guides} />
) : (
<p className="typo-body-medium text-center text-text-secondary md:mt-16.25">
Nessuna guida disponibile al momento per questo corso.
</p>
<p className={EMPTY_STATE}>Nessuna guida disponibile al momento per questo corso.</p>
)}
</main>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
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 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"
/** 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. */
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"
Expand All @@ -11,7 +16,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,
Expand Down
4 changes: 4 additions & 0 deletions src/components/layout/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
7 changes: 2 additions & 5 deletions src/components/projects/project-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { type ReactNode, useId, useState } from "react"
import { FiArrowDown, FiArrowUp } from "react-icons/fi"
import { EMPTY_STATE } from "@/components/layout/page"
import { PeekCarousel } from "../peek-carousel"
import { Button } from "../ui/button"
import { ProjectCard } from "./project-card"
Expand Down Expand Up @@ -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 (
<div className="typo-body-large wrap-anywhere rounded-rectangles border border-slate-300 border-dashed px-6 py-10 text-center text-slate-600">
{children}
</div>
)
return <div className={EMPTY_STATE}>{children}</div>
}
5 changes: 2 additions & 3 deletions src/components/wizard/course-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -76,9 +77,7 @@ export async function CourseStep({
}
>
<div className="flex flex-col gap-3">
{courseSlugs.length === 0 && (
<p className="typo-body-medium text-text-secondary">Nessun corso disponibile al momento.</p>
)}
{courseSlugs.length === 0 && <p className={EMPTY_STATE}>Nessun corso disponibile al momento.</p>}
{courseSlugs.map((course) => {
const facets = facetsByCourse.get(course) ?? []
const courseCampus = campusFacet(facets)
Expand Down
Loading