From 8f3bd3845b8bed62723a4bdeaa0725acf90c03bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Tue, 17 Feb 2026 22:16:53 -0300 Subject: [PATCH] Migrate from unstable_cache to use cache. And start Petition Form --- package.json | 12 +- pnpm-lock.yaml | 160 +++++++++++++++++- postcss.config.mjs | 4 +- .../(frontend)/abaixo-assinado/[id]/page.tsx | 94 +--------- src/app/(frontend)/layout.tsx | 12 +- src/app/(frontend)/page.tsx | 11 +- src/app/my-route/route.ts | 12 -- src/components/Header.tsx | 2 +- src/components/PetitionForm.tsx | 122 +++++++++++++ src/components/socialLinks.tsx | 2 +- src/components/ui/input.tsx | 22 +++ src/utilities/documents.ts | 16 +- src/utilities/globals.ts | 21 ++- tests/e2e/admin.e2e.spec.ts | 6 +- tests/e2e/frontend.e2e.spec.ts | 5 +- 15 files changed, 356 insertions(+), 145 deletions(-) delete mode 100644 src/app/my-route/route.ts create mode 100644 src/components/PetitionForm.tsx create mode 100644 src/components/ui/input.tsx diff --git a/package.json b/package.json index 0f989383..0b50ad58 100644 --- a/package.json +++ b/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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53b8ed0b..42ed5be5 100644 --- a/pnpm-lock.yaml +++ b/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 diff --git a/postcss.config.mjs b/postcss.config.mjs index a7f73a2d..ae85b2fe 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,5 +1,7 @@ -export default { +const config = { plugins: { '@tailwindcss/postcss': {}, }, } + +export default config diff --git a/src/app/(frontend)/abaixo-assinado/[id]/page.tsx b/src/app/(frontend)/abaixo-assinado/[id]/page.tsx index 7a051fd8..b44e9cfc 100644 --- a/src/app/(frontend)/abaixo-assinado/[id]/page.tsx +++ b/src/app/(frontend)/abaixo-assinado/[id]/page.tsx @@ -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>({ - 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) => { - console.log(data) - } - return (
@@ -49,73 +29,7 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
-
-
- {petition.formTitle} - {petition.formDescription} -
-
+
) } - -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(), -}) diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx index a5d18ae5..98a9bd08 100644 --- a/src/app/(frontend)/layout.tsx +++ b/src/app/(frontend)/layout.tsx @@ -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 { - 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 { diff --git a/src/app/(frontend)/page.tsx b/src/app/(frontend)/page.tsx index 111a13f8..4880d854 100644 --- a/src/app/(frontend)/page.tsx +++ b/src/app/(frontend)/page.tsx @@ -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 } diff --git a/src/app/my-route/route.ts b/src/app/my-route/route.ts deleted file mode 100644 index 5e7d2e0c..00000000 --- a/src/app/my-route/route.ts +++ /dev/null @@ -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.', - }) -} diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 93e1f545..3ad86b16 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -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 (
diff --git a/src/components/PetitionForm.tsx b/src/components/PetitionForm.tsx new file mode 100644 index 00000000..beeaf6af --- /dev/null +++ b/src/components/PetitionForm.tsx @@ -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>({ + resolver: zodResolver(formSchema), + defaultValues: { + name: '', + email: '', + phone: '', + state: '', + city: '', + postalCode: '', + comment: '', + }, + }) + + const onSubmit = (data: z.infer) => { + console.log(data) + } + + return ( +
+
+ Revogar concessão da orla de Salvador + Assine esta petição e ajude a manter a orla de Salvador para todos nós! + + + + Nome Completo + + + + + + E-mail + + + + + + Celular + + + + +
+
+ ) +} + +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(), +}) diff --git a/src/components/socialLinks.tsx b/src/components/socialLinks.tsx index 7fea2f36..a340d682 100644 --- a/src/components/socialLinks.tsx +++ b/src/components/socialLinks.tsx @@ -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 diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 00000000..69b64fb2 --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,22 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/src/utilities/documents.ts b/src/utilities/documents.ts index 2bee5a09..207576aa 100644 --- a/src/utilities/documents.ts +++ b/src/utilities/documents.ts @@ -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 = ( const getTag = (collection: Slug, id: string) => `document_${collection}:${id}` -export const getCachedDocumentById = ( +export const getCachedDocumentById = async ( 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 = (collection: Slug, id: string) => - revalidateTag(getTag(collection, id)) + revalidateTag(getTag(collection, id), 'max') diff --git a/src/utilities/globals.ts b/src/utilities/globals.ts index 1d7355eb..0fea4e39 100644 --- a/src/utilities/globals.ts +++ b/src/utilities/globals.ts @@ -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: Slug, depth = 0) { +export async function getGlobal(slug: Slug, depth?: number) { const payload = await getPayload({ config: configPromise }) const global = await payload.findGlobal({ @@ -18,12 +17,12 @@ export async function getGlobal(slug: Slug, depth = 0) { return global } -/** - * Returns a unstable_cache function mapped with the cache tag for the slug - */ -export const getCachedGlobal = (slug: Slug, depth = 0) => - unstable_cache(async () => getGlobal(slug, depth), [slug], { - tags: [`global_${slug}`], - }) +const getTag = (slug: string) => `global_${slug}` -export const revalidateGlobal = (slug: Slug) => revalidateTag(`global_${slug}`) +export const getCachedGlobal = async (slug: Slug, depth?: number) => { + 'use cache' + cacheTag(getTag(slug)) + return await getGlobal(slug, depth) +} + +export const revalidateGlobal = (slug: Slug) => revalidateTag(getTag(slug), 'max') diff --git a/tests/e2e/admin.e2e.spec.ts b/tests/e2e/admin.e2e.spec.ts index 67fc9217..2aded214 100644 --- a/tests/e2e/admin.e2e.spec.ts +++ b/tests/e2e/admin.e2e.spec.ts @@ -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() diff --git a/tests/e2e/frontend.e2e.spec.ts b/tests/e2e/frontend.e2e.spec.ts index 65c31fd5..7a047f47 100644 --- a/tests/e2e/frontend.e2e.spec.ts +++ b/tests/e2e/frontend.e2e.spec.ts @@ -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() })