[resumes][feat] submit resume mutation (#310)

pull/314/head
Keane Chan 2 years ago committed by GitHub
parent 7c40353f6b
commit 9f61ecf9c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,8 +6,11 @@ CREATE TABLE "ResumesResume" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
"title" TEXT NOT NULL, "title" TEXT NOT NULL,
"additionalInfo" TEXT NOT NULL, "role" TEXT NOT NULL,
"experience" TEXT NOT NULL,
"location" TEXT NOT NULL,
"url" TEXT NOT NULL, "url" TEXT NOT NULL,
"additionalInfo" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL, "updatedAt" TIMESTAMP(3) NOT NULL,

@ -94,9 +94,12 @@ model ResumesResume {
id String @id @default(cuid()) id String @id @default(cuid())
userId String userId String
title String @db.Text title String @db.Text
additionalInfo String @db.Text // TODO: Update role, experience, location to use Enums
// TODO: Add role, experience, location from Enums role String @db.Text
experience String @db.Text
location String @db.Text
url String url String
additionalInfo String? @db.Text
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)

@ -1,10 +1,13 @@
import Head from 'next/head'; import Head from 'next/head';
import { useRouter } from 'next/router';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { PaperClipIcon } from '@heroicons/react/24/outline'; import { PaperClipIcon } from '@heroicons/react/24/outline';
import { Button, Select, TextInput } from '@tih/ui'; import { Button, Select, TextInput } from '@tih/ui';
import { trpc } from '~/utils/trpc';
const TITLE_PLACEHOLDER = const TITLE_PLACEHOLDER =
'e.g. Applying for Company XYZ, please help me to review!'; 'e.g. Applying for Company XYZ, please help me to review!';
const ADDITIONAL_INFO_PLACEHOLDER = `e.g. Im applying for company XYZ. I have been resume-rejected by N companies that I have applied for. Please help me to review so company XYZ gives me an interview!`; const ADDITIONAL_INFO_PLACEHOLDER = `e.g. Im applying for company XYZ. I have been resume-rejected by N companies that I have applied for. Please help me to review so company XYZ gives me an interview!`;
@ -13,7 +16,7 @@ const FILE_UPLOAD_ERROR = 'Please upload a PDF file that is less than 10MB.';
const MAX_FILE_SIZE_LIMIT = 10485760; const MAX_FILE_SIZE_LIMIT = 10485760;
type IFormInput = { type IFormInput = {
additionalInformation?: string; additionalInfo?: string;
experience: string; experience: string;
file: File; file: File;
location: string; location: string;
@ -68,6 +71,9 @@ export default function SubmitResumeForm() {
}, },
]; ];
const resumeCreateMutation = trpc.useMutation('resumes.resume.user.create');
const router = useRouter();
const [resumeFile, setResumeFile] = useState<File | null>(); const [resumeFile, setResumeFile] = useState<File | null>();
const [invalidFileUploadError, setInvalidFileUploadError] = useState< const [invalidFileUploadError, setInvalidFileUploadError] = useState<
string | null string | null
@ -81,10 +87,11 @@ export default function SubmitResumeForm() {
formState: { errors }, formState: { errors },
} = useForm<IFormInput>(); } = useForm<IFormInput>();
// TODO: Add Create resume mutation const onSubmit: SubmitHandler<IFormInput> = async (data) => {
const onSubmit: SubmitHandler<IFormInput> = (data) => { await resumeCreateMutation.mutate({
alert(JSON.stringify(data)); ...data,
onClickReset(); });
router.push('/resumes');
}; };
const onUploadFile = (event: React.ChangeEvent<HTMLInputElement>) => { const onUploadFile = (event: React.ChangeEvent<HTMLInputElement>) => {
@ -196,10 +203,10 @@ export default function SubmitResumeForm() {
<div className="mb-4"> <div className="mb-4">
{/* TODO: Use TextInputArea instead */} {/* TODO: Use TextInputArea instead */}
<TextInput <TextInput
{...register('additionalInformation')} {...register('additionalInfo')}
label="Additional Information" label="Additional Information"
placeholder={ADDITIONAL_INFO_PLACEHOLDER} placeholder={ADDITIONAL_INFO_PLACEHOLDER}
onChange={(val) => setValue('additionalInformation', val)} onChange={(val) => setValue('additionalInfo', val)}
/> />
</div> </div>
<div className="mt-4 flex justify-end gap-4"> <div className="mt-4 flex justify-end gap-4">

@ -2,6 +2,7 @@ import superjson from 'superjson';
import { createRouter } from './context'; import { createRouter } from './context';
import { protectedExampleRouter } from './protected-example-router'; import { protectedExampleRouter } from './protected-example-router';
import { resumesResumeUserRouter } from './resumes-resume-user-router';
import { todosRouter } from './todos'; import { todosRouter } from './todos';
import { todosUserRouter } from './todos-user-router'; import { todosUserRouter } from './todos-user-router';
@ -12,7 +13,8 @@ export const appRouter = createRouter()
// Example routers. Learn more about tRPC routers: https://trpc.io/docs/v9/router // Example routers. Learn more about tRPC routers: https://trpc.io/docs/v9/router
.merge('auth.', protectedExampleRouter) .merge('auth.', protectedExampleRouter)
.merge('todos.', todosRouter) .merge('todos.', todosRouter)
.merge('todos.user.', todosUserRouter); .merge('todos.user.', todosUserRouter)
.merge('resumes.resume.user.', resumesResumeUserRouter);
// Export type definition of API // Export type definition of API
export type AppRouter = typeof appRouter; export type AppRouter = typeof appRouter;

@ -0,0 +1,30 @@
import { z } from 'zod';
import { createProtectedRouter } from './context';
export const resumesResumeUserRouter = createProtectedRouter().mutation(
'create',
{
// TODO: Use enums for experience, location, role
input: z.object({
additionalInfo: z.string().optional(),
experience: z.string(),
location: z.string(),
role: z.string(),
title: z.string(),
}),
async resolve({ ctx, input }) {
const userId = ctx.session?.user.id;
// TODO: Store file in file storage and retrieve URL
return await ctx.prisma.resumesResume.create({
data: {
...input,
url: '',
userId,
},
});
},
},
);
Loading…
Cancel
Save