Skip to content
Open

Card #41

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a7f088f
Card initial commit
Diubii Mar 11, 2026
cb61f09
Text and icon gradients
Diubii Mar 11, 2026
2e3f23d
Used figma classes
Diubii Mar 11, 2026
d4975c0
Moved card.tsx to components/ui/cards
Diubii Mar 17, 2026
4b4056a
refactor: move card.tsx into ui
Diubii Apr 2, 2026
f34b630
Card initial commit
Diubii Mar 11, 2026
260de21
Text and icon gradients
Diubii Mar 11, 2026
714bf5a
Used figma classes
Diubii Mar 11, 2026
9d80222
Moved card.tsx to components/ui/cards
Diubii Mar 17, 2026
74b8885
refactor: move card.tsx into ui
Diubii Apr 2, 2026
fb70a85
Merge branch 'Diubii/card' of github.com:PoliNetworkOrg/web into Diub…
Diubii Apr 2, 2026
95497ac
chore: biome; fix: imports
Diubii Apr 2, 2026
8d16fc7
aligned card.tsx between all cards
Diubii Apr 9, 2026
e40ed5c
Merge branch 'main' into Diubii/card
Diubii Apr 9, 2026
6a72e27
feat: optional gradient
Diubii Apr 9, 2026
6c2c5cd
fix: text transparency to see gradient
Diubii Apr 9, 2026
cef8d1d
feat: added bg-background-blur to all cards
Diubii Apr 9, 2026
f4d4bcf
feat: add card-caption (#48)
Diubii Apr 9, 2026
7124c21
feat: add card-path-selection (#56)
Diubii Apr 9, 2026
97a3005
feat: add card-course (#63)
Diubii Apr 9, 2026
c7bd2bd
feat: add card-course-group (#67)
Diubii Apr 9, 2026
cd4e805
Merge branch 'main' into Diubii/card
Diubii Apr 9, 2026
1fbb836
fix: some of CodeRabbit's proposed fixes
Diubii Apr 9, 2026
155b62d
Merge branch 'main' into Diubii/card
toto04 Apr 9, 2026
75a1458
Merge branch 'main' into Diubii/card
lorenzocorallo Apr 9, 2026
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
28 changes: 28 additions & 0 deletions src/components/card-caption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { IconType } from "react-icons"
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "./ui/card"

export function CardCaption({
title,
caption,
icon,
iconPosition = "right",
}: {
title: string
caption: string
icon?: IconType
iconPosition?: "top" | "right"
}) {
return (
<Card>
<CardHeader
className={`typo-headline-medium flex ${iconPosition === "right" ? "justify-between" : "flex-col-reverse"}`}
>
<CardTitle>{title}</CardTitle>
{icon && <CardAction icon={icon} iconSize={iconPosition === "right" ? "normal" : "large"}></CardAction>}
</CardHeader>
<CardContent className="typo-body-medium">
<p>{caption}</p>
</CardContent>
</Card>
)
}
47 changes: 47 additions & 0 deletions src/components/card-course-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { cva, type VariantProps } from "class-variance-authority"
import type { IconType } from "react-icons"
import { FaWhatsapp } from "react-icons/fa"
import { LiaTelegramPlane } from "react-icons/lia"
import { cn } from "@/lib/utils"
import { Card, CardAction, CardTitle } from "./ui/card"

export const cardCourseGroupVariants = cva(
"flex h-fit w-full flex-row items-center gap-5 px-7.5 py-6.25 font-normal leading-6 tracking-[0.03125rem]",
{
variants: {
secondary: {
true: "bg-[rgba(148,192,237,0.40)]",
false: "",
},
},
defaultVariants: {
secondary: false,
},
}
)

export function CardCourseGroup({
groupName,
hasWhatsapp = true,
iconWhatsApp: IconWhatsApp = FaWhatsapp,
hasTelegram = true,
iconTelegram: IconTelegram = LiaTelegramPlane,
secondary = false,
}: {
groupName: string
hasWhatsapp?: boolean
iconWhatsApp?: IconType
hasTelegram?: boolean
iconTelegram?: IconType
} & VariantProps<typeof cardCourseGroupVariants>) {
const actionClassName = cn("rounded-full p-3.75", secondary ? "bg-[#51A2FF]" : "bg-[#74D4FF]")
return (
<Card className={cn(cardCourseGroupVariants({ secondary }))}>
<CardTitle gradient={false} className="typo-headline-small grow">
{groupName}
</CardTitle>
{hasWhatsapp && <CardAction gradient={false} className={actionClassName} icon={IconWhatsApp} iconSize="normal" />}
{hasTelegram && <CardAction gradient={false} className={actionClassName} icon={IconTelegram} iconSize="normal" />}
</Card>
)
}
36 changes: 36 additions & 0 deletions src/components/card-course.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { IconType } from "react-icons"
import { CiGlobe } from "react-icons/ci"
import { FaAngleRight } from "react-icons/fa6"
import { IoLocationOutline } from "react-icons/io5"
import { Card, CardAction, CardContent } from "./ui/card"

export function CardCourse({
courseName,
iconLocation: IconLocation = IoLocationOutline,
location = "Milano Leonardo",
iconLanguage: IconLanguage = CiGlobe,
language = "ITA",
iconSelect: IconSelect = FaAngleRight,
}: {
courseName: string
iconLocation?: IconType
location?: "Milano Leonardo" | "Milano Bovisa" | "Cremona" | "Lecco" | "Mantova" | "Piacenza" | "Xi'an" //Magari poi si mette solo string come tipo nel caso si pullino da un DB
iconLanguage?: IconType
language?: "ITA" | "ENG" //Idem
iconSelect?: IconType
}) {
return (
<Card className="typo-body-large flex h-fit w-full flex-row px-5 py-3.75 font-normal leading-6 tracking-[0.03125rem]">
<CardContent className="basis-1/3 truncate">{courseName}</CardContent>
<CardContent className="flex basis-1/3 items-center gap-1.25">
<CardAction icon={IconLocation} iconSize="small" /> {location}
</CardContent>
<CardContent className="flex basis-1/3 items-center gap-1.25">
<CardAction icon={IconLanguage} iconSize="small" /> {language}
</CardContent>
<CardContent className="flex items-center">
<CardAction icon={IconSelect} iconSize="small" />
</CardContent>
</Card>
)
}
14 changes: 14 additions & 0 deletions src/components/card-path-selection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { IconType } from "react-icons"
import { BsBook } from "react-icons/bs"
import { Card, CardAction, CardContent } from "./ui/card"

export function CardPathSelection({ caption, icon: Icon = BsBook }: { caption: string; icon?: IconType }) {
return (
<Card className="h-fit w-136 px-10 py-7.5">
<CardContent className="typo-body-large flex flex-row items-center justify-center gap-1.25 whitespace-nowrap font-normal leading-6 tracking-[0.03125rem]">
<CardAction icon={Icon} iconSize="small" /> {caption}
</CardContent>
</Card>
//TODO: add hover effect
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ora c'è! Aspetto che tu faccia la magia che vuoi fare per approvare

)
}
103 changes: 103 additions & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import * as React from "react"
import type { IconType } from "react-icons"
import { cn } from "@/lib/utils"
import { Glass } from "../glass"
import { Button } from "./button"

function Card({
className,
size = "default",
...props
}: React.ComponentProps<typeof Glass> & { size?: "default" | "sm" }) {
return (
<Glass
data-slot="card"
data-size={size}
className={cn(
"group/card flex h-66 w-78 flex-col gap-4 overflow-hidden rounded-[1.25rem] bg-background-blur text-card-foreground text-sm ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className
)}
{...props}
/>
)
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
className
)}
{...props}
/>
)
}

function CardTitle({ gradient = true, className, ...props }: React.ComponentProps<"div"> & { gradient?: boolean }) {
return (
<div
data-slot="card-title"
className={cn(
`${gradient ? "bg-linear-to-b from-blue-secondary to-blue-primary bg-clip-text text-transparent" : ""} font-medium text-[1.5rem] leading-snug group-data-[size=sm]/card:text-base`,
className
)}
{...props}
/>
)
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return <div data-slot="card-description" className={cn("text-muted-foreground text-sm", className)} {...props} />
}

function CardAction({
className,
icon: Icon,
iconSize = "normal",
gradient = true,
...props
}: React.ComponentProps<"div"> & { icon: IconType; iconSize?: "small" | "normal" | "large"; gradient?: boolean }) {
const gradientId = React.useId()

return (
<div
data-slot="card-action"
className={cn("col-start-2 row-span-2 row-start-1 self-auto justify-self-end", className)}
{...props}
>
{gradient && (
<svg width="0" height="0" className="absolute" aria-hidden="true" focusable="false">
<title>Icon gradient helper</title>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quello che ha detto il coniglio

Suggested change
<title>Icon gradient helper</title>

<linearGradient id={gradientId} x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" className="text-blue-secondary" stopColor="currentColor" />
<stop offset="100%" className="text-blue-primary" stopColor="currentColor" />
</linearGradient>
</svg>
)}

<Icon
size={iconSize === "small" ? "1.125rem" : iconSize === "normal" ? "2rem" : "3.5rem"}
fill={gradient ? `url(#${gradientId})` : "currentColor"}
stroke={gradient ? `url(#${gradientId})` : "currentColor"}
/>
</div>
)
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("text-[.875rem] group-data-[size=sm]/card:px-3", className)}
{...props}
/>
)
}

function CardBottomButton({ className, ...props }: React.ComponentProps<typeof Button>) {
return <Button data-slot="card-footer" className={cn("self-end", className)} {...props} />
}

export { Card, CardHeader, CardBottomButton, CardTitle, CardAction, CardDescription, CardContent }
Loading