From f7d18543093ef2c5fa46c86c96083b7c4bc413d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Sun, 28 Dec 2025 18:48:55 -0300 Subject: [PATCH] add cep and cnpj to api --- .../_.$orgid.enrollments.buy/payment.tsx | 6 +- .../_.$orgid.enrollments.buy/review.tsx | 173 +++++++++++------- .../app/routes/_.setup.cnpj[.]son/route.ts | 65 ------- .../app/routes/~.api.cep.$cep[.]json/route.ts | 23 +++ .../routes/~.api.cnpj.$cnpj[.]json/route.ts | 30 +++ enrollments-events/app/events/issue_cert.py | 33 ++-- enrollments-events/template.yaml | 6 +- 7 files changed, 185 insertions(+), 151 deletions(-) delete mode 100644 apps/admin.saladeaula.digital/app/routes/_.setup.cnpj[.]son/route.ts create mode 100644 apps/admin.saladeaula.digital/app/routes/~.api.cep.$cep[.]json/route.ts create mode 100644 apps/admin.saladeaula.digital/app/routes/~.api.cnpj.$cnpj[.]json/route.ts diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx index 70a0ad6..f689ad0 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx @@ -32,6 +32,7 @@ import { import { useWizard } from '@/components/wizard' import { isName } from '../_.$orgid.users.add/data' +import type { PaymentMethod } from '@repo/ui/routes/orders/data' const creditCard = z.object({ holder_name: z @@ -60,6 +61,9 @@ const formSchema = z.discriminatedUnion( z.object({ payment_method: z.literal('BANK_SLIP') }), + z.object({ + payment_method: z.literal('MANUAL') + }), z.object({ payment_method: z.literal('CREDIT_CARD'), credit_card: creditCard @@ -74,7 +78,7 @@ export type CreditCard = z.infer type PaymentProps = { onSubmit: (value: any) => void | Promise - payment_method?: 'PIX' | 'BANK_SLIP' | 'CREDIT_CARD' + payment_method?: PaymentMethod credit_card?: CreditCard } diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx index ae90395..ad3962b 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx @@ -1,4 +1,4 @@ -import { useToggle } from 'ahooks' +import { useRequest, useToggle } from 'ahooks' import { PatternFormat } from 'react-number-format' import { ExternalLinkIcon, PencilIcon, SearchIcon } from 'lucide-react' import { Controller, useForm } from 'react-hook-form' @@ -229,19 +229,21 @@ const formSchema = z.object({ state: z.string().min(1, 'Digite o estado') }) -type Schema = z.infer +type Address = z.infer export function AddressDialog() { - const { handleSubmit, control } = useForm({ + const [open, { toggle, set }] = useToggle() + const { handleSubmit, control, reset } = useForm({ resolver: zodResolver(formSchema) }) - const onSubmit = async (data: Schema) => { + const onSubmit = async (data: Address) => { + set(false) console.log(data) } return ( - + ) } + +type PostcodeFormProps = { + onChange: (value: Address) => void +} + +function PostcodeForm({ onChange }: PostcodeFormProps) { + const formSchema = z.object({ + postcode: z.string().min(1, 'Digite um CEP') + }) + + type Schema = z.infer + + const { control, handleSubmit, setError } = useForm({ + resolver: zodResolver(formSchema) + }) + + const { runAsync, loading } = useRequest( + async (cep: string) => { + return await fetch(`/~/api/cep/${cep}.json`) + }, + { manual: true } + ) + + const onSubmit = async ({ postcode }: Schema) => { + const r = await runAsync(postcode) + + if (r.ok) { + onChange?.((await r.json()) as Address) + return + } + + setError('postcode', { message: 'CEP não encontrado' }) + } + + return ( + + + ( + + {/* CEP */} + CEP + + { + onChange(value) + }} + {...field} + /> + + + {loading ? : } + + + + + {fieldState.invalid && } + + + + Não sei o CEP + + + + )} + /> + + + ) +} diff --git a/apps/admin.saladeaula.digital/app/routes/_.setup.cnpj[.]son/route.ts b/apps/admin.saladeaula.digital/app/routes/_.setup.cnpj[.]son/route.ts deleted file mode 100644 index 5b7f5f7..0000000 --- a/apps/admin.saladeaula.digital/app/routes/_.setup.cnpj[.]son/route.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { Route } from './+types/route' - -import { data } from 'react-router' - -export async function loader({ params, context, request }: Route.LoaderArgs) { - const url = new URL(request.url) - const cnpj = url.searchParams.get('cnpj') - - const r = await fetch(`https://brasilapi.com.br/api/cnpj/v1/${cnpj}`, { - method: 'GET', - headers: { - 'User-Agent': - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36', - Accept: 'application/json' - } - }) - - if (!r.ok) { - throw new Response(await r.text(), { status: r.status }) - } - - return data({}) -} - -// export const prerender = false -// import type { APIRoute } from 'astro' -// import lodash from 'lodash' - -// export const GET: APIRoute = async ({ params }) => { -// // await new Promise((r) => setTimeout(r, 2000)) - -// const cnpj = params.cnpj -// const res = await fetch(`https://brasilapi.com.br/api/cnpj/v1/${cnpj}`, { -// method: 'GET', -// headers: { -// 'User-Agent': -// 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36', -// Accept: 'application/json' -// } -// }) - -// if (!res.ok) { -// return new Response(null, { -// status: 404 -// }) -// } - -// const json = await res.json() -// const addr = lodash.startCase( -// lodash.toLower(`${json.descricao_tipo_de_logradouro} ${json.logradouro}`) -// ) - -// return new Response( -// JSON.stringify({ -// name: json.razao_social, -// address: { -// postcode: json.cep, -// address1: `${addr}, ${json.numero}`, -// neighborhood: lodash.capitalize(json.bairro), -// city: lodash.capitalize(json.municipio), -// state: json.uf -// } -// }) -// ) -// } diff --git a/apps/admin.saladeaula.digital/app/routes/~.api.cep.$cep[.]json/route.ts b/apps/admin.saladeaula.digital/app/routes/~.api.cep.$cep[.]json/route.ts new file mode 100644 index 0000000..1466bfd --- /dev/null +++ b/apps/admin.saladeaula.digital/app/routes/~.api.cep.$cep[.]json/route.ts @@ -0,0 +1,23 @@ +import { data } from 'react-router' +import type { Route } from './+types/route' + +export async function loader({ params }: Route.LoaderArgs) { + const r = await fetch(`https://opencep.com/v1/${params.cep}`, { + method: 'GET' + }) + + if (!r.ok) { + throw new Response(await r.text(), { status: r.status }) + } + + const json = (await r.json()) as any + + return data({ + postcode: json.cep.replace(/\D/g, ''), + address1: json.logradouro, + address2: json.complemento, + neighborhood: json.bairro, + city: json.localidade, + state: json.uf + }) +} diff --git a/apps/admin.saladeaula.digital/app/routes/~.api.cnpj.$cnpj[.]json/route.ts b/apps/admin.saladeaula.digital/app/routes/~.api.cnpj.$cnpj[.]json/route.ts new file mode 100644 index 0000000..ac92399 --- /dev/null +++ b/apps/admin.saladeaula.digital/app/routes/~.api.cnpj.$cnpj[.]json/route.ts @@ -0,0 +1,30 @@ +import type { Route } from './+types/route' + +import { data } from 'react-router' +import lodash from 'lodash' + +export async function loader({ params }: Route.LoaderArgs) { + const r = await fetch(`https://minhareceita.org/${params.cnpj}`, { + method: 'GET' + }) + + if (!r.ok) { + throw new Response(await r.text(), { status: r.status }) + } + + const json = (await r.json()) as any + const addr = lodash.startCase( + lodash.toLower(`${json.descricao_tipo_de_logradouro} ${json.logradouro}`) + ) + + return data({ + name: json.razao_social, + address: { + postcode: json.cep, + address1: `${addr}, ${json.numero}`, + neighborhood: lodash.capitalize(json.bairro), + city: lodash.capitalize(json.municipio), + state: json.uf + } + }) +} diff --git a/enrollments-events/app/events/issue_cert.py b/enrollments-events/app/events/issue_cert.py index ddbd162..1ff082c 100644 --- a/enrollments-events/app/events/issue_cert.py +++ b/enrollments-events/app/events/issue_cert.py @@ -1,4 +1,3 @@ -import json from datetime import datetime, timedelta from typing import NotRequired, TypedDict @@ -135,24 +134,22 @@ def _generate_cert( # Send template URI and data to Paperforge API to generate a PDF r = requests.post( PAPERFORGE_API, - data=json.dumps( - { - 'template_uri': cert['s3_uri'], - 'args': { - 'name': user['name'], - 'cpf': _cpffmt(user['cpf']), - 'score': score, - 'started_at': started_at.strftime('%d/%m/%Y'), - 'completed_at': completed_at.strftime('%d/%m/%Y'), - 'today': _datefmt(now_), - 'year': now_.strftime('%Y'), - 'expires_at': expires_at.strftime('%d/%m/%Y') - if expires_at - else None, - }, + json={ + 'template_uri': cert['s3_uri'], + 'args': { + 'name': user['name'], + 'cpf': _cpffmt(user['cpf']), + 'score': score, + 'started_at': started_at.strftime('%d/%m/%Y'), + 'completed_at': completed_at.strftime('%d/%m/%Y'), + 'today': _datefmt(now_), + 'year': now_.strftime('%Y'), + 'expires_at': expires_at.strftime('%d/%m/%Y') + if expires_at + else None, }, - ), - timeout=6, + }, + timeout=(1, 3), ) r.raise_for_status() diff --git a/enrollments-events/template.yaml b/enrollments-events/template.yaml index 580c228..d5c39e9 100644 --- a/enrollments-events/template.yaml +++ b/enrollments-events/template.yaml @@ -369,7 +369,7 @@ Resources: Properties: Handler: events.issue_cert.lambda_handler Tracing: Active - Timeout: 12 + # Timeout: 12 LoggingConfig: LogGroup: !Ref EventLog Policies: @@ -443,6 +443,10 @@ Resources: sk: ['0'] new_image: status: [COMPLETED] + # Post-migration: uncomment the following line + # cert: + # expires_at: + # - exists: true cert_expires_at: - exists: true org_id: