This commit is contained in:
		@@ -25,51 +25,52 @@
 | 
				
			|||||||
@layer base {
 | 
					@layer base {
 | 
				
			||||||
  :root {
 | 
					  :root {
 | 
				
			||||||
    --background: 0 0% 100%;
 | 
					    --background: 0 0% 100%;
 | 
				
			||||||
    --foreground: 240 10% 3.9%;
 | 
					    --foreground: 20 14.3% 4.1%;
 | 
				
			||||||
    --card: 0 0% 100%;
 | 
					    --card: 0 0% 100%;
 | 
				
			||||||
    --card-foreground: 240 10% 3.9%;
 | 
					    --card-foreground: 20 14.3% 4.1%;
 | 
				
			||||||
    --popover: 0 0% 100%;
 | 
					    --popover: 0 0% 100%;
 | 
				
			||||||
    --popover-foreground: 240 10% 3.9%;
 | 
					    --popover-foreground: 20 14.3% 4.1%;
 | 
				
			||||||
    --primary: 240 5.9% 10%;
 | 
					    --primary: 24.6 95% 53.1%;
 | 
				
			||||||
    --primary-foreground: 0 0% 98%;
 | 
					    --primary-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --secondary: 240 4.8% 95.9%;
 | 
					    --secondary: 60 4.8% 95.9%;
 | 
				
			||||||
    --secondary-foreground: 240 5.9% 10%;
 | 
					    --secondary-foreground: 24 9.8% 10%;
 | 
				
			||||||
    --muted: 240 4.8% 95.9%;
 | 
					    --muted: 60 4.8% 95.9%;
 | 
				
			||||||
    --muted-foreground: 240 3.8% 46.1%;
 | 
					    --muted-foreground: 25 5.3% 44.7%;
 | 
				
			||||||
    --accent: 240 4.8% 95.9%;
 | 
					    --accent: 60 4.8% 95.9%;
 | 
				
			||||||
    --accent-foreground: 240 5.9% 10%;
 | 
					    --accent-foreground: 24 9.8% 10%;
 | 
				
			||||||
    --destructive: 0 84.2% 60.2%;
 | 
					    --destructive: 0 84.2% 60.2%;
 | 
				
			||||||
    --destructive-foreground: 0 0% 98%;
 | 
					    --destructive-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --border: 240 5.9% 90%;
 | 
					    --border: 20 5.9% 90%;
 | 
				
			||||||
    --input: 240 5.9% 90%;
 | 
					    --input: 20 5.9% 90%;
 | 
				
			||||||
    --ring: 240 10% 3.9%;
 | 
					    --ring: 24.6 95% 53.1%;
 | 
				
			||||||
 | 
					    --radius: 1rem;
 | 
				
			||||||
    --chart-1: 12 76% 61%;
 | 
					    --chart-1: 12 76% 61%;
 | 
				
			||||||
    --chart-2: 173 58% 39%;
 | 
					    --chart-2: 173 58% 39%;
 | 
				
			||||||
    --chart-3: 197 37% 24%;
 | 
					    --chart-3: 197 37% 24%;
 | 
				
			||||||
    --chart-4: 43 74% 66%;
 | 
					    --chart-4: 43 74% 66%;
 | 
				
			||||||
    --chart-5: 27 87% 67%;
 | 
					    --chart-5: 27 87% 67%;
 | 
				
			||||||
    --radius: 0.5rem;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .dark {
 | 
					  .dark {
 | 
				
			||||||
    --background: 240 10% 3.9%;
 | 
					    --background: 20 14.3% 4.1%;
 | 
				
			||||||
    --foreground: 0 0% 98%;
 | 
					    --foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --card: 240 10% 3.9%;
 | 
					    --card: 20 14.3% 4.1%;
 | 
				
			||||||
    --card-foreground: 0 0% 98%;
 | 
					    --card-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --popover: 240 10% 3.9%;
 | 
					    --popover: 20 14.3% 4.1%;
 | 
				
			||||||
    --popover-foreground: 0 0% 98%;
 | 
					    --popover-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --primary: 0 0% 98%;
 | 
					    --primary: 20.5 90.2% 48.2%;
 | 
				
			||||||
    --primary-foreground: 240 5.9% 10%;
 | 
					    --primary-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --secondary: 240 3.7% 15.9%;
 | 
					    --secondary: 12 6.5% 15.1%;
 | 
				
			||||||
    --secondary-foreground: 0 0% 98%;
 | 
					    --secondary-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --muted: 240 3.7% 15.9%;
 | 
					    --muted: 12 6.5% 15.1%;
 | 
				
			||||||
    --muted-foreground: 240 5% 64.9%;
 | 
					    --muted-foreground: 24 5.4% 63.9%;
 | 
				
			||||||
    --accent: 240 3.7% 15.9%;
 | 
					    --accent: 12 6.5% 15.1%;
 | 
				
			||||||
    --accent-foreground: 0 0% 98%;
 | 
					    --accent-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --destructive: 0 62.8% 30.6%;
 | 
					    --destructive: 0 72.2% 50.6%;
 | 
				
			||||||
    --destructive-foreground: 0 0% 98%;
 | 
					    --destructive-foreground: 60 9.1% 97.8%;
 | 
				
			||||||
    --border: 240 3.7% 15.9%;
 | 
					    --border: 12 6.5% 15.1%;
 | 
				
			||||||
    --input: 240 3.7% 15.9%;
 | 
					    --input: 12 6.5% 15.1%;
 | 
				
			||||||
    --ring: 240 4.9% 83.9%;
 | 
					    --ring: 20.5 90.2% 48.2%;
 | 
				
			||||||
    --chart-1: 220 70% 50%;
 | 
					    --chart-1: 220 70% 50%;
 | 
				
			||||||
    --chart-2: 160 60% 45%;
 | 
					    --chart-2: 160 60% 45%;
 | 
				
			||||||
    --chart-3: 30 80% 55%;
 | 
					    --chart-3: 30 80% 55%;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ export default function RootLayout({
 | 
				
			|||||||
  children: React.ReactNode;
 | 
					  children: React.ReactNode;
 | 
				
			||||||
}>) {
 | 
					}>) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <html lang="en" className="scroll-smooth">
 | 
					    <html lang="en" className="scroll-smooth dark">
 | 
				
			||||||
      <head>
 | 
					      <head>
 | 
				
			||||||
        <PlausibleProvider
 | 
					        <PlausibleProvider
 | 
				
			||||||
          domain="bangersandmashgbg.com"
 | 
					          domain="bangersandmashgbg.com"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								app/page.tsx
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								app/page.tsx
									
									
									
									
									
								
							@@ -1,37 +1,3 @@
 | 
				
			|||||||
export default function Home() {
 | 
					export default function Home() {
 | 
				
			||||||
  return (
 | 
					  return "helloworld";
 | 
				
			||||||
    <form
 | 
					 | 
				
			||||||
      method="post"
 | 
					 | 
				
			||||||
      action="https://listmonk.schulze.network/subscription/form"
 | 
					 | 
				
			||||||
      className="listmonk-form"
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      <div>
 | 
					 | 
				
			||||||
        <h3>Subscribe</h3>
 | 
					 | 
				
			||||||
        <input type="hidden" name="nonce" />
 | 
					 | 
				
			||||||
        <p>
 | 
					 | 
				
			||||||
          <input type="email" name="email" required placeholder="E-post" />
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
        <p>
 | 
					 | 
				
			||||||
          <input type="text" name="name" placeholder="Namn (ej obligatorisk)" />
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <p>
 | 
					 | 
				
			||||||
          <input
 | 
					 | 
				
			||||||
            id="c99ab"
 | 
					 | 
				
			||||||
            type="checkbox"
 | 
					 | 
				
			||||||
            name="l"
 | 
					 | 
				
			||||||
            checked
 | 
					 | 
				
			||||||
            value="c99ab65c-cd0a-4ccc-ba63-0c9f7de99ac3"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
          <label htmlFor="c99ab">Produktnyheter</label>
 | 
					 | 
				
			||||||
          <br />
 | 
					 | 
				
			||||||
          <span>Produktnyheter för Schulze Network</span>
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <p>
 | 
					 | 
				
			||||||
          <input type="submit" value="Prenumerera" />
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </form>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								app/sign-up/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/sign-up/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import Image from "next/image";
 | 
				
			||||||
 | 
					import SignUp from "./sign-up-form";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Page() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="w-4/5 max-w-2xl mx-auto my-12">
 | 
				
			||||||
 | 
					      <Image
 | 
				
			||||||
 | 
					        src="/image/bam.png"
 | 
				
			||||||
 | 
					        alt="Bangers and Mash GBG"
 | 
				
			||||||
 | 
					        width={200}
 | 
				
			||||||
 | 
					        height={200}
 | 
				
			||||||
 | 
					        className="mx-auto"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <SignUp />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										124
									
								
								app/sign-up/sign-up-form.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								app/sign-up/sign-up-form.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 { format } from "date-fns";
 | 
				
			||||||
 | 
					import { CalendarIcon } from "lucide-react";
 | 
				
			||||||
 | 
					import { Input } from "@/components/ui/input";
 | 
				
			||||||
 | 
					import { Calendar } from "@/components/ui/calendar";
 | 
				
			||||||
 | 
					import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					import { signupFormSubmit } from "@/lib/actions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const signupFormSchema = z.object({
 | 
				
			||||||
 | 
					  name: z.string().min(2, { message: "Name is required" }).max(50, { message: "Name is too long" }),
 | 
				
			||||||
 | 
					  email: z.string().email({ message: "Email is invalid" }),
 | 
				
			||||||
 | 
					  dob: z.date({ required_error: "Birthday is required" }),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const youngestDate = new Date(new Date().setFullYear(new Date().getFullYear() - 21));
 | 
				
			||||||
 | 
					export const oldestDate = new Date(new Date().setFullYear(new Date().getFullYear() - 100));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function SignUp() {
 | 
				
			||||||
 | 
					  const form = useForm<z.infer<typeof signupFormSchema>>({
 | 
				
			||||||
 | 
					    resolver: zodResolver(signupFormSchema),
 | 
				
			||||||
 | 
					    defaultValues: {
 | 
				
			||||||
 | 
					      name: "",
 | 
				
			||||||
 | 
					      email: "",
 | 
				
			||||||
 | 
					      dob: youngestDate,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  function onSubmit(values: z.infer<typeof signupFormSchema>) {
 | 
				
			||||||
 | 
					    signupFormSubmit(values);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Form {...form}>
 | 
				
			||||||
 | 
					      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
 | 
				
			||||||
 | 
					        <FormField
 | 
				
			||||||
 | 
					          control={form.control}
 | 
				
			||||||
 | 
					          name="email"
 | 
				
			||||||
 | 
					          render={({ field }) => (
 | 
				
			||||||
 | 
					            <FormItem>
 | 
				
			||||||
 | 
					              <FormLabel>Email</FormLabel>
 | 
				
			||||||
 | 
					              <FormControl>
 | 
				
			||||||
 | 
					                <Input placeholder="name@example.com" {...field} />
 | 
				
			||||||
 | 
					              </FormControl>
 | 
				
			||||||
 | 
					              <FormDescription>
 | 
				
			||||||
 | 
					                We will contact you here with information about events.
 | 
				
			||||||
 | 
					              </FormDescription>
 | 
				
			||||||
 | 
					              <FormMessage />
 | 
				
			||||||
 | 
					            </FormItem>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <FormField
 | 
				
			||||||
 | 
					          control={form.control}
 | 
				
			||||||
 | 
					          name="name"
 | 
				
			||||||
 | 
					          render={({ field }) => (
 | 
				
			||||||
 | 
					            <FormItem>
 | 
				
			||||||
 | 
					              <FormLabel>Name</FormLabel>
 | 
				
			||||||
 | 
					              <FormControl>
 | 
				
			||||||
 | 
					                <Input placeholder="Firstname Lastname" {...field} />
 | 
				
			||||||
 | 
					              </FormControl>
 | 
				
			||||||
 | 
					              <FormDescription>Please enter your full name.</FormDescription>
 | 
				
			||||||
 | 
					              <FormMessage />
 | 
				
			||||||
 | 
					            </FormItem>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <FormField
 | 
				
			||||||
 | 
					          control={form.control}
 | 
				
			||||||
 | 
					          name="dob"
 | 
				
			||||||
 | 
					          render={({ field }) => (
 | 
				
			||||||
 | 
					            <FormItem className="flex flex-col">
 | 
				
			||||||
 | 
					              <FormLabel>Date of birth</FormLabel>
 | 
				
			||||||
 | 
					              <Popover>
 | 
				
			||||||
 | 
					                <PopoverTrigger asChild>
 | 
				
			||||||
 | 
					                  <FormControl>
 | 
				
			||||||
 | 
					                    <Button
 | 
				
			||||||
 | 
					                      variant={"outline"}
 | 
				
			||||||
 | 
					                      className={cn(
 | 
				
			||||||
 | 
					                        "w-[240px] pl-3 text-left font-normal",
 | 
				
			||||||
 | 
					                        !field.value && "text-muted-foreground"
 | 
				
			||||||
 | 
					                      )}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      {field.value ? format(field.value, "PPP") : <span>Pick a date</span>}
 | 
				
			||||||
 | 
					                      <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                  </FormControl>
 | 
				
			||||||
 | 
					                </PopoverTrigger>
 | 
				
			||||||
 | 
					                <PopoverContent className="w-auto p-0" align="start">
 | 
				
			||||||
 | 
					                  <Calendar
 | 
				
			||||||
 | 
					                    required
 | 
				
			||||||
 | 
					                    mode="single"
 | 
				
			||||||
 | 
					                    showOutsideDays={false}
 | 
				
			||||||
 | 
					                    selected={field.value}
 | 
				
			||||||
 | 
					                    onSelect={field.onChange}
 | 
				
			||||||
 | 
					                    disabled={(date) => date > youngestDate}
 | 
				
			||||||
 | 
					                    defaultMonth={field.value}
 | 
				
			||||||
 | 
					                    fromDate={oldestDate}
 | 
				
			||||||
 | 
					                    toDate={youngestDate}
 | 
				
			||||||
 | 
					                    captionLayout="dropdown"
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                </PopoverContent>
 | 
				
			||||||
 | 
					              </Popover>
 | 
				
			||||||
 | 
					              <FormDescription>You must be over 21 to sign up.</FormDescription>
 | 
				
			||||||
 | 
					              <FormMessage />
 | 
				
			||||||
 | 
					            </FormItem>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Button type="submit">Submit</Button>
 | 
				
			||||||
 | 
					      </form>
 | 
				
			||||||
 | 
					    </Form>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										95
									
								
								components/ui/calendar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								components/ui/calendar.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					import { ChevronLeft, ChevronRight } from "lucide-react";
 | 
				
			||||||
 | 
					import { DayPicker } from "react-day-picker";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					import { buttonVariants } from "@/components/ui/button";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Select,
 | 
				
			||||||
 | 
					  SelectContent,
 | 
				
			||||||
 | 
					  SelectItem,
 | 
				
			||||||
 | 
					  SelectTrigger,
 | 
				
			||||||
 | 
					  SelectValue,
 | 
				
			||||||
 | 
					} from "@/components/ui/select";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CalendarProps = React.ComponentProps<typeof DayPicker>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <DayPicker
 | 
				
			||||||
 | 
					      showOutsideDays={showOutsideDays}
 | 
				
			||||||
 | 
					      className={cn("p-3", className)}
 | 
				
			||||||
 | 
					      classNames={{
 | 
				
			||||||
 | 
					        months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
 | 
				
			||||||
 | 
					        month: "space-y-4",
 | 
				
			||||||
 | 
					        caption: "flex flex-col justify-center pt-1 relative items-stretch space-x-4 w-full",
 | 
				
			||||||
 | 
					        caption_label: "text-sm font-medium",
 | 
				
			||||||
 | 
					        nav: "space-x-1 flex items-center",
 | 
				
			||||||
 | 
					        nav_button: cn(
 | 
				
			||||||
 | 
					          buttonVariants({ variant: "outline" }),
 | 
				
			||||||
 | 
					          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        nav_button_previous: "absolute left-1",
 | 
				
			||||||
 | 
					        nav_button_next: "absolute right-1",
 | 
				
			||||||
 | 
					        table: "w-full border-collapse space-y-1",
 | 
				
			||||||
 | 
					        head_row: "flex",
 | 
				
			||||||
 | 
					        head_cell: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
 | 
				
			||||||
 | 
					        row: "flex w-full mt-2",
 | 
				
			||||||
 | 
					        cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
 | 
				
			||||||
 | 
					        day: cn(
 | 
				
			||||||
 | 
					          buttonVariants({ variant: "ghost" }),
 | 
				
			||||||
 | 
					          "h-9 w-9 p-0 font-normal aria-selected:opacity-100"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        day_range_end: "day-range-end",
 | 
				
			||||||
 | 
					        day_selected:
 | 
				
			||||||
 | 
					          "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
 | 
				
			||||||
 | 
					        day_today: "bg-accent text-accent-foreground",
 | 
				
			||||||
 | 
					        day_outside:
 | 
				
			||||||
 | 
					          "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
 | 
				
			||||||
 | 
					        day_disabled: "text-muted-foreground opacity-50",
 | 
				
			||||||
 | 
					        day_range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground",
 | 
				
			||||||
 | 
					        day_hidden: "invisible",
 | 
				
			||||||
 | 
					        ...classNames,
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      components={{
 | 
				
			||||||
 | 
					        IconLeft: ({ ...props }) => <ChevronLeft className="h-4 w-4" />,
 | 
				
			||||||
 | 
					        IconRight: ({ ...props }) => <ChevronRight className="h-4 w-4" />,
 | 
				
			||||||
 | 
					        Dropdown: ({ children, value, onChange, ...props }) => {
 | 
				
			||||||
 | 
					          const options = React.Children.toArray(children) as React.ReactElement<
 | 
				
			||||||
 | 
					            React.HTMLProps<HTMLOptionElement>
 | 
				
			||||||
 | 
					          >[];
 | 
				
			||||||
 | 
					          const selected = options.find((child) => child.props.value === value);
 | 
				
			||||||
 | 
					          const handleChange = (value: string) => {
 | 
				
			||||||
 | 
					            const changeEvent = {
 | 
				
			||||||
 | 
					              target: { value },
 | 
				
			||||||
 | 
					            } as React.ChangeEvent<HTMLSelectElement>;
 | 
				
			||||||
 | 
					            onChange?.(changeEvent);
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <Select value={value?.toString()} onValueChange={(v) => handleChange(v)}>
 | 
				
			||||||
 | 
					              <SelectTrigger className="inline-flex w-2/5 mx-2 mt-2">
 | 
				
			||||||
 | 
					                <SelectValue>{selected?.props?.children}</SelectValue>
 | 
				
			||||||
 | 
					              </SelectTrigger>
 | 
				
			||||||
 | 
					              <SelectContent position="popper">
 | 
				
			||||||
 | 
					                {options?.map((option, id: number) => (
 | 
				
			||||||
 | 
					                  <SelectItem
 | 
				
			||||||
 | 
					                    key={`${option.props.value}-${id}`}
 | 
				
			||||||
 | 
					                    value={option.props.value?.toString() ?? ""}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    {option.props.children}
 | 
				
			||||||
 | 
					                  </SelectItem>
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					              </SelectContent>
 | 
				
			||||||
 | 
					            </Select>
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					Calendar.displayName = "Calendar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Calendar };
 | 
				
			||||||
							
								
								
									
										25
									
								
								components/ui/input.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								components/ui/input.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface InputProps
 | 
				
			||||||
 | 
					  extends React.InputHTMLAttributes<HTMLInputElement> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Input = React.forwardRef<HTMLInputElement, InputProps>(
 | 
				
			||||||
 | 
					  ({ className, type, ...props }, ref) => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        type={type}
 | 
				
			||||||
 | 
					        className={cn(
 | 
				
			||||||
 | 
					          "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
 | 
				
			||||||
 | 
					          className
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        ref={ref}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					Input.displayName = "Input"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Input }
 | 
				
			||||||
							
								
								
									
										31
									
								
								components/ui/popover.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								components/ui/popover.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					import * as PopoverPrimitive from "@radix-ui/react-popover"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Popover = PopoverPrimitive.Root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const PopoverTrigger = PopoverPrimitive.Trigger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const PopoverContent = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof PopoverPrimitive.Content>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
 | 
				
			||||||
 | 
					>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <PopoverPrimitive.Portal>
 | 
				
			||||||
 | 
					    <PopoverPrimitive.Content
 | 
				
			||||||
 | 
					      ref={ref}
 | 
				
			||||||
 | 
					      align={align}
 | 
				
			||||||
 | 
					      sideOffset={sideOffset}
 | 
				
			||||||
 | 
					      className={cn(
 | 
				
			||||||
 | 
					        "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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",
 | 
				
			||||||
 | 
					        className
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  </PopoverPrimitive.Portal>
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					PopoverContent.displayName = PopoverPrimitive.Content.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Popover, PopoverTrigger, PopoverContent }
 | 
				
			||||||
							
								
								
									
										160
									
								
								components/ui/select.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								components/ui/select.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					import * as SelectPrimitive from "@radix-ui/react-select"
 | 
				
			||||||
 | 
					import { Check, ChevronDown, ChevronUp } from "lucide-react"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Select = SelectPrimitive.Root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectGroup = SelectPrimitive.Group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectValue = SelectPrimitive.Value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectTrigger = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof SelectPrimitive.Trigger>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
 | 
				
			||||||
 | 
					>(({ className, children, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <SelectPrimitive.Trigger
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn(
 | 
				
			||||||
 | 
					      "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
 | 
				
			||||||
 | 
					      className
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    {children}
 | 
				
			||||||
 | 
					    <SelectPrimitive.Icon asChild>
 | 
				
			||||||
 | 
					      <ChevronDown className="h-4 w-4 opacity-50" />
 | 
				
			||||||
 | 
					    </SelectPrimitive.Icon>
 | 
				
			||||||
 | 
					  </SelectPrimitive.Trigger>
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectScrollUpButton = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <SelectPrimitive.ScrollUpButton
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn(
 | 
				
			||||||
 | 
					      "flex cursor-default items-center justify-center py-1",
 | 
				
			||||||
 | 
					      className
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <ChevronUp className="h-4 w-4" />
 | 
				
			||||||
 | 
					  </SelectPrimitive.ScrollUpButton>
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectScrollDownButton = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <SelectPrimitive.ScrollDownButton
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn(
 | 
				
			||||||
 | 
					      "flex cursor-default items-center justify-center py-1",
 | 
				
			||||||
 | 
					      className
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <ChevronDown className="h-4 w-4" />
 | 
				
			||||||
 | 
					  </SelectPrimitive.ScrollDownButton>
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					SelectScrollDownButton.displayName =
 | 
				
			||||||
 | 
					  SelectPrimitive.ScrollDownButton.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectContent = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof SelectPrimitive.Content>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
 | 
				
			||||||
 | 
					>(({ className, children, position = "popper", ...props }, ref) => (
 | 
				
			||||||
 | 
					  <SelectPrimitive.Portal>
 | 
				
			||||||
 | 
					    <SelectPrimitive.Content
 | 
				
			||||||
 | 
					      ref={ref}
 | 
				
			||||||
 | 
					      className={cn(
 | 
				
			||||||
 | 
					        "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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",
 | 
				
			||||||
 | 
					        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)]"
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {children}
 | 
				
			||||||
 | 
					      </SelectPrimitive.Viewport>
 | 
				
			||||||
 | 
					      <SelectScrollDownButton />
 | 
				
			||||||
 | 
					    </SelectPrimitive.Content>
 | 
				
			||||||
 | 
					  </SelectPrimitive.Portal>
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					SelectContent.displayName = SelectPrimitive.Content.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectLabel = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof SelectPrimitive.Label>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <SelectPrimitive.Label
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					SelectLabel.displayName = SelectPrimitive.Label.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectItem = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof SelectPrimitive.Item>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
 | 
				
			||||||
 | 
					>(({ className, children, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <SelectPrimitive.Item
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn(
 | 
				
			||||||
 | 
					      "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
 | 
				
			||||||
 | 
					      className
 | 
				
			||||||
 | 
					    )}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
 | 
				
			||||||
 | 
					      <SelectPrimitive.ItemIndicator>
 | 
				
			||||||
 | 
					        <Check className="h-4 w-4" />
 | 
				
			||||||
 | 
					      </SelectPrimitive.ItemIndicator>
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
 | 
				
			||||||
 | 
					  </SelectPrimitive.Item>
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					SelectItem.displayName = SelectPrimitive.Item.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectSeparator = React.forwardRef<
 | 
				
			||||||
 | 
					  React.ElementRef<typeof SelectPrimitive.Separator>,
 | 
				
			||||||
 | 
					  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
 | 
				
			||||||
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
 | 
					  <SelectPrimitive.Separator
 | 
				
			||||||
 | 
					    ref={ref}
 | 
				
			||||||
 | 
					    className={cn("-mx-1 my-1 h-px bg-muted", className)}
 | 
				
			||||||
 | 
					    {...props}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					SelectSeparator.displayName = SelectPrimitive.Separator.displayName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  Select,
 | 
				
			||||||
 | 
					  SelectGroup,
 | 
				
			||||||
 | 
					  SelectValue,
 | 
				
			||||||
 | 
					  SelectTrigger,
 | 
				
			||||||
 | 
					  SelectContent,
 | 
				
			||||||
 | 
					  SelectLabel,
 | 
				
			||||||
 | 
					  SelectItem,
 | 
				
			||||||
 | 
					  SelectSeparator,
 | 
				
			||||||
 | 
					  SelectScrollUpButton,
 | 
				
			||||||
 | 
					  SelectScrollDownButton,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								lib/actions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								lib/actions.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					"use server";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { z } from "zod";
 | 
				
			||||||
 | 
					import { oldestDate, signupFormSchema, youngestDate } from "@/app/sign-up/sign-up-form";
 | 
				
			||||||
 | 
					import listmonk from "./listmonk";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function signupFormSubmit(data: z.infer<typeof signupFormSchema>) {
 | 
				
			||||||
 | 
					  if (data.dob > youngestDate || data.dob < oldestDate) {
 | 
				
			||||||
 | 
					    return { error: "Invalid date of birth" };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const listmonkData = {
 | 
				
			||||||
 | 
					    email: data.email,
 | 
				
			||||||
 | 
					    name: data.name,
 | 
				
			||||||
 | 
					    attribs: {
 | 
				
			||||||
 | 
					      dob: data.dob.toISOString(),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  console.log(listmonkData);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								lib/listmonk.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/listmonk.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					import "server-only";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function makeApiCall(url: string, options?: RequestInit) {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const response = await fetch(url, options);
 | 
				
			||||||
 | 
					    if (!response.ok) {
 | 
				
			||||||
 | 
					      throw new Error(`HTTP error! Status: ${response.status}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return await response.json();
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    console.error("Error making API call:", error);
 | 
				
			||||||
 | 
					    throw error;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function listmonk() {
 | 
				
			||||||
 | 
					  const listmonkUrl = process.env.LISTMONK_URL ?? "";
 | 
				
			||||||
 | 
					  makeApiCall(listmonkUrl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default listmonk;
 | 
				
			||||||
		Reference in New Issue
	
	Block a user