Compare commits

..

2 Commits

Author SHA1 Message Date
0bab29653a created delete function
All checks were successful
Vercel Production Deployment / Deploy-Production (push) Successful in 1m25s
2024-05-28 22:41:55 +02:00
dbdb5356e2 updated components for pending 2024-05-28 22:16:39 +02:00
14 changed files with 121 additions and 130 deletions

View File

@ -1,3 +1,4 @@
import { deleteCountry } from "~/server/actions/deleteCountry";
import { db } from "~/server/db"; import { db } from "~/server/db";
export default async function AllCountries() { export default async function AllCountries() {
@ -6,7 +7,12 @@ export default async function AllCountries() {
<div className="pt-4"> <div className="pt-4">
<h1 className="text-2xl">All Countries:</h1> <h1 className="text-2xl">All Countries:</h1>
{countries.map((country) => ( {countries.map((country) => (
<div className="flex gap-1">
<p key={country.id}>{country.name}</p> <p key={country.id}>{country.name}</p>
<form action={deleteCountry.bind(null, country.id)}>
<button></button>
</form>
</div>
))} ))}
</div> </div>
); );

View File

@ -4,14 +4,12 @@ import { Input } from "~/components/ui/input";
import useFilterStore from "../store"; import useFilterStore from "../store";
export default function SearchBar() { export default function SearchBar() {
const [query, setQuery] = useState("");
const setStoreSearchQuery = useFilterStore((state) => state.setSearchQuery); const setStoreSearchQuery = useFilterStore((state) => state.setSearchQuery);
const { searchQuery } = useFilterStore((state) => state.filters); const { searchQuery } = useFilterStore((state) => state.filters);
function handleInput(e: ChangeEvent<HTMLInputElement>) { function handleInput(e: ChangeEvent<HTMLInputElement>) {
e.preventDefault(); e.preventDefault();
const newValue = e.target.value; const newValue = e.target.value;
setQuery(newValue);
setStoreSearchQuery(newValue); setStoreSearchQuery(newValue);
} }
return ( return (

View File

@ -0,0 +1,11 @@
import { useFormStatus } from "react-dom";
import { Button } from "~/components/ui/button";
export default function SubmitButton() {
const { pending } = useFormStatus();
return (
<Button disabled={pending}>
{pending ? "Adding country.." : "Add country"}
</Button>
);
}

View File

@ -1,17 +1,17 @@
"use server"; "use server";
import { deleteCountry } from "~/server/actions/deleteCountry";
import { db } from "~/server/db"; import { db } from "~/server/db";
import WineName from "./WineName";
export default async function WineList() { export default async function WineList() {
const wines = await db.query.wines.findMany(); const wines = await db.query.wines.findMany();
return ( return (
<div className="pt-4"> <div className="pt-4">
<h1 className="text-2xl">All wines:</h1> <h1 className="text-2xl">All wines:</h1>
{wines && wines.length > 0 ? ( {wines ? (
<> <>
<ul> <ul>
{wines.map((wine) => ( {wines.map((wine) => (
<WineName key={wine.id} name={wine.name} /> <li key={wine.id}>{wine.name}</li>
))} ))}
</ul> </ul>
</> </>

View File

@ -1,4 +0,0 @@
export default function WineName(props: { name: string }) {
const { name } = props;
return <p>{name}</p>;
}

View File

@ -1,26 +1,23 @@
import { revalidatePath } from "next/cache"; "use client";
import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input"; import { Input } from "~/components/ui/input";
import { db } from "~/server/db"; import { addCountry } from "~/server/actions/createCountry";
import { countries } from "~/server/db/schema"; import SubmitButton from "../SubmitButton";
import { useRef } from "react";
type NewCountry = { const initialState = {
name: string; name: "" as string,
}; };
export default async function CreateCountryForm() { export default function CreateCountryForm() {
const addCountry = async (formData: FormData) => { const ref = useRef<HTMLFormElement>(null);
"use server"; const handleSubmit = async (formData: FormData) => {
const newCountry: NewCountry = { ref.current?.reset();
name: formData.get("name") as string, await addCountry(formData);
};
await db.insert(countries).values(newCountry).returning();
revalidatePath("/");
}; };
return ( return (
<form action={addCountry}> <form ref={ref} action={handleSubmit}>
<Input name="name" required /> <Input name="name" required />
<Button>Add country</Button> <SubmitButton />
</form> </form>
); );
} }

View File

@ -0,0 +1,59 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "~/lib/utils"
const alertVariants = cva(
"relative w-full rounded-lg border border-slate-200 p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-slate-950 dark:border-slate-800 dark:[&>svg]:text-slate-50",
{
variants: {
variant: {
default: "bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50",
destructive:
"border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"
export { Alert, AlertTitle, AlertDescription }

View File

@ -1,67 +0,0 @@
'use server'
import { ZodError, z } from 'zod';
import { db } from '../db/index'
import { wines } from '../db/schema'
import { QueryResult } from 'pg';
type NewWine = typeof wines.$inferInsert;
export type InsertResult = {
name: string;
producer: string;
id: string;
createdAt: Date;
updatedAt: Date | null;
}[];
export type Fields = {
name: FormDataEntryValue | null
producer: FormDataEntryValue | null;
}
export type FormState = {
message: string | QueryResult<never>;
errors: Record<keyof Fields, string> | undefined;
fieldValues: NewWine;
}
const schema = z.object({
name: z.string(),
producer: z.string().uuid(),
})
export const addWine = async (
prevState: FormState,
formData: FormData): Promise<FormState> => {
const newWine: NewWine = {
name: formData.get('name') as string,
producer: formData.get('producer') as string
}
try {
schema.parse(newWine)
await db.insert(wines).values(newWine);
return {
message: 'success',
errors: undefined,
fieldValues: {
name: "",
producer: ""
}
}
} catch (error) {
const zodError = error as ZodError;
const errorMap = zodError.flatten().fieldErrors;
return {
message: "error",
errors: {
name: errorMap["name"]?.[0] ?? "",
producer: errorMap["producer"]?.[0] ?? ""
},
fieldValues: {
name: newWine.name,
producer: newWine.producer
}
}
}
}

View File

@ -1,7 +0,0 @@
'use server'
import { db } from "../db/index";
export async function getProducers(){
return db.query.producers.findMany();
}

View File

@ -0,0 +1,17 @@
"use server";
import { revalidatePath } from "next/cache";
import { db } from "../db";
import { countries } from "../db/schema";
type NewCountry = {
name: string;
};
export const addCountry = async (formData: FormData) => {
const newCountry: NewCountry = {
name: (formData.get("name") as string).toLowerCase(),
};
revalidatePath("/");
await db.insert(countries).values(newCountry).returning();
};

View File

@ -0,0 +1,11 @@
"use server";
import { eq } from "drizzle-orm";
import { db } from "../db";
import { countries } from "../db/schema";
import { revalidatePath } from "next/cache";
export async function deleteCountry(id: string) {
await db.delete(countries).where(eq(countries.id, id));
revalidatePath("/");
}

View File

@ -1,7 +0,0 @@
'use server'
import { db } from "../db/index";
export async function getAllWines(){
const wines = db.query.wines.findMany()
return wines;
}

View File

@ -1,10 +0,0 @@
"use server";
import { producers } from "../db/schema";
import { db } from "../db";
import { eq } from "drizzle-orm";
export default async function getProducer(id: string) {
return db.query.producers.findFirst({
where: eq(producers.id, id),
});
}

View File

@ -1,13 +0,0 @@
"use server";
import { sql } from "drizzle-orm";
import { db } from "../db";
import { wines, producers } from "../db/schema";
import { UUID } from "crypto";
export async function getWineDetails(id: string) {
const results = await db
.select()
.from(producers)
.where(sql`${producers.id} = ${id}`);
return results;
}