code:
"use client"

import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"

import { cn } from "@/app/lib/utils"

function Dialog({
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
  return <DialogPrimitive.Root data-slot="dialog" {...props} />
}

function DialogTrigger({
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
}

function DialogPortal({
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
  return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
}

function DialogClose({
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
}

function DialogOverlay({
  className,
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
  return (
    <DialogPrimitive.Overlay
      data-slot="dialog-overlay"
      className={cn(
        "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
        className
      )}
      {...props}
    />
  )
}

function DialogContent({
  className,
  children,
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
  return (
    <DialogPortal data-slot="dialog-portal">
      <DialogOverlay />
      <DialogPrimitive.Content
        data-slot="dialog-content"
        className={cn(
          "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
          className
        )}
        {...props}
      >
        {children}
        <DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
          <XIcon />
          Close
        </DialogPrimitive.Close>
      </DialogPrimitive.Content>
    </DialogPortal>
  )
}

function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function DialogTitle({
  className,
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
  return (
    <DialogPrimitive.Title
      data-slot="dialog-title"
      className={cn("text-lg leading-none font-semibold", className)}
      {...props}
    />
  )
}

function DialogDescription({
  className,
  ...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
  return (
    <DialogPrimitive.Description
      data-slot="dialog-description"
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  )
}

export {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogOverlay,
  DialogPortal,
  DialogTitle,
  DialogTrigger,
}


code:
"use client"

import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"

import { cn } from "@/app/lib/utils"

function DropdownMenu({
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
  return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}

function DropdownMenuPortal({
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
  return (
    <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
  )
}

function DropdownMenuTrigger({
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
  return (
    <DropdownMenuPrimitive.Trigger
      data-slot="dropdown-menu-trigger"
      {...props}
    />
  )
}

function DropdownMenuContent({
  className,
  sideOffset = 4,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
  return (
    <DropdownMenuPrimitive.Portal>
      <DropdownMenuPrimitive.Content
        data-slot="dropdown-menu-content"
        sideOffset={sideOffset}
        className={cn(
          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
          className
        )}
        {...props}
      />
    </DropdownMenuPrimitive.Portal>
  )
}

function DropdownMenuGroup({
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
  return (
    <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
  )
}

function DropdownMenuItem({
  className,
  inset,
  variant = "default",
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
  inset?: boolean
  variant?: "default" | "destructive"
}) {
  return (
    <DropdownMenuPrimitive.Item
      data-slot="dropdown-menu-item"
      data-inset={inset}
      data-variant={variant}
      className={cn(
        "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    />
  )
}

function DropdownMenuCheckboxItem({
  className,
  children,
  checked,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
  return (
    <DropdownMenuPrimitive.CheckboxItem
      data-slot="dropdown-menu-checkbox-item"
      className={cn(
        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      checked={checked}
      {...props}
    >

        <DropdownMenuPrimitive.ItemIndicator>
          <CheckIcon className="size-4" />
        </DropdownMenuPrimitive.ItemIndicator>

      {children}
    </DropdownMenuPrimitive.CheckboxItem>
  )
}

function DropdownMenuRadioGroup({
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
  return (
    <DropdownMenuPrimitive.RadioGroup
      data-slot="dropdown-menu-radio-group"
      {...props}
    />
  )
}

function DropdownMenuRadioItem({
  className,
  children,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
  return (
    <DropdownMenuPrimitive.RadioItem
      data-slot="dropdown-menu-radio-item"
      className={cn(
        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    >

        <DropdownMenuPrimitive.ItemIndicator>
          <CircleIcon className="size-2 fill-current" />
        </DropdownMenuPrimitive.ItemIndicator>

      {children}
    </DropdownMenuPrimitive.RadioItem>
  )
}

function DropdownMenuLabel({
  className,
  inset,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
  inset?: boolean
}) {
  return (
    <DropdownMenuPrimitive.Label
      data-slot="dropdown-menu-label"
      data-inset={inset}
      className={cn(
        "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
        className
      )}
      {...props}
    />
  )
}

function DropdownMenuSeparator({
  className,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
  return (
    <DropdownMenuPrimitive.Separator
      data-slot="dropdown-menu-separator"
      className={cn("bg-border -mx-1 my-1 h-px", className)}
      {...props}
    />
  )
}

function DropdownMenuShortcut({
  className,
  ...props
}: React.ComponentProps<"span">) {
  return (

  )
}

function DropdownMenuSub({
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
  return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
}

function DropdownMenuSubTrigger({
  className,
  inset,
  children,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
  inset?: boolean
}) {
  return (
    <DropdownMenuPrimitive.SubTrigger
      data-slot="dropdown-menu-sub-trigger"
      data-inset={inset}
      className={cn(
        "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
        className
      )}
      {...props}
    >
      {children}
      <ChevronRightIcon className="ml-auto size-4" />
    </DropdownMenuPrimitive.SubTrigger>
  )
}

function DropdownMenuSubContent({
  className,
  ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
  return (
    <DropdownMenuPrimitive.SubContent
      data-slot="dropdown-menu-sub-content"
      className={cn(
        "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
        className
      )}
      {...props}
    />
  )
}

export {
  DropdownMenu,
  DropdownMenuPortal,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuItem,
  DropdownMenuCheckboxItem,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubTrigger,
  DropdownMenuSubContent,
}

license: mit


code:
"use client"

import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import {
  Controller,
  FormProvider,
  useFormContext,
  useFormState,
  type ControllerProps,
  type FieldPath,
  type FieldValues,
} from "react-hook-form"

import { cn } from "@/app/lib/utils"
import { Label } from "@/app/components/ui/label"

const Form = FormProvider

type FormFieldContextValue<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
  name: TName
}

const FormFieldContext = React.createContext<FormFieldContextValue>(
  {} as FormFieldContextValue
)

const FormField = <
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
  ...props
}: ControllerProps<TFieldValues, TName>) => {
  return (
    <FormFieldContext.Provider value={{ name: props.name }}>
      <Controller {...props} />
    </FormFieldContext.Provider>
  )
}

const useFormField = () => {
  const fieldContext = React.useContext(FormFieldContext)
  const itemContext = React.useContext(FormItemContext)
  const { getFieldState } = useFormContext()
  const formState = useFormState({ name: fieldContext.name })
  const fieldState = getFieldState(fieldContext.name, formState)

  if (!fieldContext) {
    throw new Error("useFormField should be used within <FormField>")
  }

  const { id } = itemContext

  return {
    id,
    name: fieldContext.name,
    formItemId: `${id}-form-item`,
    formDescriptionId: `${id}-form-item-description`,
    formMessageId: `${id}-form-item-message`,
    ...fieldState,
  }
}

type FormItemContextValue = {
  id: string
}

const FormItemContext = React.createContext<FormItemContextValue>(
  {} as FormItemContextValue
)

function FormItem({ className, ...props }: React.ComponentProps<"div">) {
  const id = React.useId()

  return (
    <FormItemContext.Provider value={{ id }}>
      <div
        data-slot="form-item"
        className={cn("grid gap-2", className)}
        {...props}
      />
    </FormItemContext.Provider>
  )
}

function FormLabel({
  className,
  ...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
  const { error, formItemId } = useFormField()

  return (
    <Label
      data-slot="form-label"
      data-error={!!error}
      className={cn("data-[error=true]:text-destructive-foreground", className)}
      htmlFor={formItemId}
      {...props}
    />
  )
}

function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
  const { error, formItemId, formDescriptionId, formMessageId } = useFormField()

  return (
    <Slot
      data-slot="form-control"
      id={formItemId}
      aria-describedby={
        !error
          ? `${formDescriptionId}`
          : `${formDescriptionId} ${formMessageId}`
      }
      aria-invalid={!!error}
      {...props}
    />
  )
}

function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
  const { formDescriptionId } = useFormField()

  return (
    <p
      data-slot="form-description"
      id={formDescriptionId}
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  )
}

function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
  const { error, formMessageId } = useFormField()
  const body = error ? String(error?.message ?? "") : props.children

  if (!body) {
    return null
  }

  return (
    <p
      data-slot="form-message"
      id={formMessageId}
      className={cn("text-destructive-foreground text-sm", className)}
      {...props}
    >
      {body}
    </p>
  )
}

export {
  useFormField,
  Form,
  FormItem,
  FormLabel,
  FormControl,
  FormDescription,
  FormMessage,
  FormField,
}


code:
import * as React from "react";

import { cn } from "@/app/lib/utils";

function Input({ className, type, ...props }: React.ComponentProps<"input">) {
  return (
    <input
      type={type}
      data-slot="input"
      className={cn(
        "border-input file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
        "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
        "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
        className
      )}
      {...props}
    />
  );
}

export { Input };


code:
"use client";

import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";

import { cn } from "@/app/lib/utils";

function Label({
  className,
  ...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
  return (
    <LabelPrimitive.Root
      data-slot="label"
      className={cn(
        "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
        className
      )}
      {...props}
    />
  );
}

export { Label };


code:
"use client"

import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"

import { cn } from "@/app/lib/utils"

function Popover({
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
  return <PopoverPrimitive.Root data-slot="popover" {...props} />
}

function PopoverTrigger({
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
  return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
}

function PopoverContent({
  className,
  align = "center",
  sideOffset = 4,
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
  return (
    <PopoverPrimitive.Portal>
      <PopoverPrimitive.Content
        data-slot="popover-content"
        align={align}
        sideOffset={sideOffset}
        className={cn(
          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-hidden",
          className
        )}
        {...props}
      />
    </PopoverPrimitive.Portal>
  )
}

function PopoverAnchor({
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
  return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}

export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }


code:
"use client"

import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"

import { cn } from "@/app/lib/utils"

function Select({
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
  return <SelectPrimitive.Root data-slot="select" {...props} />
}

function SelectGroup({
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
  return <SelectPrimitive.Group data-slot="select-group" {...props} />
}

function SelectValue({
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
  return <SelectPrimitive.Value data-slot="select-value" {...props} />
}

function SelectTrigger({
  className,
  size = "default",
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
  size?: "sm" | "default"
}) {
  return (
    <SelectPrimitive.Trigger
      data-slot="select-trigger"
      data-size={size}
      className={cn(
        "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    >
      {children}
      <SelectPrimitive.Icon asChild>
        <ChevronDownIcon className="size-4 opacity-50" />
      </SelectPrimitive.Icon>
    </SelectPrimitive.Trigger>
  )
}

function SelectContent({
  className,
  children,
  position = "popper",
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
  return (
    <SelectPrimitive.Portal>
      <SelectPrimitive.Content
        data-slot="select-content"
        className={cn(
          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
          position === "popper" &&
            "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
          className
        )}
        position={position}
        {...props}
      >
        <SelectScrollUpButton />
        <SelectPrimitive.Viewport
          className={cn(
            "p-1",
            position === "popper" &&
              "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
          )}
        >
          {children}
        </SelectPrimitive.Viewport>
        <SelectScrollDownButton />
      </SelectPrimitive.Content>
    </SelectPrimitive.Portal>
  )
}

function SelectLabel({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
  return (
    <SelectPrimitive.Label
      data-slot="select-label"
      className={cn("px-2 py-1.5 text-sm font-medium", className)}
      {...props}
    />
  )
}

function SelectItem({
  className,
  children,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
  return (
    <SelectPrimitive.Item
      data-slot="select-item"
      className={cn(
        "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
        className
      )}
      {...props}
    >
      <span className="absolute right-2 flex size-3.5 items-center justify-center">
        <SelectPrimitive.ItemIndicator>
          <CheckIcon className="size-4" />
        </SelectPrimitive.ItemIndicator>
      </span>
      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
    </SelectPrimitive.Item>
  )
}

function SelectSeparator({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
  return (
    <SelectPrimitive.Separator
      data-slot="select-separator"
      className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
      {...props}
    />
  )
}

function SelectScrollUpButton({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
  return (
    <SelectPrimitive.ScrollUpButton
      data-slot="select-scroll-up-button"
      className={cn(
        "flex cursor-default items-center justify-center py-1",
        className
      )}
      {...props}
    >
      <ChevronUpIcon className="size-4" />
    </SelectPrimitive.ScrollUpButton>
  )
}

function SelectScrollDownButton({
  className,
  ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
  return (
    <SelectPrimitive.ScrollDownButton
      data-slot="select-scroll-down-button"
      className={cn(
        "flex cursor-default items-center justify-center py-1",
        className
      )}
      {...props}
    >
      <ChevronDownIcon className="size-4" />
    </SelectPrimitive.ScrollDownButton>
  )
}

export {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectScrollDownButton,
  SelectScrollUpButton,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
}


code:
"use client"

import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"

import { cn } from "@/app/lib/utils"

function Separator({
  className,
  orientation = "horizontal",
  decorative = true,
  ...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
  return (
    <SeparatorPrimitive.Root
      data-slot="separator-root"
      decorative={decorative}
      orientation={orientation}
      className={cn(
        "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
        className
      )}
      {...props}
    />
  )
}

export { Separator }


code:
"use client"

import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"

import { cn } from "@/app/lib/utils"

function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
  return <SheetPrimitive.Root data-slot="sheet" {...props} />
}

function SheetTrigger({
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
  return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
}

function SheetClose({
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
  return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
}

function SheetPortal({
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
  return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
}

function SheetOverlay({
  className,
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
  return (
    <SheetPrimitive.Overlay
      data-slot="sheet-overlay"
      className={cn(
        "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
        className
      )}
      {...props}
    />
  )
}

function SheetContent({
  className,
  children,
  side = "right",
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
  side?: "top" | "right" | "bottom" | "left"
}) {
  return (
    <SheetPortal>
      <SheetOverlay />
      <SheetPrimitive.Content
        data-slot="sheet-content"
        className={cn(
          "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
          side === "right" &&
            "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
          side === "left" &&
            "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
          side === "top" &&
            "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
          side === "bottom" &&
            "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
          className
        )}
        {...props}
      >
        {children}
        <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
          <XIcon className="size-4" />
          Close
        </SheetPrimitive.Close>
      </SheetPrimitive.Content>
    </SheetPortal>
  )
}

function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function SheetTitle({
  className,
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
  return (
    <SheetPrimitive.Title
      data-slot="sheet-title"
      className={cn("text-foreground font-semibold", className)}
      {...props}
    />
  )
}

function SheetDescription({
  className,
  ...props
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
  return (
    <SheetPrimitive.Description
      data-slot="sheet-description"
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  )
}

export {
  Sheet,
  SheetTrigger,
  SheetClose,
  SheetContent,
  SheetHeader,
  SheetFooter,
  SheetTitle,
  SheetDescription,
}


code:
"use client"

import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { VariantProps, cva } from "class-variance-authority"
import { PanelLeftIcon } from "lucide-react"

import { useIsMobile } from "@/app/hooks/use-mobile"
import { cn } from "@/app/lib/utils"
import { Button } from "@/app/components/ui/button"
import { Input } from "@/app/components/ui/input"
import { Separator } from "@/app/components/ui/separator"
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
} from "@/app/components/ui/sheet"
import { Skeleton } from "@/app/components/ui/skeleton"
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/app/components/ui/tooltip"

const SIDEBAR_COOKIE_NAME = "sidebar_state"
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
const SIDEBAR_WIDTH = "16rem"
const SIDEBAR_WIDTH_MOBILE = "18rem"
const SIDEBAR_WIDTH_ICON = "3rem"
const SIDEBAR_KEYBOARD_SHORTCUT = "b"

type SidebarContext = {
  state: "expanded" | "collapsed"
  open: boolean
  setOpen: (open: boolean) => void
  openMobile: boolean
  setOpenMobile: (open: boolean) => void
  isMobile: boolean
  toggleSidebar: () => void
}

const SidebarContext = React.createContext<SidebarContext | null>(null)

function useSidebar() {
  const context = React.useContext(SidebarContext)
  if (!context) {
    throw new Error("useSidebar must be used within a SidebarProvider.")
  }

  return context
}

function SidebarProvider({
  defaultOpen = true,
  open: openProp,
  onOpenChange: setOpenProp,
  className,
  style,
  children,
  ...props
}: React.ComponentProps<"div"> & {
  defaultOpen?: boolean
  open?: boolean
  onOpenChange?: (open: boolean) => void
}) {
  const isMobile = useIsMobile()
  const [openMobile, setOpenMobile] = React.useState(false)

  // This is the internal state of the sidebar.
  // We use openProp and setOpenProp for control from outside the component.
  const [_open, _setOpen] = React.useState(defaultOpen)
  const open = openProp ?? _open
  const setOpen = React.useCallback(
    (value: boolean | ((value: boolean) => boolean)) => {
      const openState = typeof value === "function" ? value(open) : value
      if (setOpenProp) {
        setOpenProp(openState)
      } else {
        _setOpen(openState)
      }

      // This sets the cookie to keep the sidebar state.
      document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
    },
    [setOpenProp, open]
  )

  // Helper to toggle the sidebar.
  const toggleSidebar = React.useCallback(() => {
    return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
  }, [isMobile, setOpen, setOpenMobile])

  // Adds a keyboard shortcut to toggle the sidebar.
  React.useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (
        event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
        (event.metaKey || event.ctrlKey)
      ) {
        event.preventDefault()
        toggleSidebar()
      }
    }

    window.addEventListener("keydown", handleKeyDown)
    return () => window.removeEventListener("keydown", handleKeyDown)
  }, [toggleSidebar])

  // We add a state so that we can do data-state="expanded" or "collapsed".
  // This makes it easier to style the sidebar with Tailwind classes.
  const state = open ? "expanded" : "collapsed"

  const contextValue = React.useMemo<SidebarContext>(
    () => ({
      state,
      open,
      setOpen,
      isMobile,
      openMobile,
      setOpenMobile,
      toggleSidebar,
    }),
    [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
  )

  return (
    <SidebarContext.Provider value={contextValue}>
      <TooltipProvider delayDuration={0}>

          {children}

      </TooltipProvider>
    </SidebarContext.Provider>
  )
}

function Sidebar({
  side = "left",
  variant = "sidebar",
  collapsible = "offcanvas",
  className,
  children,
  ...props
}: React.ComponentProps<"div"> & {
  side?: "left" | "right"
  variant?: "sidebar" | "floating" | "inset"
  collapsible?: "offcanvas" | "icon" | "none"
}) {
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar()

  if (collapsible === "none") {
    return (

        {children}

    )
  }

  if (isMobile) {
    return (
      <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
        <SheetContent
          data-sidebar="sidebar"
          data-slot="sidebar"
          data-mobile="true"
          className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
          style={
            {
              "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
            } as React.CSSProperties
          }
          side={side}
        >
          <SheetHeader className="sr-only">
            <SheetTitle>Sidebar</SheetTitle>
            <SheetDescription>Displays the mobile sidebar.</SheetDescription>
          </SheetHeader>

{children}

        </SheetContent>
      </Sheet>
    )
  }

  return (

      {/* This is what handles the sidebar gap on desktop */}

      <div
        className={cn(
          "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
          side === "left"
            ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
            : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
          // Adjust the padding for floating and inset variants.
          variant === "floating" || variant === "inset"
            ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
            : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
          className
        )}
        {...props}
      >

          {children}

  )
}

function SidebarTrigger({
  className,
  onClick,
  ...props
}: React.ComponentProps<typeof Button>) {
  const { toggleSidebar } = useSidebar()

  return (
    <Button
      data-sidebar="trigger"
      data-slot="sidebar-trigger"
      variant="ghost"
      size="icon"
      className={cn("h-7 w-7", className)}
      onClick={(event) => {
        onClick?.(event)
        toggleSidebar()
      }}
      {...props}
    >
      <PanelLeftIcon />
      Toggle Sidebar
    </Button>
  )
}

function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
  const { toggleSidebar } = useSidebar()

  return (
    <button
      data-sidebar="rail"
      data-slot="sidebar-rail"
      aria-label="Toggle Sidebar"
      tabIndex={-1}
      onClick={toggleSidebar}
      title="Toggle Sidebar"
      className={cn(
        "hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
        "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
        "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
        "hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
        "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
        "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
        className
      )}
      {...props}
    />
  )
}

function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
  return (

  )
}

function SidebarInput({
  className,
  ...props
}: React.ComponentProps<typeof Input>) {
  return (
    <Input
      data-slot="sidebar-input"
      data-sidebar="input"
      className={cn("bg-background h-8 w-full shadow-none", className)}
      {...props}
    />
  )
}

function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function SidebarSeparator({
  className,
  ...props
}: React.ComponentProps<typeof Separator>) {
  return (
    <Separator
      data-slot="sidebar-separator"
      data-sidebar="separator"
      className={cn("bg-sidebar-border mx-2 w-auto", className)}
      {...props}
    />
  )
}

function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
  return (

  )
}

function SidebarGroupLabel({
  className,
  asChild = false,
  ...props
}: React.ComponentProps<"div"> & { asChild?: boolean }) {
  const Comp = asChild ? Slot : "div"

  return (
    <Comp
      data-slot="sidebar-group-label"
      data-sidebar="group-label"
      className={cn(
        "text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
        "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
        className
      )}
      {...props}
    />
  )
}

function SidebarGroupAction({
  className,
  asChild = false,
  ...props
}: React.ComponentProps<"button"> & { asChild?: boolean }) {
  const Comp = asChild ? Slot : "button"

  return (
    <Comp
      data-slot="sidebar-group-action"
      data-sidebar="group-action"
      className={cn(
        "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
        // Increases the hit area of the button on mobile.
        "after:absolute after:-inset-2 md:after:hidden",
        "group-data-[collapsible=icon]:hidden",
        className
      )}
      {...props}
    />
  )
}

function SidebarGroupContent({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (

  )
}

function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
  return (

  )
}

function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
  return (

  )
}

const sidebarMenuButtonVariants = cva(
  "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
  {
    variants: {
      variant: {
        default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
        outline:
          "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
      },
      size: {
        default: "h-8 text-sm",
        sm: "h-7 text-xs",
        lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

function SidebarMenuButton({
  asChild = false,
  isActive = false,
  variant = "default",
  size = "default",
  tooltip,
  className,
  ...props
}: React.ComponentProps<"button"> & {
  asChild?: boolean
  isActive?: boolean
  tooltip?: string | React.ComponentProps<typeof TooltipContent>
} & VariantProps<typeof sidebarMenuButtonVariants>) {
  const Comp = asChild ? Slot : "button"
  const { isMobile, state } = useSidebar()

  const button = (
    <Comp
      data-slot="sidebar-menu-button"
      data-sidebar="menu-button"
      data-size={size}
      data-active={isActive}
      className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
      {...props}
    />
  )

  if (!tooltip) {
    return button
  }

  if (typeof tooltip === "string") {
    tooltip = {
      children: tooltip,
    }
  }

  return (
    <Tooltip>
      <TooltipTrigger asChild>{button}</TooltipTrigger>
      <TooltipContent
        side="right"
        align="center"
        hidden={state !== "collapsed" || isMobile}
        {...tooltip}
      />
    </Tooltip>
  )
}

function SidebarMenuAction({
  className,
  asChild = false,
  showOnHover = false,
  ...props
}: React.ComponentProps<"button"> & {
  asChild?: boolean
  showOnHover?: boolean
}) {
  const Comp = asChild ? Slot : "button"

  return (
    <Comp
      data-slot="sidebar-menu-action"
      data-sidebar="menu-action"
      className={cn(
        "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
        // Increases the hit area of the button on mobile.
        "after:absolute after:-inset-2 md:after:hidden",
        "peer-data-[size=sm]/menu-button:top-1",
        "peer-data-[size=default]/menu-button:top-1.5",
        "peer-data-[size=lg]/menu-button:top-2.5",
        "group-data-[collapsible=icon]:hidden",
        showOnHover &&
          "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
        className
      )}
      {...props}
    />
  )
}

function SidebarMenuBadge({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="sidebar-menu-badge"
      data-sidebar="menu-badge"
      className={cn(
        "text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none",
        "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
        "peer-data-[size=sm]/menu-button:top-1",
        "peer-data-[size=default]/menu-button:top-1.5",
        "peer-data-[size=lg]/menu-button:top-2.5",
        "group-data-[collapsible=icon]:hidden",
        className
      )}
      {...props}
    />
  )
}

function SidebarMenuSkeleton({
  className,
  showIcon = false,
  ...props
}: React.ComponentProps<"div"> & {
  showIcon?: boolean
}) {
  // Random width between 50 to 90%.
  const width = React.useMemo(() => {
    return `${Math.floor(Math.random() * 40) + 50}%`
  }, [])

  return (

      {showIcon && (
        <Skeleton
          className="size-4 rounded-md"
          data-sidebar="menu-skeleton-icon"
        />
      )}
      <Skeleton
        className="h-4 max-w-(--skeleton-width) flex-1"
        data-sidebar="menu-skeleton-text"
        style={
          {
            "--skeleton-width": width,
          } as React.CSSProperties
        }
      />

  )
}

function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
  return (

  )
}

function SidebarMenuSubItem({
  className,
  ...props
}: React.ComponentProps<"li">) {
  return (

  )
}

function SidebarMenuSubButton({
  asChild = false,
  size = "md",
  isActive = false,
  className,
  ...props
}: React.ComponentProps<"a"> & {
  asChild?: boolean
  size?: "sm" | "md"
  isActive?: boolean
}) {
  const Comp = asChild ? Slot : "a"

  return (
    <Comp
      data-slot="sidebar-menu-sub-button"
      data-sidebar="menu-sub-button"
      data-size={size}
      data-active={isActive}
      className={cn(
        "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
        "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
        size === "sm" && "text-xs",
        size === "md" && "text-sm",
        "group-data-[collapsible=icon]:hidden",
        className
      )}
      {...props}
    />
  )
}

export {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarGroupAction,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarHeader,
  SidebarInput,
  SidebarInset,
  SidebarMenu,
  SidebarMenuAction,
  SidebarMenuBadge,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarMenuSkeleton,
  SidebarMenuSub,
  SidebarMenuSubButton,
  SidebarMenuSubItem,
  SidebarProvider,
  SidebarRail,
  SidebarSeparator,
  SidebarTrigger,
  useSidebar,
}


code:
import { cn } from "@/app/lib/utils"

function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="skeleton"
      className={cn("bg-primary/10 animate-pulse rounded-md", className)}
      {...props}
    />
  )
}

export { Skeleton }


code:
const shimmer =
  "before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/60 before:to-transparent";

export function CardSkeleton() {
  return (
    <div
      className={`${shimmer} relative overflow-hidden rounded-xl bg-gray-400 p-2 shadow-sm`}
    >

  );
}

export function CardsSkeleton() {
  return (
    <>
      <CardSkeleton />
      <CardSkeleton />
      <CardSkeleton />
      <CardSkeleton />
      <CardSkeleton />
      <CardSkeleton />
      <CardSkeleton />
    </>
  );
}


code:
"use client"

import { useTheme } from "next-themes"
import { Toaster as Sonner, ToasterProps } from "sonner"

const Toaster = ({ ...props }: ToasterProps) => {
  const { theme = "system" } = useTheme()

  return (
    <Sonner
      theme={theme as ToasterProps["theme"]}
      className="toaster group"
      style={
        {
          "--normal-bg": "var(--popover)",
          "--normal-text": "var(--popover-foreground)",
          "--normal-border": "var(--border)",
        } as React.CSSProperties
      }
      {...props}
    />
  )
}

export { Toaster }


code:
"use client"

import * as React from "react"
import * as SwitchPrimitive from "@radix-ui/react-switch"

import { cn } from "@/app/lib/utils"

function Switch({
  className,
  ...props
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
  return (
    <SwitchPrimitive.Root
      data-slot="switch"
      className={cn(
        "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
        className
      )}
      {...props}
    >
      <SwitchPrimitive.Thumb
        data-slot="switch-thumb"
        className={cn(
          "bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
        )}
      />
    </SwitchPrimitive.Root>
  )
}

export { Switch }


code:
"use client"

import * as React from "react"

import { cn } from "@/app/lib/utils"

function Table({ className, ...props }: React.ComponentProps<"table">) {
  return (
    <div
      data-slot="table-container"
      className="relative w-full overflow-x-auto"
    >
      <table
        data-slot="table"
        className={cn("w-full caption-bottom text-sm", className)}
        {...props}
      />
    </div>
  )
}

function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
  return (
    <thead
      data-slot="table-header"
      className={cn("[&_tr]:border-b", className)}
      {...props}
    />
  )
}

function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
  return (
    <tbody
      data-slot="table-body"
      className={cn("[&_tr:last-child]:border-0", className)}
      {...props}
    />
  )
}

function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
  return (
    <tfoot
      data-slot="table-footer"
      className={cn(
        "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
        className
      )}
      {...props}
    />
  )
}

function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
  return (
    <tr
      data-slot="table-row"
      className={cn(
        "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
        className
      )}
      {...props}
    />
  )
}

function TableHead({ className, ...props }: React.ComponentProps<"th">) {
  return (
    <th
      data-slot="table-head"
      className={cn(
        "text-muted-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
        className
      )}
      {...props}
    />
  )
}

function TableCell({ className, ...props }: React.ComponentProps<"td">) {
  return (
    <td
      data-slot="table-cell"
      className={cn(
        "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
        className
      )}
      {...props}
    />
  )
}

function TableCaption({
  className,
  ...props
}: React.ComponentProps<"caption">) {
  return (
    <caption
      data-slot="table-caption"
      className={cn("text-muted-foreground mt-4 text-sm", className)}
      {...props}
    />
  )
}

export {
  Table,
  TableHeader,
  TableBody,
  TableFooter,
  TableHead,
  TableRow,
  TableCell,
  TableCaption,
}


code:
"use client"

import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"

import { cn } from "@/app/lib/utils"

function TooltipProvider({
  delayDuration = 0,
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
  return (
    <TooltipPrimitive.Provider
      data-slot="tooltip-provider"
      delayDuration={delayDuration}
      {...props}
    />
  )
}

function Tooltip({
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
  return (
    <TooltipProvider>
      <TooltipPrimitive.Root data-slot="tooltip" {...props} />
    </TooltipProvider>
  )
}

function TooltipTrigger({
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}

function TooltipContent({
  className,
  sideOffset = 0,
  children,
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
  return (
    <TooltipPrimitive.Portal>
      <TooltipPrimitive.Content
        data-slot="tooltip-content"
        sideOffset={sideOffset}
        className={cn(
          "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
          className
        )}
        {...props}
      >
        {children}
        <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
      </TooltipPrimitive.Content>
    </TooltipPrimitive.Portal>
  )
}

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }


code:
import Link from "next/link";
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import { Category } from "@/app/lib/definitions";
import { auth } from "@/auth";
import { LinkIcon } from "lucide-react";
import { fetchCategories, fetchGroups } from "@/app/lib/data";
import GenerateGroupsPlayers from "@/app/components/GenerateGroupsMatches";

export default async function GroupsPage() {
  const session = await auth();
  if (!session?.user || !session?.user?.apiKey) {
    throw new Error("No autorizado");
  }
  const apiKey = session.user?.apiKey;
  const { pagination } = await fetchGroups(apiKey);
  const existGruops = pagination.count > 0;
  let categories = null;
  if (existGruops) {
    const { data } = await fetchCategories(apiKey);
    categories = data;
  }

  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
          ]}
          currentPage="Grupos"
        />

          Grupos

          {!existGruops ? (
            <GenerateGroupsPlayers />
          ) : (
            categories.map((category: Category) => {
              return (
                <Link
                  key={category.id}
                  href={`/grupos/${category.id}`}
                  className="flex flex-col items-center gap-y-1.5 px-4 py-2 border bg-primary text-white font-bold hover:opacity-80 transition-colors"
                >
                  Ver Grupos Categoria {category.description}
                  <LinkIcon />
                </Link>
              );
            })
          )}

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import InscriptionForm from "@/app/components/InscripcionForm";
import { Suspense } from "react";

export default function InscriptionPage() {
  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
          ]}
          currentPage="Inscripcion"
        />

          Inscripción de jugadores

        <Suspense>
          <InscriptionForm />
        </Suspense>

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import EditPlayerForm from "@/app/components/EditPlayerForm";
import { fetchPlayerById, getApiKey } from "@/app/lib/data";
import { Player } from "@/app/lib/definitions";
import { notFound } from "next/navigation";
// import { notFound } from "next/navigation";

export default async function EditPlayerPage(props: {
  params: Promise<{ id: string }>;
}) {
  const params = await props.params;
  const id = params.id;
  const apiKey = await getApiKey();
  const playerInfo = await fetchPlayerById({ apiKey, idPlayer: id });
  if (!playerInfo) {
    notFound();
  }

  const playerData: Player = playerInfo.data;
  console.log(playerData);
  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
            {
              label: "Jugadores",
              href: "/dashboard/jugadores",
            },
          ]}
          currentPage="Editar Juagdor"
        />

          Editar Jugador

        {/* form */}
        {playerData && <EditPlayerForm player={playerData} />}

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import { columns, PlayerRow } from "@/app/components/PlayersCols";
import TablePlayers from "@/app/components/TablePlayers";
import { fetchTablePlayerData } from "@/app/lib/data";
import { auth } from "@/auth";

export default async function PlayerPage() {
  const session = await auth();
  if (!session?.user || !session?.user?.apiKey) {
    throw new Error("No autorizado");
  }
  const apiKey = session.user?.apiKey;
  const tableData: PlayerRow[] = await fetchTablePlayerData(apiKey);
  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Juagdores",
              href: "/dashboard",
            },
          ]}
          currentPage="Parejas"
        />

          Jugadores

        {/* Table of Players */}
        <TablePlayers columns={columns} data={tableData} />

  );
}


code:
import { SidebarProvider, SidebarTrigger } from "@/app/components/ui/sidebar";
import AppSideNav from "@/app/components/AppSideNav";
import { Toaster } from "@/app/components/ui/sonner";

export default function DashBoardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div>
      <SidebarProvider>
        <AppSideNav />
        <main className="w-full">
          <SidebarTrigger />
          {children}
        </main>
      </SidebarProvider>
      <Toaster />
    </div>
  );
}


code:
import CardsDashboard from "@/app/components/CardsDashboard";
import { Suspense } from "react";
import { CardsSkeleton } from "@/app/components/ui/skeletons";

export default function DashboardPage() {
  return (

          Dashboard

          <Suspense fallback={<CardsSkeleton />}>
            <CardsDashboard />
          </Suspense>

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import PairEditForm from "@/app/components/PairEditForm";
import {
  fetchAllPlayers,
  fetchCategories,
  fetchPairById,
  fetchPairPlayersById,
  getApiKey,
} from "@/app/lib/data";
import { Player } from "@/app/lib/definitions";
import { notFound } from "next/navigation";
// import { notFound } from "next/navigation";

export default async function EditPairPage(props: {
  params: Promise<{ id: string }>;
}) {
  const params = await props.params;
  const id = params.id;
  const apiKey = await getApiKey();
  const pairInfo = await fetchPairById({ apiKey, idPair: id });
  if (!pairInfo) {
    notFound();
  }

  const playersData: Player[] = await fetchAllPlayers(apiKey);
  const categoriesData = await fetchCategories(apiKey);
  const { data: players } = await fetchPairPlayersById({ apiKey, idPair: id });
  const playersIds = players.map(({ player }: { player: Player }) => {
    return {
      idPlayer: player.id,
    };
  });
  const pair = {
    id,
    categoryId: pairInfo.data.registration_category.id,
    playersIds: playersIds,
  };

  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
            {
              label: "Parejas",
              href: "/dashboard/parejas",
            },
          ]}
          currentPage="Editar Pareja"
        />

          Editar Pareja

        {/* form */}
        <PairEditForm
          pair={pair}
          playersList={playersData}
          categoriesList={categoriesData.data}
        />

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import PairsForm from "@/app/components/PairsForm";
import { Player } from "@/app/lib/definitions";
import { fetchAllPlayers, fetchCategories } from "@/app/lib/data";
import { auth } from "@/auth";
import { Suspense } from "react";

export default async function CreatePairsPage() {
  const session = await auth();
  if (!session?.user || !session?.user?.apiKey) {
    throw new Error("No autorizado");
  }
  const apiKey = session.user.apiKey;

  const playersData: Player[] = await fetchAllPlayers(apiKey);
  const categoriesData = await fetchCategories(apiKey);

  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
            {
              label: "Parejas",
              href: "/dashboard/parejas",
            },
          ]}
          currentPage="Crear Parejas"
        />

          Crear Parejas

        <Suspense>
          <PairsForm
            playersList={playersData}
            categoriesList={categoriesData.data}
          />
        </Suspense>

  );
}


code:
"use client";

import { useEffect } from "react";

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // Optionally log the error to an error reporting service
    console.error(error);
  }, [error]);

  return (

Algo ha salido mal 😔!

      <button
        className="mt-4 rounded-md bg-primary px-4 py-2 text-sm text-white transition-colors hover:bg-primary/80"
        onClick={
          // Attempt to recover by trying to re-render the invoices route
          () => reset()
        }
      >
        Volver a intentar
      </button>

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import { ButtonCreatePair } from "@/app/components/ButtonsPairs";
import { columns, PairRow } from "@/app/components/PairsCols";
import TablePairs from "@/app/components/TablePairs";
import { fetchTablePairData } from "@/app/lib/data";
import { auth } from "@/auth";

export default async function PairsPage() {
  const session = await auth();
  if (!session?.user || !session?.user?.apiKey) {
    throw new Error("No autorizado");
  }
  const apiKey = session.user?.apiKey;
  const tableData: PairRow[] = await fetchTablePairData(apiKey);

  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
          ]}
          currentPage="Parejas"
        />

          Parejas

        {/* Create button and searchBAR*/}

          {/* <Search placeholder="Search invoices..." /> */}
          <ButtonCreatePair />

        {/* Table of Pairs */}
        <TablePairs columns={columns} data={tableData} />
        {/* Pagination */}

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import MatchesForm from "@/app/components/MatchesForm";
import {
  fetchAllPairs,
  fetchCategories,
  fetchMatchesCategories,
  fetchStatusCategories,
} from "@/app/lib/data";
import { auth } from "@/auth";
import { Suspense } from "react";

export default async function CreatePairsPage() {
  const session = await auth();
  if (!session?.user || !session?.user?.apiKey) {
    throw new Error("No autorizado");
  }
  const apiKey = session.user.apiKey;

  const pairsList = await fetchAllPairs(apiKey);
  const { data: categoriesList } = await fetchCategories(apiKey);
  const { data: matchCategoriesList } = await fetchMatchesCategories(apiKey);
  const { data: matchStatusList } = await fetchStatusCategories(apiKey);

  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
            {
              label: "Partidos",
              href: "/dashboard/partidos",
            },
          ]}
          currentPage="Crear Partidos"
        />

          Crear Partdidos

        <Suspense>
          <MatchesForm
            pairsList={pairsList}
            categoriesList={categoriesList}
            matchCategoriesList={matchCategoriesList}
            mastStatusList={matchStatusList}
          />
        </Suspense>

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import TableMatches from "@/app/components/TableMatches";
import { fetchTableMatchesByCategory } from "@/app/lib/data";
import { auth } from "@/auth";
import { columns, MatchesRow } from "@/app/components/MatchesCols";
import Link from "next/link";
import { PlusIcon } from "lucide-react";

type Player = {
  id: string;
  fullname: string;
};

type MatchPair = {
  id: string;
  players: Player[];
  score: number;
  isWinner: number;
};

type MatchByGroup = {
  id: string;
  isActive: number;
  matchCategory: {
    id: string;
    name: string;
    description: string;
  };
  matchStatus: {
    id: string;
    name: string;
    description: string;
  };
  matchPairs: MatchPair[];
};

export default async function MatchesByCategoryPage(props: {
  params: Promise<{ idCategory: string }>;
}) {
  const session = await auth();
  if (!session?.user || !session?.user?.apiKey) {
    throw new Error("No autorizado");
  }
  const apiKey = session.user?.apiKey;
  const params = await props.params;
  const idCategory = params.idCategory;
  const { categoryData, matchesData } = await fetchTableMatchesByCategory({
    apiKey,
    idCategory,
  });

  const transformedDataMatches: MatchesRow[] = matchesData.flatMap((group) =>
    group.matchesInfoByCategoryByGroup.map((match: MatchByGroup) => ({
      id: match.id,
      idCategory,
      group: group.description,
      pair1: {
        id: match.matchPairs[0].id,
        players: match.matchPairs[0].players.map((player) => player.fullname),
        isWinner: match.matchPairs[0].isWinner,
      },
      pair2: {
        id: match.matchPairs[1].id,
        players: match.matchPairs[1].players.map((player) => player.fullname),
        isWinner: match.matchPairs[1].isWinner,
      },
      pointsPair1: match.matchPairs[0].score,
      pointsPair2: match.matchPairs[1].score,
    }))
  );

  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
            {
              label: "Partidos",
              href: "/dashboard/partidos",
            },
          ]}
          currentPage={`Partidos Categoria ${categoryData.description}`}
        />

          Partidos Categoria {categoryData.description}

          <Link
            href={`/dashboard/partidos/${idCategory}/crear`}
            className="px-4 py-3 flex items-center rounded-lg text-sm font-medium transition-colors hover:bg-dark/40 border border-gray-800"
          >
            Agregar Partido{" "}
            <PlusIcon className="h-5 md:ml-4" />
          </Link>

        <TableMatches columns={columns} data={transformedDataMatches} />

  );
}


code:
import AppBreadcrumb from "@/app/components/AppBreadcrumb";
import GenerateGroupsPlayers from "@/app/components/GenerateGroupsMatches";
import { fetchCategories, fetchGroups } from "@/app/lib/data";
import { Category } from "@/app/lib/definitions";
import { auth } from "@/auth";
import Link from "next/link";

export default async function MatchesPage() {
  const session = await auth();
  if (!session?.user || !session?.user?.apiKey) {
    throw new Error("No autorizado");
  }
  const apiKey = session.user?.apiKey;
  const { pagination } = await fetchGroups(apiKey);
  const existGruops = pagination.count > 0;
  let categories = null;
  if (existGruops) {
    const { data } = await fetchCategories(apiKey);
    categories = data;
  }
  return (

        <AppBreadcrumb
          prevBreadcrumbs={[
            {
              label: "Dashboard",
              href: "/dashboard",
            },
          ]}
          currentPage="Partidos"
        />

          Partidos

          {!existGruops ? (
            <GenerateGroupsPlayers />
          ) : (
            categories.map((category: Category) => {
              return (
                <Link
                  key={category.id}
                  href={`/dashboard/partidos/${category.id}`}
                  className="flex flex-col items-center gap-y-1.5 px-4 py-2 border bg-primary text-white font-bold hover:opacity-80 transition-colors"
                >
                  Ver Partidos Categoria {category.description}
                </Link>
              );
            })
          )}

  );
}


title: 👋 Recepción de participantes

copy: Bienvenida a los participantes

title: 🤝 Cierre de Recepción

copy: Cierre de registro de asistencia de parejas

title: 🥳 Inaguración del Torneo

copy: Ceremonia de Inaguración e indicaciones completas a los y las participantes

title: 💪 Inicio de Fase de Grupos

copy: Inicia la fase de grupos a doble eliminación

title: 🎾 Inicio de Fase Eliminatoria

copy: Inicio a la recta final, fase eliminatoria (8vos, 4tos, semifinales)

title: 🎉 Gran Final del Torneo

copy: Damos paso a la gran final


title: Playera TDLF-2025

description: ¡Lleva el espíritu urbano de Irapuato en cada golpe! La playera conmemorativa del Torneo de las Fresas 2025 es un tributo vibrante a la cultura callejera o urbana de la ciudad. Con una tipografía inspirada en el estilo de graffiti y grietas que recuerdan las calles de la ciudad, esta playera combina autenticidad urbana con un diseño exclusivo. Hecha con material suave y resistente, es perfecta para portarla en un juego o para lucirla en cualquier ocasión. ¡Un recuerdo único de la emoción del torneo!

title: Gorra TDLF-2025

description: ¡Lleva el orgullo del Torneo de las Fresas 2025 en tu cabeza! La gorra conmemorativa destaca el escudo oficial del torneo, un emblemático diseño que combina la fresa, símbolo de la ciudad, con el frontenis, representando la pasión por el deporte. Las sombras de fondo evocan los edificios icónicos de Irapuato, creando un tributo visual a la ciudad y su espíritu deportivo. Hecha para ser un recuerdo duradero, esta gorra es perfecta para mostrar tu apoyo durante y después del torneo.


code:
import Image from "next/image";
import Footer from "@/app/components/landing/sections/Footer";
import {
  MouseFollowWrapper,
  MouseFollowCard,
} from "@/app/components/landing/MouseFollowWrapper";
import EmotionsResults from "@/app/components/EmotionsResults";

export default async function EmotionsPage() {
  return (

              Torneo De Las Fresas

            Tercera edición

              2025

                Análisis de emociones
                Final TDLF

                  Las palabras que definieron la tercera edición del Torneo De
                  Las Fresas

                <MouseFollowWrapper>
                  <MouseFollowCard>

                        ¿Para qué sirve?

                          Visualizar tendencias: Las{" "}

                            palabras más grandes son las que más se mencionaron

                          , revelando los temas que dominaron la conversación
                          (ej: "Macizo", "bola",
                          "marcador").

                          Capturar la esencia: Refleja el{" "}

                            ambiente del evento

                          , desde gritos de apoyo hasta reacciones a jugadas
                          polémicas.

                          Identificar protagonistas: ¿Se{" "}

                            {" "}
                            habló más de un jugador
                          {" "}
                          en particular? ¿De estrategias clave? El wordcloud lo
                          muestra.

                  </MouseFollowCard>
                  <MouseFollowCard>

                        ¿Qué encontrarás?

                          Palabras clave: Términos como

                            "chapa", "que bola" (para
                            jugadas brillantes) o "fuera"
                          {" "}
                          que marcaron la final.

                          Emociones en tiempo real: ¡Vamos!,
                          ¡Fuerza!, ¡Robo!...{" "}

                            Una radiografía del ánimo del público.

                          Homenaje al torneo: Incluye
                          referencias únicas al espíritu del "Torneo de las
                          Fresas", como{" "}

                            menciones locales, saludos o apodos de los
                            jugadores.

                  </MouseFollowCard>
                  <MouseFollowCard>

                        ¿Por qué es relevante?

                        Este gráfico no solo decora,{" "}

                          cuenta la historia no escrita del partido.
                        {" "}
                        Es una herramienta para los analistas de datos,{" "}

                          un recuerdo para los fanáticos y un tributo a la
                          pasión
                        {" "}
                        que convierte cada edición del torneo en algo
                        legendario.

                  </MouseFollowCard>
                </MouseFollowWrapper>

                  Este wordcloud (nube de palabras) es una ventana a las
                  emociones y momentos clave vividos durante la Gran Final del
                  Torneo de las Fresas. Aquí podrás descubrir, de un vistazo,
                  las palabras más repetidas por el público, comentaristas y
                  jugadores durante el partido decisivo disputado entre la
                  pareja de{" "}

                    Gerardo Martinez y Jesus Arguellas y la pareja de Fernando
                    Cortez y Abimael Tapia

                <picture className="flex justify-center w-full h-full max-w-4xl mx-auto">
                  <Image
                    src={"/final-wordcloud.png"}
                    alt="Nube de palabras final TDLF 2025"
                    width={900}
                    height={768}
                    className="w-full object-contain h-full min-h-[30vh]"
                  ></Image>
                </picture>

                Análisis de Sentimientos con Machine Learning: La Emoción en
                Palabras

                Usando VADER y otros modelos de Inteligencia Artificial
                especializados en detectar emociones en textos, analizamos los
                gritos y comentarios del público durante la final. Este sistema,
                entrenado para capturar intensidad y contexto, reveló qué tan
                positivas, neutras o negativas fueron las reacciones ante cada
                jugada clave.

              <EmotionsResults />

  );
}


code:
import BarChart from "@/app/components/BarChart";
import {
  fetchCategoryById,
  fetchParitiesByCategory,
  fetchPointsChartByCategory,
  fetchSynergyByCategory,
  getApiKey,
} from "@/app/lib/data";
import Footer from "@/app/components/landing/sections/Footer";
import { notFound } from "next/navigation";
import { Suspense } from "react";

export default async function StatsCategoryPage(props: {
  params: Promise<{ categoryId: string }>;
}) {
  const apiKey = await getApiKey();
  const params = await props.params;
  const idCategory = params.categoryId;
  const categoryData = await fetchCategoryById({ apiKey, idCategory });
  if (!categoryData) {
    notFound();
  }

  const { data: category } = categoryData;
  const { data: dataParitiesByCategory } = await fetchParitiesByCategory({
    apiKey,
    idCategory,
  });
  const { data: dataSynergyByCategory } = await fetchSynergyByCategory({
    apiKey,
    idCategory,
  });

  const pointsByGroup = await fetchPointsChartByCategory({
    apiKey,
    idCategory,
  });

  const {
    data: dataParity,
    title: titleParity,
    "x-axis": xAxisParity,
    "y-axis": yAxisParity,
  } = dataParitiesByCategory;

  const {
    data: dataSynergy,
    title: titleSynergy,
    "x-axis": xAxisSynergy,
    "y-axis": yAxisSynergy,
  } = dataSynergyByCategory;

  dataSynergy[0] = {
    name: "Sinergia positiva",
    ...dataSynergy[0],
  };
  dataSynergy[1] = {
    name: "Sinergia negativa",
    ...dataSynergy[1],
  };

  return (

              Torneo De Las Fresas

            Tercera edición

              2025

                Estadisticas Categoria{" "}

                  {category.description}

                {pointsByGroup.map((pointsGroup) => {
                  const {
                    data: dataGroup,
                    title: titleGroup,
                    "y-axis": yAxisGroup,
                  } = pointsGroup;

                  return (
                    <Suspense fallback={
Cargando...
} key={titleGroup}>
                      <BarChart
                        dataChart={dataGroup}
                        titleChart={titleGroup}
                        xAxisTitle={""}
                        yAxisTitle={yAxisGroup}
                      >

                            Compara el rendimiento ofensivo (puntos hechos) y
                            defensivo (puntos recibidos) de cada pareja en su
                            grupo.

Puntos hechos: Efectividad en ataque.

Puntos recibidos: Solidez defensiva.

                              Muchos puntos hechos y muchos puntos recibidos:
                              Estilo arriesgado.

                              Pocos puntos recibidos: Buen control o rivales
                              débiles.

                            Pasa el mouse o da click en las barras para ver más
                            detalles de cada registro

                      </BarChart>
                    </Suspense>
                  );
                })}

              <Suspense fallback={
Cargando...
}>
                <BarChart
                  dataChart={dataParity}
                  titleChart={titleParity}
                  xAxisTitle={xAxisParity}
                  yAxisTitle={yAxisParity}
                >

                      Mide el equilibrio competitivo en cada grupo.{" "}
                      Cercano a cero = más parejo.

                        Cerca de 0: Grupo equilibrado (resultados
                        impredecibles).

Lejos de 0: Mayor dominio de algunos equipos.

                      Calculado con desviación estándar (std): menor valor,
                      mayor paridad.

                      Pasa el mouse o da click en las barras para ver más
                      detalles de cada registro

                </BarChart>
              </Suspense>
              <Suspense fallback={
Cargando...
}>
                <BarChart
                  dataChart={dataSynergy}
                  titleChart={titleSynergy}
                  xAxisTitle={xAxisSynergy}
                  yAxisTitle={yAxisSynergy}
                >

                      Mide el rendimiento colectivo comparando puntos ganados
                      (Ph) vs. recibidos (Pr), expresado en porcentaje.

                        🔥+40% o más (Alta sinergia) → ¡Casi la excelencia! El
                        equipo domina con estrategias sólidas.

                        📈 +10% a +39% (Buena sinergia) → Hay muy buen
                        potencial, con práctica y consistencia se elevará el
                        nivel.

                        ⚖ -10% a +10% (Ni buena, ni mala sinergia) → Partidos
                        ajustados, los detalles marcan la diferencia.

                        ⚠ -10% a -50% (Alerta) → Patrones de error detectados.
                        Se recomienda analizar la situación.

                        🆘 -50% o menos (Sinergia crítica) → Problemas con la
                        sinergia. No quiere decir que sean malos, simplemente se
                        podrían redefinir los roles o mejorar la comunicación.

                      Nota:  El % refleja el momento actual.
                      ¡Con trabajo, puede mejorar!

                      Pasa el mouse o da click en las barras para ver más
                      detalles de cada registro

                </BarChart>
              </Suspense>

  );
}


code:
import { Suspense } from "react";
import { CardsSkeleton } from "@/app/components/ui/skeletons";
import CardsStats from "@/app/components/CardsStats";
import {
  fetchCategories,
  fetchHeatMap,
  fetchPointsComparative,
  fetchSynergiesComparative,
  getApiKey,
} from "@/app/lib/data";
import HeaderMap from "@/app/components/HeaderMap";
import Footer from "@/app/components/landing/sections/Footer";
import Link from "next/link";
import { Category } from "../lib/definitions";
import BarChart from "../components/BarChart";

export default async function StatsPage() {
  const apiKey = await getApiKey();
  const { data: dataMapContent } = await fetchHeatMap(apiKey);

  const mapFormatedContent = dataMapContent.map.replaceAll(/[+']/g, "");
  const { data: categories } = await fetchCategories(apiKey);

  const { data: dataPointsComparative } = await fetchPointsComparative(apiKey);
  const { data: dataSynergiesComparative } = await fetchSynergiesComparative(
    apiKey
  );

  return (

              Torneo De Las Fresas

            Tercera edición

              2025

              Estadisticas Generales

              <Suspense fallback={<CardsSkeleton />}>
                <CardsStats />
              </Suspense>

                Mapa de calor de origen de los participantes

                  Este mapa destaca la distribución geográfica de los jugadores
                  o parejas que participaron en el Torneo de las Fresas 2025.
                  Cada marca de calor representa el origen de los competidores,
                  donde las zonas de calor se hacen más grandes y faciles de
                  vizualizar a medida de que el numero de participantes de esa
                  zona geografica fue mayor.

div>div>div>iframe]:h-[400px] md:[&>iframe]:h-full">
                  <HeaderMap content={mapFormatedContent} />

              <Suspense fallback={
Cargando...
}>
                <BarChart
                  dataChart={dataPointsComparative.data}
                  titleChart={dataPointsComparative.title}
                  xAxisTitle={dataPointsComparative["x-axis"]}
                  yAxisTitle={dataPointsComparative["y-axis"]}
                >

                      Total puntos todos los grupos ÷ Número de grupos

Que muestra:

Promedios altos: Categorías ofensivas/dominantes

                        Promedios bajos: Categorías defensivas/equilibradas

                        Promedios similares: Mismo ritmo de juego en todas las
                        categorías

Cuando son muy similares:

El torneo mantiene equilibrio entre categorías

La edad/tipo no afecta la productividad

                        Experiencia de juego consistente en todos los grupos

                      Pasa el mouse o da click en las barras para ver más
                      detalles de cada registro

                </BarChart>
              </Suspense>
              <Suspense fallback={
Cargando...
}>
                <BarChart
                  dataChart={dataSynergiesComparative.data}
                  titleChart={dataSynergiesComparative.title}
                  xAxisTitle={dataSynergiesComparative["x-axis"]}
                  yAxisTitle={dataSynergiesComparative["y-axis"]}
                >

                      Muestra el promedio de sinergia entre ambas categorías:

Libre: Sin restricción de edad.

50 y más: Jugadores de 50 años o más.

Interpretación:

                        Mayor sinergia en Libre: Posible mejor coordinación en
                        equipos jóvenes/mixtos.

                        Mayor sinergia en 50 y más: La experiencia compensa la
                        física.

                        Valores similares: Mismo nivel de trabajo en equipo.

                      Pasa el mouse o da click en las barras para ver más
                      detalles de cada registro

                </BarChart>
              </Suspense>

              {categories.map((category: Category) => {
                return (
                  <Link
                    key={category.id}
                    href={`/estadisticas/${category.id}`}
                    className="flex flex-col items-center gap-y-1.5 px-4 py-2 border bg-primary text-white font-bold hover:opacity-80 transition-colors"
                  >
                    Ver Estadisticas Categoria {category.description}
                  </Link>
                );
              })}

  );
}


code:
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "tailwindcss-animate";

@custom-variant dark (&:is(.dark *));

@theme {
  /* Brakpoints */
  --breakpoint-xs: 28rem;
  /* Fonts */
  --font-space: var(--font-space-grotesk);
  --font-nosifer: var(--font-nosifer);
  /* Colors */
  --color-primary: #E61357;
  --color-primary-darker: #810B31;
  --color-dark: #212529;
  /* Animations */
  --animate-scroll-header: scroll-header linear both;
  --shadow-awards: inset 0 2px 8px #fff,0 0 17px rgba(255,255,255,.77),inset 0 1px 10px hsla(0,0%,100%,.55);

  @keyframes scroll-header {
    to {
      top: 0;
      backdrop-filter: blur(8px);
      background: rgba(0,0,0,.5);
    }
  }

}
html {
  scroll-behavior: smooth;
}

body {
  color: var(--foreground);
  background: var(--background);
  min-height: 100vh;
  min-height: 100dvh;
  font-family: var(--font-space-grotesk), Arial, Helvetica, sans-serif;
}

p, li {
  text-wrap: pretty;
}

h1, h2, h3, h4, h5, h6 {
  text-wrap: balance;
}

:root {
  --background: #0a0a0a;
  --foreground: #ededed;
  --card: oklch(0.145 0 0);
  --card-foreground: oklch(0.985 0 0);
  --popover: oklch(0.145 0 0);
  --popover-foreground: oklch(0.985 0 0);
  --primary-foreground: oklch(0.205 0 0);
  --secondary: oklch(0.269 0 0);
  --secondary-foreground: oklch(0.985 0 0);
  --muted: oklch(0.269 0 0);
  --muted-foreground: oklch(0.708 0 0);
  --accent: oklch(0.269 0 0);
  --accent-foreground: oklch(0.985 0 0);
  --destructive: oklch(0.396 0.141 25.723);
  --destructive-foreground: oklch(0.637 0.237 25.331);
  --border: oklch(0.269 0 0);
  --input: oklch(0.269 0 0);
  --ring: #b8b8b8;
  --chart-1: oklch(0.488 0.243 264.376);
  --chart-2: oklch(0.696 0.17 162.48);
  --chart-3: oklch(0.769 0.188 70.08);
  --chart-4: oklch(0.627 0.265 303.9);
  --chart-5: oklch(0.645 0.246 16.439);
  --sidebar: oklch(0.205 0 0);
  --sidebar-foreground: oklch(0.985 0 0);
  --sidebar-primary: oklch(0.488 0.243 264.376);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent: oklch(0.269 0 0);
  --sidebar-accent-foreground: oklch(0.985 0 0);
  --sidebar-border: oklch(0.269 0 0);
  --sidebar-ring: oklch(0.439 0 0);
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #0a0a0a;
    --foreground: #ededed;
  }
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-card: var(--card);
  --color-card-foreground: var(--card-foreground);
  --color-popover: var(--popover);
  --color-popover-foreground: var(--popover-foreground);
  --color-primary-foreground: var(--primary-foreground);
  --color-secondary: var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);
  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);
  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);
  --color-destructive: var(--destructive);
  --color-destructive-foreground: var(--destructive-foreground);
  --color-border: var(--border);
  --color-input: var(--input);
  --color-ring: var(--ring);
  --color-chart-1: var(--chart-1);
  --color-chart-2: var(--chart-2);
  --color-chart-3: var(--chart-3);
  --color-chart-4: var(--chart-4);
  --color-chart-5: var(--chart-5);
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
  --color-sidebar: var(--sidebar);
  --color-sidebar-foreground: var(--sidebar-foreground);
  --color-sidebar-primary: var(--sidebar-primary);
  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
  --color-sidebar-accent: var(--sidebar-accent);
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
  --color-sidebar-border: var(--sidebar-border);
  --color-sidebar-ring: var(--sidebar-ring);
}

.dark {
  --sidebar: hsl(240 5.9% 10%);
  --sidebar-foreground: hsl(240 4.8% 95.9%);
  --sidebar-primary: hsl(224.3 76.3% 48%);
  --sidebar-primary-foreground: hsl(0 0% 100%);
  --sidebar-accent: hsl(240 3.7% 15.9%);
  --sidebar-accent-foreground: hsl(240 4.8% 95.9%);
  --sidebar-border: hsl(240 3.7% 15.9%);
  --sidebar-ring: hsl(217.2 91.2% 59.8%);

}

@layer base {
  * {
    @apply border-border outline-ring/50;

  }
  body {
    @apply bg-background text-foreground;

  }

}

.grid-pattern-background {
  --_heigh-pattern: 100vh;
  --_num-lines: 7vmin;
  --_degrees-mask: 0deg;
  --line: hsl(0 0% 95% / 0.2);
}

.grid-pattern-background::before {
  content: "";
  display: block;
  height: var(--_heigh-pattern);
  width: 100%;
  position: absolute;
  z-index: -1;
  background:
  linear-gradient(90deg, var(--line) 1px, transparent 1px 10vmin) 50% -5vmin /
  var(--_num-lines) var(--_num-lines),
  linear-gradient(var(--line) 1px, transparent 1px 10vmin) 50% -5vmin / var(--_num-lines) var(--_num-lines);
  top: 0;
  -webkit-mask: linear-gradient(var(--_degrees-mask), transparent 15%, white);
  mask: linear-gradient(var(--_degrees-mask), transparent 15%, white);
}


code:
import { notFound } from "next/navigation";
import {
  getApiKey,
  fetchMatchesPairsPlayersByGroup,
  fetchRankingByGroup,
} from "@/app/lib/data";
import Footer from "@/app/components/landing/sections/Footer";
import Match from "@/app/components/Match";
import { MatchByGroup } from "@/app/lib/definitions";
import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableFooter,
  TableHead,
  TableHeader,
  TableRow,
} from "@/app/components/ui/table";

type StatsByPair = {
  id: string;
  player1: string;
  player2: string;
  wins: string;
  totalPoints: string;
};

interface Player {
  id: string;
  fullname: string;
  city: string;
  weight: string;
  height: string;
  age: number;
  experience: number;
  is_active: number;
  created_at: string;
  updated_at: string;
}

interface Relationship {
  id: string;
  player_id: string;
  pair_id: string;
  created_at: string;
  updated_at: string;
}

interface PlayerWithRelationship {
  player: Player;
  relationship: Relationship;
}

interface RegistrationCategory {
  id: string;
  name: string;
  description: string;
}

interface Pair {
  id: string;
  is_eliminated: number;
  is_active: number;
  created_at: string;
  updated_at: string;
  registration_category: RegistrationCategory;
  players: PlayerWithRelationship[];
}

interface Rating {
  total_winners: string;
  total_score: string;
}

interface PairWithRating {
  pair: Pair;
  rating: Rating;
}

export default async function GroupPageByCategory(props: {
  params: Promise<{ categoryId: string; groupId: string }>;
}) {
  const apiKey = await getApiKey();
  const params = await props.params;
  const { groupId } = params;
  const groupsByCategory: MatchByGroup = await fetchMatchesPairsPlayersByGroup({
    idGroup: groupId,
    apiKey,
  });
  if (!groupsByCategory) {
    notFound();
  }

  const { categoryData, groupData, matchesData } = groupsByCategory;

  const { data: rankingData } = await fetchRankingByGroup({
    apiKey,
    idGroup: groupId,
  });

  const statsByPair = rankingData.map((item: PairWithRating) => {
    const player1 = item.pair.players[0].player.fullname;
    const player2 = item.pair.players[1].player.fullname;
    const wins = parseInt(item.rating.total_winners);
    const totalPoints = parseInt(item.rating.total_score);

    return {
      id: item.pair.id,
      player1,
      player2,
      wins,
      totalPoints,
    };
  });

  return (

              Torneo De Las Fresas

            Tercera edición

              2025

              Partidos Grupo {groupData.description} - Categoria{" "}
              {categoryData.description}

              {matchesData.map((match) => {
                return <Match key={match.id} pairs={match.matchPairs} />;
              })}

              Tabla de Matches

              <TableCaption>Lista de matches</TableCaption>
              <TableHeader>
                <TableRow>
                  <TableHead className="text-center xs:text-left text-sm xs:text-base">
                    Pareja
                  </TableHead>
                  <TableHead className="text-center xs:text-left text-sm xs:text-base">
                    Victorias
                  </TableHead>
                  <TableHead className="text-center xs:text-left text-sm xs:text-base">
                    Puntos Totales
                  </TableHead>
                </TableRow>
              </TableHeader>

              <TableBody>
                {statsByPair.map((statDuo: StatsByPair) => (
                  <TableRow key={statDuo.id}>
                    <TableCell>
                      {statDuo.player1} / {statDuo.player2}
                    </TableCell>
                    <TableCell>{statDuo.wins}</TableCell>
                    <TableCell>{statDuo.totalPoints}</TableCell>
                  </TableRow>
                ))}
              </TableBody>

              <TableFooter>
                <TableRow>
                  <TableCell colSpan={2}>Total Parejas</TableCell>
                  <TableCell>4</TableCell>
                </TableRow>
              </TableFooter>

  );
}


code:
import { notFound } from "next/navigation";
import { fetchGroupsPairsPlayersByCategory, getApiKey } from "@/app/lib/data";
import Group from "@/app/components/Group";
import { GroupByCategory } from "@/app/lib/definitions";
import Footer from "@/app/components/landing/sections/Footer";
import { Suspense } from "react";
import { CardsSkeleton } from "@/app/components/ui/skeletons";

export default async function GroupsPageByCategory(props: {
  params: Promise<{ categoryId: string }>;
}) {
  const apiKey = await getApiKey();
  const params = await props.params;
  const idCategory = params.categoryId;
  const groupsByCategory: GroupByCategory =
    await fetchGroupsPairsPlayersByCategory({ idCategory, apiKey });
  if (!groupsByCategory) {
    notFound();
  }

  // console.log(groupsByCategory);
  const { categoryData, groupsData } = groupsByCategory;
  return (

              Torneo De Las Fresas

            Tercera edición

              2025

              Grupos Categoria {categoryData.description}

              <Suspense fallback={<CardsSkeleton />}>
                {groupsData.map((group) => {
                  return (
                    <Group
                      key={group.id}
                      categoryId={idCategory}
                      groupId={group.id}
                      name={group.description}
                      listPairs={group.pairs}
                    />
                  );
                })}
              </Suspense>

  );
}


code:
export default function GroupsPagePublic() {
  return (

  );
}


code:
import * as React from "react"

const MOBILE_BREAKPOINT = 768

export function useIsMobile() {
  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)

  React.useEffect(() => {
    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
    const onChange = () => {
      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    }
    mql.addEventListener("change", onChange)
    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    return () => mql.removeEventListener("change", onChange)
  }, [])

  return !!isMobile
}


code:
import { Frown } from "lucide-react";
import Link from "next/link";

export default function NotFound() {
  return (

      <Frown className="w-10 text-gray-400" />

404 Not Found

No se pudo encontrar el jugador

      <Link
        href="/dashboard/parejas"
        className="mt-4 rounded-md bg-primary px-4 py-2 text-sm text-white transition-colors hover:bg-primary-darker"
      >
        Volver
      </Link>

  );
}


code:
import Image from "next/image";
import { notFound } from "next/navigation";
import { fetchPlayerById, fetchProfile, getApiKey } from "@/app/lib/data";
import { MapPin, Anvil, Ruler, Cake } from "lucide-react";

export default async function PlayerPage(props: {
  params: Promise<{ id: string }>;
}) {
  const apiKey = await getApiKey();
  const params = await props.params;
  const id = params.id;
  const playerInfo = await fetchPlayerById({ idPlayer: id, apiKey });
  if (!playerInfo.data) {
    notFound();
  }
  const { data: profileData } = await fetchProfile({ apiKey, idPlayer: id });

  const { fullname, city, weight, height, age, experience } = playerInfo.data;
  const { position, priority, workout } = profileData;
  return (

            Torneo De Las Fresas

          Tercera edición

            2025

{fullname}

              {experience} años jugando

                Recomendaciones generales:

                  🚩Posicion: {position}

                  💪Entrenamiento: {workout}

                  🎯Prioridad: {priority}

                * Nota: Estas son recomendaciones de juego y
                entrenamiento basadas en las características físicas del
                jugador. Si el estilo de juego o entrenamiento es diferente no
                tienes que seguirlas estrictamente.

                <MapPin size={36} />
                {city}

                <Anvil size={36} />
                {weight} kgs

                <Ruler size={36} />
                {height} mts

                <Cake size={36} />
                {age} años

            <Image
              src={"/logo-black-white.svg"}
              width={600}
              height={600}
              alt="Logo Blaco-Negro TDLF-2025"
              className="pointer-events-none absolute top-1/2 left-1/2 -translate-1/2 opacity-5 invert-100"
            />

      <picture className="inline-block absolute -bottom-[20%] -left-[10%]">
        <Image
          src={"/logo-black-white.svg"}
          width={700}
          height={700}
          alt="Logo Blaco-Negro TDLF-2025"
          className="opacity-25"
        />
      </picture>

  );
}


code:
import type { Metadata } from "next";
import { Space_Grotesk, Nosifer } from "next/font/google";
import "./globals.css";

const spaceGrotesk = Space_Grotesk({
  variable: "--font-space-grotesk",
  subsets: ["latin"],
});

const nosifer = Nosifer({
  variable: "--font-nosifer",
  subsets: ["latin"],
  weight: ["400"],
});

export const metadata: Metadata = {
  title: "Torneo de las Fresas - 2025",
  description:
    "¡Participa en el Torneo de Frontenis de las Fresas 2025 en Irapuato! Promovemos el deporte y la competencia entre nuevas generaciones. ¡Descubre más sobre el evento y únete a la emoción!",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="es">
      <body
        className={`${nosifer.variable}
        ${spaceGrotesk.variable} antialiased`}
      >
        {children}
      </body>
    </html>
  );
}