diff --git a/infrastructure/group-charter-manager/.gitignore b/infrastructure/group-charter-manager/.gitignore new file mode 100644 index 00000000..f9ba7f8b --- /dev/null +++ b/infrastructure/group-charter-manager/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.DS_Store +server/public +vite.config.ts.* +*.tar.gz \ No newline at end of file diff --git a/infrastructure/group-charter-manager/.replit b/infrastructure/group-charter-manager/.replit new file mode 100644 index 00000000..51f344ff --- /dev/null +++ b/infrastructure/group-charter-manager/.replit @@ -0,0 +1,36 @@ +modules = ["nodejs-20", "web", "postgresql-16"] +run = "npm run dev" +hidden = [".config", ".git", "generated-icon.png", "node_modules", "dist"] + +[nix] +channel = "stable-24_05" + +[deployment] +deploymentTarget = "autoscale" +build = ["npm", "run", "build"] +run = ["npm", "run", "start"] + +[[ports]] +localPort = 5000 +externalPort = 80 + +[workflows] +runButton = "Project" + +[[workflows.workflow]] +name = "Project" +mode = "parallel" +author = "agent" + +[[workflows.workflow.tasks]] +task = "workflow.run" +args = "Start application" + +[[workflows.workflow]] +name = "Start application" +author = "agent" + +[[workflows.workflow.tasks]] +task = "shell.exec" +args = "npm run dev" +waitForPort = 5000 diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 12.43.45_1752749027859.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 12.43.45_1752749027859.png new file mode 100644 index 00000000..ed3cba5f Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 12.43.45_1752749027859.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 12.47.10_1752749231620.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 12.47.10_1752749231620.png new file mode 100644 index 00000000..ff22ecd8 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 12.47.10_1752749231620.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.12.33_1752750863549.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.12.33_1752750863549.png new file mode 100644 index 00000000..e602293d Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.12.33_1752750863549.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.28.14_1752751697377.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.28.14_1752751697377.png new file mode 100644 index 00000000..1829578e Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.28.14_1752751697377.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.33.06_1752751989104.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.33.06_1752751989104.png new file mode 100644 index 00000000..4af069c3 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.33.06_1752751989104.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.39.54_1752752395852.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.39.54_1752752395852.png new file mode 100644 index 00000000..d9d86634 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.39.54_1752752395852.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.40.33_1752752434539.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.40.33_1752752434539.png new file mode 100644 index 00000000..4868ecb4 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.40.33_1752752434539.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.43.03_1752752586307.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.43.03_1752752586307.png new file mode 100644 index 00000000..73cc6409 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.43.03_1752752586307.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.51.09_1752753070718.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.51.09_1752753070718.png new file mode 100644 index 00000000..955fee31 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 13.51.09_1752753070718.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.09.23_1752754166263.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.09.23_1752754166263.png new file mode 100644 index 00000000..64d97e9e Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.09.23_1752754166263.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.09.31_1752754173307.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.09.31_1752754173307.png new file mode 100644 index 00000000..f152112b Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.09.31_1752754173307.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.12.48_1752754372657.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.12.48_1752754372657.png new file mode 100644 index 00000000..f21a2647 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.12.48_1752754372657.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.24.09_1752755052382.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.24.09_1752755052382.png new file mode 100644 index 00000000..8f00fb74 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 14.24.09_1752755052382.png differ diff --git a/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 15.08.40_1752757722789.png b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 15.08.40_1752757722789.png new file mode 100644 index 00000000..95370567 Binary files /dev/null and b/infrastructure/group-charter-manager/attached_assets/Screenshot 2025-07-17 at 15.08.40_1752757722789.png differ diff --git a/infrastructure/group-charter-manager/client/index.html b/infrastructure/group-charter-manager/client/index.html new file mode 100644 index 00000000..796256b8 --- /dev/null +++ b/infrastructure/group-charter-manager/client/index.html @@ -0,0 +1,15 @@ + + + + + + + Group Charter + + +
+ + + + + \ No newline at end of file diff --git a/infrastructure/group-charter-manager/client/src/App.tsx b/infrastructure/group-charter-manager/client/src/App.tsx new file mode 100644 index 00000000..6f3fc9b0 --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/App.tsx @@ -0,0 +1,82 @@ +import { Switch, Route } from "wouter"; +import { queryClient } from "./lib/queryClient"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { Toaster } from "@/components/ui/toaster"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { useAuth } from "@/hooks/useAuth"; +import Navbar from "@/components/layout/navbar"; +import Dashboard from "@/pages/dashboard"; +import CharterDetail from "@/pages/charter-detail"; +import CreateCharter from "@/pages/create-charter"; +import EditCharter from "@/pages/edit-charter"; +import NotFound from "@/pages/not-found"; + +function Router() { + return ; +} + +function AuthenticatedApp() { + const { isAuthenticated, isLoading } = useAuth(); + + // Show loading state while checking authentication + if (isLoading) { + return ( +
+
+
+
Checking authentication...
+
+
+ ); + } + + // Show login button for unauthenticated users instead of auto-redirect + if (!isAuthenticated) { + return ( +
+
+
+

