rsnext/examples/with-mongodb-mongoose/components/Form.tsx
Elijah Ohiwerei 7e76c353b3
feat(examples): Migrate 'with-mongoose' example to TypeScript with total type safety (#53603)
### What?
I kindly request the maintainers to review this Pull Request, which aims to migrate the `with-mongoose` example to TypeScript with total type safety.

### Why?
By doing so, we enhance the overall quality and maintainability of the example, aligning it with modern best practices for type-safe codebases.

### How?
I have thoroughly tested the changes to ensure they do not introduce regressions and maintain compatibility with the existing codebase.



Co-authored-by: Balázs Orbán <18369201+balazsorban44@users.noreply.github.com>
2023-09-01 14:34:13 +00:00

232 lines
5.4 KiB
TypeScript

import { useState } from 'react'
import { useRouter } from 'next/router'
import { mutate } from 'swr'
interface FormData {
name: string
owner_name: string
species: string
age: number
poddy_trained: boolean
diet: string[]
image_url: string
likes: string[]
dislikes: string[]
}
interface Error {
name?: string
owner_name?: string
species?: string
image_url?: string
}
type Props = {
formId: string
petForm: FormData
forNewPet?: boolean
}
const Form = ({ formId, petForm, forNewPet = true }: Props) => {
const router = useRouter()
const contentType = 'application/json'
const [errors, setErrors] = useState({})
const [message, setMessage] = useState('')
const [form, setForm] = useState({
name: petForm.name,
owner_name: petForm.owner_name,
species: petForm.species,
age: petForm.age,
poddy_trained: petForm.poddy_trained,
diet: petForm.diet,
image_url: petForm.image_url,
likes: petForm.likes,
dislikes: petForm.dislikes,
})
/* The PUT method edits an existing entry in the mongodb database. */
const putData = async (form: FormData) => {
const { id } = router.query
try {
const res = await fetch(`/api/pets/${id}`, {
method: 'PUT',
headers: {
Accept: contentType,
'Content-Type': contentType,
},
body: JSON.stringify(form),
})
// Throw error with status code in case Fetch API req failed
if (!res.ok) {
throw new Error(res.status.toString())
}
const { data } = await res.json()
mutate(`/api/pets/${id}`, data, false) // Update the local data without a revalidation
router.push('/')
} catch (error) {
setMessage('Failed to update pet')
}
}
/* The POST method adds a new entry in the mongodb database. */
const postData = async (form: FormData) => {
try {
const res = await fetch('/api/pets', {
method: 'POST',
headers: {
Accept: contentType,
'Content-Type': contentType,
},
body: JSON.stringify(form),
})
// Throw error with status code in case Fetch API req failed
if (!res.ok) {
throw new Error(res.status.toString())
}
router.push('/')
} catch (error) {
setMessage('Failed to add pet')
}
}
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
const target = e.target
const value =
target.name === 'poddy_trained'
? (target as HTMLInputElement).checked
: target.value
const name = target.name
setForm({
...form,
[name]: value,
})
}
/* Makes sure pet info is filled for pet name, owner name, species, and image url*/
const formValidate = () => {
let err: Error = {}
if (!form.name) err.name = 'Name is required'
if (!form.owner_name) err.owner_name = 'Owner is required'
if (!form.species) err.species = 'Species is required'
if (!form.image_url) err.image_url = 'Image URL is required'
return err
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const errs = formValidate()
if (Object.keys(errs).length === 0) {
forNewPet ? postData(form) : putData(form)
} else {
setErrors({ errs })
}
}
return (
<>
<form id={formId} onSubmit={handleSubmit}>
<label htmlFor="name">Name</label>
<input
type="text"
maxLength={20}
name="name"
value={form.name}
onChange={handleChange}
required
/>
<label htmlFor="owner_name">Owner</label>
<input
type="text"
maxLength={20}
name="owner_name"
value={form.owner_name}
onChange={handleChange}
required
/>
<label htmlFor="species">Species</label>
<input
type="text"
maxLength={30}
name="species"
value={form.species}
onChange={handleChange}
required
/>
<label htmlFor="age">Age</label>
<input
type="number"
name="age"
value={form.age}
onChange={handleChange}
/>
<label htmlFor="poddy_trained">Potty Trained</label>
<input
type="checkbox"
name="poddy_trained"
checked={form.poddy_trained}
onChange={handleChange}
/>
<label htmlFor="diet">Diet</label>
<textarea
name="diet"
maxLength={60}
value={form.diet}
onChange={handleChange}
/>
<label htmlFor="image_url">Image URL</label>
<input
type="url"
name="image_url"
value={form.image_url}
onChange={handleChange}
required
/>
<label htmlFor="likes">Likes</label>
<textarea
name="likes"
maxLength={60}
value={form.likes}
onChange={handleChange}
/>
<label htmlFor="dislikes">Dislikes</label>
<textarea
name="dislikes"
maxLength={60}
value={form.dislikes}
onChange={handleChange}
/>
<button type="submit" className="btn">
Submit
</button>
</form>
<p>{message}</p>
<div>
{Object.keys(errors).map((err, index) => (
<li key={index}>{err}</li>
))}
</div>
</>
)
}
export default Form