From 967542c1c8052a48be3a4dcee79efde92ee8f56e Mon Sep 17 00:00:00 2001 From: Bianca Roberta Ianosel Date: Tue, 2 Jun 2026 22:20:29 +0200 Subject: [PATCH 1/7] feat: add Hero component and remove unused CarouselMock (#115) Co-authored-by: Tommaso Morganti --- src/app/associations/page.tsx | 19 +++------ src/components/home/carousel-mock.tsx | 49 ---------------------- src/components/projects/collection.tsx | 2 +- src/components/projects/community-news.tsx | 10 +---- src/components/projects/deprecated.tsx | 2 +- src/components/ui/hero.tsx | 10 +++++ 6 files changed, 20 insertions(+), 72 deletions(-) delete mode 100644 src/components/home/carousel-mock.tsx create mode 100644 src/components/ui/hero.tsx diff --git a/src/app/associations/page.tsx b/src/app/associations/page.tsx index e0553bb..df87e64 100644 --- a/src/app/associations/page.tsx +++ b/src/app/associations/page.tsx @@ -12,6 +12,7 @@ import { FiYoutube, } from "react-icons/fi" import AccordionAssociation from "@/components/accordion-association" +import { Hero } from "@/components/ui/hero" import esnLogo from "../../../public/logos/esn.svg" const accordionItems = [ @@ -158,19 +159,11 @@ const accordionItems = [ export default function AssociationsPage() { return ( -
-
-
-

- Associazioni -

-

- Scopri le associazioni studentesche del Politecnico -

-
-
- -
+
+ + +
+
) diff --git a/src/components/home/carousel-mock.tsx b/src/components/home/carousel-mock.tsx deleted file mode 100644 index c4f9758..0000000 --- a/src/components/home/carousel-mock.tsx +++ /dev/null @@ -1,49 +0,0 @@ -"use client" - -import { CardCaption } from "@/components/card-caption" -import { Carousel, CarouselContent, CarouselDots, CarouselItem } from "@/components/ui/carousel" - -const mockCards = [ - { - title: "WeBeepSync", - caption: - "WeBeep Sync è una semplice app, user-friendly e senza compromessi che serve per tenere sincronizzati tutti i tuoi file di WeBeep.", - }, - { - title: "PolimiSchedule", - caption: - "Genera un file iCalendar (.ics) a partire dal formato testuale dell’Orario delle lezioni. Possibilità di importare su Google Calendar.", - }, - { - title: "WiFiLinux", - caption: "Scarica ed esegui lo script Python per attivare la connessione permanente al WiFi Polimi.", - }, - { - title: "The TOL Project", - caption: "Un simulatore gratuito del test di ammissione per le aspiranti matricole di Ingegneria del PoliMi.", - }, -] as const - -// TODO: delete this when merging -export function CarouselMock() { - return ( -
-

- PoliNetwork -