Group Charter

+

Please sign in to access your dashboard

+
+ +
+
+ ); + } + + // Show authenticated app + return ( +
+ +
+ + + + + + + + +
+
+ ); +} + +function App() { + return ( + + + + + + + ); +} + +export default App; diff --git a/infrastructure/group-charter-manager/client/src/components/charter-card.tsx b/infrastructure/group-charter-manager/client/src/components/charter-card.tsx new file mode 100644 index 00000000..9e81e84e --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/charter-card.tsx @@ -0,0 +1,57 @@ +import { Link } from "wouter"; +import { CheckCircle, Clock, Eye, Crown } from "lucide-react"; +import { CharterWithGroup } from "@shared/schema"; +import { PlatformBadge } from "./platform-badge"; +import { useAuth } from "@/hooks/useAuth"; + +interface CharterCardProps { + charter: CharterWithGroup; +} + +export default function CharterCard({ charter }: CharterCardProps) { + const { user } = useAuth(); + const StatusIcon = charter.isActive ? CheckCircle : Clock; + const statusColor = charter.isActive ? "text-green-600" : "text-yellow-600"; + const statusBg = charter.isActive ? "bg-green-100" : "bg-yellow-100"; + const isOwner = user?.id === charter.ownerId; + + + + return ( +
+
+
+
+

{charter.name}

+

{charter.group.name}

+
+ +
+
+
+
+ {isOwner && ( +
+ +
+ )} +
+ +
+
+
+ +
+ + Updated {charter.updatedAt ? new Date(charter.updatedAt).toLocaleDateString() : "recently"} + + + + +
+
+ ); +} diff --git a/infrastructure/group-charter-manager/client/src/components/icons/certificate-ribbon.tsx b/infrastructure/group-charter-manager/client/src/components/icons/certificate-ribbon.tsx new file mode 100644 index 00000000..1e38c8dd --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/icons/certificate-ribbon.tsx @@ -0,0 +1,99 @@ +interface CertificateRibbonProps { + className?: string; + size?: number; +} + +export function CertificateRibbon({ className = "", size = 24 }: CertificateRibbonProps) { + return ( + + {/* Outer seal ring */} + + + {/* Inner seal ring */} + + + {/* Star in center */} + + + {/* Inner circle around star */} + + + {/* Decorative notches around outer edge */} + + + + + + {/* Diagonal notches */} + + + + + + ); +} \ No newline at end of file diff --git a/infrastructure/group-charter-manager/client/src/components/layout/navbar.tsx b/infrastructure/group-charter-manager/client/src/components/layout/navbar.tsx new file mode 100644 index 00000000..a6ab6ffa --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/layout/navbar.tsx @@ -0,0 +1,123 @@ +import { useState } from "react"; +import { Link, useLocation } from "wouter"; +import { Plus, BarChart3, User, LogOut, Menu, X } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { useAuth } from "@/hooks/useAuth"; +import { CertificateRibbon } from "@/components/icons/certificate-ribbon"; + +export default function Navbar() { + const [location] = useLocation(); + const { user } = useAuth(); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + + const isActive = (path: string) => { + if (path === "/" && location === "/") return true; + if (path !== "/" && location.startsWith(path)) return true; + return false; + }; + + const handleLogout = () => { + window.location.href = "/api/logout"; + }; + + return ( + + ); +} diff --git a/infrastructure/group-charter-manager/client/src/components/member-avatar.tsx b/infrastructure/group-charter-manager/client/src/components/member-avatar.tsx new file mode 100644 index 00000000..70aa737b --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/member-avatar.tsx @@ -0,0 +1,48 @@ +interface MemberAvatarProps { + name: string; + role?: string; + size?: "sm" | "md" | "lg"; +} + +export default function MemberAvatar({ name, role, size = "md" }: MemberAvatarProps) { + // Handle undefined or empty name + const displayName = name || "Unknown"; + + const initials = displayName + .split(" ") + .map((n) => n[0]) + .join("") + .toUpperCase() + .slice(0, 2); + + const sizeClasses = { + sm: "w-8 h-8 text-xs", + md: "w-10 h-10 text-sm", + lg: "w-12 h-12 text-base", + }; + + const gradients = [ + "gradient-primary", + "gradient-secondary", + "gradient-accent", + "bg-gradient-to-r from-indigo-500 to-purple-500", + "bg-gradient-to-r from-pink-500 to-red-500", + "bg-gradient-to-r from-teal-500 to-green-500", + ]; + + const gradient = gradients[displayName.length % gradients.length]; + + return ( +
+
+ {initials} +
+ {role && ( +
+

{displayName}

+

{role}

+
+ )} +
+ ); +} diff --git a/infrastructure/group-charter-manager/client/src/components/platform-badge.tsx b/infrastructure/group-charter-manager/client/src/components/platform-badge.tsx new file mode 100644 index 00000000..b9827a47 --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/platform-badge.tsx @@ -0,0 +1,38 @@ +import { Badge } from "@/components/ui/badge"; + +interface PlatformBadgeProps { + platform: string; + className?: string; +} + +export function PlatformBadge({ platform, className = "" }: PlatformBadgeProps) { + const getPlatformConfig = (platform: string) => { + switch (platform) { + case 'instagram': + case 'facebook': + return { + name: 'Pictique', + className: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600' + }; + case 'twitter': + case 'discord': + return { + name: 'Blabsy', + className: 'bg-blue-500 text-white hover:bg-blue-600' + }; + default: + return { + name: 'Unknown', + className: 'bg-gray-500 text-white hover:bg-gray-600' + }; + } + }; + + const config = getPlatformConfig(platform); + + return ( + + {config.name} + + ); +} \ No newline at end of file diff --git a/infrastructure/group-charter-manager/client/src/components/stats-card.tsx b/infrastructure/group-charter-manager/client/src/components/stats-card.tsx new file mode 100644 index 00000000..3f3c8822 --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/stats-card.tsx @@ -0,0 +1,24 @@ +import { LucideIcon } from "lucide-react"; + +interface StatsCardProps { + title: string; + value: string | number; + icon: LucideIcon; + gradient: string; +} + +export default function StatsCard({ title, value, icon: Icon, gradient }: StatsCardProps) { + return ( +
+
+
+

{title}

+

{value}

+
+
+ +
+
+
+ ); +} diff --git a/infrastructure/group-charter-manager/client/src/components/ui/accordion.tsx b/infrastructure/group-charter-manager/client/src/components/ui/accordion.tsx new file mode 100644 index 00000000..e6a723d0 --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/ui/accordion.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/infrastructure/group-charter-manager/client/src/components/ui/alert-dialog.tsx b/infrastructure/group-charter-manager/client/src/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..8722561c --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/infrastructure/group-charter-manager/client/src/components/ui/alert.tsx b/infrastructure/group-charter-manager/client/src/components/ui/alert.tsx new file mode 100644 index 00000000..41fa7e05 --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/infrastructure/group-charter-manager/client/src/components/ui/aspect-ratio.tsx b/infrastructure/group-charter-manager/client/src/components/ui/aspect-ratio.tsx new file mode 100644 index 00000000..c4abbf37 --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,5 @@ +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/infrastructure/group-charter-manager/client/src/components/ui/avatar.tsx b/infrastructure/group-charter-manager/client/src/components/ui/avatar.tsx new file mode 100644 index 00000000..51e507ba --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/infrastructure/group-charter-manager/client/src/components/ui/badge.tsx b/infrastructure/group-charter-manager/client/src/components/ui/badge.tsx new file mode 100644 index 00000000..f000e3ef --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/infrastructure/group-charter-manager/client/src/components/ui/breadcrumb.tsx b/infrastructure/group-charter-manager/client/src/components/ui/breadcrumb.tsx new file mode 100644 index 00000000..60e6c96f --- /dev/null +++ b/infrastructure/group-charter-manager/client/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>