From c4eb7e37750dd175f67446b3925ab4abdf8fa673 Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 10:45:30 -0300 Subject: [PATCH 01/24] Add utility functions for formatting CPF, CNPJ, and phone numbers --- src/shared/utils/string-extensions.ts | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/shared/utils/string-extensions.ts diff --git a/src/shared/utils/string-extensions.ts b/src/shared/utils/string-extensions.ts new file mode 100644 index 0000000..ebc39a6 --- /dev/null +++ b/src/shared/utils/string-extensions.ts @@ -0,0 +1,75 @@ +export const formatCPF = (value: string) => { + // Remove tudo que não é dígito + const cleanValue = value.replace(/\D/g, '') + + // Aplica a máscara conforme o usuário digita + if (cleanValue.length <= 3) { + return cleanValue + } else if (cleanValue.length <= 6) { + return `${cleanValue.slice(0, 3)}.${cleanValue.slice(3)}` + } else if (cleanValue.length <= 9) { + return `${cleanValue.slice(0, 3)}.${cleanValue.slice(3, 6)}.${cleanValue.slice(6)}` + } else { + return `${cleanValue.slice(0, 3)}.${cleanValue.slice(3, 6)}.${cleanValue.slice(6, 9)}-${cleanValue.slice(9, 11)}` + } +} + +export const formatCNPJ = (value: string) => { + // Remove tudo que não é dígito + const cleanValue = value.replace(/\D/g, '') + + // Aplica a máscara conforme o usuário digita + if (cleanValue.length <= 2) { + return cleanValue + } else if (cleanValue.length <= 5) { + return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2)}` + } else if (cleanValue.length <= 8) { + return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2, 5)}.${cleanValue.slice(5)}` + } else if (cleanValue.length <= 12) { + return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2, 5)}.${cleanValue.slice(5, 8)}/${cleanValue.slice(8)}` + } else { + return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2, 5)}.${cleanValue.slice(5, 8)}/${cleanValue.slice(8, 12)}-${cleanValue.slice(12, 14)}` + } +} + +export const formatPhone = (value: string) => { + // Remove tudo que não é dígito + const cleanValue = value.replace(/\D/g, '') + + // Aplica a máscara conforme o usuário digita + if (cleanValue.length <= 2) { + return cleanValue + } else if (cleanValue.length <= 6) { + return `(${cleanValue.slice(0, 2)}) ${cleanValue.slice(2)}` + } else if (cleanValue.length <= 10) { + // Para telefone fixo (8 dígitos): (11) 5320-2121 + return `(${cleanValue.slice(0, 2)}) ${cleanValue.slice(2, 6)}-${cleanValue.slice(6)}` + } else { + // Para celular (9 dígitos): (11) 95320-2121 + return `(${cleanValue.slice(0, 2)}) ${cleanValue.slice(2, 7)}-${cleanValue.slice(7, 11)}` + } +} + +export const handleCPFChange = ( + value: string, + onChange: (value: string) => void +) => { + const formattedValue = formatCPF(value) + onChange(formattedValue) +} + +export const handleCNPJChange = ( + value: string, + onChange: (value: string) => void +) => { + const formattedValue = formatCNPJ(value) + onChange(formattedValue) +} + +export const handlePhoneChange = ( + value: string, + onChange: (value: string) => void +) => { + const formattedValue = formatPhone(value) + onChange(formattedValue) +} From bbda11d47dabb1ed6bbf8dae49688dbc7f406377 Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 10:47:26 -0300 Subject: [PATCH 02/24] chore: remove unused document formatting functions for CPF, CNPJ, and phone numbers --- src/shared/utils/format-documents.ts | 51 ---------------------------- 1 file changed, 51 deletions(-) diff --git a/src/shared/utils/format-documents.ts b/src/shared/utils/format-documents.ts index a753172..e69de29 100644 --- a/src/shared/utils/format-documents.ts +++ b/src/shared/utils/format-documents.ts @@ -1,51 +0,0 @@ -export const formatCPF = (value: string) => { - // Remove tudo que não é dígito - const cleanValue = value.replace(/\D/g, '') - - // Aplica a máscara conforme o usuário digita - if (cleanValue.length <= 3) { - return cleanValue - } else if (cleanValue.length <= 6) { - return `${cleanValue.slice(0, 3)}.${cleanValue.slice(3)}` - } else if (cleanValue.length <= 9) { - return `${cleanValue.slice(0, 3)}.${cleanValue.slice(3, 6)}.${cleanValue.slice(6)}` - } else { - return `${cleanValue.slice(0, 3)}.${cleanValue.slice(3, 6)}.${cleanValue.slice(6, 9)}-${cleanValue.slice(9, 11)}` - } -} - -export const formatCNPJ = (value: string) => { - // Remove tudo que não é dígito - const cleanValue = value.replace(/\D/g, '') - - // Aplica a máscara conforme o usuário digita - if (cleanValue.length <= 2) { - return cleanValue - } else if (cleanValue.length <= 5) { - return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2)}` - } else if (cleanValue.length <= 8) { - return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2, 5)}.${cleanValue.slice(5)}` - } else if (cleanValue.length <= 12) { - return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2, 5)}.${cleanValue.slice(5, 8)}/${cleanValue.slice(8)}` - } else { - return `${cleanValue.slice(0, 2)}.${cleanValue.slice(2, 5)}.${cleanValue.slice(5, 8)}/${cleanValue.slice(8, 12)}-${cleanValue.slice(12, 14)}` - } -} - -export const formatPhone = (value: string) => { - // Remove tudo que não é dígito - const cleanValue = value.replace(/\D/g, '') - - // Aplica a máscara conforme o usuário digita - if (cleanValue.length <= 2) { - return cleanValue - } else if (cleanValue.length <= 6) { - return `(${cleanValue.slice(0, 2)}) ${cleanValue.slice(2)}` - } else if (cleanValue.length <= 10) { - // Para telefone fixo (8 dígitos): (11) 5320-2121 - return `(${cleanValue.slice(0, 2)}) ${cleanValue.slice(2, 6)}-${cleanValue.slice(6)}` - } else { - // Para celular (9 dígitos): (11) 95320-2121 - return `(${cleanValue.slice(0, 2)}) ${cleanValue.slice(2, 7)}-${cleanValue.slice(7, 11)}` - } -} From b18bcde32cb646a1d833c7dd050855dd4a085a8f Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 10:47:35 -0300 Subject: [PATCH 03/24] refactor: replace document type string literals with enum values and remove unused formatting functions --- src/features/sign-up/pages/sign-up-page.tsx | 62 ++++++++------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/src/features/sign-up/pages/sign-up-page.tsx b/src/features/sign-up/pages/sign-up-page.tsx index 7107a90..37d5ae0 100644 --- a/src/features/sign-up/pages/sign-up-page.tsx +++ b/src/features/sign-up/pages/sign-up-page.tsx @@ -27,11 +27,6 @@ import { zodResolver } from '@hookform/resolvers/zod' import { useForm } from 'react-hook-form' import { Checkbox } from '@/shared/components/checkbox' import { Label } from '@/shared/components/label' -import { - formatCNPJ, - formatCPF, - formatPhone -} from '@/shared/utils/format-documents' import toast from 'react-hot-toast' import { BackgroundBlobs } from '@/shared/components/background-blobs' import { motion } from 'framer-motion' @@ -41,6 +36,12 @@ import { containerVariants, cardVariants } from '@/shared/utils/animations' import { useCreateUserMutation } from '@/shared/hooks/use-user' import { AxiosError } from 'axios' import { useNavigate } from 'react-router-dom' +import { DOCUMENT_TYPE } from '@/shared/enums/document-type' +import { + handleCNPJChange, + handleCPFChange, + handlePhoneChange +} from '@/shared/utils/string-extensions' export function SignUpPage() { const [showPassword, setShowPassword] = useState(false) @@ -55,33 +56,9 @@ export function SignUpPage() { mode: 'onBlur' }) - const handleCPFChange = ( - value: string, - onChange: (value: string) => void - ) => { - const formattedValue = formatCPF(value) - onChange(formattedValue) - } - - const handleCNPJChange = ( - value: string, - onChange: (value: string) => void - ) => { - const formattedValue = formatCNPJ(value) - onChange(formattedValue) - } - - const handlePhoneChange = ( - value: string, - onChange: (value: string) => void - ) => { - const formattedValue = formatPhone(value) - onChange(formattedValue) - } - async function onSubmit(values: SignUpFormData) { try { - const isIndividual = values.documentType === 'individual' + const isIndividual = values.documentType === DOCUMENT_TYPE.INDIVIDUAL // Convert birthDate to seconds since epoch if provided const birthDateSeconds = values.birthDate @@ -196,9 +173,11 @@ export function SignUpPage() { > { - field.onChange('individual') + field.onChange(DOCUMENT_TYPE.INDIVIDUAL) form.setValue('document', '') }} /> @@ -210,9 +189,9 @@ export function SignUpPage() { > { - field.onChange('business') + field.onChange(DOCUMENT_TYPE.BUSINESS) form.setValue('document', '') form.setValue('birthDate', undefined) }} @@ -231,7 +210,8 @@ export function SignUpPage() { render={({ field }) => ( - {form.getValues('documentType') === 'individual' + {form.getValues('documentType') === + DOCUMENT_TYPE.INDIVIDUAL ? 'CPF' : 'CNPJ'} @@ -240,7 +220,8 @@ export function SignUpPage() { {...field} onChange={(e) => { if ( - form.getValues('documentType') === 'individual' + form.getValues('documentType') === + DOCUMENT_TYPE.INDIVIDUAL ) { handleCPFChange(e.target.value, field.onChange) } else { @@ -248,12 +229,14 @@ export function SignUpPage() { } }} placeholder={ - form.getValues('documentType') === 'individual' + form.getValues('documentType') === + DOCUMENT_TYPE.INDIVIDUAL ? '000.000.000-00' : '00.000.000/0000-00' } maxLength={ - form.getValues('documentType') === 'individual' + form.getValues('documentType') === + DOCUMENT_TYPE.INDIVIDUAL ? 14 : 18 } @@ -263,7 +246,8 @@ export function SignUpPage() { )} /> - {form.getValues('documentType') === 'individual' && ( + {form.getValues('documentType') === + DOCUMENT_TYPE.INDIVIDUAL && ( Date: Thu, 23 Oct 2025 10:47:46 -0300 Subject: [PATCH 04/24] refactor: replace string literals with enum values for document type in sign-up schema --- src/features/sign-up/types/sign-up-schema.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/sign-up/types/sign-up-schema.ts b/src/features/sign-up/types/sign-up-schema.ts index 1642bf0..1631038 100644 --- a/src/features/sign-up/types/sign-up-schema.ts +++ b/src/features/sign-up/types/sign-up-schema.ts @@ -68,7 +68,7 @@ export const signUpFormSchema = z }) } - if (data.documentType === 'individual' && !data.birthDate) { + if (data.documentType === DOCUMENT_TYPE.INDIVIDUAL && !data.birthDate) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'A data de nascimento é obrigatória para pessoa física.', @@ -76,7 +76,7 @@ export const signUpFormSchema = z }) } - if (data.documentType === 'individual') { + if (data.documentType === DOCUMENT_TYPE.INDIVIDUAL) { // Validação CPF const cpfRegex = /^\d{3}\.?\d{3}\.?\d{3}-?\d{2}$/ if (!cpfRegex.test(data.document)) { @@ -104,7 +104,7 @@ export const signUpFormInitialValues: SignUpFormData = { name: '', email: '', phone: '', - documentType: 'individual', + documentType: DOCUMENT_TYPE.INDIVIDUAL, document: '', birthDate: undefined, password: '', From 3eb6de3b594773aab52db448aeb835a2c337749b Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 10:47:53 -0300 Subject: [PATCH 05/24] refactor: update user info page to use new string-extensions for document formatting and improve personType handling --- src/features/user-area/pages/user-info.tsx | 86 ++++++++-------------- 1 file changed, 29 insertions(+), 57 deletions(-) diff --git a/src/features/user-area/pages/user-info.tsx b/src/features/user-area/pages/user-info.tsx index 2f7ac09..6531d06 100644 --- a/src/features/user-area/pages/user-info.tsx +++ b/src/features/user-area/pages/user-info.tsx @@ -23,15 +23,15 @@ import { TooltipTrigger } from '@/shared/components/tooltip' import { DOCUMENT_TYPE } from '@/shared/enums/document-type' -import { - formatCNPJ, - formatCPF, - formatPhone -} from '@/shared/utils/format-documents' import { Background } from '@/shared/components/background' import { useUpdateUserMutation, useUser } from '@/shared/hooks/use-user' import { UpdateUserRequest } from '@/services/user' import toast from 'react-hot-toast' +import { + formatCPF, + formatCNPJ, + handlePhoneChange +} from '@/shared/utils/string-extensions' export function UserInfoPage() { const [isEditing, setIsEditing] = useState(false) @@ -45,7 +45,7 @@ export function UserInfoPage() { name: user?.name || '', email: user?.email || '', cellphone: user?.cellphone || '', - personType: user?.personType || 'individual', + personType: user?.personType || DOCUMENT_TYPE.INDIVIDUAL, cpfCnpj: user?.cpfCnpj || '', birthDate: user?.birthDate }, @@ -56,30 +56,6 @@ export function UserInfoPage() { return
Loading
} - const handleCPFChange = ( - value: string, - onChange: (value: string) => void - ) => { - const formattedValue = formatCPF(value) - onChange(formattedValue) - } - - const handleCNPJChange = ( - value: string, - onChange: (value: string) => void - ) => { - const formattedValue = formatCNPJ(value) - onChange(formattedValue) - } - - const handlePhoneChange = ( - value: string, - onChange: (value: string) => void - ) => { - const formattedValue = formatPhone(value) - onChange(formattedValue) - } - function handleEdit() { if (!user) return @@ -129,8 +105,10 @@ export function UserInfoPage() { await updateUser(changedData as UpdateUserRequest) refetch?.() } catch (error) { - console.error('Failed to update user:', error) - toast.error('Erro ao atualizar os dados. Tente novamente.') + toast.error( + 'Erro ao atualizar os dados. Tente novamente. ' + + (error as Error).message || (error as { details: string }).details + ) } finally { setIsEditing(false) } @@ -258,7 +236,7 @@ export function UserInfoPage() { ( + render={({ field }) => ( Tipo de Pessoa @@ -266,8 +244,7 @@ export function UserInfoPage() { Documento - { - if ( - form.getValues('personType') === - 'individual' - ) { - handleCPFChange( - e.target.value, - field.onChange - ) - } else { - handleCNPJChange( - e.target.value, - field.onChange - ) - } - }} - /> + + + + + +

Não é possível alterar o documento

+
+
@@ -331,9 +305,7 @@ export function UserInfoPage() { .split('T')[0] : '' } - onChange={(e) => - field.onChange(new Date(e.target.value)) - } + disabled /> From f4d0645f45f590dad83a01e8f901e42b0b74868f Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 11:03:11 -0300 Subject: [PATCH 06/24] feat: add linkedin links for team members and update navigation to use react-router --- src/features/home/pages/home-page.tsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/features/home/pages/home-page.tsx b/src/features/home/pages/home-page.tsx index 3c2da9f..d1ceeff 100644 --- a/src/features/home/pages/home-page.tsx +++ b/src/features/home/pages/home-page.tsx @@ -16,6 +16,7 @@ import { import { ArrowDown, ArrowRight, Check } from 'lucide-react' import { BackgroundBlobs } from '@/shared/components/background-blobs' import { Footer } from '@/features/home/components/footer' +import { Link } from 'react-router-dom' const plans = [ { @@ -130,7 +131,8 @@ const owners = [ { name: 'Júlia Galhardi Cerqueira', image: 'https://d1b8zs4rmj3xdl.cloudfront.net/julia.jpeg', - role: 'Estagiária' + role: 'Estagiária', + linkedin: 'https://www.linkedin.com/in/j%C3%BAliacerqueira/' }, { name: 'Vinícius Berti', @@ -255,14 +257,16 @@ export function HomePage() { transition={transition} variants={variants} > - - + + + - + @@ -444,9 +448,9 @@ export function HomePage() { variants={variants} > {owners.map((owner, index) => ( - - + ))} From d290cea77f5c1bf69263ab5af79a993f8e9e28da Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 11:30:54 -0300 Subject: [PATCH 07/24] refactor: update color variables for improved contrast and consistency in light and dark themes --- src/shared/styles/global.css | 166 ++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 82 deletions(-) diff --git a/src/shared/styles/global.css b/src/shared/styles/global.css index 65c1036..1cae253 100644 --- a/src/shared/styles/global.css +++ b/src/shared/styles/global.css @@ -4,101 +4,103 @@ :root { --radius: 0.625rem; - /* neutros de fundo / texto (modo claro: mais claro e contrastante) */ - --background: oklch(0.995 0.02 280); /* quase branco com leve toque violeta */ - --foreground: oklch(0.15 0.07 280); /* texto roxo escuro para contraste */ + /* neutros de fundo / texto (modo claro) */ + --background: oklch(0.995 0.02 280); + --foreground: oklch(0.15 0.07 280); /* cards e popovers */ - --card: oklch(0.995 0.02 280); /* quase branco */ + --card: oklch(0.995 0.02 280); --card-foreground: oklch(0.15 0.07 280); --popover: oklch(0.98 0.02 280); --popover-foreground: oklch(0.15 0.07 280); - /* cores principais – roxo brilhante */ - --primary: oklch(0.5 0.2 280); /* roxo vivo e claro */ - --primary-foreground: oklch(0.995 0.02 280); /* texto quase branco */ + /* cores principais */ + --primary: oklch(0.5 0.2 280); + --primary-foreground: oklch(0.995 0.02 280); /* secundárias e neutras */ - --secondary: oklch(0.95 0.05 280); - --secondary-foreground: oklch(0.5 0.2 280); - --muted: oklch(0.97 0.03 280); - --muted-foreground: oklch(0.6 0.05 280); + --secondary: oklch(0.92 0.05 280); + --secondary-foreground: oklch(0.2 0.15 280); + --muted: oklch(0.94 0.03 280); + --muted-foreground: oklch(0.45 0.05 280); /* acentos e destrutivo */ - --accent: oklch(0.9 0.15 280); - --accent-foreground: oklch(0.5 0.2 280); - --destructive: oklch(0.6338 0.1801 15.35); + --accent: oklch(0.88 0.12 280); + --accent-foreground: oklch(0.2 0.15 280); + --destructive: oklch(0.55 0.22 25); + --destructive-foreground: oklch(0.98 0.02 280); /* bordas, inputs e foco */ - --border: oklch(0.9 0.01 280); - --input: oklch(0.9 0.01 280); - --ring: oklch(0.75 0.02 280); - - /* paleta para charts – cores destacadas */ - --chart-1: oklch(0.7 0.25 260); - --chart-2: oklch(0.65 0.2 280); - --chart-3: oklch(0.6 0.15 300); - --chart-4: oklch(0.75 0.22 280); - --chart-5: oklch(0.7 0.18 300); - - /* sidebar (modo claro aprimorado) */ - --sidebar: oklch(0.98 0.02 280); - --sidebar-foreground: oklch(0.15 0.07 280); + --border: oklch(0.88 0.02 280); + --input: oklch(0.88 0.02 280); + --ring: oklch(0.5 0.2 280); + + /* charts */ + --chart-1: oklch(0.65 0.25 260); + --chart-2: oklch(0.6 0.22 280); + --chart-3: oklch(0.55 0.18 300); + --chart-4: oklch(0.7 0.24 280); + --chart-5: oklch(0.65 0.2 300); + + /* sidebar */ + --sidebar: oklch(0.97 0.02 280); + --sidebar-foreground: oklch(0.2 0.1 280); --sidebar-primary: oklch(0.5 0.2 280); --sidebar-primary-foreground: oklch(0.995 0.02 280); - --sidebar-accent: oklch(0.9 0.15 280); - --sidebar-accent-foreground: oklch(0.5 0.2 280); - --sidebar-border: oklch(0.9 0.01 280); - --sidebar-ring: oklch(0.75 0.02 280); + --sidebar-accent: oklch(0.88 0.12 280); + --sidebar-accent-foreground: oklch(0.2 0.15 280); + --sidebar-border: oklch(0.88 0.02 280); + --sidebar-ring: oklch(0.5 0.2 280); } .dark { - /* fundo escuro profundo e texto claro */ - --background: oklch(0.15 0.08 280); - --foreground: oklch(0.9 0.02 280); - - /* cards e popovers */ - --card: oklch(0.3 0.05 280); - --card-foreground: oklch(0.9 0.02 280); - --popover: oklch(0.35 0.06 280); - --popover-foreground: oklch(0.9 0.02 280); - - /* principais */ - --primary: oklch(0.7 0.05 280); - --primary-foreground: oklch(0.25 0.12 280); - - /* secundárias e neutras */ - --secondary: oklch(0.35 0.07 280); - --secondary-foreground: oklch(0.9 0.02 280); - --muted: oklch(0.35 0.07 280); - --muted-foreground: oklch(0.7 0.02 280); - - /* acento e destrutivo */ - --accent: oklch(0.5 0.08 280); - --accent-foreground: oklch(0.9 0.02 280); - --destructive: oklch(0.6 0.2 300); - - /* bordas, inputs e foco */ - --border: oklch(0.25 0 280 / 10%); - --input: oklch(0.25 0 280 / 15%); - --ring: oklch(0.5 0.02 280); - - /* charts */ - --chart-1: oklch(0.4 0.15 260); - --chart-2: oklch(0.5 0.12 280); - --chart-3: oklch(0.55 0.1 300); - --chart-4: oklch(0.38 0.2 280); - --chart-5: oklch(0.45 0.12 300); - - /* sidebar */ - --sidebar: oklch(0.3 0.05 280); - --sidebar-foreground: oklch(0.9 0.02 280); - --sidebar-primary: oklch(0.4 0.15 260); - --sidebar-primary-foreground: oklch(0.9 0.02 280); - --sidebar-accent: oklch(0.35 0.07 280); - --sidebar-accent-foreground: oklch(0.9 0.02 280); - --sidebar-border: oklch(0.25 0 280 / 10%); - --sidebar-ring: oklch(0.45 0.02 280); + /* fundo escuro e texto claro com melhor contraste */ + --background: oklch(0.15 0.04 280); + --foreground: oklch(0.98 0.005 280); + + /* cards e popovers com contraste aprimorado */ + --card: oklch(0.2 0.04 280); + --card-foreground: oklch(0.98 0.005 280); + --popover: oklch(0.18 0.04 280); + --popover-foreground: oklch(0.98 0.005 280); + + /* cores principais com melhor visibilidade */ + --primary: oklch(0.7 0.18 280); + --primary-foreground: oklch(0.98 0.005 280); + + /* secundárias e neutras otimizadas */ + --secondary: oklch(0.28 0.05 280); + --secondary-foreground: oklch(0.98 0.005 280); + --muted: oklch(0.25 0.04 280); + --muted-foreground: oklch(0.75 0.02 280); + + /* acento e destrutivo com contraste adequado */ + --accent: oklch(0.35 0.08 280); + --accent-foreground: oklch(0.98 0.005 280); + --destructive: oklch(0.6 0.25 25); + --destructive-foreground: oklch(0.98 0.005 280); + + /* bordas, inputs e foco mais visíveis */ + --border: oklch(0.35 0.04 280); + --input: oklch(0.35 0.04 280); + --ring: oklch(0.7 0.18 280); + + /* charts com cores distintas e visíveis */ + --chart-1: oklch(0.65 0.2 260); + --chart-2: oklch(0.7 0.18 280); + --chart-3: oklch(0.75 0.15 300); + --chart-4: oklch(0.63 0.22 280); + --chart-5: oklch(0.68 0.18 300); + + /* sidebar otimizada */ + --sidebar: oklch(0.18 0.04 280); + --sidebar-foreground: oklch(0.98 0.005 280); + --sidebar-primary: oklch(0.7 0.18 280); + --sidebar-primary-foreground: oklch(0.98 0.005 280); + --sidebar-accent: oklch(0.28 0.05 280); + --sidebar-accent-foreground: oklch(0.98 0.005 280); + --sidebar-border: oklch(0.35 0.04 280); + --sidebar-ring: oklch(0.7 0.18 280); } @theme { @@ -139,6 +141,7 @@ --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); @@ -157,17 +160,16 @@ 0%, 100% { transform: translateX(0); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); /* saída rápida */ + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); } 50% { - transform: translateX(25%); /* “salto” → */ - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); /* volta suave */ + transform: translateX(25%); + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); } } } html { scroll-behavior: smooth; - /* background-color: var(--background); */ font-family: 'Geist', sans-serif; } From 931ed8c82d9e84727817f01faf20905861946e30 Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 11:30:59 -0300 Subject: [PATCH 08/24] refactor: update sidebar component styles for improved border definition --- src/shared/components/sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/sidebar.tsx b/src/shared/components/sidebar.tsx index 0c223fd..ada4dce 100644 --- a/src/shared/components/sidebar.tsx +++ b/src/shared/components/sidebar.tsx @@ -46,7 +46,7 @@ export function Sidebar() { return ( Date: Thu, 23 Oct 2025 11:31:52 -0300 Subject: [PATCH 09/24] fix: improve error handling in create base pipeline process --- src/features/user-area/pages/create-base-pipeline.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/user-area/pages/create-base-pipeline.tsx b/src/features/user-area/pages/create-base-pipeline.tsx index a74fdab..935fedc 100644 --- a/src/features/user-area/pages/create-base-pipeline.tsx +++ b/src/features/user-area/pages/create-base-pipeline.tsx @@ -25,6 +25,7 @@ import { CreateBaseData } from '../types/create-base-schema' import { useCreateKnowledgeBaseMutation } from '../hooks/use-kb' import { knowledgeBaseService } from '@/services/knowledge-base' import { bucketName } from '@/shared/enviroment' +import { AxiosError } from 'axios' type PipelineStep = { id: string @@ -257,7 +258,6 @@ export function CreateBasePipeline() { navigate('/bases') }, 5000) } catch (error) { - console.error('Pipeline error:', error) const currentStepIndex = pipeline.currentStep updateStepStatus( currentStepIndex, @@ -265,7 +265,9 @@ export function CreateBasePipeline() { 'Erro inesperado durante o processo' ) toast.error( - 'Erro ao criar base de conhecimento: ' + (error as Error).message + 'Erro ao criar base de conhecimento: ' + + (error as AxiosError<{ details: string }>).response?.data?.details || + (error as Error).message ) } } From f9736cd2affd7e8e6e247032f1dc3de2f6d777da Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 11:32:09 -0300 Subject: [PATCH 10/24] refactor: update styles for knowledge base detail modal for improved readability and consistency --- .../knowledge-base-detail-modal.tsx | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/src/features/user-area/components/knowledge-base-detail-modal.tsx b/src/features/user-area/components/knowledge-base-detail-modal.tsx index 3145035..7d851cb 100644 --- a/src/features/user-area/components/knowledge-base-detail-modal.tsx +++ b/src/features/user-area/components/knowledge-base-detail-modal.tsx @@ -274,7 +274,7 @@ echo $data['response'];` - + {knowledgeBase.displayName} {knowledgeBase.status} @@ -287,31 +287,35 @@ echo $data['response'];` {/* Informações gerais */}
- - Criada em: - -

{knowledgeBase.createdAt.toLocaleDateString('pt-BR')}

+ Criada em: +

+ {knowledgeBase.createdAt.toLocaleDateString('pt-BR')} +

- + Última modificação: -

{knowledgeBase.updatedAt.toLocaleDateString('pt-BR')}

+

+ {knowledgeBase.updatedAt.toLocaleDateString('pt-BR')} +

- + Total de arquivos: -

+

{knowledgeBase.files.length} arquivo {knowledgeBase.files.length !== 1 ? 's' : ''}

- + Tamanho total: -

{knowledgeBase.totalSizeMB.toFixed(1)} MB

+

+ {knowledgeBase.totalSizeMB.toFixed(1)} MB +

@@ -319,9 +323,9 @@ echo $data['response'];` {/* API Keys */}
-

API Keys

+

API Keys

{knowledgeBase.keys.length === 0 ? ( -
+
Nenhuma chave de API encontrada
) : ( @@ -337,12 +341,12 @@ echo $data['response'];` className="hover:bg-muted/50 border-border rounded-lg border-1 p-4 transition-colors" >
- + {key.kbKeyAlias}
-
+
{isVisible ? key.kbKey : maskKey(key.kbKey)}
@@ -386,13 +390,15 @@ echo $data['response'];` className="hover:bg-muted/50 flex w-full items-center justify-between rounded-lg p-2 transition-colors" >
- -

Exemplos de Código

+ +

+ Exemplos de Código +

{isCodeSectionExpanded ? ( - + ) : ( - + )} @@ -405,7 +411,7 @@ echo $data['response'];` >
{knowledgeBase.keys.length === 0 ? ( -
+
Crie uma API Key para ver os exemplos de código
) : ( @@ -418,9 +424,11 @@ echo $data['response'];` onClick={() => setSelectedLanguage(lang.id)} size="sm" variant={ - selectedLanguage === lang.id ? 'default' : 'outline' + selectedLanguage === lang.id + ? 'secondary' + : 'outline' } - className="flex items-center gap-1.5" + className="text-foreground flex items-center gap-1.5" > {lang.icon} {lang.name} @@ -468,12 +476,14 @@ echo $data['response'];` {/* Lista de arquivos */}
-

Arquivos

+

+ Arquivos +

{knowledgeBase.files.length === 0 ? ( -
+

Nenhum arquivo encontrado

@@ -499,12 +509,12 @@ echo $data['response'];`

{file.fileName}

-

+

{formatFileSize(file.sizeMB)}

@@ -528,7 +538,11 @@ echo $data['response'];` {/* Botões de ação */}
- + - + @@ -278,13 +293,18 @@ export function HomePage() { whileInView="visible" transition={{ staggerChildren: 0.04 }} > - - Como o Knowly Funciona - +

+ Como o Knowly Funciona +

+

+ Três passos simples para transformar seus documentos em IA +

+ ( - +
+ {idx + 1} +
+ {content.title}
@@ -306,15 +329,15 @@ export function HomePage() {
-

+

{content.content}

{idx < process.length - 1 && window.innerWidth >= 768 && ( - + )} {idx < process.length - 1 && window.innerWidth < 768 && ( - + )} ))} @@ -328,13 +351,18 @@ export function HomePage() { whileInView="visible" transition={{ staggerChildren: 0.04 }} > - - Preços - +

+ Preços Simples e Transparentes +

+

+ Escolha o plano perfeito para suas necessidades +

+ ( - - {plan.title} - - {plan.price}{' '} - - p/mês - + {idx === 1 && ( +
+ Mais Popular +
+ )} + + + {plan.title} + + + {plan.price} + + p/mês + - + {plan.features.map((feature, idx) => (
- +
+ +
{feature}
))}
- - + +
))} @@ -385,19 +430,24 @@ export function HomePage() { }} variants={variants} > - - FAQ - +

+ Perguntas Frequentes +

+

+ Tudo o que você precisa saber sobre o Knowly +

+
- + {faqs.map((faq, i) => ( {faq.content} @@ -408,7 +458,7 @@ export function HomePage() { Acesse a página completa de FAQ
+ + {/* Sub-items */} + {hasSubItems && isExpanded && (!isCollapsed || isMobileOpen) && ( + + {item.subItems?.map((subItem) => { + const isSubItemActive = location.pathname === subItem.path + return ( + + {subItem.label} + + ) + })} + + )} ) })} From 068bf4d53ce87ea962b04a15d86ded15d1262939 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:27:42 +0000 Subject: [PATCH 18/24] Update API code snippets across all integration docs to match actual API format Co-authored-by: enzosakamoto <98707474+enzosakamoto@users.noreply.github.com> --- src/features/docs/pages/getting-started.tsx | 49 ++++++++++--------- src/features/docs/pages/integration-api.tsx | 9 ++-- .../docs/pages/integration-instagram.tsx | 18 +++---- .../docs/pages/integration-whatsapp.tsx | 18 +++---- 4 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/features/docs/pages/getting-started.tsx b/src/features/docs/pages/getting-started.tsx index 310a6dd..1dfbbd3 100644 --- a/src/features/docs/pages/getting-started.tsx +++ b/src/features/docs/pages/getting-started.tsx @@ -220,12 +220,12 @@ export function GettingStartedPage() { {pre({ children: `# Exemplo usando cURL -curl -X POST https://api.knowly.ai/v1/query \\ +curl -X POST https://api.knowly.dev.br/chat \\ -H "Content-Type: application/json" \\ - -H "Authorization: Bearer SUA_CHAVE_DE_API" \\ -d '{ - "prompt": "Qual é o horário de atendimento?", - "baseId": "SUA_BASE_ID" + "kb_key": "knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd", + "model": "MISTRAL_SMALL", + "prompt": "Qual é o horário de atendimento?" }'` })} @@ -238,20 +238,19 @@ curl -X POST https://api.knowly.ai/v1/query \\ })} {pre({ - children: `const response = await fetch('https://api.knowly.ai/v1/query', { + children: `fetch('https://api.knowly.dev.br/chat', { method: 'POST', headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer SUA_CHAVE_DE_API' + 'Content-Type': 'application/json' }, body: JSON.stringify({ - prompt: 'Qual é o horário de atendimento?', - baseId: 'SUA_BASE_ID' + kb_key: 'knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd', + model: 'MISTRAL_SMALL', + prompt: 'Qual é o horário de atendimento?' }) -}); - -const data = await response.json(); -console.log(data.response);` +}) + .then(response => response.json()) + .then(data => console.log(data.response));` })} {p({ @@ -265,18 +264,20 @@ console.log(data.response);` {pre({ children: `import requests -url = "https://api.knowly.ai/v1/query" -headers = { - "Content-Type": "application/json", - "Authorization": "Bearer SUA_CHAVE_DE_API" -} -data = { - "prompt": "Qual é o horário de atendimento?", - "baseId": "SUA_BASE_ID" -} +response = requests.post( + 'https://api.knowly.dev.br/chat', + headers={ + 'Content-Type': 'application/json', + }, + json={ + 'kb_key': 'knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd', + 'model': 'MISTRAL_SMALL', + 'prompt': 'Qual é o horário de atendimento?' + } +) -response = requests.post(url, json=data, headers=headers) -print(response.json()["response"])` +data = response.json() +print(data['response'])` })} {h2({ children: 'Próximos Passos' })} diff --git a/src/features/docs/pages/integration-api.tsx b/src/features/docs/pages/integration-api.tsx index 45bc9b7..52f5d09 100644 --- a/src/features/docs/pages/integration-api.tsx +++ b/src/features/docs/pages/integration-api.tsx @@ -56,13 +56,14 @@ export function IntegrationApiPage() { {h2({ children: 'Exemplo de requisição' })} {pre({ - children: `POST https://api.knowly.ai/v1/query + children: `POST https://api.knowly.dev.br/chat Headers: - Authorization: Bearer SUA_CHAVE_DE_API + Content-Type: application/json Body: { - "prompt": "Como faço para atualizar meus dados cadastrais?", - "baseId": "sua-base-id" + "kb_key": "knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd", + "model": "MISTRAL_SMALL", + "prompt": "Como faço para atualizar meus dados cadastrais?" }` })} diff --git a/src/features/docs/pages/integration-instagram.tsx b/src/features/docs/pages/integration-instagram.tsx index 4dfcb77..82c23fa 100644 --- a/src/features/docs/pages/integration-instagram.tsx +++ b/src/features/docs/pages/integration-instagram.tsx @@ -162,15 +162,15 @@ app.post('/webhook/instagram', async (req, res) => { if (!messageText) continue; // Consultar API do Knowly - const knowlyResponse = await fetch('https://api.knowly.ai/v1/query', { + const knowlyResponse = await fetch('https://api.knowly.dev.br/chat', { method: 'POST', headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer SUA_CHAVE_DE_API' + 'Content-Type': 'application/json' }, body: JSON.stringify({ - prompt: messageText, - baseId: 'SUA_BASE_ID' + kb_key: 'knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd', + model: 'MISTRAL_SMALL', + prompt: messageText }) }); @@ -245,14 +245,14 @@ def instagram_webhook(): # Consultar API do Knowly knowly_response = requests.post( - 'https://api.knowly.ai/v1/query', + 'https://api.knowly.dev.br/chat', headers={ 'Content-Type': 'application/json', - 'Authorization': 'Bearer SUA_CHAVE_DE_API' }, json={ - 'prompt': message_text, - 'baseId': 'SUA_BASE_ID' + 'kb_key': 'knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd', + 'model': 'MISTRAL_SMALL', + 'prompt': message_text } ) diff --git a/src/features/docs/pages/integration-whatsapp.tsx b/src/features/docs/pages/integration-whatsapp.tsx index f2aed4d..8b6da8e 100644 --- a/src/features/docs/pages/integration-whatsapp.tsx +++ b/src/features/docs/pages/integration-whatsapp.tsx @@ -117,15 +117,15 @@ app.post('/webhook/whatsapp', async (req, res) => { const { from, message } = req.body; // Consultar API do Knowly - const knowlyResponse = await fetch('https://api.knowly.ai/v1/query', { + const knowlyResponse = await fetch('https://api.knowly.dev.br/chat', { method: 'POST', headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer SUA_CHAVE_DE_API' + 'Content-Type': 'application/json' }, body: JSON.stringify({ - prompt: message, - baseId: 'SUA_BASE_ID' + kb_key: 'knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd', + model: 'MISTRAL_SMALL', + prompt: message }) }); @@ -177,14 +177,14 @@ def whatsapp_webhook(): # Consultar API do Knowly knowly_response = requests.post( - 'https://api.knowly.ai/v1/query', + 'https://api.knowly.dev.br/chat', headers={ 'Content-Type': 'application/json', - 'Authorization': 'Bearer SUA_CHAVE_DE_API' }, json={ - 'prompt': message, - 'baseId': 'SUA_BASE_ID' + 'kb_key': 'knowly_c81a7410-4c69-45e2-b6c6-79205c3a99fd', + 'model': 'MISTRAL_SMALL', + 'prompt': message } ) From d98ee79a361856f65875e4f788fd2479ce399f55 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:50:33 +0000 Subject: [PATCH 19/24] Add Next Steps documentation page with roadmap for multi-tenant chatbot system Co-authored-by: enzosakamoto <98707474+enzosakamoto@users.noreply.github.com> --- src/features/docs/pages/next-steps.tsx | 319 +++++++++++++++++++++++++ src/routes/app-routes.tsx | 2 + src/shared/components/docs-sidebar.tsx | 8 +- 3 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 src/features/docs/pages/next-steps.tsx diff --git a/src/features/docs/pages/next-steps.tsx b/src/features/docs/pages/next-steps.tsx new file mode 100644 index 0000000..bee3a1b --- /dev/null +++ b/src/features/docs/pages/next-steps.tsx @@ -0,0 +1,319 @@ +import { motion } from 'framer-motion' +import { h1, h2, p, ul, li } from '@/shared/components/typography' + +export function NextStepsPage() { + return ( + + {h1({ children: 'Próximos Passos' })} + + {p({ + children: ( + <> + Estamos constantemente inovando e desenvolvendo novas funcionalidades + para tornar o Knowly ainda mais poderoso e fácil de usar. Confira o + que estamos planejando para o futuro da plataforma. + + ) + })} + + {h2({ children: 'Roadmap de Novas Features' })} + + {h2({ children: '🤖 Sistema de Chatbot Multi-Tenant (Em Desenvolvimento)' })} + {p({ + children: ( + <> + Nossa próxima grande funcionalidade é um{' '} + sistema de chatbot multi-tenant completo que + simplificará drasticamente a integração do Knowly em suas + plataformas. + + ) + })} + + {p({ + children: ( + <> + O que é? + + ) + })} + {p({ + children: ( + <> + Um sistema de chatbot gerenciado e hospedado pela Knowly que você + poderá integrar diretamente em seus canais de atendimento, sem + precisar se preocupar com infraestrutura, webhooks ou gerenciamento + de APIs. + + ) + })} + + {p({ + children: ( + <> + Benefícios: + + ) + })} + {ul({ + children: ( + <> + {li({ + children: ( + <> + Zero configuração de infraestrutura: Não + será mais necessário criar e manter servidores para consumir + as APIs das bases de conhecimento + + ) + })} + {li({ + children: ( + <> + Integração simplificada: Basta conectar suas + contas do WhatsApp Business, Instagram, ou outras plataformas + diretamente no painel do Knowly + + ) + })} + {li({ + children: ( + <> + Gerenciamento centralizado: Controle todos + os seus chatbots de diferentes plataformas em um único lugar + + ) + })} + {li({ + children: ( + <> + Multi-tenant: Suporte a múltiplas empresas e + organizações com isolamento completo de dados + + ) + })} + {li({ + children: ( + <> + Escalabilidade automática: Nossa + infraestrutura cuida de todo o dimensionamento automaticamente + + ) + })} + {li({ + children: ( + <> + Monitoramento em tempo real: Acompanhe + métricas de conversas, satisfação e performance dos chatbots + + ) + })} + + ) + })} + + {p({ + children: ( + <> + Plataformas suportadas: + + ) + })} + {ul({ + children: ( + <> + {li({ children: '✅ WhatsApp Business' })} + {li({ children: '✅ Instagram Direct Messages' })} + {li({ children: '✅ Website (Widget de chat embarcado)' })} + {li({ children: '🔜 Facebook Messenger' })} + {li({ children: '🔜 Telegram' })} + {li({ children: '🔜 Slack' })} + {li({ + children: '🔜 E-commerce (integração com Shopify, WooCommerce, etc.)' + })} + + ) + })} + + {p({ + children: ( + <> + Como funcionará: + + ) + })} + {ul({ + children: ( + <> + {li({ + children: ( + <> + Passo 1: No painel do Knowly, acesse a seção + "Chatbots" + + ) + })} + {li({ + children: ( + <> + Passo 2: Selecione a plataforma que deseja + integrar (WhatsApp, Instagram, Website, etc.) + + ) + })} + {li({ + children: ( + <> + Passo 3: Conecte sua conta seguindo o fluxo + de autenticação simplificado + + ) + })} + {li({ + children: ( + <> + Passo 4: Associe uma ou mais bases de + conhecimento ao chatbot + + ) + })} + {li({ + children: ( + <> + Passo 5: Configure opções como horário de + atendimento, mensagens de boas-vindas, e regras de escalação + + ) + })} + {li({ + children: ( + <> + Passo 6: Ative o chatbot e comece a receber + atendimentos automatizados! + + ) + })} + + ) + })} + + {h2({ children: '📊 Análises e Insights Avançados' })} + {p({ + children: ( + <> + Além do chatbot multi-tenant, estamos desenvolvendo um sistema + completo de análises para você entender melhor como seus clientes + interagem com a IA: + + ) + })} + {ul({ + children: ( + <> + {li({ + children: + 'Dashboard com métricas de performance das bases de conhecimento' + })} + {li({ + children: 'Análise de sentimento das conversas' + })} + {li({ + children: 'Identificação de perguntas frequentes não respondidas' + })} + {li({ + children: + 'Sugestões de melhoria para otimizar suas bases de conhecimento' + })} + {li({ + children: 'Relatórios de satisfação do cliente' + })} + + ) + })} + + {h2({ children: '🔧 Customização Avançada de Respostas' })} + {ul({ + children: ( + <> + {li({ + children: + 'Templates personalizáveis para diferentes tipos de perguntas' + })} + {li({ + children: 'Tom de voz configurável (formal, casual, técnico, etc.)' + })} + {li({ + children: 'Regras de negócio customizadas para tratamento especial de certos tópicos' + })} + {li({ + children: 'Respostas com rich media (imagens, vídeos, carrosséis)' + })} + + ) + })} + + {h2({ children: '🌐 Suporte Multilíngue' })} + {ul({ + children: ( + <> + {li({ + children: 'Detecção automática do idioma do usuário' + })} + {li({ + children: 'Respostas em múltiplos idiomas a partir da mesma base de conhecimento' + })} + {li({ + children: 'Tradução automática de documentos' + })} + + ) + })} + + {h2({ children: '🔗 Integrações com CRMs e Ferramentas' })} + {ul({ + children: ( + <> + {li({ + children: 'Integração nativa com Salesforce, HubSpot, Pipedrive' + })} + {li({ + children: 'Webhooks personalizados para integração com qualquer sistema' + })} + {li({ + children: 'API de eventos para rastreamento de conversas' + })} + + ) + })} + + {h2({ children: 'Fique por Dentro' })} + {p({ + children: ( + <> + Estamos trabalhando arduamente para trazer essas funcionalidades o + mais rápido possível. O sistema de chatbot multi-tenant está em fase + avançada de desenvolvimento e será lançado em breve! + + ) + })} + + {p({ + children: ( + <> + Quer ser notificado quando essas features forem lançadas? Entre em{' '} + + contato conosco + {' '} + ou acompanhe nossas atualizações. + + ) + })} + + ) +} + +export default NextStepsPage diff --git a/src/routes/app-routes.tsx b/src/routes/app-routes.tsx index b243982..6408b47 100644 --- a/src/routes/app-routes.tsx +++ b/src/routes/app-routes.tsx @@ -30,6 +30,7 @@ import { IntegrationWhatsAppPage } from '@/features/docs/pages/integration-whats import { IntegrationInstagramPage } from '@/features/docs/pages/integration-instagram' import { PlaygroundTestingPage } from '@/features/docs/pages/playground-testing' import { SubscriptionManagementPage as DocsSubscriptionPage } from '@/features/docs/pages/subscription-management' +import { NextStepsPage } from '@/features/docs/pages/next-steps' export function AppRoutes() { return ( @@ -98,6 +99,7 @@ export function AppRoutes() { path="docs/subscription-management" element={} /> + } /> diff --git a/src/shared/components/docs-sidebar.tsx b/src/shared/components/docs-sidebar.tsx index 3c8c3cf..6a569a8 100644 --- a/src/shared/components/docs-sidebar.tsx +++ b/src/shared/components/docs-sidebar.tsx @@ -12,7 +12,8 @@ import { X, Rocket, ChevronDown, - ChevronRight + ChevronRight, + TrendingUp } from 'lucide-react' import { useState, useEffect } from 'react' import { Button } from '@/shared/components/button' @@ -78,6 +79,11 @@ const docsItems: DocsSidebarItem[] = [ label: 'Gerenciar Assinatura', icon: CreditCard, path: '/docs/subscription-management' + }, + { + label: 'Próximos Passos', + icon: TrendingUp, + path: '/docs/next-steps' } ] From a4569b4797d8b860abdeb4a3a56a5bfc52c369d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 20:02:08 +0000 Subject: [PATCH 20/24] Remove emojis from Next Steps documentation page Co-authored-by: enzosakamoto <98707474+enzosakamoto@users.noreply.github.com> --- src/features/docs/pages/next-steps.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/features/docs/pages/next-steps.tsx b/src/features/docs/pages/next-steps.tsx index bee3a1b..9645a4d 100644 --- a/src/features/docs/pages/next-steps.tsx +++ b/src/features/docs/pages/next-steps.tsx @@ -22,7 +22,7 @@ export function NextStepsPage() { {h2({ children: 'Roadmap de Novas Features' })} - {h2({ children: '🤖 Sistema de Chatbot Multi-Tenant (Em Desenvolvimento)' })} + {h2({ children: 'Sistema de Chatbot Multi-Tenant (Em Desenvolvimento)' })} {p({ children: ( <> @@ -126,14 +126,14 @@ export function NextStepsPage() { {ul({ children: ( <> - {li({ children: '✅ WhatsApp Business' })} - {li({ children: '✅ Instagram Direct Messages' })} - {li({ children: '✅ Website (Widget de chat embarcado)' })} - {li({ children: '🔜 Facebook Messenger' })} - {li({ children: '🔜 Telegram' })} - {li({ children: '🔜 Slack' })} + {li({ children: 'WhatsApp Business' })} + {li({ children: 'Instagram Direct Messages' })} + {li({ children: 'Website (Widget de chat embarcado)' })} + {li({ children: 'Facebook Messenger (Em breve)' })} + {li({ children: 'Telegram (Em breve)' })} + {li({ children: 'Slack (Em breve)' })} {li({ - children: '🔜 E-commerce (integração com Shopify, WooCommerce, etc.)' + children: 'E-commerce (integração com Shopify, WooCommerce, etc.) (Em breve)' })} ) @@ -201,7 +201,7 @@ export function NextStepsPage() { ) })} - {h2({ children: '📊 Análises e Insights Avançados' })} + {h2({ children: 'Análises e Insights Avançados' })} {p({ children: ( <> @@ -235,7 +235,7 @@ export function NextStepsPage() { ) })} - {h2({ children: '🔧 Customização Avançada de Respostas' })} + {h2({ children: 'Customização Avançada de Respostas' })} {ul({ children: ( <> @@ -256,7 +256,7 @@ export function NextStepsPage() { ) })} - {h2({ children: '🌐 Suporte Multilíngue' })} + {h2({ children: 'Suporte Multilíngue' })} {ul({ children: ( <> @@ -273,7 +273,7 @@ export function NextStepsPage() { ) })} - {h2({ children: '🔗 Integrações com CRMs e Ferramentas' })} + {h2({ children: 'Integrações com CRMs e Ferramentas' })} {ul({ children: ( <> From eb09863545db10528739dc140a682103da68eb85 Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 17:15:04 -0300 Subject: [PATCH 21/24] refactor: improve formatting and readability in documentation pages --- .../docs/pages/integration-instagram.tsx | 38 +++++------ .../docs/pages/integration-whatsapp.tsx | 28 ++++----- src/features/docs/pages/next-steps.tsx | 33 +++++----- src/shared/components/docs-sidebar.tsx | 63 ++++++++++--------- 4 files changed, 79 insertions(+), 83 deletions(-) diff --git a/src/features/docs/pages/integration-instagram.tsx b/src/features/docs/pages/integration-instagram.tsx index 82c23fa..234c1ab 100644 --- a/src/features/docs/pages/integration-instagram.tsx +++ b/src/features/docs/pages/integration-instagram.tsx @@ -13,9 +13,9 @@ export function IntegrationInstagramPage() { {p({ children: ( <> - Automatize respostas nas mensagens diretas (DMs) do Instagram usando a - API do Knowly para responder automaticamente perguntas dos usuários - com base na sua base de conhecimento. + Automatize respostas nas mensagens diretas (DMs) do Instagram usando + a API do Knowly para responder automaticamente perguntas dos + usuários com base na sua base de conhecimento. ) })} @@ -50,9 +50,7 @@ export function IntegrationInstagramPage() { })} {li({ children: ( - <> - Conecte sua conta do Instagram a uma Página do Facebook - + <>Conecte sua conta do Instagram a uma Página do Facebook ) })} {li({ @@ -66,7 +64,8 @@ export function IntegrationInstagramPage() { children: ( <> Configure as permissões necessárias:{' '} - instagram_basic, instagram_manage_messages + instagram_basic,{' '} + instagram_manage_messages ) })} @@ -301,16 +300,13 @@ def verify_webhook(): ) })} {li({ - children: ( - <> - Verifique se o webhook está recebendo a notificação - - ) + children: <>Verifique se o webhook está recebendo a notificação })} {li({ children: ( <> - Confirme que a API do Knowly está retornando respostas adequadas + Confirme que a API do Knowly está retornando respostas + adequadas ) })} @@ -333,8 +329,8 @@ def verify_webhook(): {li({ children: ( <> - Respostas rápidas: Configure botões de resposta - rápida para melhorar a experiência + Respostas rápidas: Configure botões de + resposta rápida para melhorar a experiência ) })} @@ -381,8 +377,8 @@ def verify_webhook(): {li({ children: ( <> - Escalação: Ofereça opção de falar com atendente - humano quando necessário + Escalação: Ofereça opção de falar com + atendente humano quando necessário ) })} @@ -397,16 +393,16 @@ def verify_webhook(): {li({ children: ( <> - Monitoramento: Acompanhe métricas de satisfação - e qualidade das respostas + Monitoramento: Acompanhe métricas de + satisfação e qualidade das respostas ) })} {li({ children: ( <> - Políticas do Instagram: Respeite as diretrizes - de mensagens automatizadas da plataforma + Políticas do Instagram: Respeite as + diretrizes de mensagens automatizadas da plataforma ) })} diff --git a/src/features/docs/pages/integration-whatsapp.tsx b/src/features/docs/pages/integration-whatsapp.tsx index 8b6da8e..7e43943 100644 --- a/src/features/docs/pages/integration-whatsapp.tsx +++ b/src/features/docs/pages/integration-whatsapp.tsx @@ -52,8 +52,8 @@ export function IntegrationWhatsAppPage() { {li({ children: ( <> - Provedores terceiros - Como Twilio, 360Dialog, - ou MessageBird + Provedores terceiros - Como Twilio, + 360Dialog, ou MessageBird ) })} @@ -216,29 +216,22 @@ def send_whatsapp_message(to, message): <> {li({ children: ( - <> - Envie uma mensagem para seu número do WhatsApp Business - + <>Envie uma mensagem para seu número do WhatsApp Business ) })} {li({ - children: ( - <> - Verifique se o webhook está recebendo a mensagem - - ) + children: <>Verifique se o webhook está recebendo a mensagem })} {li({ children: ( - <> - Confirme que a API do Knowly está retornando respostas - + <>Confirme que a API do Knowly está retornando respostas ) })} {li({ children: ( <> - Valide se as respostas estão sendo enviadas de volta ao cliente + Valide se as respostas estão sendo enviadas de volta ao + cliente ) })} @@ -289,9 +282,10 @@ def send_whatsapp_message(to, message): {p({ children: ( <> - Com essa integração, seu WhatsApp Business responderá automaticamente - aos clientes usando o conhecimento da sua base, proporcionando - atendimento 24/7 e reduzindo a carga de trabalho da equipe. + Com essa integração, seu WhatsApp Business responderá + automaticamente aos clientes usando o conhecimento da sua base, + proporcionando atendimento 24/7 e reduzindo a carga de trabalho da + equipe. ) })} diff --git a/src/features/docs/pages/next-steps.tsx b/src/features/docs/pages/next-steps.tsx index 9645a4d..94120c8 100644 --- a/src/features/docs/pages/next-steps.tsx +++ b/src/features/docs/pages/next-steps.tsx @@ -13,9 +13,9 @@ export function NextStepsPage() { {p({ children: ( <> - Estamos constantemente inovando e desenvolvendo novas funcionalidades - para tornar o Knowly ainda mais poderoso e fácil de usar. Confira o - que estamos planejando para o futuro da plataforma. + Estamos constantemente inovando e desenvolvendo novas + funcionalidades para tornar o Knowly ainda mais poderoso e fácil de + usar. Confira o que estamos planejando para o futuro da plataforma. ) })} @@ -65,9 +65,9 @@ export function NextStepsPage() { {li({ children: ( <> - Zero configuração de infraestrutura: Não - será mais necessário criar e manter servidores para consumir - as APIs das bases de conhecimento + Zero configuração de infraestrutura: Não será + mais necessário criar e manter servidores para consumir as + APIs das bases de conhecimento ) })} @@ -83,8 +83,8 @@ export function NextStepsPage() { {li({ children: ( <> - Gerenciamento centralizado: Controle todos - os seus chatbots de diferentes plataformas em um único lugar + Gerenciamento centralizado: Controle todos os + seus chatbots de diferentes plataformas em um único lugar ) })} @@ -133,7 +133,8 @@ export function NextStepsPage() { {li({ children: 'Telegram (Em breve)' })} {li({ children: 'Slack (Em breve)' })} {li({ - children: 'E-commerce (integração com Shopify, WooCommerce, etc.) (Em breve)' + children: + 'E-commerce (integração com Shopify, WooCommerce, etc.) (Em breve)' })} ) @@ -153,7 +154,7 @@ export function NextStepsPage() { children: ( <> Passo 1: No painel do Knowly, acesse a seção - "Chatbots" + "Chatbots" ) })} @@ -244,10 +245,12 @@ export function NextStepsPage() { 'Templates personalizáveis para diferentes tipos de perguntas' })} {li({ - children: 'Tom de voz configurável (formal, casual, técnico, etc.)' + children: + 'Tom de voz configurável (formal, casual, técnico, etc.)' })} {li({ - children: 'Regras de negócio customizadas para tratamento especial de certos tópicos' + children: + 'Regras de negócio customizadas para tratamento especial de certos tópicos' })} {li({ children: 'Respostas com rich media (imagens, vídeos, carrosséis)' @@ -264,7 +267,8 @@ export function NextStepsPage() { children: 'Detecção automática do idioma do usuário' })} {li({ - children: 'Respostas em múltiplos idiomas a partir da mesma base de conhecimento' + children: + 'Respostas em múltiplos idiomas a partir da mesma base de conhecimento' })} {li({ children: 'Tradução automática de documentos' @@ -281,7 +285,8 @@ export function NextStepsPage() { children: 'Integração nativa com Salesforce, HubSpot, Pipedrive' })} {li({ - children: 'Webhooks personalizados para integração com qualquer sistema' + children: + 'Webhooks personalizados para integração com qualquer sistema' })} {li({ children: 'API de eventos para rastreamento de conversas' diff --git a/src/shared/components/docs-sidebar.tsx b/src/shared/components/docs-sidebar.tsx index 6a569a8..6ded96b 100644 --- a/src/shared/components/docs-sidebar.tsx +++ b/src/shared/components/docs-sidebar.tsx @@ -107,9 +107,7 @@ export function DocsSidebar() { function toggleExpanded(path: string) { setExpandedItems((prev) => - prev.includes(path) - ? prev.filter((p) => p !== path) - : [...prev, path] + prev.includes(path) ? prev.filter((p) => p !== path) : [...prev, path] ) } @@ -228,7 +226,7 @@ export function DocsSidebar() { const isExpanded = expandedItems.includes(item.path) const hasSubItems = item.subItems && item.subItems.length > 0 const IconComponent = item.icon - + return ( {/* Sub-items */} - {hasSubItems && isExpanded && (!isCollapsed || isMobileOpen) && ( - - {item.subItems?.map((subItem) => { - const isSubItemActive = location.pathname === subItem.path - return ( - - {subItem.label} - - ) - })} - - )} + {hasSubItems && + isExpanded && + (!isCollapsed || isMobileOpen) && ( + + {item.subItems?.map((subItem) => { + const isSubItemActive = + location.pathname === subItem.path + return ( + + {subItem.label} + + ) + })} + + )} ) })} From d9284ef23971b328ed422c163ca0e7bc36d937ca Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 18:01:39 -0300 Subject: [PATCH 22/24] fix: update anchor links in navbar to use absolute paths --- src/shared/components/navbar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/components/navbar.tsx b/src/shared/components/navbar.tsx index 0cd6bde..fef5e4f 100644 --- a/src/shared/components/navbar.tsx +++ b/src/shared/components/navbar.tsx @@ -72,12 +72,12 @@ export function Navbar() { variants={variants} > Nosso produto - + Preços From 423df2e0dabe849f5bf7bd783570d73342d34b80 Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 21:23:15 -0300 Subject: [PATCH 23/24] fix: improve error handling in message sending process --- src/features/playground/pages/playground.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features/playground/pages/playground.tsx b/src/features/playground/pages/playground.tsx index 67eb205..d9944cf 100644 --- a/src/features/playground/pages/playground.tsx +++ b/src/features/playground/pages/playground.tsx @@ -25,6 +25,7 @@ import { Badge } from '@/shared/components/badge' import { MODELS } from '@/shared/enums/models' import { useChatWithKnowledgeBaseMutation } from '@/features/playground/hooks/use-chat' import { useGetKnowledgeBaseQuery } from '@/features/user-area/hooks/use-kb' +import { AxiosError } from 'axios' type Message = { id: string @@ -111,9 +112,14 @@ export function PlaygroundPage() { } catch (error) { toast.error('Erro ao enviar mensagem. Tente novamente.') + const message = + (error as AxiosError<{ details: string }>).response?.data.details ?? + (error as Error).message ?? + '' + const errorMessage: Message = { id: (Date.now() + 1).toString(), - content: `Desculpe, ocorreu um erro ao processar sua mensagem. Tente novamente. ${(error as { details: string }).details}`, + content: `Desculpe, ocorreu um erro ao processar sua mensagem. Tente novamente. ${message}`, type: 'bot', timestamp: new Date() } From c3fc1cf06b31681844d49be82e34dc46ab06ade7 Mon Sep 17 00:00:00 2001 From: Enzo Sakamoto Date: Thu, 23 Oct 2025 21:54:10 -0300 Subject: [PATCH 24/24] feat: add loading message and improve message handling in chat --- src/features/playground/pages/playground.tsx | 44 ++++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/features/playground/pages/playground.tsx b/src/features/playground/pages/playground.tsx index d9944cf..a0d57cd 100644 --- a/src/features/playground/pages/playground.tsx +++ b/src/features/playground/pages/playground.tsx @@ -90,7 +90,14 @@ export function PlaygroundPage() { timestamp: new Date() } - setMessages((prev) => [...prev, userMessage]) + const loadingMessage: Message = { + id: 'loading', + content: 'Pensando...', + type: 'bot', + timestamp: new Date() + } + + setMessages((prev) => [...prev, userMessage, loadingMessage]) setInputValue('') try { @@ -108,7 +115,9 @@ export function PlaygroundPage() { timestamp: new Date() } - setMessages((prev) => [...prev, botMessage]) + setMessages((prev) => + prev.filter((msg) => msg.id !== 'loading').concat(botMessage) + ) } catch (error) { toast.error('Erro ao enviar mensagem. Tente novamente.') @@ -124,7 +133,9 @@ export function PlaygroundPage() { timestamp: new Date() } - setMessages((prev) => [...prev, errorMessage]) + setMessages((prev) => + prev.filter((msg) => msg.id !== 'loading').concat(errorMessage) + ) } } @@ -259,12 +270,27 @@ export function PlaygroundPage() { : 'bg-muted text-foreground' }`} > -

- {message.content} -

-

- {formatTime(message.timestamp)} -

+ {message.id === 'loading' ? ( +
+
+
+
+
+
+ + {message.content} + +
+ ) : ( + <> +

+ {message.content} +

+

+ {formatTime(message.timestamp)} +

+ + )}
{message.type === 'user' && (