Chapter 3, Improving the Contact Form.

This commit is contained in:
Colin Diem
2022-04-21 17:52:22 -04:00
parent bb1638b38e
commit bc1cf7b5c3
7 changed files with 188 additions and 10 deletions

View File

@@ -0,0 +1,8 @@
-- CreateTable
CREATE TABLE "Contact" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"message" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

View File

@@ -14,3 +14,11 @@ model Post {
body String
createdAt DateTime @default(now())
}
model Contact {
id Int @id @default(autoincrement())
name String
email String
message String
createdAt DateTime @default(now())
}

View File

@@ -0,0 +1,30 @@
export const schema = gql`
type Contact {
id: Int!
name: String!
email: String!
message: String!
createdAt: DateTime!
}
type Query {
contacts: [Contact!]! @requireAuth
contact(id: Int!): Contact @requireAuth
}
input CreateContactInput {
name: String!
email: String!
message: String!
}
input UpdateContactInput {
name: String
email: String
message: String
}
type Mutation {
createContact(input: CreateContactInput!): Contact! @skipAuth
}
`

View File

@@ -0,0 +1,17 @@
import { db } from 'src/lib/db'
export const contacts = () => {
return db.contact.findMany()
}
export const contact = ({ id }) => {
return db.contact.findUnique({
where: { id },
})
}
export const createContact = ({ input }) => {
return db.contact.create({
data: input,
})
}

View File

@@ -0,0 +1,6 @@
export const standard = defineScenario({
contact: {
one: { data: { name: 'String', email: 'String', message: 'String' } },
two: { data: { name: 'String', email: 'String', message: 'String' } },
},
})

View File

@@ -0,0 +1,54 @@
import {
contacts,
contact,
createContact,
updateContact,
deleteContact,
} from './contacts'
// Generated boilerplate tests do not account for all circumstances
// and can fail without adjustments, e.g. Float and DateTime types.
// Please refer to the RedwoodJS Testing Docs:
// https://redwoodjs.com/docs/testing#testing-services
// https://redwoodjs.com/docs/testing#jest-expect-type-considerations
describe('contacts', () => {
scenario('returns all contacts', async (scenario) => {
const result = await contacts()
expect(result.length).toEqual(Object.keys(scenario.contact).length)
})
scenario('returns a single contact', async (scenario) => {
const result = await contact({ id: scenario.contact.one.id })
expect(result).toEqual(scenario.contact.one)
})
scenario('creates a contact', async () => {
const result = await createContact({
input: { name: 'String', email: 'String', message: 'String' },
})
expect(result.name).toEqual('String')
expect(result.email).toEqual('String')
expect(result.message).toEqual('String')
})
scenario('updates a contact', async (scenario) => {
const original = await contact({ id: scenario.contact.one.id })
const result = await updateContact({
id: original.id,
input: { name: 'String2' },
})
expect(result.name).toEqual('String2')
})
scenario('deletes a contact', async (scenario) => {
const original = await deleteContact({ id: scenario.contact.one.id })
const result = await contact({ id: original.id })
expect(result).toEqual(null)
})
})

View File

@@ -1,21 +1,76 @@
import { MetaTags } from '@redwoodjs/web'
import { Form, TextField, Submit, TextAreaField } from '@redwoodjs/forms'
import { MetaTags, useMutation } from '@redwoodjs/web'
import { toast, Toaster } from '@redwoodjs/web/toast'
import {
FieldError,
Form,
Label,
TextField,
Submit,
TextAreaField,
} from '@redwoodjs/forms'
const CREATE_CONTACT = gql`
mutation CreateContactMutation($input: CreateContactInput!) {
createContact(input: $input) {
id
}
}
`
const ContactPage = () => {
const [create, { loading, error }] = useMutation(CREATE_CONTACT, {
onCompleted: () => {
toast.success('Thank you for your submission!')
},
})
const onSubmit = (data) => {
create({ variables: { input: data } })
console.log(data)
}
return (
<>
<MetaTags title="Contact" description="Contact page" />
<Form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<TextField name="name" validation={{required: true}} />
<label htmlFor="email">Email</label>
<TextField name="email" validation={{required: true}} />
<label htmlFor="message">Message</label>
<TextAreaField name="message" validation={{required: true}} />
<Submit>Save</Submit>
<Toaster />
<Form onSubmit={onSubmit} config={{ mode: 'onBlur' }}>
<Label name="name" errorClassName="error">
Name
</Label>
<TextField
name="name"
validation={{ required: true }}
errorClassName="error"
/>
<FieldError name="name" className="error" />
<Label name="email" errorClassName="error">
Email
</Label>
<TextField
name="email"
validation={{
required: true,
pattern: {
value: /^[^@]+@[^.]+\..+$/,
message: 'Please enter a valid email address',
},
}}
errorClassName="error"
/>
<FieldError name="email" className="error" />
<Label name="message" errorClassName="error">
Message
</Label>
<TextAreaField
name="message"
validation={{ required: true }}
errorClassName="error"
/>
<FieldError name="message" className="error" />
<Submit disabled={loading}>Save</Submit>
</Form>
</>
)