diff --git a/.vscode/launch.json b/.vscode/launch.json index 6226a33..c87c06f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,7 @@ { "version": "0.2.0", "configurations": [ + { "name": "Next.js", "type": "node-terminal", diff --git a/apps/web/src/app/_components/combo-box.tsx b/apps/web/src/app/_components/combo-box.tsx index 9b9e573..e357a03 100644 --- a/apps/web/src/app/_components/combo-box.tsx +++ b/apps/web/src/app/_components/combo-box.tsx @@ -11,6 +11,7 @@ import { CommandItem, CommandList, } from "@cooper/ui/command"; +import { FormLabel } from "@cooper/ui/form"; import { Popover, PopoverContent, PopoverTrigger } from "@cooper/ui/popover"; export interface ComboBoxOption { @@ -26,6 +27,8 @@ interface ComboBoxProps { currLabel: string; onSelect: (option: string) => void; triggerClassName?: string; + onChange?: (value: string) => void; + variant?: "default" | "form"; } /** @@ -41,9 +44,16 @@ export default function ComboBox({ currLabel, onSelect, triggerClassName, + onChange, + variant, }: ComboBoxProps) { const [isOpen, setIsOpen] = useState(false); + const styleVariant = + variant === "form" + ? "flex h-16 w-full rounded-md border-[3px] border-cooper-blue-600 bg-white px-3 py-2 text-xl font-normal ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" + : ""; + return ( - {defaultLabel} + + {defaultLabel} + - + + onChange && onChange((e.target as HTMLInputElement).value) + } + /> {searchEmpty} diff --git a/apps/web/src/app/_components/form/sections/review-section.tsx b/apps/web/src/app/_components/form/sections/review-section.tsx index 01eaa6e..60484e5 100644 --- a/apps/web/src/app/_components/form/sections/review-section.tsx +++ b/apps/web/src/app/_components/form/sections/review-section.tsx @@ -1,5 +1,9 @@ +"use client"; + +import { useEffect, useState } from "react"; import { useFormContext } from "react-hook-form"; +import type { LocationType } from "@cooper/db/schema"; import { FormControl, FormField, @@ -10,7 +14,10 @@ import { import { Input } from "@cooper/ui/input"; import { Textarea } from "@cooper/ui/textarea"; +import type { ComboBoxOption } from "~/app/_components/combo-box"; +import ComboBox from "~/app/_components/combo-box"; import { FormSection } from "~/app/_components/form/form-section"; +import { api } from "~/trpc/react"; /** * ReviewSection component renders form fields for writing a co-op review. @@ -18,6 +25,31 @@ import { FormSection } from "~/app/_components/form/form-section"; export function ReviewSection({ textColor }: { textColor: string }) { const form = useFormContext(); + const [locations, setLocations] = useState[]>([]); + const [locationLabel, setLocationLabel] = useState(""); + const [searchTerm, setSearchTerm] = useState(""); + + const prefix = + searchTerm.length === 2 || searchTerm.length === 3 + ? searchTerm.toLowerCase() + : ""; + + const locationsToUpdate = api.location.getByPrefix.useQuery( + { prefix }, + { enabled: !!prefix }, + ); + + useEffect(() => { + if (locationsToUpdate.isSuccess && locationsToUpdate.data) { + setLocations( + locationsToUpdate.data.map((location: LocationType) => ({ + value: location.id, + label: location.city, + })), + ); + } + }, [locationsToUpdate.isSuccess, locationsToUpdate.data]); + return ( ( Location - - - - + { + setSearchTerm(value); + field.onChange(value); + }} + onSelect={(currentValue) => { + setLocationLabel( + currentValue === locationLabel ? "" : currentValue, + ); + field.onChange(currentValue); + }} + /> )} - /> + > { + return ctx.db.query.Location.findMany({ + orderBy: asc(Location.city), + }); + }), + + getByPrefix: publicProcedure + .input(z.object({ prefix: z.string() })) + .query(({ ctx, input }) => { + return ctx.db.query.Location.findMany({ + where: (loc, { ilike }) => ilike(loc.city, `${input.prefix}%`), + orderBy: asc(Location.city), + }); + }), + + create: protectedProcedure + .input(CreateLocationSchema) + .mutation(({ ctx, input }) => { + return ctx.db.insert(Location).values(input); + }), +} satisfies TRPCRouterRecord; diff --git a/packages/db/drizzle/0004_low_steve_rogers.sql b/packages/db/drizzle/0004_low_steve_rogers.sql new file mode 100644 index 0000000..3756426 --- /dev/null +++ b/packages/db/drizzle/0004_low_steve_rogers.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS "location" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "city" varchar NOT NULL, + "state" varchar, + "country" varchar NOT NULL +); +--> statement-breakpoint +-- ALTER TABLE "company_request" RENAME COLUMN "location" TO "locationId" IF EXISTS "location";--> statement-breakpoint +-- ALTER TABLE "review" RENAME COLUMN "location" TO "locationId" IF EXISTS "location";--> statement-breakpoint +ALTER TABLE "company_request" ALTER COLUMN "locationId" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "company" DROP COLUMN IF EXISTS "location"; \ No newline at end of file diff --git a/packages/db/drizzle/0006_fantastic_inertia.sql b/packages/db/drizzle/0006_fantastic_inertia.sql new file mode 100644 index 0000000..544957d --- /dev/null +++ b/packages/db/drizzle/0006_fantastic_inertia.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS "location" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "city" varchar NOT NULL, + "state" varchar, + "country" varchar NOT NULL +); +--> statement-breakpoint +ALTER TABLE "company_request" ADD COLUMN "locationId" varchar;--> statement-breakpoint +ALTER TABLE "review" ADD COLUMN "locationId" varchar;--> statement-breakpoint +ALTER TABLE "company_request" DROP COLUMN IF EXISTS "location";--> statement-breakpoint +ALTER TABLE "review" DROP COLUMN IF EXISTS "location"; \ No newline at end of file diff --git a/packages/db/drizzle/meta/0006_snapshot.json b/packages/db/drizzle/meta/0006_snapshot.json new file mode 100644 index 0000000..4d1ed79 --- /dev/null +++ b/packages/db/drizzle/meta/0006_snapshot.json @@ -0,0 +1,781 @@ +{ + "id": "96425608-00cd-4894-80d2-1b1e12676eda", + "prevId": "8c23acb3-2c81-4141-a602-68b595688adf", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": [ + "provider", + "providerAccountId" + ] + } + }, + "uniqueConstraints": {} + }, + "public.company": { + "name": "company", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "industry": { + "name": "industry", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "averageHourlyPay": { + "name": "averageHourlyPay", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageOverallRating": { + "name": "averageOverallRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageCultureRating": { + "name": "averageCultureRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageSupervisorRating": { + "name": "averageSupervisorRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageInterviewRating": { + "name": "averageInterviewRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageInterviewDifficulty": { + "name": "averageInterviewDifficulty", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "totalReviews": { + "name": "totalReviews", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.company_request": { + "name": "company_request", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "industry": { + "name": "industry", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "locationId": { + "name": "locationId", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status": { + "name": "status", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.location": { + "name": "location", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "city": { + "name": "city", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.profile": { + "name": "profile", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "firstName": { + "name": "firstName", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "lastName": { + "name": "lastName", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "major": { + "name": "major", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "minor": { + "name": "minor", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "graduationYear": { + "name": "graduationYear", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "graduationMonth": { + "name": "graduationMonth", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "profile_userId_unique": { + "name": "profile_userId_unique", + "nullsNotDistinct": false, + "columns": [ + "userId" + ] + } + } + }, + "public.review": { + "name": "review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workTerm": { + "name": "workTerm", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "workYear": { + "name": "workYear", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "overallRating": { + "name": "overallRating", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cultureRating": { + "name": "cultureRating", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "supervisorRating": { + "name": "supervisorRating", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "interviewRating": { + "name": "interviewRating", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "interviewDifficulty": { + "name": "interviewDifficulty", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "interviewReview": { + "name": "interviewReview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reviewHeadline": { + "name": "reviewHeadline", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "textReview": { + "name": "textReview", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "locationId": { + "name": "locationId", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hourlyPay": { + "name": "hourlyPay", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "workEnvironment": { + "name": "workEnvironment", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "drugTest": { + "name": "drugTest", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "overtimeNormal": { + "name": "overtimeNormal", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "pto": { + "name": "pto", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "federalHolidays": { + "name": "federalHolidays", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "freeLunch": { + "name": "freeLunch", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "freeTransport": { + "name": "freeTransport", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "freeMerch": { + "name": "freeMerch", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "otherBenefits": { + "name": "otherBenefits", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "roleId": { + "name": "roleId", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "profileId": { + "name": "profileId", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "companyId": { + "name": "companyId", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.role_request": { + "name": "role_request", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyId": { + "name": "companyId", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status": { + "name": "status", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.role": { + "name": "role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "companyId": { + "name": "companyId", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "averageHourlyPay": { + "name": "averageHourlyPay", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageOverallRating": { + "name": "averageOverallRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageCultureRating": { + "name": "averageCultureRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageSupervisorRating": { + "name": "averageSupervisorRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageInterviewRating": { + "name": "averageInterviewRating", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "averageInterviewDifficulty": { + "name": "averageInterviewDifficulty", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "totalReviews": { + "name": "totalReviews", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index 74cb141..1c94202 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -43,6 +43,13 @@ "when": 1740174136288, "tag": "0005_loving_cable", "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1740510786190, + "tag": "0006_fantastic_inertia", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index 74581a8..3589711 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -8,6 +8,11 @@ import type { ReviewType } from "./schema/reviews"; import type { RoleType } from "./schema/roles"; import { Account } from "./schema/accounts"; import { Company, CreateCompanySchema } from "./schema/companies"; +import { + CreateLocationSchema, + Location, + LocationType, +} from "./schema/locations"; import { Industry, WorkEnvironment, WorkTerm } from "./schema/misc"; import { CreateProfileSchema, Profile } from "./schema/profiles"; import { CreateReviewSchema, Review } from "./schema/reviews"; @@ -24,10 +29,12 @@ export { Industry, Role, User, + Location, CreateCompanySchema, CreateProfileSchema, CreateReviewSchema, CreateRoleSchema, + CreateLocationSchema, WorkTerm, WorkEnvironment, }; @@ -39,4 +46,5 @@ export type { IndustryType, WorkEnvironmentType, WorkTermType, + LocationType, }; diff --git a/packages/db/src/schema/companies.ts b/packages/db/src/schema/companies.ts index aba1467..63c1859 100644 --- a/packages/db/src/schema/companies.ts +++ b/packages/db/src/schema/companies.ts @@ -11,6 +11,7 @@ import { import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; +import { Location } from "./locations"; import { Industry } from "./misc"; import { Review } from "./reviews"; import { Role } from "./roles"; @@ -40,6 +41,7 @@ export type CompanyType = typeof Company.$inferSelect; export const CompanyRelations = relations(Company, ({ many }) => ({ roles: many(Role), reviews: many(Review), + locations: many(Location), })); export const CreateCompanySchema = createInsertSchema(Company, { diff --git a/packages/db/src/schema/companyRequest.ts b/packages/db/src/schema/companyRequest.ts index 68fa032..a8bf46d 100644 --- a/packages/db/src/schema/companyRequest.ts +++ b/packages/db/src/schema/companyRequest.ts @@ -11,7 +11,7 @@ export const CompanyRequest = pgTable("company_request", { companyName: varchar("name").notNull(), companyDescription: text("description"), industry: varchar("industry").notNull(), - location: varchar("location").notNull(), + locationId: varchar("locationId"), roleTitle: varchar("title").notNull(), roleDescription: text("description"), createdAt: timestamp("createdAt").defaultNow().notNull(), @@ -36,7 +36,7 @@ export const CreateCompanyRequestSchema = createInsertSchema(CompanyRequest, { companyName: z.string(), companyDescription: z.string().optional(), industry: z.nativeEnum(Industry), - location: z.string(), + locationId: z.string(), roleTitle: z.string(), roleDescription: z.string(), status: z.nativeEnum(RequestStatus), diff --git a/packages/db/src/schema/locations.ts b/packages/db/src/schema/locations.ts new file mode 100644 index 0000000..9d936d1 --- /dev/null +++ b/packages/db/src/schema/locations.ts @@ -0,0 +1,27 @@ +import { relations } from "drizzle-orm"; +import { pgTable, uuid, varchar } from "drizzle-orm/pg-core"; +import { createInsertSchema } from "drizzle-zod"; +import { z } from "zod"; + +import { Company } from "./companies"; + +export const Location = pgTable("location", { + id: uuid("id").notNull().primaryKey().defaultRandom(), + city: varchar("city").notNull(), + state: varchar("state"), + country: varchar("country").notNull(), +}); + +export type LocationType = typeof Location.$inferSelect; + +export const LocationReviews = relations(Location, ({ many }) => ({ + companies: many(Company), +})); + +export const CreateLocationSchema = createInsertSchema(Location, { + city: z.string(), + state: z.string(), + country: z.string(), +}).omit({ + id: true, +}); diff --git a/packages/db/src/schema/reviews.ts b/packages/db/src/schema/reviews.ts index a9dcaea..dcefdac 100644 --- a/packages/db/src/schema/reviews.ts +++ b/packages/db/src/schema/reviews.ts @@ -13,6 +13,7 @@ import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; import { Company } from "./companies"; +import { Location } from "./locations"; import { WorkEnvironment, WorkTerm } from "./misc"; import { Profile } from "./profiles"; import { Role } from "./roles"; @@ -29,7 +30,7 @@ export const Review = pgTable("review", { interviewReview: text("interviewReview"), reviewHeadline: varchar("reviewHeadline").notNull(), textReview: text("textReview").notNull(), - location: varchar("location"), + locationId: varchar("locationId"), hourlyPay: decimal("hourlyPay"), workEnvironment: varchar("workEnvironment").notNull(), drugTest: boolean("drugTest").notNull(), @@ -65,6 +66,10 @@ export const ReviewRelations = relations(Review, ({ one }) => ({ fields: [Review.companyId], references: [Company.id], }), + location: one(Location, { + fields: [Review.locationId], + references: [Location.id], + }), })); export const CreateReviewSchema = createInsertSchema(Review, { @@ -78,7 +83,7 @@ export const CreateReviewSchema = createInsertSchema(Review, { interviewReview: z.string().optional(), reviewHeadline: z.string(), textReview: z.string(), - location: z.string().optional(), + locationId: z.string().optional(), hourlyPay: z.string().optional(), workEnvironment: z.nativeEnum(WorkEnvironment), drugTest: z.boolean(),