mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-12 22:13:05 +00:00
Migrate from unstable_cache to use cache. And start Petition Form
This commit is contained in:
parent
0e9dcceb83
commit
8f3bd3845b
15 changed files with 356 additions and 145 deletions
12
package.json
12
package.json
|
|
@ -35,11 +35,16 @@
|
|||
"test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@payloadcms/db-postgres": "3.76.1",
|
||||
"@payloadcms/live-preview-react": "^3.76.1",
|
||||
"@payloadcms/next": "3.76.1",
|
||||
"@payloadcms/richtext-lexical": "3.76.1",
|
||||
"@payloadcms/ui": "3.76.1",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "16.4.7",
|
||||
"graphql": "^16.8.1",
|
||||
|
|
@ -47,12 +52,15 @@
|
|||
"payload": "3.76.1",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-hook-form": "^7.71.1",
|
||||
"sharp": "0.34.2",
|
||||
"tailwind-merge": "^3.4.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tw-animate-css": "^1.4.0"
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/eslint-plugin-next": "^16.1.6",
|
||||
"@next/eslint-plugin-next": "@hookform/resolvers/zod",
|
||||
"@playwright/test": "1.58.2",
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@testing-library/react": "16.3.0",
|
||||
|
|
|
|||
160
pnpm-lock.yaml
160
pnpm-lock.yaml
|
|
@ -8,6 +8,9 @@ importers:
|
|||
|
||||
.:
|
||||
dependencies:
|
||||
'@hookform/resolvers':
|
||||
specifier: ^5.2.2
|
||||
version: 5.2.2(react-hook-form@7.71.1(react@19.2.4))
|
||||
'@payloadcms/db-postgres':
|
||||
specifier: 3.76.1
|
||||
version: 3.76.1(payload@3.76.1(graphql@16.12.0)(typescript@5.7.3))
|
||||
|
|
@ -23,6 +26,18 @@ importers:
|
|||
'@payloadcms/ui':
|
||||
specifier: 3.76.1
|
||||
version: 3.76.1(@types/react@19.2.14)(monaco-editor@0.55.1)(next@16.1.6(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.77.4))(payload@3.76.1(graphql@16.12.0)(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3)
|
||||
'@radix-ui/react-label':
|
||||
specifier: ^2.1.8
|
||||
version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-separator':
|
||||
specifier: ^1.1.8
|
||||
version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
|
|
@ -44,18 +59,27 @@ importers:
|
|||
react-dom:
|
||||
specifier: ^19.2.4
|
||||
version: 19.2.4(react@19.2.4)
|
||||
react-hook-form:
|
||||
specifier: ^7.71.1
|
||||
version: 7.71.1(react@19.2.4)
|
||||
sharp:
|
||||
specifier: 0.34.2
|
||||
version: 0.34.2
|
||||
tailwind-merge:
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1
|
||||
tailwindcss-animate:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(tailwindcss@4.1.18)
|
||||
tw-animate-css:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
zod:
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6
|
||||
devDependencies:
|
||||
'@next/eslint-plugin-next':
|
||||
specifier: ^16.1.6
|
||||
specifier: '@hookform/resolvers/zod'
|
||||
version: 16.1.6
|
||||
'@playwright/test':
|
||||
specifier: 1.58.2
|
||||
|
|
@ -880,6 +904,11 @@ packages:
|
|||
'@floating-ui/utils@0.2.10':
|
||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||
|
||||
'@hookform/resolvers@5.2.2':
|
||||
resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==}
|
||||
peerDependencies:
|
||||
react-hook-form: ^7.55.0
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
|
|
@ -1426,6 +1455,63 @@ packages:
|
|||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.2':
|
||||
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-label@2.1.8':
|
||||
resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-primitive@2.1.4':
|
||||
resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-separator@1.1.8':
|
||||
resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-slot@1.2.4':
|
||||
resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.11':
|
||||
resolution: {integrity: sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==}
|
||||
|
||||
|
|
@ -1560,6 +1646,9 @@ packages:
|
|||
'@standard-schema/spec@1.1.0':
|
||||
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||
|
||||
'@standard-schema/utils@0.3.0':
|
||||
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||
|
||||
|
|
@ -2170,6 +2259,9 @@ packages:
|
|||
resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||
|
||||
client-only@0.0.1:
|
||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||
|
||||
|
|
@ -3737,6 +3829,12 @@ packages:
|
|||
peerDependencies:
|
||||
react: '>=16.13.1'
|
||||
|
||||
react-hook-form@7.71.1:
|
||||
resolution: {integrity: sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||
|
||||
react-image-crop@10.1.8:
|
||||
resolution: {integrity: sha512-4rb8XtXNx7ZaOZarKKnckgz4xLMvds/YrU6mpJfGhGAsy2Mg4mIw1x+DCCGngVGq2soTBVVOxx2s/C6mTX9+pA==}
|
||||
peerDependencies:
|
||||
|
|
@ -4038,6 +4136,9 @@ packages:
|
|||
tabbable@6.4.0:
|
||||
resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
|
||||
|
||||
tailwind-merge@3.4.1:
|
||||
resolution: {integrity: sha512-2OA0rFqWOkITEAOFWSBSApYkDeH9t2B3XSJuI4YztKBzK3mX0737A2qtxDZ7xkw9Zfh0bWl+r34sF3HXV+Ig7Q==}
|
||||
|
||||
tailwindcss-animate@1.0.7:
|
||||
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
|
||||
peerDependencies:
|
||||
|
|
@ -5043,6 +5144,11 @@ snapshots:
|
|||
|
||||
'@floating-ui/utils@0.2.10': {}
|
||||
|
||||
'@hookform/resolvers@5.2.2(react-hook-form@7.71.1(react@19.2.4))':
|
||||
dependencies:
|
||||
'@standard-schema/utils': 0.3.0
|
||||
react-hook-form: 7.71.1(react@19.2.4)
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.7':
|
||||
|
|
@ -5687,6 +5793,46 @@ snapshots:
|
|||
dependencies:
|
||||
playwright: 1.58.2
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.4)':
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
|
||||
'@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
||||
|
||||
'@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.4)
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
||||
|
||||
'@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
||||
|
||||
'@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.4)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4)
|
||||
react: 19.2.4
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.11': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.57.1':
|
||||
|
|
@ -5768,6 +5914,8 @@ snapshots:
|
|||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
|
||||
'@standard-schema/utils@0.3.0': {}
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
|
@ -6396,6 +6544,10 @@ snapshots:
|
|||
|
||||
ci-info@4.4.0: {}
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
|
||||
client-only@0.0.1: {}
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
|
@ -8234,6 +8386,10 @@ snapshots:
|
|||
'@babel/runtime': 7.28.6
|
||||
react: 19.2.4
|
||||
|
||||
react-hook-form@7.71.1(react@19.2.4):
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
|
||||
react-image-crop@10.1.8(react@19.2.4):
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
|
|
@ -8646,6 +8802,8 @@ snapshots:
|
|||
|
||||
tabbable@6.4.0: {}
|
||||
|
||||
tailwind-merge@3.4.1: {}
|
||||
|
||||
tailwindcss-animate@1.0.7(tailwindcss@4.1.18):
|
||||
dependencies:
|
||||
tailwindcss: 4.1.18
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export default {
|
||||
const config = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
import { PetitionForm } from '@/components/PetitionForm'
|
||||
import { getCachedDocumentById, getDocuments } from '@/utilities/documents'
|
||||
import { convertLexicalToHTML } from '@payloadcms/richtext-lexical/html'
|
||||
import { notFound } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { FieldDescription, FieldLegend, FieldSet } from '@/components/ui/field'
|
||||
import z from 'zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const payload = await getDocuments('petition')
|
||||
|
|
@ -16,29 +13,12 @@ export async function generateStaticParams() {
|
|||
}
|
||||
|
||||
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||
const petition = await getCachedDocumentById('petition', (await params).id)()
|
||||
|
||||
const methods = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
state: '',
|
||||
city: '',
|
||||
postalCode: '',
|
||||
comment: '',
|
||||
},
|
||||
})
|
||||
const petition = await getCachedDocumentById('petition', (await params).id)
|
||||
|
||||
if (!petition || !petition.enabled) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<section className="h-screen w-screen flex items-center justify-center">
|
||||
|
|
@ -49,73 +29,7 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
|
|||
<article
|
||||
dangerouslySetInnerHTML={{ __html: convertLexicalToHTML({ data: petition.body }) }}
|
||||
/>
|
||||
<form id="formulario" onSubmit={methods.handleSubmit(onSubmit)}>
|
||||
<FieldSet>
|
||||
<FieldLegend>{petition.formTitle}</FieldLegend>
|
||||
<FieldDescription>{petition.formDescription}</FieldDescription>
|
||||
</FieldSet>
|
||||
</form>
|
||||
<PetitionForm id="formulario" petition={petition} />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
const STATES = [
|
||||
'AC',
|
||||
'AL',
|
||||
'AM',
|
||||
'AP',
|
||||
'BA',
|
||||
'CE',
|
||||
'DF',
|
||||
'ES',
|
||||
'GO',
|
||||
'MA',
|
||||
'MG',
|
||||
'MS',
|
||||
'MT',
|
||||
'PA',
|
||||
'PB',
|
||||
'PE',
|
||||
'PI',
|
||||
'PR',
|
||||
'RJ',
|
||||
'RN',
|
||||
'RO',
|
||||
'RR',
|
||||
'RS',
|
||||
'SC',
|
||||
'SE',
|
||||
'SP',
|
||||
'TO',
|
||||
]
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(2, 'Nome deve ter pelo menos 2 caracteres')
|
||||
.max(120, 'Nome deve ter no máximo 100 caracteres')
|
||||
.regex(
|
||||
/^(?=.* )[\p{L}\p{M}]+(?:[- ][\p{L}\p{M}]+)*$/u,
|
||||
'Informe nome e sobrenome. Use apenas letras, no máximo um espaço ou hífen entre termos, e sem espaço no início ou no fim.',
|
||||
),
|
||||
email: z.email('Email inválido'),
|
||||
phone: z
|
||||
.string()
|
||||
.trim()
|
||||
.length(11, 'Telefone celular inválido')
|
||||
.regex(/^\d{11}$/, 'Telefone celular inválido'),
|
||||
state: z.literal(STATES, 'Estado inválido'),
|
||||
city: z.string().trim().min(3, 'Cidade inválida').max(100, 'Cidade muito longa'),
|
||||
postalCode: z
|
||||
.string()
|
||||
.trim()
|
||||
.regex(/^(?:\d{8})?$/, 'CEP inválido')
|
||||
.optional(),
|
||||
comment: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(1000, 'Comentário muito longo')
|
||||
.refine((v) => !/<\/?[a-z][\s\S]*>/i.test(v), 'O comentário não pode conter HTML')
|
||||
.optional(),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import { Footer } from '@/components/Footer'
|
||||
import { Header } from '@/components/Header'
|
||||
import React from 'react'
|
||||
import './styles.css'
|
||||
import { getCachedDocumentById } from '@/utilities/documents'
|
||||
import { getCachedGlobal } from '@/utilities/globals'
|
||||
import { RefreshRouteOnSave } from '@/utilities/RefreshRouteOnSave'
|
||||
import { Metadata } from 'next'
|
||||
import { getCachedGlobal } from '@/utilities/globals'
|
||||
import { getCachedDocumentById } from '@/utilities/documents'
|
||||
import React from 'react'
|
||||
import './styles.css'
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const payload = await getCachedGlobal('metadata')()
|
||||
const payload = await getCachedGlobal('metadata')
|
||||
|
||||
let image = payload.image
|
||||
|
||||
if (typeof image === 'number') {
|
||||
image = await getCachedDocumentById('media', String(payload.image))()
|
||||
image = await getCachedDocumentById('media', String(payload.image))
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
import { getCachedGlobal } from '@/utilities/globals'
|
||||
import Image from 'next/image'
|
||||
import { getPayload } from 'payload'
|
||||
import React from 'react'
|
||||
import configPromise from '@payload-config'
|
||||
import { Media } from '@/payload-types'
|
||||
import { getCachedDocumentById } from '@/utilities/documents'
|
||||
import { getCachedGlobal } from '@/utilities/globals'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default async function HomePage() {
|
||||
const payload = await getCachedGlobal('home', 2)()
|
||||
const payload = await getCachedGlobal('home', 2)
|
||||
|
||||
let image: Media
|
||||
|
||||
if (typeof payload.image === 'number') {
|
||||
image = await getCachedDocumentById('media', `${payload.image}`)()
|
||||
image = await getCachedDocumentById('media', `${payload.image}`)
|
||||
} else {
|
||||
image = payload.image
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
import configPromise from '@payload-config'
|
||||
import { getPayload } from 'payload'
|
||||
|
||||
export const GET = async (request: Request) => {
|
||||
const payload = await getPayload({
|
||||
config: configPromise,
|
||||
})
|
||||
|
||||
return Response.json({
|
||||
message: 'Este é um exemplo de rota personalizada.',
|
||||
})
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import { getCachedGlobal } from '@/utilities/globals'
|
|||
import { SocialLinks } from './socialLinks'
|
||||
|
||||
export const Header = async () => {
|
||||
const settings = await getCachedGlobal('site-settings', 2)()
|
||||
const settings = await getCachedGlobal('site-settings', 2)
|
||||
|
||||
return (
|
||||
<header className="flex items-center justify-between p-4 absolute top-0 left-0 right-0 bg-black">
|
||||
|
|
|
|||
122
src/components/PetitionForm.tsx
Normal file
122
src/components/PetitionForm.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
'use client'
|
||||
|
||||
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSet } from '@/components/ui/field'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Petition } from '@/payload-types'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import z from 'zod'
|
||||
|
||||
interface PetitionFormProps {
|
||||
id: string
|
||||
petition: Petition
|
||||
}
|
||||
|
||||
export const PetitionForm = ({ id, petition }: PetitionFormProps) => {
|
||||
const methods = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
state: '',
|
||||
city: '',
|
||||
postalCode: '',
|
||||
comment: '',
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
return (
|
||||
<form id={id} onSubmit={methods.handleSubmit(onSubmit)}>
|
||||
<FieldSet>
|
||||
<FieldLegend>Revogar concessão da orla de Salvador</FieldLegend>
|
||||
<FieldDescription>Assine esta petição e ajude a manter a orla de Salvador para todos nós!</FieldDescription>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="name">
|
||||
Nome Completo
|
||||
</FieldLabel>
|
||||
<Input id="name" placeholder="Digite seu nome completo" autoComplete='name' maxLength={120} required />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="email">
|
||||
E-mail
|
||||
</FieldLabel>
|
||||
<Input id="email" placeholder="Digite seu e-mail" autoComplete='email' inputMode="email" type='text' maxLength={254} required />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="phone">
|
||||
Celular
|
||||
</FieldLabel>
|
||||
<Input id="phone" placeholder="(71) 99999-9999" autoComplete='tel' inputMode="tel" type='tel' maxLength={15} required />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
const STATES = [
|
||||
'AC',
|
||||
'AL',
|
||||
'AM',
|
||||
'AP',
|
||||
'BA',
|
||||
'CE',
|
||||
'DF',
|
||||
'ES',
|
||||
'GO',
|
||||
'MA',
|
||||
'MG',
|
||||
'MS',
|
||||
'MT',
|
||||
'PA',
|
||||
'PB',
|
||||
'PE',
|
||||
'PI',
|
||||
'PR',
|
||||
'RJ',
|
||||
'RN',
|
||||
'RO',
|
||||
'RR',
|
||||
'RS',
|
||||
'SC',
|
||||
'SE',
|
||||
'SP',
|
||||
'TO',
|
||||
]
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(2, 'Nome deve ter pelo menos 2 caracteres')
|
||||
.max(120, 'Nome deve ter no máximo 100 caracteres')
|
||||
.regex(
|
||||
/^(?=.* )[\p{L}\p{M}]+(?:[- ][\p{L}\p{M}]+)*$/u,
|
||||
'Informe nome e sobrenome. Use apenas letras, no máximo um espaço ou hífen entre termos, e sem espaço no início ou no fim.',
|
||||
),
|
||||
email: z.email('Email inválido'),
|
||||
phone: z
|
||||
.string()
|
||||
.trim()
|
||||
.length(11, 'Telefone celular inválido')
|
||||
.regex(/^\d{11}$/, 'Telefone celular inválido'),
|
||||
state: z.literal(STATES, 'Estado inválido'),
|
||||
city: z.string().trim().min(3, 'Cidade inválida').max(100, 'Cidade muito longa'),
|
||||
postalCode: z
|
||||
.string()
|
||||
.trim()
|
||||
.regex(/^(?:\d{8})?$/, 'CEP inválido')
|
||||
.optional(),
|
||||
comment: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(1000, 'Comentário muito longo')
|
||||
.refine((v) => !/<\/?[a-z][\s\S]*>/i.test(v), 'O comentário não pode conter HTML')
|
||||
.optional(),
|
||||
})
|
||||
|
|
@ -79,7 +79,7 @@ type SocialLinksProps = {
|
|||
}
|
||||
|
||||
export const SocialLinks = async ({ ariaLabel }: SocialLinksProps) => {
|
||||
const { socialLinks: links } = await getCachedGlobal('site-settings', 2)()
|
||||
const { socialLinks: links } = await getCachedGlobal('site-settings', 2)
|
||||
|
||||
if (!links?.length) return null
|
||||
|
||||
|
|
|
|||
22
src/components/ui/input.tsx
Normal file
22
src/components/ui/input.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Input.displayName = "Input"
|
||||
|
||||
export { Input }
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
import { Config } from '@/payload-types'
|
||||
|
||||
import configPromise from '@payload-config'
|
||||
import { revalidateTag } from 'next/cache'
|
||||
import { unstable_cache } from 'next/cache'
|
||||
import { cacheTag, revalidateTag } from 'next/cache'
|
||||
import { getPayload } from 'payload'
|
||||
|
||||
type Collection = keyof Config['collections']
|
||||
|
|
@ -22,14 +21,15 @@ export const getDocumentById = <Slug extends Collection>(
|
|||
const getTag = <Slug extends Collection>(collection: Slug, id: string) =>
|
||||
`document_${collection}:${id}`
|
||||
|
||||
export const getCachedDocumentById = <Slug extends Collection>(
|
||||
export const getCachedDocumentById = async <Slug extends Collection>(
|
||||
collection: Slug,
|
||||
id: string,
|
||||
depth?: number,
|
||||
) =>
|
||||
unstable_cache(() => getDocumentById(collection, id, depth), [id], {
|
||||
tags: [getTag(collection, id)],
|
||||
})
|
||||
) => {
|
||||
'use cache'
|
||||
cacheTag(getTag(collection, id))
|
||||
return await getDocumentById(collection, id, depth)
|
||||
}
|
||||
|
||||
export const revalidateDocumentById = <Slug extends Collection>(collection: Slug, id: string) =>
|
||||
revalidateTag(getTag(collection, id))
|
||||
revalidateTag(getTag(collection, id), 'max')
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import type { Config } from 'src/payload-types'
|
||||
|
||||
import configPromise from '@payload-config'
|
||||
import { unstable_cache } from 'next/cache'
|
||||
import { cacheTag, revalidateTag } from 'next/cache'
|
||||
import { getPayload } from 'payload'
|
||||
import { revalidateTag } from 'next/cache'
|
||||
|
||||
type Global = keyof Config['globals']
|
||||
|
||||
export async function getGlobal<Slug extends Global>(slug: Slug, depth = 0) {
|
||||
export async function getGlobal<Slug extends Global>(slug: Slug, depth?: number) {
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const global = await payload.findGlobal({
|
||||
|
|
@ -18,12 +17,12 @@ export async function getGlobal<Slug extends Global>(slug: Slug, depth = 0) {
|
|||
return global
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unstable_cache function mapped with the cache tag for the slug
|
||||
*/
|
||||
export const getCachedGlobal = <Slug extends Global>(slug: Slug, depth = 0) =>
|
||||
unstable_cache(async () => getGlobal(slug, depth), [slug], {
|
||||
tags: [`global_${slug}`],
|
||||
})
|
||||
const getTag = (slug: string) => `global_${slug}`
|
||||
|
||||
export const revalidateGlobal = <Slug extends Global>(slug: Slug) => revalidateTag(`global_${slug}`)
|
||||
export const getCachedGlobal = async <Slug extends Global>(slug: Slug, depth?: number) => {
|
||||
'use cache'
|
||||
cacheTag(getTag(slug))
|
||||
return await getGlobal(slug, depth)
|
||||
}
|
||||
|
||||
export const revalidateGlobal = <Slug extends Global>(slug: Slug) => revalidateTag(getTag(slug), 'max')
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { test, expect, Page } from '@playwright/test'
|
||||
import { expect, Page, test } from '@playwright/test'
|
||||
import { login } from '../helpers/login'
|
||||
import { seedTestUser, cleanupTestUser, testUser } from '../helpers/seedUser'
|
||||
import { cleanupTestUser, seedTestUser, testUser } from '../helpers/seedUser'
|
||||
|
||||
test.describe('Admin Panel', () => {
|
||||
let page: Page
|
||||
|
||||
test.beforeAll(async ({ browser }, testInfo) => {
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
await seedTestUser()
|
||||
|
||||
const context = await browser.newContext()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { test, expect, Page } from '@playwright/test'
|
||||
import { expect, Page, test } from '@playwright/test'
|
||||
|
||||
test.describe('Frontend', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let page: Page
|
||||
|
||||
test.beforeAll(async ({ browser }, testInfo) => {
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue