2025-01-14 21:37:04 +07:00

259 lines
8.3 KiB
TypeScript

import React from "react";
import Image from "next/image";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Pencil } from "lucide-react";
import { formatDate } from "@/lib/utils";
import { Tables } from "@/utils/supabase/database.types";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Badge } from "@/components/ui/badge";
import Spinner from "@/components/spinner";
import { Textarea } from "@/components/ui/textarea";
import clsx from "clsx";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import axios, { AxiosError } from "axios";
import { toast } from "sonner";
type AgentCardProps = { data: Tables<"agents"> };
export default function AgentCard(props: AgentCardProps) {
const { id, image_url, name, description, model_type, created_at } =
props.data;
const [openDialogUpdate, setOpenDialogUpdate] = React.useState(false);
const formSchema = z.object({
question: z
.string({
required_error: "Question is required",
})
.min(2, {
message: "Minimum Question is 2 characters.",
}),
});
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
question: "",
},
mode: "onChange",
});
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (values: z.infer<typeof formSchema>) => {
const url = encodeURI(
`https://ai-endpoint-one.dev3vds1.link/deepinfra-ai/${name}/${model_type}/${description}/${values.question}`
);
const json = await axios.get(url);
return json.data;
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["agents"],
});
},
onError: (error) => {
if (error instanceof AxiosError) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
const errResponse = error.response.data;
console.log({ errResponse });
// if (errResponse.errors && Array.isArray(errResponse.errors)) {
// errResponse.errors.forEach(
// (inputErr: { field: string; message: string }) => {
// toast.error(`Error field : ${inputErr.field}`, {
// description: inputErr.message,
// });
// }
// );
// }
// if (errResponse.code) {
// toast.error(errResponse?.code, {
// description: errResponse?.message,
// });
// }
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
toast.error("No response from the server", {
description: "Failed to fetch the data, server returns null.",
});
} else {
// Something happened in setting up the request that triggered an Error
toast.error("Failed to set up the request", {
description: "There is something wrong when setting up the request",
});
}
}
// toast.error(err.message);
},
});
const onSubmit = (values: z.infer<typeof formSchema>) => {
mutation.mutate(values);
};
const {
formState: { errors },
} = form;
return (
<li className="bg-background flex flex-col rounded-lg p-4 border hover:shadow-sm transition-shadow duration-200">
<figure className="overflow-hidden relative rounded-lg object-cover border mb-3 w-full h-[200px]">
<Image
className="w-full object-cover"
src={image_url ? image_url : "/placeholder.png"}
fill={true}
alt={name}
/>
<div className="h-full w-full absolute top-0 left-0" />
<Badge className="absolute bottom-3 left-3">{model_type}</Badge>
</figure>
<p className="mb-2">
<time dateTime={created_at} className="text-sm text-muted-foreground">
{formatDate(created_at)}
</time>
</p>
<h3 className="text-xl font-semibold mb-2 truncate">{name}</h3>
<p className="text-sm text-foreground line-clamp-2 mb-4">{description}</p>
<div className="mt-auto grid">
<Dialog
open={openDialogUpdate}
onOpenChange={(val) => {
// form.reset();
setOpenDialogUpdate(val);
}}
>
<DialogTrigger asChild>
<Button size={"sm"} variant={"outline"}>
<Pencil className="h-4 w-4 mr-2" />
<span>Try it</span>
</Button>
</DialogTrigger>
<DialogContent className="w-[90%] h-[90vh] max-w-xl rounded-md overflow-y-scroll">
<DialogHeader>
<DialogTitle>Try Agent</DialogTitle>
<DialogDescription>
Try to ask a question to our agent
</DialogDescription>
</DialogHeader>
<figure className="overflow-hidden relative rounded-lg object-cover border mb-3 w-full h-[200px]">
<Image
className="w-full object-cover"
src={image_url ? image_url : "/placeholder.png"}
fill={true}
alt={name}
/>
<div className="h-full w-full absolute top-0 left-0" />
<Badge className="absolute bottom-3 left-3">{model_type}</Badge>
</figure>
<ul className="text-sm space-y-3">
<li>
<h3 className="font-bold">Name:</h3>
<p className="text-gray-500">{name}</p>
</li>
<li>
<h3 className="font-bold">Description:</h3>
<p className="text-gray-500">{description}</p>
</li>
</ul>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-3"
>
<FormField
control={form.control}
name="question"
render={({ field }) => (
<FormItem>
<FormLabel className="font-bold">Question:</FormLabel>
<FormControl>
<Textarea
rows={4}
placeholder="For answering daily questions."
className={clsx(
errors.question && "focus-visible:ring-destructive"
)}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{mutation.isSuccess && (
<>
<h3 className="text-sm font-bold">Answer:</h3>
<blockquote className="border-l-2 pl-6 italic">
{mutation.data.response}
</blockquote>
</>
)}
<DialogFooter className="mt-4 flex justify-end space-x-2">
<DialogClose asChild>
<Button
type="button"
variant="outline"
disabled={mutation.isPending}
size="sm"
>
{mutation.isPending && (
<Spinner className="mr-2 h-4 w-4 " />
)}
Cancel
</Button>
</DialogClose>
<Button type="submit" disabled={mutation.isPending} size="sm">
{mutation.isPending && (
<Spinner className="mr-2 h-4 w-4 " />
)}
Submit
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
</div>
</li>
);
}