- - - - {mockCards.map((card) => ( - -
- -
-
- ))} -
- -
-
- ) -} diff --git a/src/components/projects/collection.tsx b/src/components/projects/collection.tsx index 81221e0..81c651d 100644 --- a/src/components/projects/collection.tsx +++ b/src/components/projects/collection.tsx @@ -83,7 +83,7 @@ export function Collection() {
-
+
{collectionCards.map((card) => ( ))} diff --git a/src/components/projects/community-news.tsx b/src/components/projects/community-news.tsx index f5881ff..1aa9e9b 100644 --- a/src/components/projects/community-news.tsx +++ b/src/components/projects/community-news.tsx @@ -1,6 +1,7 @@ import { FiCrop } from "react-icons/fi" import { CardCaption } from "../card-caption" import { Carousel, CarouselContent, CarouselDots, CarouselItem } from "../ui/carousel" +import { Hero } from "../ui/hero" const communityCards = [ { @@ -36,14 +37,7 @@ const communityCards = [ export function CommunityNews() { return (
-
-

- Projects -

-

- Esplora e contribuisci ai progetti degli studenti -

-
+
diff --git a/src/components/projects/deprecated.tsx b/src/components/projects/deprecated.tsx index ebff0a9..67a3c07 100644 --- a/src/components/projects/deprecated.tsx +++ b/src/components/projects/deprecated.tsx @@ -47,7 +47,7 @@ export function Deprecated() {
-
+
{communityCards.map((card) => ( ))} diff --git a/src/components/ui/hero.tsx b/src/components/ui/hero.tsx new file mode 100644 index 0000000..d4ebc4b --- /dev/null +++ b/src/components/ui/hero.tsx @@ -0,0 +1,10 @@ +export function Hero({ title, description }: { title: string; description: string }) { + return ( +
+

+ {title} +

+

{description}

+
+ ) +} From 62f71ad0041711c2e346bdd0d0757217a6b77110 Mon Sep 17 00:00:00 2001 From: Bianca Roberta Ianosel Date: Tue, 2 Jun 2026 22:42:36 +0200 Subject: [PATCH 2/7] feat: add MatricoleGuides component and related cards (#122) Co-authored-by: Tommaso Morganti --- src/app/matricole/page.tsx | 2 + src/components/card-group/header.tsx | 26 +++ src/components/card-group/index.tsx | 22 +++ src/components/card-group/types.ts | 11 ++ src/components/card-icon/classes.ts | 14 ++ src/components/card-icon/index.tsx | 89 ++++----- .../card-icon/inline-card-media.tsx | 17 ++ src/components/card-icon/types.ts | 4 +- src/components/card-icon/utils.ts | 27 ++- src/components/matricole/constants.ts | 169 ++++++++++++++++++ src/components/matricole/faqs.tsx | 37 +--- src/components/matricole/guides.tsx | 61 +++++++ src/components/matricole/intro.tsx | 19 +- 13 files changed, 401 insertions(+), 97 deletions(-) create mode 100644 src/components/card-group/header.tsx create mode 100644 src/components/card-group/index.tsx create mode 100644 src/components/card-group/types.ts create mode 100644 src/components/card-icon/inline-card-media.tsx create mode 100644 src/components/matricole/constants.ts create mode 100644 src/components/matricole/guides.tsx diff --git a/src/app/matricole/page.tsx b/src/app/matricole/page.tsx index 38f6395..9d1e36d 100644 --- a/src/app/matricole/page.tsx +++ b/src/app/matricole/page.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next" import { FAQsPage } from "@/components/matricole/faqs" +import { MatricoleGuides } from "@/components/matricole/guides" import { MatricoleIntro } from "@/components/matricole/intro" export const metadata: Metadata = { @@ -11,6 +12,7 @@ export default function MatricolePage() { return (
+
) diff --git a/src/components/card-group/header.tsx b/src/components/card-group/header.tsx new file mode 100644 index 0000000..a8fd10d --- /dev/null +++ b/src/components/card-group/header.tsx @@ -0,0 +1,26 @@ +import { cn } from "@/lib/utils" +import { GradientIcon } from "../gradient-icon" +import type { CardGroupProps } from "./types" + +export function CardGroupHeader({ + icon, + title, + description, + horizontal, +}: Pick) { + return ( +
+
+ {icon && } +

+ {title} +

+
+ {description && ( +

+ {description} +

+ )} +
+ ) +} diff --git a/src/components/card-group/index.tsx b/src/components/card-group/index.tsx new file mode 100644 index 0000000..84c994a --- /dev/null +++ b/src/components/card-group/index.tsx @@ -0,0 +1,22 @@ +import { Glass } from "@/components/glass" +import { cn } from "@/lib/utils" +import { CardGroupHeader } from "./header" +import type { CardGroupProps } from "./types" + +export function CardGroup({ icon, title, description, children, horizontal = false, className }: CardGroupProps) { + return ( + + {horizontal ? ( +
+ +
{children}
+
+ ) : ( + <> + +
{children}
+ + )} +
+ ) +} diff --git a/src/components/card-group/types.ts b/src/components/card-group/types.ts new file mode 100644 index 0000000..2ca0cf6 --- /dev/null +++ b/src/components/card-group/types.ts @@ -0,0 +1,11 @@ +import type { ReactNode } from "react" +import type { GradientIconType } from "../gradient-icon" + +export type CardGroupProps = { + icon?: GradientIconType + title: string + description?: string + children: ReactNode + horizontal?: boolean + className?: string +} diff --git a/src/components/card-icon/classes.ts b/src/components/card-icon/classes.ts index fb4256f..801eb48 100644 --- a/src/components/card-icon/classes.ts +++ b/src/components/card-icon/classes.ts @@ -6,6 +6,7 @@ export const ICON_SIZE_CLASSES: SizeClassMap = { sm: "h-14 w-14", md: "h-32 w-32", lg: "h-44 w-44", + inline: "h-6 w-6", } export const CARD_PADDING_WITHOUT_DESCRIPTION: SizeClassMap = { @@ -14,6 +15,7 @@ export const CARD_PADDING_WITHOUT_DESCRIPTION: SizeClassMap = { sm: "px-8 py-4", md: "p-8", lg: "p-8", + inline: "p-6", } export const CARD_PADDING_WITH_DESCRIPTION: SizeClassMap = { @@ -22,6 +24,7 @@ export const CARD_PADDING_WITH_DESCRIPTION: SizeClassMap = { sm: "p-8", md: "p-8", lg: "p-8", + inline: "p-6", } export const CONTENT_GAP_CLASSES: SizeClassMap = { @@ -30,6 +33,7 @@ export const CONTENT_GAP_CLASSES: SizeClassMap = { sm: "gap-2", md: "gap-6", lg: "gap-6", + inline: "gap-4", } export const TITLE_SIZE_CLASSES: SizeClassMap = { @@ -38,4 +42,14 @@ export const TITLE_SIZE_CLASSES: SizeClassMap = { sm: "typo-headline-medium", md: "typo-headline-medium", lg: "typo-headline-medium", + inline: "typo-title-large", +} + +export const INLINE_CONTAINER_SIZE_CLASSES: SizeClassMap = { + compact: "h-12 w-12", + xs: "h-10 w-10", + sm: "h-12 w-12", + md: "h-14 w-14", + lg: "h-16 w-16", + inline: "h-10 w-10", } diff --git a/src/components/card-icon/index.tsx b/src/components/card-icon/index.tsx index 1ad086c..a38598f 100644 --- a/src/components/card-icon/index.tsx +++ b/src/components/card-icon/index.tsx @@ -3,30 +3,18 @@ import { cn } from "@/lib/utils" import { BasicCardMedia } from "./basic-card-media" import { DescriptionCardMedia } from "./description-card-media" import { CardHoverBackground } from "./hover-background" +import { InlineCardMedia } from "./inline-card-media" import type { CardIconProps } from "./types" -import { getCardPaddingClasses, getContentGapClasses, getTitleSizeClasses } from "./utils" +import { getAlignmentClasses, getCardPaddingClasses, getContentGapClasses, getTitleSizeClasses } from "./utils" export function CardIcon(props: CardIconProps) { const { title, icon, size = "md", href, hoverEffect = false, align = "center", className } = props const description = "description" in props ? props.description : undefined const Root = href ? "a" : "div" const isDescriptionCard = Boolean(description) - const isStartAligned = align === "start" + const isInlineAligned = align === "inline" const isCompactDescriptionCard = isDescriptionCard && size === "compact" - const contentAlignmentClass = isDescriptionCard - ? isStartAligned - ? "items-start justify-start text-left" - : "justify-between" - : isStartAligned - ? "items-start justify-center text-left" - : "items-center justify-center text-center" - const textAlignmentClass = isDescriptionCard - ? isStartAligned - ? "items-start gap-5 text-left" - : "gap-2 text-left" - : isStartAligned - ? "items-start text-left" - : "items-center text-center" + const { contentClass, textClass, iconWrapClass } = getAlignmentClasses(align, isDescriptionCard) return ( {hoverEffect && } -
-
- {isDescriptionCard ? ( - - ) : ( - - )} + {isInlineAligned ? ( +
+
+ +

+ {title} +

+
+ {description &&

{description}

}
- -
-

+
+ {isDescriptionCard ? ( + + ) : ( + )} - > - {title} -

- {description && ( -

+ +

+

- {description} -

- )} + {title} +

+ {description && ( +

+ {description} +

+ )} +
-
+ )} ) diff --git a/src/components/card-icon/inline-card-media.tsx b/src/components/card-icon/inline-card-media.tsx new file mode 100644 index 0000000..be18ca5 --- /dev/null +++ b/src/components/card-icon/inline-card-media.tsx @@ -0,0 +1,17 @@ +import { cn } from "@/lib/utils" +import type { GradientIconType } from "../gradient-icon" +import type { ResponsiveCardSize } from "./types" +import { getIconSizeClasses, getInlineContainerClasses } from "./utils" + +export function InlineCardMedia({ icon: Icon, size }: { icon: GradientIconType; size: ResponsiveCardSize }) { + return ( +
+ +
+ ) +} diff --git a/src/components/card-icon/types.ts b/src/components/card-icon/types.ts index 54a333f..1ac84a5 100644 --- a/src/components/card-icon/types.ts +++ b/src/components/card-icon/types.ts @@ -1,8 +1,8 @@ import type { GradientIconType } from "@/components/gradient-icon" -export type CardSize = "compact" | "xs" | "sm" | "md" | "lg" +export type CardSize = "compact" | "xs" | "sm" | "md" | "lg" | "inline" export type CardBreakpoint = "base" | "sm" | "md" | "lg" -export type CardAlign = "center" | "start" +export type CardAlign = "center" | "start" | "inline" export type SizeClassMap = Record diff --git a/src/components/card-icon/utils.ts b/src/components/card-icon/utils.ts index 156adac..25b75ba 100644 --- a/src/components/card-icon/utils.ts +++ b/src/components/card-icon/utils.ts @@ -3,10 +3,11 @@ import { CARD_PADDING_WITHOUT_DESCRIPTION, CONTENT_GAP_CLASSES, ICON_SIZE_CLASSES, + INLINE_CONTAINER_SIZE_CLASSES, TITLE_SIZE_CLASSES, } from "./classes" -import type { CardBreakpoint, ResponsiveCardSize, SizeClassMap } from "./types" +import type { CardAlign, CardBreakpoint, ResponsiveCardSize, SizeClassMap } from "./types" const BREAKPOINTS: CardBreakpoint[] = ["base", "sm", "md", "lg"] @@ -61,3 +62,27 @@ export function getContentGapClasses(size: ResponsiveCardSize) { export function getTitleSizeClasses(size: ResponsiveCardSize) { return resolveResponsiveClasses(size, TITLE_SIZE_CLASSES) } + +export function getInlineContainerClasses(size: ResponsiveCardSize) { + return resolveResponsiveClasses(size, INLINE_CONTAINER_SIZE_CLASSES) +} + +export function getAlignmentClasses(align: CardAlign, hasDescription: boolean) { + return { + center: { + contentClass: hasDescription ? "justify-between" : "items-center justify-center text-center", + textClass: hasDescription ? "gap-2 text-left" : "items-center text-center", + iconWrapClass: "justify-center", + }, + start: { + contentClass: hasDescription ? "items-start justify-start text-left" : "items-start justify-center text-left", + textClass: hasDescription ? "items-start gap-5 text-left" : "items-start text-left", + iconWrapClass: "justify-start", + }, + inline: { + contentClass: "", + textClass: "", + iconWrapClass: "", + }, + }[align] +} diff --git a/src/components/matricole/constants.ts b/src/components/matricole/constants.ts new file mode 100644 index 0000000..c26dfa4 --- /dev/null +++ b/src/components/matricole/constants.ts @@ -0,0 +1,169 @@ +import { + FiBarChart2, + FiBell, + FiBook, + FiCpu, + FiDollarSign, + FiFolder, + FiHome, + FiMap, + FiMonitor, + FiRadio, + FiRefreshCw, + FiRepeat, + FiShoppingCart, + FiUserCheck, + FiUsers, +} from "react-icons/fi" +import type { AccordionListItem } from "../accordion-list/types" + +export const resources = [ + { + title: "Graduatorie", + description: "Risultati storici e soglie di accesso per i vari corsi di laurea.", + icon: FiBarChart2, + href: "https://rankings.polinetwork.org/", + }, + { + title: "Progetto TOL", + description: "Informazioni sul test di ingresso (TOL) e su come prepararsi al meglio.", + icon: FiMonitor, + href: "https://tol.polinetwork.org/", + }, +] as const + +export const guides = [ + { + title: "Guida della matricola", + description: "Dalla prima iscrizione alla vita in campus: il manuale completo per ogni nuovo studente.", + icon: FiBook, + href: "/guides", + }, + { + title: "Mappa Microonde", + description: "Dove scaldare il pranzo nei vari campus.", + icon: FiMap, + }, + { + title: "Corsi a Scelta", + description: "Consigli su come comporre il Piano di Studi.", + icon: FiBook, + }, + { + title: "News", + description: "Le ultime novità dagli studenti del Politecnico.", + icon: FiRadio, + }, + { + title: "Borsa di Studio", + description: "Requisiti e scadenze per le agevolazioni.", + icon: FiDollarSign, + }, +] as const + +export const hub = { + title: "Telegram Hub", + description: + "Unisciti alla più grande rete di studenti del Poli su Telegram. Gruppi specifici per ogni esigenza, dai libri alla ricerca della casa.", + cards: [ + { + title: "Generale", + icon: FiBell, + href: "https://t.me/PoliGruppo", + }, + { + title: "Matricole", + icon: FiUserCheck, + href: "https://t.me/polimatricole", + }, + { + title: "Ripetizioni", + icon: FiUsers, + href: "https://t.me/joinchat/l10OF3vF6fEzOTRk", + }, + { + title: "PoliBook", + icon: FiBook, + href: "https://t.me/joinchat/6nmuXXhAOr1mYTJk", + }, + { + title: "DSU", + icon: FiDollarSign, + href: "https://t.me/joinchat/4kO9DtAiTVM0NTU0", + }, + { + title: "Mercatino", + icon: FiShoppingCart, + href: "https://t.me/mercatinopolimi", + }, + { + title: "Case", + icon: FiHome, + href: "https://t.me/joinchat/jmpNOitFNFw1MzJk", + }, + { + title: "Consigli PC", + icon: FiMonitor, + href: "https://t.me/joinchat/oD7NGpl0W6VjYmI0", + }, + ], +} + +export const materiali = { + title: "Materiali", + description: + "Il più grande archivio didattico creato dagli studenti per gli studenti del Politecnico di Milano. Migliaia di appunti e temi d’esame.", + icon: FiFolder, + href: "/materials", +} + +export const techTools = { + title: "Tech Tools", + description: "Una serie di Tools sviluppati dagli studenti del Politecnico di Milano.", + icon: FiCpu, + cards: [ + { + title: "Sync Orario", + icon: FiRepeat, + href: "https://bebora.github.io/polimi-schedule-js/", + }, + { + title: "WeBeep Sync", + icon: FiRefreshCw, + href: "https://github.com/toto04/webeep-sync", + }, + ], +} + +export const accordionItems: AccordionListItem[] = [ + { + value: "item-1", + trigger: "Le lezioni della prima settimana si tengono regolarmente?", + content: + "Spesso, la prima settimana, i Professori utilizzano le ore destinate alle esercitazioni per qualche ora di lezione in più: saranno quindi i docenti a specificare come verranno utilizzate queste ore. Dunque, le esercitazioni non sono da considerarsi annullate, salvo diversa comunicazione da parte del docente o dell'esercitatore.", + }, + { + value: "item-2", + trigger: "Come faccio ad accedere ai materiali del corso?", + content: + "I materiali dei corsi sono disponibili su Beep (il portale e-learning del Politecnico). Puoi accedervi con le tue credenziali di Ateneo (codice persona e password). Ogni docente carica le proprie slide, esercizi e risorse direttamente sulla pagina del corso.", + }, + { + value: "item-3", + trigger: "Come funziona la prenotazione agli esami?", + content: + "La prenotazione agli esami avviene tramite il portale Online Services (servizionline.polimi.it). Devi prenotarti entro la scadenza indicata per ogni appello. Ricordati di verificare i prerequisiti necessari prima di iscriverti a un esame.", + }, + { + value: "item-4", + trigger: "Cos'è il codice persona e dove lo trovo?", + content: + "Il codice persona è il tuo identificativo univoco al Politecnico, composto da 6 cifre. Lo trovi nella lettera di ammissione o nella mail di benvenuto ricevuta dopo l'immatricolazione. Serve per accedere a tutti i servizi online dell'Ateneo.", + }, + { + value: "item-5", + trigger: "Come posso contattare PoliNetwork?", + content: + "Puoi contattare PoliNetwork tramite i nostri canali Telegram o attraverso il sito ufficiale. Siamo un'associazione studentesca e offriamo supporto, risorse e una community per tutti gli studenti del Politecnico.", + }, +] diff --git a/src/components/matricole/faqs.tsx b/src/components/matricole/faqs.tsx index 56d5569..9643cab 100644 --- a/src/components/matricole/faqs.tsx +++ b/src/components/matricole/faqs.tsx @@ -1,43 +1,10 @@ "use client" import AccordionList from "@/components/accordion-list" -import type { AccordionListItem } from "@/components/accordion-list/types" - -const accordionItems: AccordionListItem[] = [ - { - value: "item-1", - trigger: "Le lezioni della prima settimana si tengono regolarmente?", - content: - "Spesso, la prima settimana, i Professori utilizzano le ore destinate alle esercitazioni per qualche ora di lezione in più: saranno quindi i docenti a specificare come verranno utilizzate queste ore. Dunque, le esercitazioni non sono da considerarsi annullate, salvo diversa comunicazione da parte del docente o dell'esercitatore.", - }, - { - value: "item-2", - trigger: "Come faccio ad accedere ai materiali del corso?", - content: - "I materiali dei corsi sono disponibili su Beep (il portale e-learning del Politecnico). Puoi accedervi con le tue credenziali di Ateneo (codice persona e password). Ogni docente carica le proprie slide, esercizi e risorse direttamente sulla pagina del corso.", - }, - { - value: "item-3", - trigger: "Come funziona la prenotazione agli esami?", - content: - "La prenotazione agli esami avviene tramite il portale Online Services (servizionline.polimi.it). Devi prenotarti entro la scadenza indicata per ogni appello. Ricordati di verificare i prerequisiti necessari prima di iscriverti a un esame.", - }, - { - value: "item-4", - trigger: "Cos'è il codice persona e dove lo trovo?", - content: - "Il codice persona è il tuo identificativo univoco al Politecnico, composto da 6 cifre. Lo trovi nella lettera di ammissione o nella mail di benvenuto ricevuta dopo l'immatricolazione. Serve per accedere a tutti i servizi online dell'Ateneo.", - }, - { - value: "item-5", - trigger: "Come posso contattare PoliNetwork?", - content: - "Puoi contattare PoliNetwork tramite i nostri canali Telegram o attraverso il sito ufficiale. Siamo un'associazione studentesca e offriamo supporto, risorse e una community per tutti gli studenti del Politecnico.", - }, -] +import { accordionItems } from "./constants" export function FAQsPage() { return ( -
+

Domande Frequenti tra le Matricole

diff --git a/src/components/matricole/guides.tsx b/src/components/matricole/guides.tsx new file mode 100644 index 0000000..d952b32 --- /dev/null +++ b/src/components/matricole/guides.tsx @@ -0,0 +1,61 @@ +import { CardIcon } from "@/components/card-icon" +import { CardGroup } from "../card-group" +import { guides, hub, materiali, techTools } from "./constants" + +export function MatricoleGuides() { + return ( +
+
+

Guide e Utility

+ +
+ {/* Hidden in mobile */} + +
+ {guides.slice(1).map((guide) => ( + + ))} +
+
+ + +
+ {hub.cards.map((card) => ( + + ))} +
+
+ +
+ {/* hidden in mobile */} + + +
+ {techTools.cards.map((card) => ( + + ))} +
+
+
+
+
+ ) +} diff --git a/src/components/matricole/intro.tsx b/src/components/matricole/intro.tsx index e8fa720..a0c7363 100644 --- a/src/components/matricole/intro.tsx +++ b/src/components/matricole/intro.tsx @@ -1,22 +1,9 @@ -import { FiBarChart2, FiMonitor } from "react-icons/fi" import { CardIcon } from "@/components/card-icon" - -const resources = [ - { - title: "Graduatorie", - description: "Risultati storici e soglie di accesso per i vari corsi di laurea.", - icon: FiBarChart2, - }, - { - title: "Progetto TOL", - description: "Informazioni sul test di ingresso (TOL) e su come prepararsi al meglio.", - icon: FiMonitor, - }, -] as const +import { resources } from "./constants" export function MatricoleIntro() { return ( -
+

@@ -27,7 +14,7 @@ export function MatricoleIntro() {

-
+
{resources.map((resource) => ( ))} From cfeb10fa2de43ce3a1de92c440c121675c505cd0 Mon Sep 17 00:00:00 2001 From: Bianca Roberta Ianosel Date: Tue, 2 Jun 2026 22:49:32 +0200 Subject: [PATCH 3/7] chore: add link and metadata to pages (#124) --- src/app/associations/page.tsx | 164 +----------------- src/app/projects/page.tsx | 5 + .../associations/associations-list.tsx | 84 +++++++++ src/components/card-caption.tsx | 4 + src/components/header/constants.ts | 24 +-- src/components/home/about-us.tsx | 9 +- src/components/home/hero.tsx | 8 +- src/components/home/materials.tsx | 9 +- src/components/home/projects.tsx | 13 +- 9 files changed, 140 insertions(+), 180 deletions(-) create mode 100644 src/components/associations/associations-list.tsx diff --git a/src/app/associations/page.tsx b/src/app/associations/page.tsx index df87e64..34d89d0 100644 --- a/src/app/associations/page.tsx +++ b/src/app/associations/page.tsx @@ -1,161 +1,11 @@ -"use client" - -import { - FiFacebook, - FiGithub, - FiGlobe, - FiInstagram, - FiLinkedin, - FiMail, - FiTwitch, - FiX, - FiYoutube, -} from "react-icons/fi" -import AccordionAssociation from "@/components/accordion-association" +import type { Metadata } from "next" +import { AssociationsList } from "@/components/associations/associations-list" import { Hero } from "@/components/ui/hero" -import esnLogo from "../../../public/logos/esn.svg" -const accordionItems = [ - { - value: "ESN", - name: "ESN", - logo: esnLogo, - content: - "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", - links: [ - { - key: "Facebook", - href: "https://www.google.com", - icon: FiFacebook, - }, - { - key: "Instagram", - href: "https://www.google.com", - icon: FiInstagram, - }, - { - key: "Youtube", - href: "https://www.google.com", - icon: FiYoutube, - }, - // { - // key: "Discord", - // href: "https://www.google.com", - // icon: FiDiscord - // }, - { - key: "GitHub", - href: "https://www.google.com", - icon: FiGithub, - }, - // { - // key: "Telegram", - // href: "https://www.google.com", - // icon: FiTelegram - // }, - { - key: "Email", - href: "mailto:example@email.com", - icon: FiMail, - }, - { - key: "LinkedIn", - href: "https://www.google.com", - icon: FiLinkedin, - }, - { - key: "Twitch", - href: "https://www.google.com", - icon: FiTwitch, - }, - // { - // key: "Spotify", - // href: "https://www.google.com", - // icon: FiSpotify - // }, - { - key: "X", - href: "https://www.google.com", - icon: FiX, - }, - { - key: "Web", - href: "https://www.google.com", - icon: FiGlobe, - }, - ], - }, - { - value: "Lista Aperta", - name: "Lista Aperta", - logo: esnLogo, - content: - "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", - links: [ - { - key: "Facebook", - href: "https://www.google.com", - icon: FiFacebook, - }, - ], - }, - { - value: "MESA", - name: "MESA", - logo: esnLogo, - content: - "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", - links: [ - { - key: "Facebook", - href: "https://www.google.com", - icon: FiFacebook, - }, - ], - }, - { - value: "Polifonia", - name: "Polifonia", - logo: esnLogo, - content: - "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", - links: [ - { - key: "Facebook", - href: "https://www.google.com", - icon: FiFacebook, - }, - ], - }, - { - value: "POLI.RADIO", - name: "POLI.RADIO", - logo: esnLogo, - content: - "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", - links: [ - { - key: "Facebook", - href: "https://www.google.com", - icon: FiFacebook, - }, - ], - }, - { - value: "Studenti Indipendenti", - name: "Studenti Indipendenti", - logo: esnLogo, - content: - "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", - links: [ - { - key: "Facebook", - href: "https://www.google.com", - icon: FiFacebook, - }, - ], - }, -] +export const metadata: Metadata = { + title: "Associazioni", + description: "Scopri le associazioni studentesche del Politecnico", +} export default function AssociationsPage() { return ( @@ -163,7 +13,7 @@ export default function AssociationsPage() {
- +
) diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index c0a666f..f97afe0 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -3,6 +3,11 @@ import { CommunityNews } from "@/components/projects/community-news" import { Deprecated } from "@/components/projects/deprecated" import { Upload } from "@/components/projects/upload" +export const metadata = { + title: "Progetti", + description: "Esplora e contribuisci ai progetti degli studenti", +} + export default function Home() { return (
diff --git a/src/components/associations/associations-list.tsx b/src/components/associations/associations-list.tsx new file mode 100644 index 0000000..e0f51ae --- /dev/null +++ b/src/components/associations/associations-list.tsx @@ -0,0 +1,84 @@ +"use client" + +import { + FiFacebook, + FiGithub, + FiGlobe, + FiInstagram, + FiLinkedin, + FiMail, + FiTwitch, + FiX, + FiYoutube, +} from "react-icons/fi" +import AccordionAssociation from "@/components/accordion-association" +import esnLogo from "../../../public/logos/esn.svg" + +const accordionItems = [ + { + value: "ESN", + name: "ESN", + logo: esnLogo, + content: + "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", + links: [ + { key: "Facebook", href: "https://www.google.com", icon: FiFacebook }, + { key: "Instagram", href: "https://www.google.com", icon: FiInstagram }, + { key: "Youtube", href: "https://www.google.com", icon: FiYoutube }, + { key: "GitHub", href: "https://www.google.com", icon: FiGithub }, + { key: "Email", href: "mailto:example@email.com", icon: FiMail }, + { key: "LinkedIn", href: "https://www.google.com", icon: FiLinkedin }, + { key: "Twitch", href: "https://www.google.com", icon: FiTwitch }, + { key: "X", href: "https://www.google.com", icon: FiX }, + { key: "Web", href: "https://www.google.com", icon: FiGlobe }, + ], + }, + { + value: "Lista Aperta", + name: "Lista Aperta", + logo: esnLogo, + content: + "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", + links: [{ key: "Facebook", href: "https://www.google.com", icon: FiFacebook }], + }, + { + value: "MESA", + name: "MESA", + logo: esnLogo, + content: + "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", + links: [{ key: "Facebook", href: "https://www.google.com", icon: FiFacebook }], + }, + { + value: "Polifonia", + name: "Polifonia", + logo: esnLogo, + content: + "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", + links: [{ key: "Facebook", href: "https://www.google.com", icon: FiFacebook }], + }, + { + value: "POLI.RADIO", + name: "POLI.RADIO", + logo: esnLogo, + content: + "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", + links: [{ key: "Facebook", href: "https://www.google.com", icon: FiFacebook }], + }, + { + value: "Studenti Indipendenti", + name: "Studenti Indipendenti", + logo: esnLogo, + content: + "Lorem ipsum dolor sit amet consectetur. Velit integer diam in id proin blandit fames id. Volutpat at vel risus non amet tortor. Potenti sit gravida donec lacinia et posuere faucibus. Elementum libero diam nullam ultricies mauris mauris erat porttitor. At morbi commodo nunc vulputate id odio pellentesque ipsum. Adipiscing at dictumst pulvinar mattis faucibus quisque donec convallis commodo. ", + links: [{ key: "Facebook", href: "https://www.google.com", icon: FiFacebook }], + }, +] + +export function AssociationsList() { + return ( +
+ +
+ ) +} diff --git a/src/components/card-caption.tsx b/src/components/card-caption.tsx index 60be7a1..0f04b10 100644 --- a/src/components/card-caption.tsx +++ b/src/components/card-caption.tsx @@ -1,3 +1,4 @@ +import Link from "next/link" import type { ReactNode } from "react" import type { IconType } from "react-icons" import { Card, CardAction, CardContent, CardHeader, CardTitle } from "./ui/card" @@ -7,16 +8,19 @@ export function CardCaption({ caption, icon, iconPosition = "right", + href, className, }: { title: string caption: ReactNode icon?: IconType iconPosition?: "top" | "right" + href?: string className?: string }) { return ( + {href && } diff --git a/src/components/header/constants.ts b/src/components/header/constants.ts index 38a1e4f..a785ca5 100644 --- a/src/components/header/constants.ts +++ b/src/components/header/constants.ts @@ -5,29 +5,29 @@ export const headerMenuItems: HeaderMenuItem[] = [ { title: "Resources", menu: [ - { title: "Materials", href: "#", icon: FiChevronRight }, - { title: "Guides", href: "#", icon: FiChevronRight }, - { title: "Computer Recs", href: "#", icon: FiChevronRight }, - { title: "FAQs", href: "#", icon: FiChevronRight }, - { title: "Rankings", href: "#", icon: FiArrowUpRight }, - { title: "Tol Project", href: "#", icon: FiArrowUpRight }, + { title: "Materials", href: "/materials", icon: FiChevronRight }, + { title: "Guides", href: "/guides", icon: FiChevronRight }, + { title: "Computer Recs", href: "/computer-recs", icon: FiChevronRight }, + { title: "FAQs", href: "/faqs", icon: FiChevronRight }, + { title: "Rankings", href: "https://rankings.polinetwork.org/", icon: FiArrowUpRight }, + { title: "Tol Project", href: "https://tol.polinetwork.org/", icon: FiArrowUpRight }, ], }, { title: "Community", menu: [ - { title: "Groups", href: "#", icon: FiChevronRight }, - { title: "Projects", href: "#", icon: FiChevronRight }, + { title: "Groups", href: "/groups", icon: FiChevronRight }, + { title: "Projects", href: "/projects", icon: FiChevronRight }, { title: "Freshman", href: "/matricole", icon: FiChevronRight }, - { title: "Associations", href: "#", icon: FiChevronRight }, + { title: "Associations", href: "/associations", icon: FiChevronRight }, ], }, { title: "About", menu: [ - { title: "About us", href: "#", icon: FiChevronRight }, - { title: "Join us", href: "#", icon: FiChevronRight }, - { title: "Contact us", href: "#", icon: FiChevronRight }, + { title: "About us", href: "/about", icon: FiChevronRight }, + { title: "Join us", href: "/join", icon: FiChevronRight }, + { title: "Contact us", href: "/contact", icon: FiChevronRight }, ], }, ] diff --git a/src/components/home/about-us.tsx b/src/components/home/about-us.tsx index 8d9ab54..f879f91 100644 --- a/src/components/home/about-us.tsx +++ b/src/components/home/about-us.tsx @@ -1,3 +1,5 @@ +import Link from "next/link" +import { FiArrowUpRight } from "react-icons/fi" import { Button } from "@/components/ui/button" export function AboutUs() { @@ -7,8 +9,11 @@ export function AboutUs() { PoliNetwork unisce gli studenti del Poli: gruppi, risorse e supporto, tutti in un'unica community. - ) diff --git a/src/components/home/hero.tsx b/src/components/home/hero.tsx index 02d7444..35518c1 100644 --- a/src/components/home/hero.tsx +++ b/src/components/home/hero.tsx @@ -20,9 +20,11 @@ export function Hero() { className="typo-body-medium" /> - diff --git a/src/components/home/materials.tsx b/src/components/home/materials.tsx index 45a9f5e..ca54b18 100644 --- a/src/components/home/materials.tsx +++ b/src/components/home/materials.tsx @@ -1,3 +1,4 @@ +import Link from "next/link" import { FiArrowUpRight, FiBook, FiBookOpen, FiClipboard, FiFileText, FiUploadCloud } from "react-icons/fi" import { CardIcon } from "@/components/card-icon" import { Button } from "@/components/ui/button" @@ -58,9 +59,11 @@ export function Materials() { Il più grande archivio didattico degli studenti del Politecnico. Trova appunti, dispense ed esami, oppure carica i tuoi file per far crescere la community.

- diff --git a/src/components/home/projects.tsx b/src/components/home/projects.tsx index 210d37b..b66fafc 100644 --- a/src/components/home/projects.tsx +++ b/src/components/home/projects.tsx @@ -1,3 +1,4 @@ +import Link from "next/link" import { FiArrowUpRight } from "react-icons/fi" import { CardCaption } from "@/components/card-caption" import { Button } from "@/components/ui/button" @@ -8,19 +9,23 @@ const featuredCards = [ title: "WeBeepSync", caption: "WeBeep Sync è una semplice app, user-friendly e senza compromessi che serve per tenere sincronizzati tutti i tuoi file di WeBeep.", + href: "https://github.com/toto04/webeep-sync", }, { title: "PolimiSchedule", caption: "Genera un file iCalendar (.ics) a partire dal formato testuale dell’Orario delle lezioni. Possibilità di importare su Google Calendar.", + href: "https://github.com/jacopo-j/polimi-schedule", }, { title: "WiFiLinux", caption: "Scarica ed esegui lo script Python per attivare la connessione permanente al WiFi Polimi", + href: "https://github.com/PoliNetworkOrg/WiFiLinux", }, { title: "The TOL Project", caption: "Un simulatore gratuito del test di ammissione per le aspiranti matricole di Ingegneria del PoliMi.", + href: "https://tol.polinetwork.org/", }, ] as const @@ -40,9 +45,11 @@ export function Projects() { Progetti e strumenti nati dalla community del Politecnico per semplificare la vita universitaria e promuovere l’innovazione. Hai un’idea? Proponila, trova collaboratori e realizza la tua soluzione.

- From b66b55b8a02d416ee8ff6207b2723790f6ca6a46 Mon Sep 17 00:00:00 2001 From: Bianca Roberta Ianosel Date: Wed, 3 Jun 2026 01:24:49 +0200 Subject: [PATCH 4/7] feat: improve mobile layout for materials (#121) --- src/components/home/materials.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/home/materials.tsx b/src/components/home/materials.tsx index ca54b18..1dec069 100644 --- a/src/components/home/materials.tsx +++ b/src/components/home/materials.tsx @@ -2,6 +2,7 @@ import Link from "next/link" import { FiArrowUpRight, FiBook, FiBookOpen, FiClipboard, FiFileText, FiUploadCloud } from "react-icons/fi" import { CardIcon } from "@/components/card-icon" import { Button } from "@/components/ui/button" +import { CardCaption } from "../card-caption" const featuredCards = [ { @@ -31,13 +32,19 @@ const quickLinks = [ export function Materials() { return (
-
-
- {/* TODO sotto sm usare le altre card fatte da Diubi */} +
+ {/* Desktop Cards */} +
{featuredCards.map((card) => ( ))}
+ {/* Mobile Cards */} +
+ {featuredCards.map((card) => ( + + ))} +
{quickLinks.map((card) => ( From 146617ae7e0da0b03f1cf45146c0ce8ca3cdc7ed Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo <66379281+lorenzocorallo@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:54:56 +0200 Subject: [PATCH 5/7] feat: connect backend via trpc, homepage group search (#123) * feat: trpc backend, group search in homepage hero * style: fixed height for notfound, text-left * chore: biome --- .env.example | 1 + package.json | 6 ++ pnpm-lock.yaml | 139 +++++++++++++++++++++++++++ src/assets/icons/telegram-fill.svg | 1 + src/components/home/group-search.tsx | 78 +++++++++++++++ src/components/home/hero.tsx | 15 +-- src/components/spinner.tsx | 26 +++++ src/env.js | 2 + src/lib/backend.ts | 16 +++ src/queries/groups.ts | 8 ++ src/types.ts | 6 ++ 11 files changed, 287 insertions(+), 11 deletions(-) create mode 100644 src/assets/icons/telegram-fill.svg create mode 100644 src/components/home/group-search.tsx create mode 100644 src/components/spinner.tsx create mode 100644 src/lib/backend.ts create mode 100644 src/queries/groups.ts create mode 100644 src/types.ts diff --git a/.env.example b/.env.example index eceaa2d..10a4ba3 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,4 @@ # When adding additional environment variables, the schema in "/src/env.js" # should be updated accordingly. +BACKEND_URL=http://localhost:3000 diff --git a/package.json b/package.json index 56af356..dfc40cd 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,17 @@ "check:fix": "biome check --write --unsafe" }, "dependencies": { + "@polinetwork/backend": "^0.15.18", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@t3-oss/env-nextjs": "^0.13.10", + "@tanstack/react-pacer": "^0.22.1", "@tanstack/react-table": "^8.21.3", + "@trpc/client": "11.5.1", + "@trpc/next": "11.5.1", "babel-plugin-react-compiler": "^1.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -34,6 +38,7 @@ "react-dom": "^19.2.3", "react-hook-form": "^7.68.0", "react-icons": "^5.6.0", + "superjson": "^2.2.6", "tailwind-merge": "^3.4.0", "tailwindcss-animate": "^1.0.7", "zod": "^4.2.1" @@ -41,6 +46,7 @@ "devDependencies": { "@biomejs/biome": "2.2.5", "@tailwindcss/postcss": "^4.1.18", + "@trpc/server": "11.5.1", "@types/node": "^24.10.4", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69236ac..278f936 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@polinetwork/backend': + specifier: ^0.15.18 + version: 0.15.18 '@radix-ui/react-accordion': specifier: ^1.2.12 version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -26,9 +29,18 @@ importers: '@t3-oss/env-nextjs': specifier: ^0.13.10 version: 0.13.10(typescript@5.9.3)(zod@4.2.1) + '@tanstack/react-pacer': + specifier: ^0.22.1 + version: 0.22.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@trpc/client': + specifier: 11.5.1 + version: 11.5.1(@trpc/server@11.5.1(typescript@5.9.3))(typescript@5.9.3) + '@trpc/next': + specifier: 11.5.1 + version: 11.5.1(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.5.1(typescript@5.9.3))(next@15.5.18(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 @@ -68,6 +80,9 @@ importers: react-icons: specifier: ^5.6.0 version: 5.6.0(react@19.2.3) + superjson: + specifier: ^2.2.6 + version: 2.2.6 tailwind-merge: specifier: ^3.4.0 version: 3.4.0 @@ -84,6 +99,9 @@ importers: '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.1.18 + '@trpc/server': + specifier: 11.5.1 + version: 11.5.1(typescript@5.9.3) '@types/node': specifier: ^24.10.4 version: 24.10.4 @@ -429,6 +447,9 @@ packages: cpu: [x64] os: [win32] + '@polinetwork/backend@0.15.18': + resolution: {integrity: sha512-gT4TRMWIFNhwQi2mOx1OiH9HTP9BoorZHSvTti8vTCpk0qgKlL39ntAHQvFtDq1RbJ7rvS+DRd8fRBZkLVtOOw==} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -1283,6 +1304,28 @@ packages: '@tailwindcss/postcss@4.1.18': resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==} + '@tanstack/devtools-event-client@0.4.3': + resolution: {integrity: sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw==} + engines: {node: '>=18'} + hasBin: true + + '@tanstack/pacer@0.21.1': + resolution: {integrity: sha512-hB01dd4rlsYcTCNP7wK186jgAe6K5qimgM1Y5Jtvz+9PUaILvpmeLLjmQNUNSO1l23lIt+CeQR6mO1mjlPvRtQ==} + engines: {node: '>=18'} + + '@tanstack/react-pacer@0.22.1': + resolution: {integrity: sha512-CenQqK0GluSPIrnsG1yuD7w5uMSQ/4lI9AcGEFxBrRd66r260boWcYRIsS5+eHtXb238FoZYhKmJPGlhRzmHRw==} + engines: {node: '>=18'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/react-store@0.11.0': + resolution: {integrity: sha512-tX4YXh3PDkmpvGQWkWqKpzs/MSqbtuwY9dWdWhtV9Q50PmO+jOkUKIWIX4G85dwt7lxdHLXsiaEKPdKmC8F41w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tanstack/react-table@8.21.3': resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} engines: {node: '>=12'} @@ -1290,10 +1333,41 @@ packages: react: '>=16.8' react-dom: '>=16.8' + '@tanstack/store@0.11.0': + resolution: {integrity: sha512-WlzzCt3xi0G6pCAJu1U+2jiECwabETDpQDi3hfkFZvJii9AuZqEKbOiVarX1/bWhTNjU486yQtJCCasi/0q+Cw==} + '@tanstack/table-core@8.21.3': resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} + '@trpc/client@11.5.1': + resolution: {integrity: sha512-7I6JJ1I1lxv3S87ht3FAIZi0XxQa7hnQ9K+Oo5BH7cGO8ZtWe9Ftq6ItdkuDfpsnsRPcR2h158AMWbNs/iptqg==} + peerDependencies: + '@trpc/server': 11.5.1 + typescript: '>=5.7.2' + + '@trpc/next@11.5.1': + resolution: {integrity: sha512-MWb7Jqa0XYx0z2he7Eq7HSILc2z9oDqUtsbWz+S6LWCemewRSRzmG8UuoiZm+ICEoRa452RmqeQH1EYiPCWTUg==} + peerDependencies: + '@tanstack/react-query': ^5.59.15 + '@trpc/client': 11.5.1 + '@trpc/react-query': 11.5.1 + '@trpc/server': 11.5.1 + next: '*' + react: '>=16.8.0' + react-dom: '>=16.8.0' + typescript: '>=5.7.2' + peerDependenciesMeta: + '@tanstack/react-query': + optional: true + '@trpc/react-query': + optional: true + + '@trpc/server@11.5.1': + resolution: {integrity: sha512-KIDzHRS5m8U1ncPwjgtOtPWK9lNO0kYL7b+lnvKXRqowSAQIEC/z6y7g/dkt4Aqv3DKI/STLydt2/afrP1QrxQ==} + peerDependencies: + typescript: '>=5.7.2' + '@types/node@24.10.4': resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} @@ -1331,6 +1405,10 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + cross-env@10.1.0: resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} engines: {node: '>=20'} @@ -1379,6 +1457,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1610,6 +1692,10 @@ packages: babel-plugin-macros: optional: true + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + engines: {node: '>=16'} + tailwind-merge@3.4.0: resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} @@ -1886,6 +1972,8 @@ snapshots: '@next/swc-win32-x64-msvc@15.5.18': optional: true + '@polinetwork/backend@0.15.18': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -2743,14 +2831,55 @@ snapshots: postcss: 8.5.10 tailwindcss: 4.1.18 + '@tanstack/devtools-event-client@0.4.3': {} + + '@tanstack/pacer@0.21.1': + dependencies: + '@tanstack/devtools-event-client': 0.4.3 + '@tanstack/store': 0.11.0 + + '@tanstack/react-pacer@0.22.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/pacer': 0.21.1 + '@tanstack/react-store': 0.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@tanstack/react-store@0.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/store': 0.11.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + '@tanstack/react-table@8.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@tanstack/table-core': 8.21.3 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) + '@tanstack/store@0.11.0': {} + '@tanstack/table-core@8.21.3': {} + '@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@trpc/server': 11.5.1(typescript@5.9.3) + typescript: 5.9.3 + + '@trpc/next@11.5.1(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.5.1(typescript@5.9.3))(next@15.5.18(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + dependencies: + '@trpc/client': 11.5.1(@trpc/server@11.5.1(typescript@5.9.3))(typescript@5.9.3) + '@trpc/server': 11.5.1(typescript@5.9.3) + next: 15.5.18(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + typescript: 5.9.3 + + '@trpc/server@11.5.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + '@types/node@24.10.4': dependencies: undici-types: 7.16.0 @@ -2793,6 +2922,10 @@ snapshots: - '@types/react' - '@types/react-dom' + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + cross-env@10.1.0: dependencies: '@epic-web/invariant': 1.0.0 @@ -2835,6 +2968,8 @@ snapshots: graceful-fs@4.2.11: {} + is-what@5.5.0: {} + isexe@2.0.0: {} jiti@2.6.1: {} @@ -3094,6 +3229,10 @@ snapshots: client-only: 0.0.1 react: 19.2.3 + superjson@2.2.6: + dependencies: + copy-anything: 4.0.5 + tailwind-merge@3.4.0: {} tailwindcss-animate@1.0.7(tailwindcss@4.1.18): diff --git a/src/assets/icons/telegram-fill.svg b/src/assets/icons/telegram-fill.svg new file mode 100644 index 0000000..39f0e0d --- /dev/null +++ b/src/assets/icons/telegram-fill.svg @@ -0,0 +1 @@ + diff --git a/src/components/home/group-search.tsx b/src/components/home/group-search.tsx new file mode 100644 index 0000000..53d3f41 --- /dev/null +++ b/src/components/home/group-search.tsx @@ -0,0 +1,78 @@ +"use client" +import { useAsyncDebouncer } from "@tanstack/react-pacer" +import Image from "next/image" +import Link from "next/link" +import { useEffect, useState } from "react" +import { FiSearch } from "react-icons/fi" +import telegram from "@/assets/icons/telegram-fill.svg" +import { Input } from "@/components/ui/input" +import { cn } from "@/lib/utils" +import { searchGroups } from "@/queries/groups" +import type { ApiOutput } from "@/types" +import { Glass } from "../glass" +import { Spinner } from "../spinner" + +export function GroupSearch() { + const [query, setQuery] = useState("") + const [results, setResults] = useState(null) + + const debouncedSearch = useAsyncDebouncer( + async (searchTerm: string) => { + const res = await searchGroups(searchTerm, 20) + setResults(res) + }, + { + wait: 500, + }, + (state) => ({ isLoading: state.isPending || state.isExecuting }) + ) + + useEffect(() => { + setResults(null) + if (query) debouncedSearch.maybeExecute(query) + }, [query, debouncedSearch.maybeExecute]) + + return ( +
+ } + type="text" + placeholder="Find your group" + aria-label="Find your group" + containerClassName="w-full" + className="typo-body-medium" + value={query} + onChange={(e) => { + setQuery(e.target.value) + }} + /> + + {query && ( + +
+ {results && results.count > 0 ? ( + results?.groups + .filter((g): g is typeof g & { link: string } => !!g.link) + .map((g) => ( + +
+ Telegram + {g.title} +
+ + )) + ) : ( +
+ {debouncedSearch.state.isLoading ? ( + + ) : ( + "No group found" + )} +
+ )} +
+
+ )} +
+ ) +} diff --git a/src/components/home/hero.tsx b/src/components/home/hero.tsx index 35518c1..ff1152f 100644 --- a/src/components/home/hero.tsx +++ b/src/components/home/hero.tsx @@ -1,7 +1,7 @@ import Link from "next/link" -import { FiNavigation, FiSearch, FiUserPlus } from "react-icons/fi" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" +import { FiNavigation, FiUserPlus } from "react-icons/fi" +import { Button } from "../ui/button" +import { GroupSearch } from "./group-search" export function Hero() { return ( @@ -11,14 +11,7 @@ export function Hero() { Trova gruppi, risorse e supporto tra gli studenti del Polimi - } - type="text" - placeholder="Find your group" - aria-label="Find your group" - containerClassName="max-w-lg" - className="typo-body-medium" - /> +