Skip to content

Commit

Permalink
feat: use dynamic form with server actions
Browse files Browse the repository at this point in the history
  • Loading branch information
patrykomiotek committed Feb 1, 2024
1 parent 5bbef18 commit 29d8210
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 52 deletions.
67 changes: 22 additions & 45 deletions apps/jobboard/src/app/(public)/job-offers/create/actions.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,34 @@
'use server';

import { z } from 'zod';
import { JobOffer } from '@prisma/client';
import db from '@jobboard/prisma-client';
import { redirect } from 'next/navigation';
import { CreateOfferDto, offerSchema } from './types';

const offerSchema = z.object({
title: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
description: z
.string()
.min(3, 'Must be at least 3 characters')
.max(500, 'Must be at least 500 characters'),
position: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
salary: z
.number()
.min(1, 'Must be at least 1')
.max(1000000, 'Must be at least 1000000 characters'),
company: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
city: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
});

export const createJobOfferAction = async (data: FormData) => {
// export const createJobOfferAction = async (data: FormData) => {
export const createJobOfferAction = async (data: CreateOfferDto) => {
// console.log({ data: data.get('title') });
// TODO: refactor this
const title = data.get('title') as string;
const description = data.get('description') as string;
const position = data.get('position') as string;
const salary = parseFloat(data.get('salary') as string);
const company = data.get('company') as string;
const city = data.get('city') as string;
// const title = data.get('title') as string;
// const description = data.get('description') as string;
// const position = data.get('position') as string;
// const salary = parseFloat(data.get('salary') as string);
// const company = data.get('company') as string;
// const city = data.get('city') as string;

// const offer = offerSchema.parse({
// title,
// description,
// position,
// salary,
// company,
// city,
// });

const offer = offerSchema.parse({
title,
description,
position,
salary,
company,
city,
});
const offer = offerSchema.parse(data);

await db.jobOffer.create({ data: offer });

redirect('/job-offers');
// redirect('/job-offers');

return { status: 'ok' };
};
74 changes: 67 additions & 7 deletions apps/jobboard/src/app/(public)/job-offers/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,80 @@
'use client';

import { Button, Header, Input } from '@jobboard/common-ui';

import { createJobOfferAction } from './actions';
import { CreateOfferDto, offerSchema } from './types';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { use, useTransition } from 'react';
import { useRouter } from 'next/navigation';

export default function CreateOfferPage() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<CreateOfferDto>({
resolver: zodResolver(offerSchema),
});
const router = useRouter();
const [_transition, setTransition] = useTransition();

const handleOfferFormSubmit = async (data: CreateOfferDto) => {
// console.log({ data });

const result = await createJobOfferAction(data);
if (result.status === 'ok') {
setTransition(() => router.push('/job-offers'));
setTransition(() => router.refresh());
}
};

return (
<div>
<Header>Create Offer</Header>
<form action={createJobOfferAction}>
<Input label="Title" name="title" />
<Input label="Description" name="description" />
<Input label="Position" name="position" />
<Input label="Salary" name="salary" type="number" />
<Input label="Company" name="company" />
<Input label="City" name="city" />
<form onSubmit={handleSubmit(handleOfferFormSubmit)}>
<Input label="Title" {...register('title')} error={errors.title} />
<Input
label="Description"
{...register('description')}
error={errors.description}
/>
<Input
label="Position"
{...register('position')}
error={errors.position}
/>
<Input
label="Salary"
{...register('salary', { valueAsNumber: true })}
error={errors.salary}
type="number"
/>
<Input
label="Company"
{...register('company')}
error={errors.company}
/>
<Input label="City" {...register('city')} error={errors.city} />
<Button label="Submit" type="submit" />
</form>
</div>
);
}

// server-only version
// return (
// <div>
// <Header>Create Offer</Header>
// <form action={createJobOfferAction}>
// <Input label="Title" name="title" />
// <Input label="Description" name="description" />
// <Input label="Position" name="position" />
// <Input label="Salary" name="salary" type="number" />
// <Input label="Company" name="company" />
// <Input label="City" name="city" />
// <Button label="Submit" type="submit" />
// </form>
// </div>
// );
30 changes: 30 additions & 0 deletions apps/jobboard/src/app/(public)/job-offers/create/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { z } from 'zod';

export const offerSchema = z.object({
title: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
description: z
.string()
.min(3, 'Must be at least 3 characters')
.max(500, 'Must be at least 500 characters'),
position: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
salary: z
.number()
.min(1, 'Must be at least 1')
.max(1000000, 'Must be at least 1000000 characters'),
company: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
city: z
.string()
.min(3, 'Must be at least 3 characters')
.max(50, 'Must be at least 50 characters'),
});

export type CreateOfferDto = z.infer<typeof offerSchema>;

0 comments on commit 29d8210

Please sign in to comment.