Compare commits

...

2 Commits

Author SHA1 Message Date
e70ff89833 added card to show all wines in db
All checks were successful
Vercel Production Deployment / Deploy-Production (push) Successful in 1m24s
2024-05-28 15:39:21 +02:00
2f158c49d6 added form to create country 2024-05-28 15:22:06 +02:00
12 changed files with 134 additions and 176 deletions

View File

@ -1,17 +1,15 @@
"use client";
import useFilterStore from "./store";
import WineList from "./_components/WineList"; import WineList from "./_components/WineList";
import FilterStatus from "./_components/FilterState";
import CreateCountry from "./_components/admin/CreateCountry";
import AllCountries from "./_components/AllCountries";
export default function App() { export default function App() {
const filters = useFilterStore((state) => state.filters);
return ( return (
<div className="container flex w-full flex-col justify-center"> <div className="container flex w-full flex-col justify-center">
<h1 className="text-2xl">Filter state:</h1> <FilterStatus />
{JSON.stringify(filters)} <WineList />
<CreateCountry />
<AllCountries />
</div> </div>
); );
} }

View File

@ -0,0 +1,13 @@
import { db } from "~/server/db";
export default async function AllCountries() {
const countries = await db.query.countries.findMany();
return (
<div className="pt-4">
<h1 className="text-2xl">All Countries:</h1>
{countries.map((country) => (
<p key={country.id}>{country.name}</p>
))}
</div>
);
}

View File

@ -1,76 +0,0 @@
"use client";
import { useActionState, useEffect, useState } from "react";
import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
import { addWine } from "~/server/actions/addWine";
import { getProducers } from "~/server/actions/allProducers";
async function fetchProducers() {
const producers = await getProducers();
return producers;
}
type ProducersData = {
id: string;
name: string;
createdAt: Date;
country: string;
region: string;
email: string;
}[];
export default function CreateWine() {
const [formState, formAction] = useActionState(addWine, {
message: "",
errors: undefined,
fieldValues: {
name: "",
producer: "",
},
});
const [producers, setProducers] = useState<ProducersData>([]);
useEffect(() => {
async function loadProducers() {
const producersData = await fetchProducers();
setProducers(producersData);
}
loadProducers();
}, []);
return (
<div className="flex w-full flex-col">
<form action={formAction} className="flex flex-col gap-2">
<Input
placeholder="Enter wine name"
className="border-2"
type="text"
name="name"
/>
<Select name="producer">
<SelectTrigger className="w-[280px]">
<SelectValue placeholder="Select a producer" />
</SelectTrigger>
<SelectContent>
{producers.map((producer, i) => (
<SelectItem key={i} value={producer.id}>
{producer.name}
</SelectItem>
))}
</SelectContent>
</Select>
<Button type="submit">Add wine</Button>
</form>
{formState.message === "success" ? (
<span className="text-green-400">Wine succesfully submitted</span>
) : (
""
)}
</div>
);
}

View File

@ -0,0 +1,12 @@
"use client";
import useFilterStore, { FilterState } from "../store";
export default function FilterStatus() {
const filters = useFilterStore((state) => state.filters);
return (
<>
<h1 className="text-2xl">Filter state:</h1>
{JSON.stringify(filters, null, 2)}
</>
);
}

View File

@ -1,26 +0,0 @@
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import CreateWine from "./CreateWine";
export default function FormCard() {
return (
<Card>
<CardHeader>
<CardTitle>Add wine</CardTitle>
<CardDescription>All fields are required so get to it!</CardDescription>
</CardHeader>
<CardContent>
<CreateWine />
</CardContent>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</Card>
);
}

View File

@ -1,3 +0,0 @@
export default function Wine(wine) {
return <li key={wine.id}>{wine.name}</li>;
}

View File

@ -1,17 +1,23 @@
"use server"; "use server";
import { db } from "~/server/db"; import { db } from "~/server/db";
import Wine from "./Wine"; 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">
<h1>All wines:</h1> <h1 className="text-2xl">All wines:</h1>
<ul> {wines && wines.length > 0 ? (
{wines.map((wine) => ( <>
<Wine wine={wine} /> <ul>
))} {wines.map((wine) => (
</ul> <WineName key={wine.id} name={wine.name} />
</> ))}
</ul>
</>
) : (
<p>There are no wines in the db.</p>
)}
</div>
); );
} }

View File

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

View File

@ -0,0 +1,10 @@
import CreateCountryForm from "./CreateCountryForm";
export default function CreateCountry() {
return (
<>
<h1 className="pt-4 text-2xl">Fill the form to create a new country</h1>
<CreateCountryForm />
</>
);
}

View File

@ -0,0 +1,26 @@
import { revalidatePath } from "next/cache";
import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import { db } from "~/server/db";
import { countries } from "~/server/db/schema";
type NewCountry = {
name: string;
};
export default async function CreateCountryForm() {
const addCountry = async (formData: FormData) => {
"use server";
const newCountry: NewCountry = {
name: formData.get("name") as string,
};
await db.insert(countries).values(newCountry).returning();
revalidatePath("/");
};
return (
<form action={addCountry}>
<Input name="name" required />
<Button>Add country</Button>
</form>
);
}

View File

@ -2,7 +2,7 @@ import { create } from "zustand";
import { produce } from "immer"; import { produce } from "immer";
import { persist, createJSONStorage } from "zustand/middleware"; import { persist, createJSONStorage } from "zustand/middleware";
interface FilterState { export interface FilterState {
filters: Filters; filters: Filters;
} }
@ -82,7 +82,7 @@ const useFilterStore = create<FilterState & FilterActions>()(
setSearchQuery: (searchQuery: string) => setSearchQuery: (searchQuery: string) =>
set( set(
produce((state: FilterState) => { produce((state: FilterState) => {
state.filters.searchQuery = searchQuery; state.filters.searchQuery = searchQuery;
}), }),
), ),
addRegion: (region) => addRegion: (region) =>

View File

@ -6,7 +6,7 @@ import {
pgTableCreator, pgTableCreator,
text, text,
timestamp, timestamp,
uuid uuid,
} from "drizzle-orm/pg-core"; } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm"; import { relations } from "drizzle-orm";
@ -17,9 +17,9 @@ export const producers = createTable(
"producer", "producer",
{ {
id: uuid("id").primaryKey().defaultRandom(), id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(), name: text("name").notNull().unique(),
description: text("description"), description: text("description"),
imageUrl: text('imageUrl'), imageUrl: text("imageUrl"),
countryId: uuid("countryId").notNull(), countryId: uuid("countryId").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow(), updatedAt: timestamp("updatedAt").defaultNow(),
@ -37,26 +37,29 @@ export const producersRelations = relations(producers, ({ many }) => ({
})); }));
// Wines schema // Wines schema
export const typeEnum = pgEnum('type', ['sparkling', 'white', 'red', 'sweet', 'other']); export const typeEnum = pgEnum("type", [
"sparkling",
"white",
"red",
"sweet",
"other",
]);
export const wines = createTable( export const wines = createTable("wine", {
"wine", id: uuid("id").primaryKey().defaultRandom(),
{ name: text("name").notNull(),
id: uuid("id").primaryKey().defaultRandom(), type: typeEnum("type").notNull(),
name: text("name").notNull(), description: text("description"),
type: typeEnum("type").notNull(), imageUrl: text("imageUrl"),
description: text("description"), producerId: uuid("producerId").notNull(),
imageUrl: text('imageUrl'), subRegionId: uuid("subRegionId"),
producerId: uuid("producerId").notNull(), regionId: uuid("regionId").notNull(),
subRegionId: uuid("subRegionId"), countryId: uuid("countryId").notNull(),
regionId: uuid("regionId").notNull(), price: integer("price").notNull(),
countryId: uuid("countryId").notNull(), inStock: boolean("inStock").notNull().default(false),
price: integer("price").notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(),
inStock: boolean("inStock").notNull().default(false), updatedAt: timestamp("updatedAt").defaultNow(),
createdAt: timestamp("createdAt").defaultNow().notNull(), });
updatedAt: timestamp("updatedAt").defaultNow(),
},
);
// many-to-one relationship wine -> producer // many-to-one relationship wine -> producer
export const winesRelations = relations(wines, ({ one }) => ({ export const winesRelations = relations(wines, ({ one }) => ({
@ -75,53 +78,44 @@ export const winesRelations = relations(wines, ({ one }) => ({
country: one(countries, { country: one(countries, {
fields: [wines.countryId], fields: [wines.countryId],
references: [countries.id], references: [countries.id],
}) }),
})); }));
export const subRegions = createTable( export const subRegions = createTable("subRegion", {
"subRegion", id: uuid("id").primaryKey().notNull().defaultRandom(),
{ name: text("name").notNull().unique(),
id: uuid("id").primaryKey().notNull().defaultRandom(), regionId: uuid("regionId").notNull(),
name: text("name").notNull().unique(), });
regionId: uuid("regionId").notNull(),
},
);
export const subRegionsRelations = relations(subRegions, ({ many, one }) => ({ export const subRegionsRelations = relations(subRegions, ({ many, one }) => ({
wines: many(wines), wines: many(wines),
region: one(regions, { region: one(regions, {
fields: [subRegions.regionId], fields: [subRegions.regionId],
references: [regions.id] references: [regions.id],
}) }),
})); }));
export const regions = createTable( export const regions = createTable("region", {
"region", id: uuid("id").primaryKey().notNull().defaultRandom(),
{ name: text("name").notNull().unique(),
id: uuid("id").primaryKey().notNull().defaultRandom(), countryId: uuid("countryId").notNull(),
name: text("name").notNull().unique(), });
countryId: uuid("countryId").notNull(),
},
);
export const regionsRelations = relations(regions, ({ many, one }) => ({ export const regionsRelations = relations(regions, ({ many, one }) => ({
wines: many(wines), wines: many(wines),
subRegions: many(subRegions), subRegions: many(subRegions),
country: one(countries, { country: one(countries, {
fields: [regions.countryId], fields: [regions.countryId],
references: [countries.id] references: [countries.id],
}) }),
})); }));
export const countries = createTable( export const countries = createTable("country", {
"country", id: uuid("id").primaryKey().notNull().defaultRandom(),
{ name: text("name").notNull().unique(),
id: uuid("id").primaryKey().notNull().defaultRandom(), });
name: text("name").notNull(),
},
);
export const countriesRelations = relations(countries, ({ many }) => ({ export const countriesRelations = relations(countries, ({ many }) => ({
wines: many(wines), wines: many(wines),
regions: many(regions) regions: many(regions),
})); }));