diff --git a/apps/web/src/app/arrangementer/components/AttendanceCard/AttendeeList/AttendeePlate.tsx b/apps/web/src/app/arrangementer/components/AttendanceCard/AttendeeList/AttendeePlate.tsx index 6ea12e9c8f..9ec4698905 100644 --- a/apps/web/src/app/arrangementer/components/AttendanceCard/AttendeeList/AttendeePlate.tsx +++ b/apps/web/src/app/arrangementer/components/AttendanceCard/AttendeeList/AttendeePlate.tsx @@ -1,7 +1,12 @@ -import { type Attendee, isVanityVerified, type User } from "@dotkomonline/types" +import { type Attendee, isVanityVerified, type User, isExceptionallyDistinguished, FlagName } from "@dotkomonline/types" import type { FC, JSX, ReactNode } from "react" import { GenericPlate } from "./GenericPlate" import { getVanityVerifiedSmallIcon, VanityVerifiedPlate } from "./VanityVerifiedPlate" +import { + ExceptionallyDistinguishedPlate, + getExceptionallyDistinguishedLargeIcon, + getExceptionallyDistinguishedSmallIcon, +} from "./ExceptionallyDistinguished" export interface PlateProps { attendee: Attendee @@ -13,6 +18,10 @@ export interface PlateProps { } export function getAttendeePlate(attendee: Attendee): FC { + if (isExceptionallyDistinguished(attendee.user)) { + return ExceptionallyDistinguishedPlate + } + if (isVanityVerified(attendee.user)) { return VanityVerifiedPlate } @@ -22,7 +31,17 @@ export function getAttendeePlate(attendee: Attendee): FC { export function getAttendeeIcons(attendee: Attendee) { const smallIcons: JSX.Element[] = [] - const largeIcon: JSX.Element | null = null + let largeIcon: JSX.Element | null = null + + if (isExceptionallyDistinguished(attendee.user)) { + const flags = attendee.user.flags.filter(({ name }) => name === FlagName.EXCEPTIONALLY_DISTINGUISHED) + + if (largeIcon === null) { + largeIcon = getExceptionallyDistinguishedLargeIcon(flags) + } else { + smallIcons.push(getExceptionallyDistinguishedSmallIcon(flags)) + } + } if (isVanityVerified(attendee.user)) { smallIcons.push(getVanityVerifiedSmallIcon()) diff --git a/apps/web/src/app/arrangementer/components/AttendanceCard/AttendeeList/ExceptionallyDistinguished.tsx b/apps/web/src/app/arrangementer/components/AttendanceCard/AttendeeList/ExceptionallyDistinguished.tsx new file mode 100644 index 0000000000..67c5c76505 --- /dev/null +++ b/apps/web/src/app/arrangementer/components/AttendanceCard/AttendeeList/ExceptionallyDistinguished.tsx @@ -0,0 +1,225 @@ +import { FlagName, getFlagLabel, type Attendee, type UserFlag } from "@dotkomonline/types" +import { + PopoverClose, + Avatar, + AvatarFallback, + AvatarImage, + cn, + Popover, + PopoverContent, + PopoverTrigger, + Text, + Tilt, + Title, + Tooltip, + TooltipTrigger, + TooltipContent, +} from "@dotkomonline/ui" +import { IconUser, IconX } from "@tabler/icons-react" +import Link from "next/link.js" +import type { PropsWithChildren } from "react" +import { GenericPlateUserSection } from "./GenericPlate" +import Image from "next/image" +import type { PlateProps } from "./AttendeePlate" + +const ExceptionallyDistinguishedProfileLink = ({ attendee, children }: PropsWithChildren<{ attendee: Attendee }>) => ( + + + + + + + + + {children} + +) + +const ExceptionallyDistinguishedRightSection = ({ children }: PropsWithChildren) => ( +
{children}
+) + +export function ExceptionallyDistinguishedPlate({ attendee, rightSection, smallIcons, largeIcon }: PlateProps) { + const resolvedRightSection = rightSection ?? largeIcon + + return ( +
+
+
+
+
+ +
+
+
+ + + + {smallIcons} + + + +
+ + {resolvedRightSection && ( + {resolvedRightSection} + )} +
+
+ ) +} + +function getIconContent(exceptionallyDistinguishedFlags: UserFlag[]) { + if (exceptionallyDistinguishedFlags.length === 0) { + return null + } + + return ( + <> + {exceptionallyDistinguishedFlags[0].imageUrl && ( +
+ {getFlagLabel(exceptionallyDistinguishedFlags[0].name +
+ )} + {exceptionallyDistinguishedFlags[0].description && ( + + {exceptionallyDistinguishedFlags[0].description} + + )} + + ) +} + +export function getExceptionallyDistinguishedLargeIcon(exceptionallyDistinguishedFlags: UserFlag[]) { + const exceptionallyDistinguishedFlag = exceptionallyDistinguishedFlags.at(0) + + if (exceptionallyDistinguishedFlag === undefined) { + return null + } + + const triggerClassName = "relative h-fit rounded-full bg-[#fffbf2] p-1 shadow-[0_0_12px_rgba(245,183,66,.65)]" + + const triggerContent = ( +
+ {exceptionallyDistinguishedFlag.imageUrl ? ( + Særskilt utmerket + ) : ( +
SU
+ )} +
+ ) + + if (exceptionallyDistinguishedFlags.length === 0) { + return triggerContent + } + + return ( + + + {triggerContent} + + +
+ Særskilt utmerket + + + +
+ {getIconContent(exceptionallyDistinguishedFlags)} +
+
+ ) +} + +export function getExceptionallyDistinguishedSmallIcon(exceptionallyDistinguishedFlags: UserFlag[]) { + const triggerClassName = "relative h-fit rounded-full bg-[#fffbf2] p-0.5 -my-1 shadow-[0_0_12px_rgba(245,183,66,.65)]" + const imageUrl = exceptionallyDistinguishedFlags.at(0)?.imageUrl + + const triggerContent = ( + + + + + SU + + + + ) + + if (exceptionallyDistinguishedFlags.length === 0) { + return triggerContent + } + + return ( + + {triggerContent} + +
+ Særskilt utmerket + {getIconContent(exceptionallyDistinguishedFlags)} +
+
+
+ ) +} + +interface ExceptionallyDistinguishedLeafProps { + className?: string +} + +function ExceptionallyDistinguishedLeaf({ className }: ExceptionallyDistinguishedLeafProps) { + return ( + + ) +} diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts index 8316e197ad..7177a6937a 100644 --- a/packages/types/src/user.ts +++ b/packages/types/src/user.ts @@ -232,4 +232,8 @@ export function isVanityVerified(user: User) { return user.flags.some(({ name }) => name === FlagName.VANITY_VERIFIED) } +export function isExceptionallyDistinguished(user: User) { + return user.flags.some(({ name }) => name === FlagName.EXCEPTIONALLY_DISTINGUISHED) +} + export const USER_IMAGE_MAX_SIZE_KIB = 512 diff --git a/packages/ui/src/atoms/Popover/Popover.tsx b/packages/ui/src/atoms/Popover/Popover.tsx index 28de4db9e8..f08a859d97 100644 --- a/packages/ui/src/atoms/Popover/Popover.tsx +++ b/packages/ui/src/atoms/Popover/Popover.tsx @@ -12,6 +12,8 @@ export const PopoverAnchor = PopoverPrimitive.Anchor export const PopoverPortal = PopoverPrimitive.Portal +export const PopoverClose = PopoverPrimitive.Close + type PopoverContentProps = React.ComponentPropsWithRef & { align?: React.ComponentPropsWithRef["align"] sideOffset?: React.ComponentPropsWithRef["sideOffset"]