radix select boxes working
This commit is contained in:
		
							parent
							
								
									87ec3c99c4
								
							
						
					
					
						commit
						4cb077e7b9
					
				| 
						 | 
					@ -15,6 +15,7 @@
 | 
				
			||||||
        "@radix-ui/react-label": "^2.0.2",
 | 
					        "@radix-ui/react-label": "^2.0.2",
 | 
				
			||||||
        "@radix-ui/react-select": "^2.0.0",
 | 
					        "@radix-ui/react-select": "^2.0.0",
 | 
				
			||||||
        "@radix-ui/react-slot": "^1.0.2",
 | 
					        "@radix-ui/react-slot": "^1.0.2",
 | 
				
			||||||
 | 
					        "@radix-ui/react-toast": "^1.1.5",
 | 
				
			||||||
        "@tanstack/react-table": "^8.17.3",
 | 
					        "@tanstack/react-table": "^8.17.3",
 | 
				
			||||||
        "class-variance-authority": "^0.7.0",
 | 
					        "class-variance-authority": "^0.7.0",
 | 
				
			||||||
        "clsx": "^2.1.1",
 | 
					        "clsx": "^2.1.1",
 | 
				
			||||||
| 
						 | 
					@ -1076,6 +1077,40 @@
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@radix-ui/react-toast": {
 | 
				
			||||||
 | 
					      "version": "1.1.5",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@babel/runtime": "^7.13.10",
 | 
				
			||||||
 | 
					        "@radix-ui/primitive": "1.0.1",
 | 
				
			||||||
 | 
					        "@radix-ui/react-collection": "1.0.3",
 | 
				
			||||||
 | 
					        "@radix-ui/react-compose-refs": "1.0.1",
 | 
				
			||||||
 | 
					        "@radix-ui/react-context": "1.0.1",
 | 
				
			||||||
 | 
					        "@radix-ui/react-dismissable-layer": "1.0.5",
 | 
				
			||||||
 | 
					        "@radix-ui/react-portal": "1.0.4",
 | 
				
			||||||
 | 
					        "@radix-ui/react-presence": "1.0.1",
 | 
				
			||||||
 | 
					        "@radix-ui/react-primitive": "1.0.3",
 | 
				
			||||||
 | 
					        "@radix-ui/react-use-callback-ref": "1.0.1",
 | 
				
			||||||
 | 
					        "@radix-ui/react-use-controllable-state": "1.0.1",
 | 
				
			||||||
 | 
					        "@radix-ui/react-use-layout-effect": "1.0.1",
 | 
				
			||||||
 | 
					        "@radix-ui/react-visually-hidden": "1.0.3"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "@types/react": "*",
 | 
				
			||||||
 | 
					        "@types/react-dom": "*",
 | 
				
			||||||
 | 
					        "react": "^16.8 || ^17.0 || ^18.0",
 | 
				
			||||||
 | 
					        "react-dom": "^16.8 || ^17.0 || ^18.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "@types/react": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "@types/react-dom": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@radix-ui/react-use-callback-ref": {
 | 
					    "node_modules/@radix-ui/react-use-callback-ref": {
 | 
				
			||||||
      "version": "1.0.1",
 | 
					      "version": "1.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
    "@radix-ui/react-label": "^2.0.2",
 | 
					    "@radix-ui/react-label": "^2.0.2",
 | 
				
			||||||
    "@radix-ui/react-select": "^2.0.0",
 | 
					    "@radix-ui/react-select": "^2.0.0",
 | 
				
			||||||
    "@radix-ui/react-slot": "^1.0.2",
 | 
					    "@radix-ui/react-slot": "^1.0.2",
 | 
				
			||||||
 | 
					    "@radix-ui/react-toast": "^1.1.5",
 | 
				
			||||||
    "@tanstack/react-table": "^8.17.3",
 | 
					    "@tanstack/react-table": "^8.17.3",
 | 
				
			||||||
    "class-variance-authority": "^0.7.0",
 | 
					    "class-variance-authority": "^0.7.0",
 | 
				
			||||||
    "clsx": "^2.1.1",
 | 
					    "clsx": "^2.1.1",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,129 @@
 | 
				
			||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					import * as ToastPrimitives from "@radix-ui/react-toast"
 | 
				
			||||||
 | 
					import { cva, type VariantProps } from "class-variance-authority"
 | 
				
			||||||
 | 
					import { X } from "lucide-react"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ToastProvider = ToastPrimitives.Provider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ToastViewport = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof ToastPrimitives.Viewport>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <ToastPrimitives.Viewport
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn(
 | 
				
			||||||
 | 
					      "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
 | 
				
			||||||
 | 
					      className
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					ToastViewport.displayName = ToastPrimitives.Viewport.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const toastVariants = cva(
 | 
				
			||||||
 | 
					  "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    variants: {
 | 
				
			||||||
 | 
					      variant: {
 | 
				
			||||||
 | 
					        default: "border bg-background text-foreground",
 | 
				
			||||||
 | 
					        destructive:
 | 
				
			||||||
 | 
					          "destructive group border-destructive bg-destructive text-destructive-foreground",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    defaultVariants: {
 | 
				
			||||||
 | 
					      variant: "default",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Toast = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof ToastPrimitives.Root>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
 | 
				
			||||||
 | 
					    VariantProps<typeof toastVariants>
 | 
				
			||||||
 | 
					>(({ className, variant, ...props }, ref) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ToastPrimitives.Root
 | 
				
			||||||
 | 
					      ref={ref}
 | 
				
			||||||
 | 
					      className={cn(toastVariants({ variant }), className)}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					Toast.displayName = ToastPrimitives.Root.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ToastAction = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof ToastPrimitives.Action>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <ToastPrimitives.Action
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn(
 | 
				
			||||||
 | 
					      "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
 | 
				
			||||||
 | 
					      className
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					ToastAction.displayName = ToastPrimitives.Action.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ToastClose = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof ToastPrimitives.Close>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <ToastPrimitives.Close
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn(
 | 
				
			||||||
 | 
					      "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
 | 
				
			||||||
 | 
					      className
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    toast-close=""
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <X className="h-4 w-4" />
 | 
				
			||||||
 | 
					  </ToastPrimitives.Close>
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					ToastClose.displayName = ToastPrimitives.Close.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ToastTitle = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof ToastPrimitives.Title>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <ToastPrimitives.Title
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn("text-sm font-semibold", className)}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					ToastTitle.displayName = ToastPrimitives.Title.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ToastDescription = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof ToastPrimitives.Description>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <ToastPrimitives.Description
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn("text-sm opacity-90", className)}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					ToastDescription.displayName = ToastPrimitives.Description.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ToastActionElement = React.ReactElement<typeof ToastAction>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  type ToastProps,
 | 
				
			||||||
 | 
					  type ToastActionElement,
 | 
				
			||||||
 | 
					  ToastProvider,
 | 
				
			||||||
 | 
					  ToastViewport,
 | 
				
			||||||
 | 
					  Toast,
 | 
				
			||||||
 | 
					  ToastTitle,
 | 
				
			||||||
 | 
					  ToastDescription,
 | 
				
			||||||
 | 
					  ToastClose,
 | 
				
			||||||
 | 
					  ToastAction,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Toast,
 | 
				
			||||||
 | 
					  ToastClose,
 | 
				
			||||||
 | 
					  ToastDescription,
 | 
				
			||||||
 | 
					  ToastProvider,
 | 
				
			||||||
 | 
					  ToastTitle,
 | 
				
			||||||
 | 
					  ToastViewport,
 | 
				
			||||||
 | 
					} from "@/components/ui/toast"
 | 
				
			||||||
 | 
					import { useToast } from "@/components/ui/use-toast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function Toaster() {
 | 
				
			||||||
 | 
					  const { toasts } = useToast()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ToastProvider>
 | 
				
			||||||
 | 
					      {toasts.map(function ({ id, title, description, action, ...props }) {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					          <Toast key={id} {...props}>
 | 
				
			||||||
 | 
					            <div className="grid gap-1">
 | 
				
			||||||
 | 
					              {title && <ToastTitle>{title}</ToastTitle>}
 | 
				
			||||||
 | 
					              {description && (
 | 
				
			||||||
 | 
					                <ToastDescription>{description}</ToastDescription>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            {action}
 | 
				
			||||||
 | 
					            <ToastClose />
 | 
				
			||||||
 | 
					          </Toast>
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      })}
 | 
				
			||||||
 | 
					      <ToastViewport />
 | 
				
			||||||
 | 
					    </ToastProvider>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,194 @@
 | 
				
			||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Inspired by react-hot-toast library
 | 
				
			||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type {
 | 
				
			||||||
 | 
					  ToastActionElement,
 | 
				
			||||||
 | 
					  ToastProps,
 | 
				
			||||||
 | 
					} from "@/components/ui/toast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const TOAST_LIMIT = 1
 | 
				
			||||||
 | 
					const TOAST_REMOVE_DELAY = 1000000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ToasterToast = ToastProps & {
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  title?: React.ReactNode
 | 
				
			||||||
 | 
					  description?: React.ReactNode
 | 
				
			||||||
 | 
					  action?: ToastActionElement
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const actionTypes = {
 | 
				
			||||||
 | 
					  ADD_TOAST: "ADD_TOAST",
 | 
				
			||||||
 | 
					  UPDATE_TOAST: "UPDATE_TOAST",
 | 
				
			||||||
 | 
					  DISMISS_TOAST: "DISMISS_TOAST",
 | 
				
			||||||
 | 
					  REMOVE_TOAST: "REMOVE_TOAST",
 | 
				
			||||||
 | 
					} as const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let count = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function genId() {
 | 
				
			||||||
 | 
					  count = (count + 1) % Number.MAX_SAFE_INTEGER
 | 
				
			||||||
 | 
					  return count.toString()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ActionType = typeof actionTypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Action =
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					      type: ActionType["ADD_TOAST"]
 | 
				
			||||||
 | 
					      toast: ToasterToast
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					      type: ActionType["UPDATE_TOAST"]
 | 
				
			||||||
 | 
					      toast: Partial<ToasterToast>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					      type: ActionType["DISMISS_TOAST"]
 | 
				
			||||||
 | 
					      toastId?: ToasterToast["id"]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					      type: ActionType["REMOVE_TOAST"]
 | 
				
			||||||
 | 
					      toastId?: ToasterToast["id"]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface State {
 | 
				
			||||||
 | 
					  toasts: ToasterToast[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const addToRemoveQueue = (toastId: string) => {
 | 
				
			||||||
 | 
					  if (toastTimeouts.has(toastId)) {
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const timeout = setTimeout(() => {
 | 
				
			||||||
 | 
					    toastTimeouts.delete(toastId)
 | 
				
			||||||
 | 
					    dispatch({
 | 
				
			||||||
 | 
					      type: "REMOVE_TOAST",
 | 
				
			||||||
 | 
					      toastId: toastId,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }, TOAST_REMOVE_DELAY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toastTimeouts.set(toastId, timeout)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const reducer = (state: State, action: Action): State => {
 | 
				
			||||||
 | 
					  switch (action.type) {
 | 
				
			||||||
 | 
					    case "ADD_TOAST":
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...state,
 | 
				
			||||||
 | 
					        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case "UPDATE_TOAST":
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...state,
 | 
				
			||||||
 | 
					        toasts: state.toasts.map((t) =>
 | 
				
			||||||
 | 
					          t.id === action.toast.id ? { ...t, ...action.toast } : t
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case "DISMISS_TOAST": {
 | 
				
			||||||
 | 
					      const { toastId } = action
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // ! Side effects ! - This could be extracted into a dismissToast() action,
 | 
				
			||||||
 | 
					      // but I'll keep it here for simplicity
 | 
				
			||||||
 | 
					      if (toastId) {
 | 
				
			||||||
 | 
					        addToRemoveQueue(toastId)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        state.toasts.forEach((toast) => {
 | 
				
			||||||
 | 
					          addToRemoveQueue(toast.id)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...state,
 | 
				
			||||||
 | 
					        toasts: state.toasts.map((t) =>
 | 
				
			||||||
 | 
					          t.id === toastId || toastId === undefined
 | 
				
			||||||
 | 
					            ? {
 | 
				
			||||||
 | 
					                ...t,
 | 
				
			||||||
 | 
					                open: false,
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            : t
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case "REMOVE_TOAST":
 | 
				
			||||||
 | 
					      if (action.toastId === undefined) {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          ...state,
 | 
				
			||||||
 | 
					          toasts: [],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...state,
 | 
				
			||||||
 | 
					        toasts: state.toasts.filter((t) => t.id !== action.toastId),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const listeners: Array<(state: State) => void> = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let memoryState: State = { toasts: [] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function dispatch(action: Action) {
 | 
				
			||||||
 | 
					  memoryState = reducer(memoryState, action)
 | 
				
			||||||
 | 
					  listeners.forEach((listener) => {
 | 
				
			||||||
 | 
					    listener(memoryState)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Toast = Omit<ToasterToast, "id">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function toast({ ...props }: Toast) {
 | 
				
			||||||
 | 
					  const id = genId()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const update = (props: ToasterToast) =>
 | 
				
			||||||
 | 
					    dispatch({
 | 
				
			||||||
 | 
					      type: "UPDATE_TOAST",
 | 
				
			||||||
 | 
					      toast: { ...props, id },
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dispatch({
 | 
				
			||||||
 | 
					    type: "ADD_TOAST",
 | 
				
			||||||
 | 
					    toast: {
 | 
				
			||||||
 | 
					      ...props,
 | 
				
			||||||
 | 
					      id,
 | 
				
			||||||
 | 
					      open: true,
 | 
				
			||||||
 | 
					      onOpenChange: (open) => {
 | 
				
			||||||
 | 
					        if (!open) dismiss()
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    id: id,
 | 
				
			||||||
 | 
					    dismiss,
 | 
				
			||||||
 | 
					    update,
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function useToast() {
 | 
				
			||||||
 | 
					  const [state, setState] = React.useState<State>(memoryState)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    listeners.push(setState)
 | 
				
			||||||
 | 
					    return () => {
 | 
				
			||||||
 | 
					      const index = listeners.indexOf(setState)
 | 
				
			||||||
 | 
					      if (index > -1) {
 | 
				
			||||||
 | 
					        listeners.splice(index, 1)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [state])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    ...state,
 | 
				
			||||||
 | 
					    toast,
 | 
				
			||||||
 | 
					    dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { useToast, toast }
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import type { Metadata } from "next";
 | 
					import type { Metadata } from "next";
 | 
				
			||||||
import { Inter } from "next/font/google";
 | 
					import { Inter } from "next/font/google";
 | 
				
			||||||
 | 
					import { Toaster } from "@/components/ui/toaster";
 | 
				
			||||||
import "./tailwind.css";
 | 
					import "./tailwind.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const inter = Inter({ subsets: ["latin"] });
 | 
					const inter = Inter({ subsets: ["latin"] });
 | 
				
			||||||
| 
						 | 
					@ -20,7 +21,10 @@ export default function RootLayout({
 | 
				
			||||||
        <div id="sidebar">
 | 
					        <div id="sidebar">
 | 
				
			||||||
          SIDEBAR
 | 
					          SIDEBAR
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        {children}</body>
 | 
					        {children}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Toaster />
 | 
				
			||||||
 | 
					      </body>
 | 
				
			||||||
    </html>
 | 
					    </html>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
"use server"
 | 
					"use server"
 | 
				
			||||||
import { getPubs, getResponses, getStories } from "app/lib/get";
 | 
					import { getPubs, getResponses, getStories } from "app/lib/get";
 | 
				
			||||||
import SubmissionForm from "app/ui/forms/sub";
 | 
					import SubmissionForm from "app/ui/forms/sub";
 | 
				
			||||||
 | 
					import { SelectForm } from "app/ui/forms/selectDemo";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function Page() {
 | 
					export default async function Page() {
 | 
				
			||||||
	const stories = await getStories()
 | 
						const stories = await getStories()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -600,6 +600,14 @@ body {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.pointer-events-auto {
 | 
				
			||||||
 | 
					  pointer-events: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.fixed {
 | 
				
			||||||
 | 
					  position: fixed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.absolute {
 | 
					.absolute {
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -612,10 +620,26 @@ body {
 | 
				
			||||||
  left: 0.5rem;
 | 
					  left: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.right-2 {
 | 
				
			||||||
 | 
					  right: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.top-0 {
 | 
				
			||||||
 | 
					  top: 0px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.top-2 {
 | 
				
			||||||
 | 
					  top: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.z-50 {
 | 
					.z-50 {
 | 
				
			||||||
  z-index: 50;
 | 
					  z-index: 50;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.z-\[100\] {
 | 
				
			||||||
 | 
					  z-index: 100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.-mx-1 {
 | 
					.-mx-1 {
 | 
				
			||||||
  margin-left: -0.25rem;
 | 
					  margin-left: -0.25rem;
 | 
				
			||||||
  margin-right: -0.25rem;
 | 
					  margin-right: -0.25rem;
 | 
				
			||||||
| 
						 | 
					@ -631,6 +655,10 @@ body {
 | 
				
			||||||
  margin-bottom: 0.25rem;
 | 
					  margin-bottom: 0.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mb-4 {
 | 
				
			||||||
 | 
					  margin-bottom: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.ml-2 {
 | 
					.ml-2 {
 | 
				
			||||||
  margin-left: 0.5rem;
 | 
					  margin-left: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -639,18 +667,14 @@ body {
 | 
				
			||||||
  margin-left: auto;
 | 
					  margin-left: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.mt-4 {
 | 
					 | 
				
			||||||
  margin-top: 1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mb-4 {
 | 
					 | 
				
			||||||
  margin-bottom: 1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mt-2 {
 | 
					.mt-2 {
 | 
				
			||||||
  margin-top: 0.5rem;
 | 
					  margin-top: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mt-4 {
 | 
				
			||||||
 | 
					  margin-top: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.flex {
 | 
					.flex {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -667,6 +691,10 @@ body {
 | 
				
			||||||
  display: table;
 | 
					  display: table;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.grid {
 | 
				
			||||||
 | 
					  display: grid;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h-10 {
 | 
					.h-10 {
 | 
				
			||||||
  height: 2.5rem;
 | 
					  height: 2.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -703,18 +731,26 @@ body {
 | 
				
			||||||
  height: 2.25rem;
 | 
					  height: 2.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h-px {
 | 
					 | 
				
			||||||
  height: 1px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.h-\[var\(--radix-select-trigger-height\)\] {
 | 
					.h-\[var\(--radix-select-trigger-height\)\] {
 | 
				
			||||||
  height: var(--radix-select-trigger-height);
 | 
					  height: var(--radix-select-trigger-height);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.h-px {
 | 
				
			||||||
 | 
					  height: 1px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.h-8 {
 | 
				
			||||||
 | 
					  height: 2rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.max-h-96 {
 | 
					.max-h-96 {
 | 
				
			||||||
  max-height: 24rem;
 | 
					  max-height: 24rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.max-h-screen {
 | 
				
			||||||
 | 
					  max-height: 100vh;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.w-10 {
 | 
					.w-10 {
 | 
				
			||||||
  width: 2.5rem;
 | 
					  width: 2.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -735,12 +771,16 @@ body {
 | 
				
			||||||
  width: 1rem;
 | 
					  width: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.w-\[340px\] {
 | 
				
			||||||
 | 
					  width: 340px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.w-full {
 | 
					.w-full {
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.w-\[340px\] {
 | 
					.w-2\/3 {
 | 
				
			||||||
  width: 340px;
 | 
					  width: 66.666667%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.min-w-\[8rem\] {
 | 
					.min-w-\[8rem\] {
 | 
				
			||||||
| 
						 | 
					@ -777,6 +817,10 @@ body {
 | 
				
			||||||
  flex-direction: row;
 | 
					  flex-direction: row;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.flex-col-reverse {
 | 
				
			||||||
 | 
					  flex-direction: column-reverse;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.items-start {
 | 
					.items-start {
 | 
				
			||||||
  align-items: flex-start;
 | 
					  align-items: flex-start;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -797,12 +841,28 @@ body {
 | 
				
			||||||
  justify-content: space-between;
 | 
					  justify-content: space-between;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.gap-1 {
 | 
				
			||||||
 | 
					  gap: 0.25rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
 | 
					.space-x-2 > :not([hidden]) ~ :not([hidden]) {
 | 
				
			||||||
  --tw-space-x-reverse: 0;
 | 
					  --tw-space-x-reverse: 0;
 | 
				
			||||||
  margin-right: calc(0.5rem * var(--tw-space-x-reverse));
 | 
					  margin-right: calc(0.5rem * var(--tw-space-x-reverse));
 | 
				
			||||||
  margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
 | 
					  margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.space-x-3 > :not([hidden]) ~ :not([hidden]) {
 | 
				
			||||||
 | 
					  --tw-space-x-reverse: 0;
 | 
				
			||||||
 | 
					  margin-right: calc(0.75rem * var(--tw-space-x-reverse));
 | 
				
			||||||
 | 
					  margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.space-y-0 > :not([hidden]) ~ :not([hidden]) {
 | 
				
			||||||
 | 
					  --tw-space-y-reverse: 0;
 | 
				
			||||||
 | 
					  margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
 | 
				
			||||||
 | 
					  margin-bottom: calc(0px * var(--tw-space-y-reverse));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.space-y-2 > :not([hidden]) ~ :not([hidden]) {
 | 
					.space-y-2 > :not([hidden]) ~ :not([hidden]) {
 | 
				
			||||||
  --tw-space-y-reverse: 0;
 | 
					  --tw-space-y-reverse: 0;
 | 
				
			||||||
  margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
 | 
					  margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
 | 
				
			||||||
| 
						 | 
					@ -815,16 +875,16 @@ body {
 | 
				
			||||||
  margin-bottom: calc(2rem * var(--tw-space-y-reverse));
 | 
					  margin-bottom: calc(2rem * var(--tw-space-y-reverse));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.space-x-3 > :not([hidden]) ~ :not([hidden]) {
 | 
					.space-x-4 > :not([hidden]) ~ :not([hidden]) {
 | 
				
			||||||
  --tw-space-x-reverse: 0;
 | 
					  --tw-space-x-reverse: 0;
 | 
				
			||||||
  margin-right: calc(0.75rem * var(--tw-space-x-reverse));
 | 
					  margin-right: calc(1rem * var(--tw-space-x-reverse));
 | 
				
			||||||
  margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
 | 
					  margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.space-y-0 > :not([hidden]) ~ :not([hidden]) {
 | 
					.space-y-6 > :not([hidden]) ~ :not([hidden]) {
 | 
				
			||||||
  --tw-space-y-reverse: 0;
 | 
					  --tw-space-y-reverse: 0;
 | 
				
			||||||
  margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
 | 
					  margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
 | 
				
			||||||
  margin-bottom: calc(0px * var(--tw-space-y-reverse));
 | 
					  margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.overflow-auto {
 | 
					.overflow-auto {
 | 
				
			||||||
| 
						 | 
					@ -867,6 +927,10 @@ body {
 | 
				
			||||||
  border-color: hsl(var(--primary));
 | 
					  border-color: hsl(var(--primary));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.border-destructive {
 | 
				
			||||||
 | 
					  border-color: hsl(var(--destructive));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.bg-background {
 | 
					.bg-background {
 | 
				
			||||||
  background-color: hsl(var(--background));
 | 
					  background-color: hsl(var(--background));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -900,6 +964,10 @@ body {
 | 
				
			||||||
  background-color: rgb(2 6 23 / var(--tw-bg-opacity));
 | 
					  background-color: rgb(2 6 23 / var(--tw-bg-opacity));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bg-transparent {
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.fill-current {
 | 
					.fill-current {
 | 
				
			||||||
  fill: currentColor;
 | 
					  fill: currentColor;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -912,6 +980,10 @@ body {
 | 
				
			||||||
  padding: 1rem;
 | 
					  padding: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.p-6 {
 | 
				
			||||||
 | 
					  padding: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.px-2 {
 | 
					.px-2 {
 | 
				
			||||||
  padding-left: 0.5rem;
 | 
					  padding-left: 0.5rem;
 | 
				
			||||||
  padding-right: 0.5rem;
 | 
					  padding-right: 0.5rem;
 | 
				
			||||||
| 
						 | 
					@ -965,6 +1037,10 @@ body {
 | 
				
			||||||
  padding-right: 0.5rem;
 | 
					  padding-right: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.pr-8 {
 | 
				
			||||||
 | 
					  padding-right: 2rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.text-left {
 | 
					.text-left {
 | 
				
			||||||
  text-align: left;
 | 
					  text-align: left;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -982,6 +1058,11 @@ body {
 | 
				
			||||||
  line-height: 2.25rem;
 | 
					  line-height: 2.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.text-base {
 | 
				
			||||||
 | 
					  font-size: 1rem;
 | 
				
			||||||
 | 
					  line-height: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.text-sm {
 | 
					.text-sm {
 | 
				
			||||||
  font-size: 0.875rem;
 | 
					  font-size: 0.875rem;
 | 
				
			||||||
  line-height: 1.25rem;
 | 
					  line-height: 1.25rem;
 | 
				
			||||||
| 
						 | 
					@ -992,11 +1073,6 @@ body {
 | 
				
			||||||
  line-height: 1rem;
 | 
					  line-height: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.text-base {
 | 
					 | 
				
			||||||
  font-size: 1rem;
 | 
					 | 
				
			||||||
  line-height: 1.5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.font-black {
 | 
					.font-black {
 | 
				
			||||||
  font-weight: 900;
 | 
					  font-weight: 900;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1005,14 +1081,14 @@ body {
 | 
				
			||||||
  font-weight: 500;
 | 
					  font-weight: 500;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.font-semibold {
 | 
					 | 
				
			||||||
  font-weight: 600;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.font-normal {
 | 
					.font-normal {
 | 
				
			||||||
  font-weight: 400;
 | 
					  font-weight: 400;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.font-semibold {
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.capitalize {
 | 
					.capitalize {
 | 
				
			||||||
  text-transform: capitalize;
 | 
					  text-transform: capitalize;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1025,6 +1101,10 @@ body {
 | 
				
			||||||
  letter-spacing: 0.1em;
 | 
					  letter-spacing: 0.1em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.text-current {
 | 
				
			||||||
 | 
					  color: currentColor;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.text-destructive {
 | 
					.text-destructive {
 | 
				
			||||||
  color: hsl(var(--destructive));
 | 
					  color: hsl(var(--destructive));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1053,15 +1133,19 @@ body {
 | 
				
			||||||
  color: hsl(var(--secondary-foreground));
 | 
					  color: hsl(var(--secondary-foreground));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.text-current {
 | 
					 | 
				
			||||||
  color: currentColor;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.text-white {
 | 
					.text-white {
 | 
				
			||||||
  --tw-text-opacity: 1;
 | 
					  --tw-text-opacity: 1;
 | 
				
			||||||
  color: rgb(255 255 255 / var(--tw-text-opacity));
 | 
					  color: rgb(255 255 255 / var(--tw-text-opacity));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.text-foreground {
 | 
				
			||||||
 | 
					  color: hsl(var(--foreground));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.text-foreground\/50 {
 | 
				
			||||||
 | 
					  color: hsl(var(--foreground) / 0.5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.underline {
 | 
					.underline {
 | 
				
			||||||
  text-decoration-line: underline;
 | 
					  text-decoration-line: underline;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1070,12 +1154,20 @@ body {
 | 
				
			||||||
  text-underline-offset: 4px;
 | 
					  text-underline-offset: 4px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.opacity-50 {
 | 
				
			||||||
 | 
					  opacity: 0.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.opacity-60 {
 | 
					.opacity-60 {
 | 
				
			||||||
  opacity: 0.6;
 | 
					  opacity: 0.6;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.opacity-50 {
 | 
					.opacity-0 {
 | 
				
			||||||
  opacity: 0.5;
 | 
					  opacity: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.opacity-90 {
 | 
				
			||||||
 | 
					  opacity: 0.9;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.shadow-lg {
 | 
					.shadow-lg {
 | 
				
			||||||
| 
						 | 
					@ -1113,6 +1205,18 @@ body {
 | 
				
			||||||
  transition-duration: 150ms;
 | 
					  transition-duration: 150ms;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.transition-all {
 | 
				
			||||||
 | 
					  transition-property: all;
 | 
				
			||||||
 | 
					  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
 | 
				
			||||||
 | 
					  transition-duration: 150ms;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.transition-opacity {
 | 
				
			||||||
 | 
					  transition-property: opacity;
 | 
				
			||||||
 | 
					  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
 | 
				
			||||||
 | 
					  transition-duration: 150ms;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@keyframes enter {
 | 
					@keyframes enter {
 | 
				
			||||||
  from {
 | 
					  from {
 | 
				
			||||||
    opacity: var(--tw-enter-opacity, 1);
 | 
					    opacity: var(--tw-enter-opacity, 1);
 | 
				
			||||||
| 
						 | 
					@ -1172,10 +1276,18 @@ body {
 | 
				
			||||||
  background-color: hsl(var(--secondary) / 0.8);
 | 
					  background-color: hsl(var(--secondary) / 0.8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hover\:bg-secondary:hover {
 | 
				
			||||||
 | 
					  background-color: hsl(var(--secondary));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.hover\:text-accent-foreground:hover {
 | 
					.hover\:text-accent-foreground:hover {
 | 
				
			||||||
  color: hsl(var(--accent-foreground));
 | 
					  color: hsl(var(--accent-foreground));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hover\:text-foreground:hover {
 | 
				
			||||||
 | 
					  color: hsl(var(--foreground));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.hover\:underline:hover {
 | 
					.hover\:underline:hover {
 | 
				
			||||||
  text-decoration-line: underline;
 | 
					  text-decoration-line: underline;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1188,6 +1300,10 @@ body {
 | 
				
			||||||
  color: hsl(var(--accent-foreground));
 | 
					  color: hsl(var(--accent-foreground));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.focus\:opacity-100:focus {
 | 
				
			||||||
 | 
					  opacity: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.focus\:outline-none:focus {
 | 
					.focus\:outline-none:focus {
 | 
				
			||||||
  outline: 2px solid transparent;
 | 
					  outline: 2px solid transparent;
 | 
				
			||||||
  outline-offset: 2px;
 | 
					  outline-offset: 2px;
 | 
				
			||||||
| 
						 | 
					@ -1238,6 +1354,49 @@ body {
 | 
				
			||||||
  opacity: 0.5;
 | 
					  opacity: 0.5;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group:hover .group-hover\:opacity-100 {
 | 
				
			||||||
 | 
					  opacity: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:border-muted\/40 {
 | 
				
			||||||
 | 
					  border-color: hsl(var(--muted) / 0.4);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:text-red-300 {
 | 
				
			||||||
 | 
					  --tw-text-opacity: 1;
 | 
				
			||||||
 | 
					  color: rgb(252 165 165 / var(--tw-text-opacity));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:hover\:border-destructive\/30:hover {
 | 
				
			||||||
 | 
					  border-color: hsl(var(--destructive) / 0.3);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover {
 | 
				
			||||||
 | 
					  background-color: hsl(var(--destructive));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover {
 | 
				
			||||||
 | 
					  color: hsl(var(--destructive-foreground));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover {
 | 
				
			||||||
 | 
					  --tw-text-opacity: 1;
 | 
				
			||||||
 | 
					  color: rgb(254 242 242 / var(--tw-text-opacity));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus {
 | 
				
			||||||
 | 
					  --tw-ring-color: hsl(var(--destructive));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus {
 | 
				
			||||||
 | 
					  --tw-ring-opacity: 1;
 | 
				
			||||||
 | 
					  --tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus {
 | 
				
			||||||
 | 
					  --tw-ring-offset-color: #dc2626;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.peer:disabled ~ .peer-disabled\:cursor-not-allowed {
 | 
					.peer:disabled ~ .peer-disabled\:cursor-not-allowed {
 | 
				
			||||||
  cursor: not-allowed;
 | 
					  cursor: not-allowed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1270,6 +1429,25 @@ body {
 | 
				
			||||||
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
 | 
					  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel] {
 | 
				
			||||||
 | 
					  --tw-translate-x: 0px;
 | 
				
			||||||
 | 
					  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end] {
 | 
				
			||||||
 | 
					  --tw-translate-x: var(--radix-toast-swipe-end-x);
 | 
				
			||||||
 | 
					  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move] {
 | 
				
			||||||
 | 
					  --tw-translate-x: var(--radix-toast-swipe-move-x);
 | 
				
			||||||
 | 
					  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[state\=checked\]\:bg-primary[data-state=checked] {
 | 
				
			||||||
 | 
					  background-color: hsl(var(--primary));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.data-\[state\=open\]\:bg-accent[data-state=open] {
 | 
					.data-\[state\=open\]\:bg-accent[data-state=open] {
 | 
				
			||||||
  background-color: hsl(var(--accent));
 | 
					  background-color: hsl(var(--accent));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1278,10 +1456,6 @@ body {
 | 
				
			||||||
  background-color: hsl(var(--muted));
 | 
					  background-color: hsl(var(--muted));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.data-\[state\=checked\]\:bg-primary[data-state=checked] {
 | 
					 | 
				
			||||||
  background-color: hsl(var(--primary));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.data-\[state\=checked\]\:text-primary-foreground[data-state=checked] {
 | 
					.data-\[state\=checked\]\:text-primary-foreground[data-state=checked] {
 | 
				
			||||||
  color: hsl(var(--primary-foreground));
 | 
					  color: hsl(var(--primary-foreground));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1290,6 +1464,10 @@ body {
 | 
				
			||||||
  opacity: 0.5;
 | 
					  opacity: 0.5;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[swipe\=move\]\:transition-none[data-swipe=move] {
 | 
				
			||||||
 | 
					  transition-property: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.data-\[state\=open\]\:animate-in[data-state=open] {
 | 
					.data-\[state\=open\]\:animate-in[data-state=open] {
 | 
				
			||||||
  animation-name: enter;
 | 
					  animation-name: enter;
 | 
				
			||||||
  animation-duration: 150ms;
 | 
					  animation-duration: 150ms;
 | 
				
			||||||
| 
						 | 
					@ -1310,6 +1488,16 @@ body {
 | 
				
			||||||
  --tw-exit-translate-y: initial;
 | 
					  --tw-exit-translate-y: initial;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[swipe\=end\]\:animate-out[data-swipe=end] {
 | 
				
			||||||
 | 
					  animation-name: exit;
 | 
				
			||||||
 | 
					  animation-duration: 150ms;
 | 
				
			||||||
 | 
					  --tw-exit-opacity: initial;
 | 
				
			||||||
 | 
					  --tw-exit-scale: initial;
 | 
				
			||||||
 | 
					  --tw-exit-rotate: initial;
 | 
				
			||||||
 | 
					  --tw-exit-translate-x: initial;
 | 
				
			||||||
 | 
					  --tw-exit-translate-y: initial;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.data-\[state\=closed\]\:fade-out-0[data-state=closed] {
 | 
					.data-\[state\=closed\]\:fade-out-0[data-state=closed] {
 | 
				
			||||||
  --tw-exit-opacity: 0;
 | 
					  --tw-exit-opacity: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1318,6 +1506,10 @@ body {
 | 
				
			||||||
  --tw-enter-opacity: 0;
 | 
					  --tw-enter-opacity: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[state\=closed\]\:fade-out-80[data-state=closed] {
 | 
				
			||||||
 | 
					  --tw-exit-opacity: 0.8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.data-\[state\=closed\]\:zoom-out-95[data-state=closed] {
 | 
					.data-\[state\=closed\]\:zoom-out-95[data-state=closed] {
 | 
				
			||||||
  --tw-exit-scale: .95;
 | 
					  --tw-exit-scale: .95;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1342,6 +1534,42 @@ body {
 | 
				
			||||||
  --tw-enter-translate-y: 0.5rem;
 | 
					  --tw-enter-translate-y: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed] {
 | 
				
			||||||
 | 
					  --tw-exit-translate-x: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.data-\[state\=open\]\:slide-in-from-top-full[data-state=open] {
 | 
				
			||||||
 | 
					  --tw-enter-translate-y: -100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (min-width: 640px) {
 | 
				
			||||||
 | 
					  .sm\:bottom-0 {
 | 
				
			||||||
 | 
					    bottom: 0px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .sm\:right-0 {
 | 
				
			||||||
 | 
					    right: 0px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .sm\:top-auto {
 | 
				
			||||||
 | 
					    top: auto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .sm\:flex-col {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open] {
 | 
				
			||||||
 | 
					    --tw-enter-translate-y: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (min-width: 768px) {
 | 
				
			||||||
 | 
					  .md\:max-w-\[420px\] {
 | 
				
			||||||
 | 
					    max-width: 420px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]) {
 | 
					.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]) {
 | 
				
			||||||
  padding-right: 0px;
 | 
					  padding-right: 0px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,86 @@
 | 
				
			||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Link from "next/link"
 | 
				
			||||||
 | 
					import { zodResolver } from "@hookform/resolvers/zod"
 | 
				
			||||||
 | 
					import { useForm } from "react-hook-form"
 | 
				
			||||||
 | 
					import { z } from "zod"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
						Form,
 | 
				
			||||||
 | 
						FormControl,
 | 
				
			||||||
 | 
						FormDescription,
 | 
				
			||||||
 | 
						FormField,
 | 
				
			||||||
 | 
						FormItem,
 | 
				
			||||||
 | 
						FormLabel,
 | 
				
			||||||
 | 
						FormMessage,
 | 
				
			||||||
 | 
					} from "@/components/ui/form"
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
						Select,
 | 
				
			||||||
 | 
						SelectContent,
 | 
				
			||||||
 | 
						SelectItem,
 | 
				
			||||||
 | 
						SelectTrigger,
 | 
				
			||||||
 | 
						SelectValue,
 | 
				
			||||||
 | 
					} from "@/components/ui/select"
 | 
				
			||||||
 | 
					import { toast } from "@/components/ui/use-toast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const FormSchema = z.object({
 | 
				
			||||||
 | 
						email: z
 | 
				
			||||||
 | 
							.string({
 | 
				
			||||||
 | 
								required_error: "Please select an email to display.",
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.email(),
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function SelectForm() {
 | 
				
			||||||
 | 
						const form = useForm<z.infer<typeof FormSchema>>({
 | 
				
			||||||
 | 
							resolver: zodResolver(FormSchema),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function onSubmit(data: z.infer<typeof FormSchema>) {
 | 
				
			||||||
 | 
							toast({
 | 
				
			||||||
 | 
								title: "You submitted the following values:",
 | 
				
			||||||
 | 
								description: (
 | 
				
			||||||
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
 | 
										<code className="text-white">{JSON.stringify(data, null, 2)}</code>
 | 
				
			||||||
 | 
									</pre>
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<Form {...form}>
 | 
				
			||||||
 | 
								<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
 | 
				
			||||||
 | 
									<FormField
 | 
				
			||||||
 | 
										control={form.control}
 | 
				
			||||||
 | 
										name="email"
 | 
				
			||||||
 | 
										render={({ field }) => (
 | 
				
			||||||
 | 
											<FormItem>
 | 
				
			||||||
 | 
												<FormLabel>Email</FormLabel>
 | 
				
			||||||
 | 
												<Select onValueChange={field.onChange} defaultValue={field.value}>
 | 
				
			||||||
 | 
													<FormControl>
 | 
				
			||||||
 | 
														<SelectTrigger>
 | 
				
			||||||
 | 
															<SelectValue placeholder="Select a verified email to display" />
 | 
				
			||||||
 | 
														</SelectTrigger>
 | 
				
			||||||
 | 
													</FormControl>
 | 
				
			||||||
 | 
													<SelectContent>
 | 
				
			||||||
 | 
														<SelectItem value="m@example.com">m@example.com</SelectItem>
 | 
				
			||||||
 | 
														<SelectItem value="m@google.com">m@google.com</SelectItem>
 | 
				
			||||||
 | 
														<SelectItem value="m@support.com">m@support.com</SelectItem>
 | 
				
			||||||
 | 
													</SelectContent>
 | 
				
			||||||
 | 
												</Select>
 | 
				
			||||||
 | 
												<FormDescription>
 | 
				
			||||||
 | 
													You can manage email addresses in your{" "}
 | 
				
			||||||
 | 
													<Link href="/examples/forms">email settings</Link>.
 | 
				
			||||||
 | 
												</FormDescription>
 | 
				
			||||||
 | 
												<FormMessage />
 | 
				
			||||||
 | 
											</FormItem>
 | 
				
			||||||
 | 
										)}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
									<Button type="submit">Submit</Button>
 | 
				
			||||||
 | 
								</form>
 | 
				
			||||||
 | 
							</Form>
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import { z } from "zod"
 | 
				
			||||||
import { zodResolver } from "@hookform/resolvers/zod"
 | 
					import { zodResolver } from "@hookform/resolvers/zod"
 | 
				
			||||||
import { useForm } from "react-hook-form"
 | 
					import { useForm } from "react-hook-form"
 | 
				
			||||||
import { Button } from "@/components/ui/button"
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
 | 
					import { toast } from "@/components/ui/use-toast"
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
	Form,
 | 
						Form,
 | 
				
			||||||
	FormItem,
 | 
						FormItem,
 | 
				
			||||||
| 
						 | 
					@ -22,19 +23,21 @@ import {
 | 
				
			||||||
} from "@/components/ui/select"
 | 
					} from "@/components/ui/select"
 | 
				
			||||||
import { ItemText, SelectItemIndicator, SelectItemText } from "@radix-ui/react-select"
 | 
					import { ItemText, SelectItemIndicator, SelectItemText } from "@radix-ui/react-select"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const formSchema = z.object({
 | 
					const FormSchema = z.object({
 | 
				
			||||||
	storyId: z.coerce.number(),
 | 
						storyId: z.string(),
 | 
				
			||||||
	pubId: z.number(),
 | 
						pubId: z.string(),
 | 
				
			||||||
	submitted: z.date(),
 | 
						// submitted: z.date(),
 | 
				
			||||||
	responded: z.date(),
 | 
						// responded: z.date(),
 | 
				
			||||||
	responseId: z.number()
 | 
						// responseId: z.string()
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function SubmissionForm({ stories, pubs, responses }) {
 | 
					export default function SubmissionForm({ stories, pubs, responses }) {
 | 
				
			||||||
 | 
						const form = useForm<z.infer<typeof FormSchema>>({
 | 
				
			||||||
 | 
							resolver: zodResolver(FormSchema),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	const storiesSelectItems = stories.map(e => (
 | 
						const storiesSelectItems = stories.map(e => (
 | 
				
			||||||
		<SelectItem value={e.id} textValue={e.title}>
 | 
							<SelectItem value={e.id.toString()} key={e.title}>
 | 
				
			||||||
			{e.title}
 | 
								{e.title}
 | 
				
			||||||
		</SelectItem>
 | 
							</SelectItem>
 | 
				
			||||||
	))
 | 
						))
 | 
				
			||||||
| 
						 | 
					@ -50,43 +53,79 @@ export default function SubmissionForm({ stories, pubs, responses }) {
 | 
				
			||||||
		</SelectItem>
 | 
							</SelectItem>
 | 
				
			||||||
	))
 | 
						))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 1. Define your form.
 | 
					
 | 
				
			||||||
	const form = useForm<z.infer<typeof formSchema>>({
 | 
					 | 
				
			||||||
		resolver: zodResolver(formSchema),
 | 
					 | 
				
			||||||
		defaultValues: {
 | 
					 | 
				
			||||||
			storyId: stories[0].id,
 | 
					 | 
				
			||||||
			pubId: pubs[0].id,
 | 
					 | 
				
			||||||
			submitted: new Date(),
 | 
					 | 
				
			||||||
			responded: null,
 | 
					 | 
				
			||||||
			responseId: responses[0].id
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	// 2. Define a submit handler.
 | 
						// 2. Define a submit handler.
 | 
				
			||||||
	function onSubmit(values: z.infer<typeof formSchema>) {
 | 
						function onSubmit(values: z.infer<typeof FormSchema>) {
 | 
				
			||||||
		// Do something with the form values.
 | 
							// Do something with the form values.
 | 
				
			||||||
		// ✅ This will be type-safe and validated.
 | 
							// ✅ This will be type-safe and validated.
 | 
				
			||||||
 | 
							toast({
 | 
				
			||||||
 | 
								title: "You submitted the following values:",
 | 
				
			||||||
 | 
								description: (
 | 
				
			||||||
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
 | 
										<code className="text-white">{JSON.stringify(values, null, 2)}</code>
 | 
				
			||||||
 | 
									</pre>
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
		console.log(values)
 | 
							console.log(values)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function onErrors(errors) {
 | 
				
			||||||
 | 
							toast({
 | 
				
			||||||
 | 
								title: "You have errors",
 | 
				
			||||||
 | 
								description: (
 | 
				
			||||||
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
 | 
										<code className="text-white">{JSON.stringify(errors, null, 2)}</code>
 | 
				
			||||||
 | 
									</pre>
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							console.log(JSON.stringify(errors))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<Form {...form}>
 | 
							<Form {...form}>
 | 
				
			||||||
			<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
 | 
								<form onSubmit={form.handleSubmit(onSubmit, onErrors)} className="space-y-8">
 | 
				
			||||||
				<FormField
 | 
									<FormField
 | 
				
			||||||
					control={form.control}
 | 
										control={form.control}
 | 
				
			||||||
					name="storyId"
 | 
										name="storyId"
 | 
				
			||||||
					render={({ field }) => (
 | 
										render={({ field }) => (
 | 
				
			||||||
						<FormItem>
 | 
											<FormItem>
 | 
				
			||||||
							<FormLabel>Story</FormLabel>
 | 
												<FormLabel>Story</FormLabel>
 | 
				
			||||||
							<Select name="storyId" onValueChange={field.onChange}>
 | 
												<Select onValueChange={field.onChange} defaultValue={field.value}>
 | 
				
			||||||
								<FormControl>
 | 
													<FormControl>
 | 
				
			||||||
									<SelectTrigger>
 | 
														<SelectTrigger>
 | 
				
			||||||
										<SelectValue placeholder="Select the story you have submitted" />
 | 
															<SelectValue placeholder="Select something">
 | 
				
			||||||
 | 
																<p>{stories?.find(e => e.id === Number(field.value))?.title ?? null}</p>
 | 
				
			||||||
 | 
															</SelectValue>
 | 
				
			||||||
									</SelectTrigger>
 | 
														</SelectTrigger>
 | 
				
			||||||
								</FormControl>
 | 
													</FormControl>
 | 
				
			||||||
								<SelectContent>
 | 
													<SelectContent>
 | 
				
			||||||
									{storiesSelectItems}
 | 
														{storiesSelectItems}
 | 
				
			||||||
								</SelectContent>
 | 
													</SelectContent>
 | 
				
			||||||
							</Select>
 | 
												</Select>
 | 
				
			||||||
							<FormDescription>This is the story you submitted, yeah?</FormDescription>
 | 
												<FormDescription>The piece you submitted</FormDescription>
 | 
				
			||||||
 | 
												<FormMessage />
 | 
				
			||||||
 | 
											</FormItem>
 | 
				
			||||||
 | 
										)}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
									<FormField
 | 
				
			||||||
 | 
										control={form.control}
 | 
				
			||||||
 | 
										name="pubId"
 | 
				
			||||||
 | 
										render={({ field }) => (
 | 
				
			||||||
 | 
											<FormItem>
 | 
				
			||||||
 | 
												<FormLabel>Publication</FormLabel>
 | 
				
			||||||
 | 
												<Select onValueChange={field.onChange} defaultValue={field.value}>
 | 
				
			||||||
 | 
													<FormControl>
 | 
				
			||||||
 | 
														<SelectTrigger>
 | 
				
			||||||
 | 
															<SelectValue placeholder="Select something">
 | 
				
			||||||
 | 
																<p>{pubs?.find(e => e.id === Number(field.value))?.title ?? null}</p>
 | 
				
			||||||
 | 
															</SelectValue>
 | 
				
			||||||
 | 
														</SelectTrigger>
 | 
				
			||||||
 | 
													</FormControl>
 | 
				
			||||||
 | 
													<SelectContent>
 | 
				
			||||||
 | 
														{pubsSelectItems}
 | 
				
			||||||
 | 
													</SelectContent>
 | 
				
			||||||
 | 
												</Select>
 | 
				
			||||||
 | 
												<FormDescription>The market you sent it to</FormDescription>
 | 
				
			||||||
							<FormMessage />
 | 
												<FormMessage />
 | 
				
			||||||
						</FormItem>
 | 
											</FormItem>
 | 
				
			||||||
					)}
 | 
										)}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue