mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Create location table for brewery post locations
This commit is contained in:
@@ -44,7 +44,9 @@ const BeerCard: FC<{ post: z.infer<typeof beerPostQueryResult> }> = ({ post }) =
|
||||
<span className="text-sm lg:text-lg">{post.abv}% ABV</span>
|
||||
<span className="text-sm lg:text-lg">{post.ibu} IBU</span>
|
||||
</div>
|
||||
<span>liked by {likeCount} users</span>
|
||||
<span>
|
||||
liked by {likeCount} user{likeCount === 1 ? '' : 's'}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{user && <BeerPostLikeButton beerPostId={post.id} mutateCount={mutate} />}
|
||||
|
||||
@@ -32,7 +32,8 @@ const BreweryCard: FC<{ brewery: z.infer<typeof BreweryPostQueryResult> }> = ({
|
||||
</Link>
|
||||
</h2>
|
||||
<h3 className="text-xl font-normal lg:text-2xl">
|
||||
located in {brewery.city}, {brewery.stateOrProvince || brewery.country}
|
||||
located in {brewery.location.city},{' '}
|
||||
{brewery.location.stateOrProvince || brewery.location.country}
|
||||
</h3>
|
||||
<h4 className="text-lg lg:text-xl">
|
||||
est. {brewery.dateEstablished.getFullYear()}
|
||||
|
||||
@@ -54,6 +54,8 @@ const useBeerPostComments = ({ id, pageSize }: UseBeerPostCommentsProps) => {
|
||||
|
||||
const isAtEnd = !(size < data?.[0].pageCount!);
|
||||
|
||||
console.log(comments);
|
||||
|
||||
return {
|
||||
comments,
|
||||
isLoading,
|
||||
|
||||
@@ -43,8 +43,8 @@ const BreweryInfoHeader: FC<BreweryInfoHeaderProps> = ({ breweryPost }) => {
|
||||
<h1 className="text-2xl font-bold lg:text-4xl">{breweryPost.name}</h1>
|
||||
<h2 className="text-lg font-semibold lg:text-2xl">
|
||||
Located in
|
||||
{` ${breweryPost.city}, ${
|
||||
breweryPost.stateOrProvince || breweryPost.country
|
||||
{` ${breweryPost.location.city}, ${
|
||||
breweryPost.location.stateOrProvince || breweryPost.location.country
|
||||
}`}
|
||||
</h2>
|
||||
</div>
|
||||
@@ -130,7 +130,7 @@ const BreweryMap: FC<BreweryMapProps> = ({ latitude, longitude }) => {
|
||||
};
|
||||
|
||||
const BreweryByIdPage: NextPage<BreweryPageProps> = ({ breweryPost }) => {
|
||||
const [longitude, latitude] = breweryPost.coordinates;
|
||||
const [longitude, latitude] = breweryPost.location.coordinates;
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
|
||||
41
src/prisma/migrations/20230426013222_/migration.sql
Normal file
41
src/prisma/migrations/20230426013222_/migration.sql
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `address` on the `BreweryPost` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `city` on the `BreweryPost` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `coordinates` on the `BreweryPost` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `country` on the `BreweryPost` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `stateOrProvince` on the `BreweryPost` table. All the data in the column will be lost.
|
||||
- A unique constraint covering the columns `[locationId]` on the table `BreweryPost` will be added. If there are existing duplicate values, this will fail.
|
||||
- Added the required column `locationId` to the `BreweryPost` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "BreweryPost" DROP COLUMN "address";
|
||||
ALTER TABLE "BreweryPost" DROP COLUMN "city";
|
||||
ALTER TABLE "BreweryPost" DROP COLUMN "coordinates";
|
||||
ALTER TABLE "BreweryPost" DROP COLUMN "country";
|
||||
ALTER TABLE "BreweryPost" DROP COLUMN "stateOrProvince";
|
||||
ALTER TABLE "BreweryPost" ADD COLUMN "locationId" STRING NOT NULL;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Location" (
|
||||
"id" STRING NOT NULL,
|
||||
"city" STRING NOT NULL,
|
||||
"stateOrProvince" STRING,
|
||||
"country" STRING,
|
||||
"coordinates" FLOAT8[],
|
||||
"address" STRING NOT NULL,
|
||||
"postedById" STRING NOT NULL,
|
||||
|
||||
CONSTRAINT "Location_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "BreweryPost_locationId_key" ON "BreweryPost"("locationId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Location" ADD CONSTRAINT "Location_postedById_fkey" FOREIGN KEY ("postedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "BreweryPost" ADD CONSTRAINT "BreweryPost_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -30,6 +30,7 @@ model User {
|
||||
BeerImage BeerImage[]
|
||||
BreweryImage BreweryImage[]
|
||||
BreweryPostLike BreweryPostLike[]
|
||||
Location Location[]
|
||||
}
|
||||
|
||||
model BeerPost {
|
||||
@@ -93,14 +94,23 @@ model BeerType {
|
||||
beerPosts BeerPost[]
|
||||
}
|
||||
|
||||
model BreweryPost {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
model Location {
|
||||
id String @id @default(uuid())
|
||||
city String
|
||||
stateOrProvince String?
|
||||
country String?
|
||||
coordinates Float[]
|
||||
address String
|
||||
postedBy User @relation(fields: [postedById], references: [id], onDelete: Cascade)
|
||||
postedById String
|
||||
BreweryPost BreweryPost?
|
||||
}
|
||||
|
||||
model BreweryPost {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
location Location @relation(fields: [locationId], references: [id])
|
||||
locationId String @unique
|
||||
beers BeerPost[]
|
||||
description String
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
||||
|
||||
@@ -26,17 +26,23 @@ const createNewBeerPosts = async ({
|
||||
const beerType = beerTypes[Math.floor(Math.random() * beerTypes.length)];
|
||||
const breweryPost = breweryPosts[Math.floor(Math.random() * breweryPosts.length)];
|
||||
const createdAt = faker.date.past(1);
|
||||
|
||||
const abv = Math.floor(Math.random() * (12 - 4) + 4);
|
||||
const ibu = Math.floor(Math.random() * (60 - 10) + 10);
|
||||
const name = faker.commerce.productName();
|
||||
const description = faker.lorem.lines(20).replace(/(\r\n|\n|\r)/gm, ' ');
|
||||
|
||||
beerPostPromises.push(
|
||||
prisma.beerPost.create({
|
||||
data: {
|
||||
abv: Math.floor(Math.random() * (12 - 4) + 4),
|
||||
ibu: Math.floor(Math.random() * (60 - 10) + 10),
|
||||
name: faker.commerce.productName(),
|
||||
description: faker.lorem.lines(12).replace(/(\r\n|\n|\r)/gm, ' '),
|
||||
abv,
|
||||
ibu,
|
||||
name,
|
||||
description,
|
||||
createdAt,
|
||||
brewery: { connect: { id: breweryPost.id } },
|
||||
postedBy: { connect: { id: user.id } },
|
||||
type: { connect: { id: beerType.id } },
|
||||
createdAt,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { User } from '@prisma/client';
|
||||
import { Location, User } from '@prisma/client';
|
||||
import DBClient from '../../DBClient';
|
||||
import geocode from '../../../config/mapbox/geocoder';
|
||||
|
||||
interface CreateNewBreweryPostsArgs {
|
||||
numberOfPosts: number;
|
||||
joinData: {
|
||||
users: User[];
|
||||
locations: Location[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,23 +15,17 @@ const createNewBreweryPosts = async ({
|
||||
numberOfPosts,
|
||||
joinData,
|
||||
}: CreateNewBreweryPostsArgs) => {
|
||||
const { users } = joinData;
|
||||
const { users, locations } = joinData;
|
||||
|
||||
const prisma = DBClient.instance;
|
||||
const breweryPromises = [];
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let i = 0; i < numberOfPosts; i++) {
|
||||
const name = `${faker.commerce.productName()} Brewing Company`;
|
||||
const location = faker.address.cityName();
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const geodata = await geocode(location);
|
||||
|
||||
const city = geodata.text;
|
||||
const stateOrProvince = geodata.context.find((c) => c.id.startsWith('region'))?.text;
|
||||
const country = geodata.context.find((c) => c.id.startsWith('country'))?.text;
|
||||
const coordinates = geodata.center;
|
||||
const address = geodata.place_name;
|
||||
const description = faker.lorem.lines(5);
|
||||
const locationIndex = Math.floor(Math.random() * locations.length);
|
||||
const location = locations[locationIndex];
|
||||
locations.splice(locationIndex, 1); // Remove the location from the array
|
||||
const description = faker.lorem.lines(20).replace(/(\r\n|\n|\r)/gm, ' ');
|
||||
const user = users[Math.floor(Math.random() * users.length)];
|
||||
const createdAt = faker.date.past(1);
|
||||
const dateEstablished = faker.date.past(40);
|
||||
@@ -40,17 +34,11 @@ const createNewBreweryPosts = async ({
|
||||
prisma.breweryPost.create({
|
||||
data: {
|
||||
name,
|
||||
|
||||
city,
|
||||
stateOrProvince,
|
||||
country,
|
||||
coordinates,
|
||||
address,
|
||||
|
||||
description,
|
||||
postedBy: { connect: { id: user.id } },
|
||||
createdAt,
|
||||
dateEstablished,
|
||||
postedBy: { connect: { id: user.id } },
|
||||
location: { connect: { id: location.id } },
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
63
src/prisma/seed/create/createNewLocations.ts
Normal file
63
src/prisma/seed/create/createNewLocations.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { User, Location } from '@prisma/client';
|
||||
import { GeocodeFeature } from '@mapbox/mapbox-sdk/services/geocoding';
|
||||
import DBClient from '../../DBClient';
|
||||
import geocode from '../../../config/mapbox/geocoder';
|
||||
|
||||
interface CreateNewLocationsArgs {
|
||||
numberOfLocations: number;
|
||||
joinData: {
|
||||
users: User[];
|
||||
};
|
||||
}
|
||||
|
||||
const createNewLocations = async ({
|
||||
numberOfLocations,
|
||||
joinData,
|
||||
}: CreateNewLocationsArgs) => {
|
||||
const prisma = DBClient.instance;
|
||||
|
||||
const locationNames: string[] = [];
|
||||
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let i = 0; i < numberOfLocations; i++) {
|
||||
locationNames.push(faker.address.cityName());
|
||||
}
|
||||
|
||||
const geocodePromises: Promise<GeocodeFeature>[] = [];
|
||||
|
||||
locationNames.forEach((locationName) => {
|
||||
geocodePromises.push(geocode(locationName));
|
||||
});
|
||||
|
||||
const geocodedLocations = await Promise.all(geocodePromises);
|
||||
|
||||
const locationPromises: Promise<Location>[] = [];
|
||||
|
||||
geocodedLocations.forEach((geodata) => {
|
||||
const city = geodata.text;
|
||||
const user = joinData.users[Math.floor(Math.random() * joinData.users.length)];
|
||||
const stateOrProvince = geodata.context?.find((c) => c.id.startsWith('region'))?.text;
|
||||
const country = geodata.context?.find((c) => c.id.startsWith('country'))?.text;
|
||||
const coordinates = geodata.center;
|
||||
const address = geodata.place_name;
|
||||
|
||||
locationPromises.push(
|
||||
prisma.location.create({
|
||||
data: {
|
||||
city,
|
||||
stateOrProvince,
|
||||
country,
|
||||
coordinates,
|
||||
address,
|
||||
postedBy: { connect: { id: user.id } },
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(locationPromises);
|
||||
};
|
||||
|
||||
export default createNewLocations;
|
||||
@@ -16,6 +16,9 @@ const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => {
|
||||
Array.from({ length: numberOfUsers }, () => argon2.hash(faker.internet.password())),
|
||||
);
|
||||
|
||||
const takenEmails: string[] = [];
|
||||
const takenUsernames: string[] = [];
|
||||
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let i = 0; i < numberOfUsers; i++) {
|
||||
const randomValue = crypto.randomBytes(10).toString('hex');
|
||||
@@ -24,6 +27,18 @@ const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => {
|
||||
const username = `${firstName[0]}.${lastName}.${randomValue}`;
|
||||
const email = faker.internet.email(firstName, randomValue, 'example.com');
|
||||
|
||||
const usernameTaken = takenUsernames.includes(username);
|
||||
const emailTaken = takenEmails.includes(email);
|
||||
|
||||
if (usernameTaken || emailTaken) {
|
||||
i -= 1;
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
||||
takenEmails.push(email);
|
||||
takenUsernames.push(username);
|
||||
|
||||
const hash = hashedPasswords[i];
|
||||
const dateOfBirth = faker.date.birthdate({ mode: 'age', min: 19 });
|
||||
const createdAt = faker.date.past(1);
|
||||
|
||||
@@ -14,6 +14,7 @@ import createNewBreweryPostComments from './create/createNewBreweryPostComments'
|
||||
import createNewBreweryPosts from './create/createNewBreweryPosts';
|
||||
import createNewUsers from './create/createNewUsers';
|
||||
import createNewBreweryPostLikes from './create/createNewBreweryPostLikes';
|
||||
import createNewLocations from './create/createNewLocations';
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
@@ -25,15 +26,25 @@ import createNewBreweryPostLikes from './create/createNewBreweryPostLikes';
|
||||
logger.info('Database cleared successfully, preparing to seed.');
|
||||
|
||||
const users = await createNewUsers({ numberOfUsers: 1000 });
|
||||
logger.info('Users created successfully.');
|
||||
console.log(users);
|
||||
|
||||
const locations = await createNewLocations({
|
||||
numberOfLocations: 1500,
|
||||
joinData: { users },
|
||||
});
|
||||
logger.info('Locations created successfully.');
|
||||
|
||||
const [breweryPosts, beerTypes] = await Promise.all([
|
||||
createNewBreweryPosts({ numberOfPosts: 30, joinData: { users } }),
|
||||
createNewBreweryPosts({ numberOfPosts: 1300, joinData: { users, locations } }),
|
||||
createNewBeerTypes({ joinData: { users } }),
|
||||
]);
|
||||
logger.info('Brewery posts and beer types created successfully.');
|
||||
const beerPosts = await createNewBeerPosts({
|
||||
numberOfPosts: 200,
|
||||
joinData: { breweryPosts, beerTypes, users },
|
||||
});
|
||||
|
||||
logger.info('Beer posts created successfully.');
|
||||
const [
|
||||
beerPostComments,
|
||||
breweryPostComments,
|
||||
@@ -67,6 +78,10 @@ import createNewBreweryPostLikes from './create/createNewBreweryPostLikes';
|
||||
}),
|
||||
]);
|
||||
|
||||
logger.info(
|
||||
'Beer post comments, brewery post comments, beer post likes, beer images, and brewery images created successfully.',
|
||||
);
|
||||
|
||||
const end = performance.now();
|
||||
const timeElapsed = (end - start) / 1000;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
const prisma = DBClient.instance;
|
||||
@@ -14,11 +15,15 @@ const getAllBreweryPosts = async (pageNum?: number, pageSize?: number) => {
|
||||
take,
|
||||
select: {
|
||||
id: true,
|
||||
coordinates: true,
|
||||
address: true,
|
||||
city: true,
|
||||
stateOrProvince: true,
|
||||
country: true,
|
||||
location: {
|
||||
select: {
|
||||
city: true,
|
||||
address: true,
|
||||
coordinates: true,
|
||||
country: true,
|
||||
stateOrProvince: true,
|
||||
},
|
||||
},
|
||||
description: true,
|
||||
name: true,
|
||||
postedBy: { select: { username: true, id: true } },
|
||||
|
||||
@@ -9,11 +9,15 @@ const getBreweryPostById = async (id: string) => {
|
||||
await prisma.breweryPost.findFirst({
|
||||
select: {
|
||||
id: true,
|
||||
coordinates: true,
|
||||
address: true,
|
||||
city: true,
|
||||
stateOrProvince: true,
|
||||
country: true,
|
||||
location: {
|
||||
select: {
|
||||
city: true,
|
||||
address: true,
|
||||
coordinates: true,
|
||||
country: true,
|
||||
stateOrProvince: true,
|
||||
},
|
||||
},
|
||||
description: true,
|
||||
name: true,
|
||||
breweryImages: { select: { path: true, caption: true, id: true, alt: true } },
|
||||
|
||||
@@ -4,11 +4,13 @@ const BreweryPostQueryResult = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
address: z.string(),
|
||||
city: z.string(),
|
||||
stateOrProvince: z.string().or(z.null()),
|
||||
coordinates: z.array(z.number()),
|
||||
country: z.string().or(z.null()),
|
||||
location: z.object({
|
||||
city: z.string(),
|
||||
address: z.string(),
|
||||
coordinates: z.array(z.number()),
|
||||
country: z.string().nullable(),
|
||||
stateOrProvince: z.string().nullable(),
|
||||
}),
|
||||
postedBy: z.object({ id: z.string(), username: z.string() }),
|
||||
breweryImages: z.array(
|
||||
z.object({ path: z.string(), caption: z.string(), id: z.string(), alt: z.string() }),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import DBClient from '../../prisma/DBClient';
|
||||
|
||||
const findUserByEmail = async (email: string) =>
|
||||
DBClient.instance.user.findFirst({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import DBClient from '../../prisma/DBClient';
|
||||
|
||||
const findUserByUsername = async (username: string) =>
|
||||
DBClient.instance.user.findFirst({
|
||||
|
||||
Reference in New Issue
Block a user