Skip to content

Commit

Permalink
feat: add nextjs api & react hook form example
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexStack committed Sep 24, 2023
1 parent aebc9da commit 88eaa3f
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 11 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.1",
"@mui/icons-material": "^5.14.9",
"@mui/material": "^5.14.10",
"dayjs": "^1.11.10",
Expand Down
5 changes: 0 additions & 5 deletions src/app/api/hello/route.ts

This file was deleted.

20 changes: 20 additions & 0 deletions src/app/api/test/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NextResponse } from 'next/server';

export const GET = async (req: Request) => {
const { searchParams } = new URL(req.url);
const reqData = Object.fromEntries(searchParams);
return NextResponse.json({
message: 'Test getApiResponse GET success!',
method: 'GET',
reqData,
});
};

export const POST = async (req: Request) => {
const reqData = await req.json();
return NextResponse.json({
message: 'Test postApiResponse POST success!',
method: 'POST',
reqData,
});
};
11 changes: 8 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Container } from '@mui/material';
import GlobalStyles from '@mui/material/GlobalStyles';
import { Metadata } from 'next';
import * as React from 'react';

import { SITE_CONFIG } from '@/constant/config';
import { GLOBAL_STYLES, SITE_CONFIG } from '@/constant';

// !STARTERCONF Change these default meta
// !STARTERCONF Look at @/constant/config to change them
Expand Down Expand Up @@ -50,8 +52,11 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html>
<body>{children}</body>
<html lang='en'>
<GlobalStyles styles={GLOBAL_STYLES} />
<body>
<Container sx={{ pl: 0, pr: 0 }}>{children}</Container>
</body>
</html>
);
}
28 changes: 26 additions & 2 deletions src/component/Homepage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Box, Typography } from '@mui/material';
import Link from 'next/link';

import PageFooter from '@/component/shared/PageFooter';
import ReactHookForm from '@/component/shared/ReactHookForm';
import { SITE_CONFIG } from '@/constant';

