Skip to content

Commit

Permalink
build: form state, data fetching, server actions integrated
Browse files Browse the repository at this point in the history
  • Loading branch information
NemesisLW committed Jul 11, 2024
1 parent ec4a3a3 commit 29af454
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 57 deletions.
8 changes: 5 additions & 3 deletions app/(auth)/auth/callback/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { revalidatePath } from "next/cache";

export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
Expand All @@ -25,14 +26,15 @@ export async function GET(request: Request) {
cookieStore.delete({ name, ...options });
},
},
}
},
);
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
revalidatePath("/", "layout");
return NextResponse.redirect(`${origin}/generate`);
}
}

// return the user to an error page
return NextResponse.redirect(`${origin}/error`);
// return the user to home page
return NextResponse.redirect(`${origin}`);
}
5 changes: 4 additions & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Image from "next/image";

import GenerateButton from "@/components/generate-cta-button";
import Link from "next/link";

export default function Home() {
return (
Expand All @@ -25,7 +26,9 @@ export default function Home() {
Generator
</h1>
</div>
<GenerateButton className="mb-24 w-[150px] md:w-[200px] lg:w-[300px]" />
<Link href="/generate">
<GenerateButton className="mb-24 w-[150px] md:w-[200px] lg:w-[300px]" />
</Link>
</div>
</div>
</main>
Expand Down
44 changes: 23 additions & 21 deletions components/generate-cta-button.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
import Link from "next/link";
import { Button } from "./ui/button";
import Image from "next/image";
import { cn } from "@/lib/utils";

interface GenerateButtonProps {
className?: string;
text?: string;
isServerAction?: boolean;
pending?: boolean;
}

function GenerateButton({
className,
text = "Generate one for me",
isServerAction = false,
pending = false,
}: GenerateButtonProps) {
return (
<Button
asChild
className={cn(
className,
"h-8 rounded-full px-4 py-2 text-base text-white shadow-xl transition duration-300 hover:bg-rose-600 md:h-12 md:px-6 md:py-3 md:text-xl lg:text-3xl",
)}
type={isServerAction ? "submit" : "button"}
aria-disabled={isServerAction && pending}
>
<Link href="/generate">
<span className="flex items-center justify-center space-x-1 sm:space-x-2">
<Image
src="/ph_heart-fill.svg"
width={16}
height={17}
alt="Heart"
className="h-3 w-3 sm:h-4 sm:w-4 md:h-5 md:w-5"
/>
<span>{text}</span>
<Image
src="/ph_heart-fill.svg"
width={16}
height={17}
alt="Heart"
className="h-3 w-3 sm:h-4 sm:w-4 md:h-5 md:w-5"
/>
</span>
</Link>
<span className="flex items-center justify-center space-x-1 sm:space-x-2">
<Image
src="/ph_heart-fill.svg"
width={16}
height={17}
alt="Heart"
className="h-3 w-3 sm:h-4 sm:w-4 md:h-5 md:w-5"
/>
<span>{text}</span>
<Image
src="/ph_heart-fill.svg"
width={16}
height={17}
alt="Heart"
className="h-3 w-3 sm:h-4 sm:w-4 md:h-5 md:w-5"
/>
</span>
</Button>
);
}
Expand Down
41 changes: 29 additions & 12 deletions components/pickupline-input-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,65 @@
import { Textarea } from "@/components/ui/textarea";
import { Input } from "@/components/ui/input";

import React, { useState } from "react";
import React from "react";
import GenerateButton from "./generate-cta-button";
import { Generate } from "@/lib/ai/actions";
import { useFormState, useFormStatus } from "react-dom";

const initialState: {
message: string;
Data?: any;
} = { message: "" };

function PickupLineInputForm() {
const [crush, setCrush] = useState("");
const [style, setStyle] = useState("");
const { pending } = useFormStatus();
const [state, formAction] = useFormState(Generate, initialState);

return (
<div className="mx-auto w-full max-w-lg rounded-md bg-transparent text-[#FF2157]">
<h1 className="mb-4 text-3xl leading-tight md:text-4xl lg:text-5xl">
Pickup Line Generator
</h1>

<form className="space-y-4">
<form
className="flex flex-col text-xl md:text-2xl lg:space-y-4"
action={formAction}
>
<div>
<label
htmlFor="Crush"
htmlFor="crushDescription"
className="mb-1 block text-left text-[#A5455C]"
>
Tell us about your crush
</label>
<Textarea
id="Crush"
id="crushDescription"
name="crushDescription"
rows={4}
placeholder={"She is a 10 but...\nHe likes football...."}
className="w-full resize-none placeholder:text-[#DCDCDC] focus:outline-none focus:ring-0"
className="w-full resize-none placeholder:text-[#DCDCDC] focus:outline-none focus:ring-0 md:text-lg"
required
/>
</div>
<div>
<label
htmlFor="Style"
htmlFor="style"
className="mb-1 block text-left text-[#A5455C]"
>
Style
</label>
<Input
id="Style"
id="style"
name="style"
type="text"
placeholder="eg: Funny"
className="w-full placeholder:text-[#DCDCDC] focus:outline-none focus:ring-0"
className="w-full placeholder:text-[#DCDCDC] focus:outline-none focus:ring-0 md:text-lg"
required
/>
</div>
<GenerateButton className="w-full" />
<div className="pt-4 md:pt-8">
<GenerateButton className="w-full" pending={pending} isServerAction />
</div>
<p>{state.Data?.style}</p>
</form>
</div>
);
Expand Down
79 changes: 62 additions & 17 deletions lib/ai/actions.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
"use server";

import { generateObject } from "ai";
import { generateObject, streamObject } from "ai";
import { z } from "zod";
import { createStreamableValue } from "ai/rsc";
import { CoreMessage, streamText } from "ai";
import { OpenAI } from "openai";
import { openai } from "@ai-sdk/openai";

const formSchema = z.object({
crushDescription: z.string({
required_error: "Please provide a description of your crush",
}),
style: z.string({ required_error: "Style is required" }),
});

const anyscale = new OpenAI({
baseURL: "https://api.endpoints.anyscale.com/v1",
apiKey: process.env.ANYSCALE_API_KEY!,
});

async function chat_complete() {
const completion = await anyscale.chat.completions.create({
async function chat_complete(formData: FormData) {
const completion = await anyscale.completions.create({
model: "mistralai/Mixtral-8x22B-Instruct-v0.1",
messages: [],
prompt: "Generate a lasagna recipe.",
temperature: 1,
max_tokens: 256,
top_p: 1,
Expand All @@ -24,18 +31,56 @@ async function chat_complete() {
console.log(completion);
}

export async function Generate(formData: FormData) {
const { object } = await generateObject({
model: openai("mistralai/Mixtral-8x22B-Instruct-v0.1"),
schema: z.object({
recipe: z.object({
name: z.string(),
ingredients: z.array(
z.object({ name: z.string(), amount: z.string() }),
),
steps: z.array(z.string()),
}),
}),
prompt: "Generate a lasagna recipe.",
export async function Generate(
prevState: { message: string },
formData: FormData,
) {
const validatedInputs = formSchema.safeParse({
crushDescription: formData.get("crushDescription"),
style: formData.get("style"),
});

// Return early if the form data is invalid
if (!validatedInputs.success) {
return {
message: "Invalid form data",
errors: validatedInputs.error.flatten().fieldErrors,
};
}

// const { object } = await generateObject({
// model: openai("mistralai/Mixtral-8x22B-Instruct-v0.1"),
// schema: z.object({
// recipe: z.object({
// name: z.string(),
// ingredients: z.array(
// z.object({ name: z.string(), amount: z.string() }),
// ),
// steps: z.array(z.string()),
// }),
// }),
// prompt: "Generate a lasagna recipe.",
// });

// const { partialObjectStream } = await streamObject({
// model: openai("gpt-4-turbo"),
// schema: z.object({
// recipe: z.object({
// name: z.string(),
// ingredients: z.array(z.string()),
// steps: z.array(z.string()),
// }),
// }),
// prompt: "Generate a lasagna recipe.",
// });

// for await (const partialObject of partialObjectStream) {
// console.clear();
// console.log(partialObject);
// }

return {
message: "Generated recipe",
Data: validatedInputs.data,
};
}
3 changes: 0 additions & 3 deletions lib/supabase/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ export async function Login(formData: FormData) {
if (data.url) {
redirect(data.url);
}

revalidatePath("/", "layout");
// redirect("/generate");
}

export async function signOut(formData: FormData) {
Expand Down

0 comments on commit 29af454

Please sign in to comment.