diff --git a/client/package-lock.json b/client/package-lock.json index 2893612..9fb680a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,6 +14,7 @@ "@tanstack/react-query-devtools": "^5.100.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", "lucide-react": "^1.14.0", "radix-ui": "^1.4.3", "react": "^19.2.5", @@ -4844,6 +4845,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", diff --git a/client/package.json b/client/package.json index 3f89647..753d57e 100644 --- a/client/package.json +++ b/client/package.json @@ -18,6 +18,7 @@ "@tanstack/react-query-devtools": "^5.100.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", "lucide-react": "^1.14.0", "radix-ui": "^1.4.3", "react": "^19.2.5", diff --git a/client/src/components/Avatar.tsx b/client/src/components/Avatar.tsx new file mode 100644 index 0000000..5566f7f --- /dev/null +++ b/client/src/components/Avatar.tsx @@ -0,0 +1,34 @@ +import { cn } from "@/lib/utils"; +import { colorIndex, initials } from "@/lib/avatar"; + +const VARIANTS = [ + "bg-avatar-1-bg text-avatar-1-fg", + "bg-avatar-2-bg text-avatar-2-fg", + "bg-avatar-3-bg text-avatar-3-fg", + "bg-avatar-4-bg text-avatar-4-fg", + "bg-avatar-5-bg text-avatar-5-fg", +] as const; + +export function Avatar({ + name, + size = "md", + className, +}: { + name: string; + size?: "md" | "lg"; + className?: string; +}) { + return ( +
+ {initials(name)} +
+ ); +} diff --git a/client/src/components/EmptyState.tsx b/client/src/components/EmptyState.tsx new file mode 100644 index 0000000..a841e62 --- /dev/null +++ b/client/src/components/EmptyState.tsx @@ -0,0 +1,32 @@ +import { type LucideIcon } from "lucide-react"; +import { cn } from "@/lib/utils"; + +export function EmptyState({ + icon: Icon, + headline, + subtext, + cta, + className, +}: { + icon: LucideIcon; + headline: string; + subtext?: string; + cta?: React.ReactNode; + className?: string; +}) { + return ( +
+ +

{headline}

+ {subtext && ( +

{subtext}

+ )} + {cta &&
{cta}
} +
+ ); +} diff --git a/client/src/components/EventBadge.tsx b/client/src/components/EventBadge.tsx new file mode 100644 index 0000000..1323f50 --- /dev/null +++ b/client/src/components/EventBadge.tsx @@ -0,0 +1,20 @@ +import { cn } from "@/lib/utils"; + +export function EventBadge({ + label, + className, +}: { + label: string; + className?: string; +}) { + return ( + + {label} + + ); +} diff --git a/client/src/components/RingBadge.tsx b/client/src/components/RingBadge.tsx new file mode 100644 index 0000000..fa16c1a --- /dev/null +++ b/client/src/components/RingBadge.tsx @@ -0,0 +1,37 @@ +import { cn } from "@/lib/utils"; + +type Ring = "inner_circle" | "network" | "community" | "acquaintances"; + +const LABELS: Record = { + inner_circle: "Inner circle", + network: "Network", + community: "Community", + acquaintances: "Acquaintances", +}; + +const VARIANTS: Record = { + inner_circle: "bg-tier-inner-bg text-tier-inner-fg", + network: "bg-tier-network-bg text-tier-network-fg", + community: "bg-tier-community-bg text-tier-community-fg", + acquaintances: "bg-tieracquaintances-bg text-tier-acquaintances-fg", +}; + +export function RingBadge({ + ring, + className, +}: { + ring: Ring; + className?: string; +}) { + return ( + + {LABELS[ring]} + + ); +} diff --git a/client/src/components/ui/alert-dialog.tsx b/client/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..c206c86 --- /dev/null +++ b/client/src/components/ui/alert-dialog.tsx @@ -0,0 +1,199 @@ +"use client" + +import * as React from "react" +import { AlertDialog as AlertDialogPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + size = "default", + ...props +}: React.ComponentProps & { + size?: "default" | "sm" +}) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogMedia({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + variant = "default", + size = "default", + ...props +}: React.ComponentProps & + Pick, "variant" | "size">) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + variant = "outline", + size = "default", + ...props +}: React.ComponentProps & + Pick, "variant" | "size">) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogMedia, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +} diff --git a/client/src/components/ui/textarea.tsx b/client/src/components/ui/textarea.tsx new file mode 100644 index 0000000..04d27f7 --- /dev/null +++ b/client/src/components/ui/textarea.tsx @@ -0,0 +1,18 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { + return ( +