export default function Homepage({ reactVersion = 'unknown' }) {
Expand All @@ -11,10 +12,19 @@ export default function Homepage({ reactVersion = 'unknown' }) {
<section>
<Box sx={{ textAlign: 'center' }}>
<PinDropIcon />
<Typography variant='h5' component='h1' gutterBottom>
<Typography
variant='h5'
component='h1'
gutterBottom
className='page-title'
>
{SITE_CONFIG.title}
</Typography>
<Typography variant='subtitle2' gutterBottom>
<Typography
variant='subtitle2'
gutterBottom
className='page-subtitle'
>
{SITE_CONFIG.description}
</Typography>

Expand Down Expand Up @@ -42,6 +52,20 @@ export default function Homepage({ reactVersion = 'unknown' }) {
Click here to deploy a demo site to your Vercel in 1 minute
</Link>
</Box>
<Box sx={{ m: 5 }}>
<Link
href='/api/test?from=github&nextjs=yes&mui=yes&tailwind=no'
target='_blank'
>
Test NextJs API method GET with parameters
</Link>
</Box>

<Box sx={{ m: 5 }}>
<h4>Test NextJs API method POST with parameters</h4>
<ReactHookForm />
</Box>

<Box sx={{ m: 5 }}>
<Link href='/test-page-not-exists'>Test 404 page not found</Link>
</Box>
Expand Down
7 changes: 6 additions & 1 deletion src/component/shared/PageFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { Box } from '@mui/material';
import * as React from 'react';

import ServerDateTime from '@/component/shared/ServerDateTime';

const PageFooter = () => {
return (
<section>
<Box component='footer' sx={{ m: 5, textAlign: 'center' }}>
PageFooter.tsx © {new Date().getFullYear()} Boilerplate live example:
PageFooter.tsx © Boilerplate live example:
<a href='https://hihb.com' target='_blank'>
HiHB
</a>
</Box>
<Box sx={{ m: 2, textAlign: 'center', fontSize: '0.8rem' }}>
<ServerDateTime cityTimezone='America/New_York' />
</Box>
</section>
);
};
Expand Down
90 changes: 90 additions & 0 deletions src/component/shared/ReactHookForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use client';

import { zodResolver } from '@hookform/resolvers/zod';
import { Box, Button, FormHelperText } from '@mui/material';
import React from 'react';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import { z } from 'zod';

import { consoleLog } from '@/util/shared/console-log';
import { getApiResponse } from '@/util/shared/get-api-response';

const zodSchema = z.object({
name: z.string().min(5).nonempty({ message: 'Name is required' }),
email: z.string().min(10).email({ message: 'Invalid email address' }),
});

type FormValues = z.infer<typeof zodSchema>;

const ReactHookForm: React.FC = () => {
const apiEndpoint = '/api/test';
const [apiResult, setApiResult] = React.useState<FormValues>();

const {
handleSubmit,
control,
formState: { errors },
} = useForm<FormValues>({
resolver: zodResolver(zodSchema),
});

const onSubmit: SubmitHandler<FormValues> = async (data) => {
try {
const result = await getApiResponse<{
reqData: FormValues;
}>({
apiEndpoint,
method: 'POST',
requestData: JSON.stringify(data),
});
setApiResult(result?.reqData);
consoleLog('getApiResponse result', result, errors);
} catch (error) {
consoleLog('handleSubmit ERROR', error);
}
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
<Box sx={{ m: 2 }}>
<label>Name:</label>
<Controller
name='name'
control={control}
defaultValue=''
render={({ field }) => <input {...field} />}
/>
{errors.name && (
<FormHelperText sx={{ textAlign: 'center' }}>
{errors.name.message}
</FormHelperText>
)}
</Box>

<Box sx={{ m: 2 }}>
<label>Email:</label>
<Controller
name='email'
control={control}
defaultValue=''
render={({ field }) => <input {...field} />}
/>
{errors.email && (
<FormHelperText sx={{ textAlign: 'center' }}>
{errors.email.message}
</FormHelperText>
)}
</Box>
{apiResult && (
<Box sx={{ m: 2, color: 'green' }}>
API result from {apiEndpoint}: {apiResult.name} & {apiResult.email}
</Box>
)}
<Button variant='contained' type='submit'>
Test react hook form with zod
</Button>
</form>
);
};

export default ReactHookForm;
26 changes: 26 additions & 0 deletions src/component/shared/ServerDateTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';

dayjs.extend(utc);
dayjs.extend(timezone);

const ServerDateTime = ({
cityTimezone,
timeFormat = 'dddd, MMMM D, YYYY h:mm:ss A',
color,
date,
}: {
cityTimezone: string;
timeFormat?: string;
color?: string;
date?: string;
}) => {
return (
<span className='date-time' style={{ color }}>
{dayjs(date).tz(cityTimezone).format(timeFormat)}
</span>
);
};

export default ServerDateTime;
17 changes: 17 additions & 0 deletions src/constant/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { blue, grey } from '@mui/material/colors';

export const SITE_CONFIG = {
title: 'NextJs 13.x + MUI 5.x + TypeScript Starter',
description:
Expand All @@ -10,3 +12,18 @@ export const HIDE_DEBUG_ARY = [
// 'getApiResponse',
'getMongoDbApiData',
];

export const GLOBAL_STYLES = {
body: { margin: 4 },
'.page-title': { color: 'darkblue' },
'.page-subtitle': { color: grey[600] },
a: {
textDecoration: 'underline',
textDecorationColor: blue[800],
color: blue['700'],
fontSize: '1rem',
fontWeight: 400,
lineHeight: '1.8',
letterSpacing: '0.00938em',
},
};
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,11 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.4.tgz#19654d1026cc410975d46445180e70a5089b3e7d"
integrity sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==

"@hookform/resolvers@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.3.1.tgz#b7cbfe767434f52cba6b99b0a9a0b73eb8895188"
integrity sha512-K7KCKRKjymxIB90nHDQ7b9nli474ru99ZbqxiqDAWYsYhOsU3/4qLxW91y+1n04ic13ajjZ66L3aXbNef8PELQ==

"@humanwhocodes/config-array@^0.11.11":
version "0.11.11"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844"
Expand Down

0 comments on commit 88eaa3f

Please sign in to comment.