From 8305abb235bae5e8ebb5cd120c41e1735c4ba7b6 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 24 Apr 2026 20:12:59 -0300 Subject: [PATCH 1/9] Scrum-56 fix: removi os dados de mock --- components/MyBusiness/reviewCard/mock.ts | 59 ------------------------ 1 file changed, 59 deletions(-) delete mode 100644 components/MyBusiness/reviewCard/mock.ts diff --git a/components/MyBusiness/reviewCard/mock.ts b/components/MyBusiness/reviewCard/mock.ts deleted file mode 100644 index ac058c4..0000000 --- a/components/MyBusiness/reviewCard/mock.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ReviewSentiment } from '../../../types'; - -export interface Review { - id: number; - name: string; - sentiment: ReviewSentiment; - comment: string; -} - -export const MOCK_REVIEWS: Review[] = [ - { - id: 1, - name: 'Maria da Silva Santos', - sentiment: 'elogios', - comment: - 'Adorei o atendimento! Comida fresca, bem temperada e equipe muito atenciosa. Voltarei com certeza!', - }, - { - id: 2, - name: 'João Oliveira', - sentiment: 'dicas', - comment: - 'Muito bom! Só sugiro aumentar um pouco a quantidade de tempero no arroz. Mas no geral, recomendo!', - }, - { - id: 3, - name: 'Ana Costa Ribeiro', - sentiment: 'duvidas', - comment: - 'Qual é a procedência das carnes? Vocês usam fornecedores locais? Gostaria de saber mais sobre isso.', - }, - { - id: 4, - name: 'Carlos Mendes', - sentiment: 'elogios', - comment: - 'Excelente culinária nordestina! Ambiente aconchegante e preço justo. Recomendo para todos os amigos!', - }, - { - id: 5, - name: 'Fernanda Gomes', - sentiment: 'dicas', - comment: - 'Pensei que teria mais opções vegetarianas. Mas os drinks são incríveis! Voltarei para experimentar mais.', - }, - { - id: 6, - name: 'Roberto Alves', - sentiment: 'duvidas', - comment: 'Fazem delivery? Qual é o tempo de entrega para a região central?', - }, - { - id: 7, - name: 'Lucia Martins', - sentiment: 'elogios', - comment: - 'Melhor comida que já comi! Parabéns à equipe. Voltei 3 vezes em um mês, isso é tudo que preciso falar.', - }, -]; From 8cabb99ea6a0d10157b292edb932affa615bcc6c Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 24 Apr 2026 20:18:37 -0300 Subject: [PATCH 2/9] =?UTF-8?q?Scrum-56=20feat:=20integra=C3=A7=C3=A3o=20c?= =?UTF-8?q?om=20a=20api=20e=20ajustes=20menores=20de=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(mybusiness)/reviews.tsx | 38 ++++++++++++++++---- components/MyBusiness/reviewsFilter/main.tsx | 28 +++++++-------- components/report/CardList.tsx | 22 ++++++------ 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/app/(mybusiness)/reviews.tsx b/app/(mybusiness)/reviews.tsx index 4760852..851472c 100644 --- a/app/(mybusiness)/reviews.tsx +++ b/app/(mybusiness)/reviews.tsx @@ -1,19 +1,35 @@ -import { useState } from 'react'; import { Container } from '@/components/general/container'; import { Header } from '@/components/general/header'; +import { ReviewCard } from '@/components/MyBusiness/reviewCard/main'; import { ReviewsFilterNav, type ReviewFilterType, } from '@/components/MyBusiness/reviewsFilter/main'; -import { ReviewCard } from '@/components/MyBusiness/reviewCard/main'; -import { MOCK_REVIEWS } from '@/components/MyBusiness/reviewCard/mock'; -import { View, Text } from 'react-native'; +import { useAssessments } from '@/hooks/useAssessments'; +import { Assessment } from '@/services/assessmentService'; +import { ReviewSentiment } from '@/types'; +import { useState } from 'react'; +import { ActivityIndicator, Text, View } from 'react-native'; + +const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; + +interface ReviewItem { + id: string; + name: string; + sentiment: ReviewSentiment; + comment: string; +} export default function Reviews() { const [activeFilter, setActiveFilter] = useState('todos'); + const { assessments, loading, error } = useAssessments(ENTERPRISE_ID); - // requisicao do backend para pegar as reviews, por enquanto usando mock - const reviews = MOCK_REVIEWS; + const reviews: ReviewItem[] = assessments.map((assessment: Assessment) => ({ + id: assessment.id_avaliacao, + name: assessment.usuario_nome || 'Anônimo', + sentiment: assessment.tipo_avaliacao, + comment: assessment.texto, + })); const filteredReviews = activeFilter === 'todos' @@ -33,7 +49,15 @@ export default function Reviews() { onFilterChange={handleFilterChange} /> - {filteredReviews.length > 0 ? ( + {loading ? ( + + + + ) : error ? ( + + {error} + + ) : filteredReviews.length > 0 ? ( {filteredReviews.map((review) => ( void; @@ -10,9 +10,11 @@ interface ReviewsFilterNavProps { const FILTER_OPTIONS: { id: ReviewFilterType; label: string }[] = [ { id: 'todos', label: 'Todos' }, - { id: 'elogios', label: 'Elogios' }, - { id: 'dicas', label: 'Dicas' }, - { id: 'duvidas', label: 'Dúvidas' }, + { id: 'positiva', label: 'Positiva' }, + { id: 'negativa', label: 'Negativa' }, + { id: 'neutra', label: 'Neutra' }, + { id: 'sugestao', label: 'Sugestão' }, + { id: 'duvida', label: 'Dúvida' }, ]; export const ReviewsFilterNav = ({ @@ -28,16 +30,15 @@ export const ReviewsFilterNav = ({ }; return ( - + {FILTER_OPTIONS.map((filter) => ( handleFilterPress(filter.id)} - className={`flex-1 h-10 rounded-lg justify-center items-center ${ - activeFilter === filter.id - ? 'bg-primary' - : 'bg-light border border-secondary' - }`} + className={`h-10 rounded-lg justify-center items-center px-4 ${activeFilter === filter.id + ? 'bg-primary' + : 'bg-light border border-secondary' + }`} style={[ styles.filterButton, activeFilter === filter.id @@ -46,9 +47,8 @@ export const ReviewsFilterNav = ({ ]} > {filter.label} diff --git a/components/report/CardList.tsx b/components/report/CardList.tsx index 7a2db84..b55d3e2 100644 --- a/components/report/CardList.tsx +++ b/components/report/CardList.tsx @@ -2,32 +2,32 @@ import { router } from 'expo-router' import { View } from 'react-native' import { CardItem } from './CardItem' -export const CardList = ()=> { - const handlePresPositive = ()=> { +export const CardList = () => { + const handlePresPositive = () => { router.navigate('/(report)/positivePoints') } - const handlePresNegative = ()=> { + const handlePresNegative = () => { router.navigate('/(report)/negativePoints') } - const handlePresRecomendation = ()=> { + const handlePresRecomendation = () => { router.navigate('/(report)/recomendations') } - return( + return ( { Date: Fri, 24 Apr 2026 20:19:46 -0300 Subject: [PATCH 3/9] Scrum-56 feat: assessment e user services --- services/assessmentService.ts | 67 +++++++++++++++++++++++++++++++++++ services/userService.ts | 18 ++++++++++ 2 files changed, 85 insertions(+) create mode 100644 services/assessmentService.ts create mode 100644 services/userService.ts diff --git a/services/assessmentService.ts b/services/assessmentService.ts new file mode 100644 index 0000000..943dfe3 --- /dev/null +++ b/services/assessmentService.ts @@ -0,0 +1,67 @@ +import { API_URL } from '@/constants/api'; +import { ReviewSentiment } from '@/types'; +import axios from 'axios'; + +export interface Assessment { + id_avaliacao: string; + texto: string; + tipo_avaliacao: ReviewSentiment; + usuario_id: string; + usuario_nome?: string; + empresa_id: string; +} + +export const getAssessmentsByEnterprise = async ( + enterpriseId: string +): Promise => { + const response = await axios.get( + `${API_URL}/assessments/by-enterprise/${enterpriseId}` + ); + return response.data; +}; + +export const getAllAssessments = async (): Promise => { + const response = await axios.get(`${API_URL}/assessments`); + return response.data; +}; + +export const getAssessmentById = async ( + assessmentId: string +): Promise => { + const response = await axios.get(`${API_URL}/assessments/${assessmentId}`); + return response.data; +}; + +export const createAssessment = async ( + texto: string, + usuarioId: string, + empresaId: string +): Promise => { + const response = await axios.post(`${API_URL}/assessments`, { + texto, + usuario_id: usuarioId, + empresa_id: empresaId, + }); + return response.data; +}; + +export const updateAssessment = async ( + assessmentId: string, + data: Partial<{ + texto: string; + usuario_id: string; + empresa_id: string; + }> +): Promise => { + const response = await axios.put( + `${API_URL}/assessments/${assessmentId}`, + data + ); + return response.data; +}; + +export const deleteAssessment = async ( + assessmentId: string +): Promise => { + await axios.delete(`${API_URL}/assessments/${assessmentId}`); +}; diff --git a/services/userService.ts b/services/userService.ts new file mode 100644 index 0000000..8990d97 --- /dev/null +++ b/services/userService.ts @@ -0,0 +1,18 @@ +import { API_URL } from '@/constants/api'; +import axios from 'axios'; + +export interface User { + id_usuario: string; + nome: string; + email: string; +} + +export const getUserById = async (userId: string): Promise => { + try { + const response = await axios.get(`${API_URL}/users/${userId}`); + return response.data; + } catch (error) { + console.error(`Erro ao buscar usuário ${userId}:`, error); + return null; + } +}; From 9590b25ca9021900e87fe17030985ff506f9fb3a Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 24 Apr 2026 20:20:23 -0300 Subject: [PATCH 4/9] Scrum-56 feat: assessments & sentiment types --- hooks/useAssessments.ts | 61 +++++++++++++++++++++++++++++++++++++++++ types/assessment.ts | 44 +++++++++++++++++++++++++++++ types/index.ts | 2 +- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 hooks/useAssessments.ts create mode 100644 types/assessment.ts diff --git a/hooks/useAssessments.ts b/hooks/useAssessments.ts new file mode 100644 index 0000000..cd44c29 --- /dev/null +++ b/hooks/useAssessments.ts @@ -0,0 +1,61 @@ +import { Assessment, getAssessmentsByEnterprise } from '@/services/assessmentService'; +import { getUserById } from '@/services/userService'; +import { useFocusEffect } from '@react-navigation/native'; +import { useCallback, useState } from 'react'; + +interface UseAssessmentsResult { + assessments: Assessment[]; + loading: boolean; + error: string | null; + refetch: () => Promise; +} + +export const useAssessments = (enterpriseId: string): UseAssessmentsResult => { + const [assessments, setAssessments] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const enrichAssessmentsWithUserNames = async (data: Assessment[]) => { + const enriched = await Promise.all( + data.map(async (assessment) => { + if (!assessment.usuario_nome) { + const user = await getUserById(assessment.usuario_id); + return { + ...assessment, + usuario_nome: user?.nome || 'Anônimo', + }; + } + return assessment; + }) + ); + return enriched; + }; + + const loadAssessments = useCallback(async () => { + try { + setLoading(true); + setError(null); + const data = await getAssessmentsByEnterprise(enterpriseId); + const enriched = await enrichAssessmentsWithUserNames(data); + setAssessments(enriched); + } catch (err) { + console.error('Erro ao carregar avaliações:', err); + setError('Não foi possível carregar as avaliações'); + } finally { + setLoading(false); + } + }, [enterpriseId]); + + useFocusEffect( + useCallback(() => { + loadAssessments(); + }, [loadAssessments]) + ); + + return { + assessments, + loading, + error, + refetch: loadAssessments, + }; +}; diff --git a/types/assessment.ts b/types/assessment.ts new file mode 100644 index 0000000..d558d81 --- /dev/null +++ b/types/assessment.ts @@ -0,0 +1,44 @@ +import { ReviewSentiment } from './index'; + +export interface AssessmentDTO { + id_avaliacao: string; + texto: string; + tipo_avaliacao: ReviewSentiment; + usuario_id: string; + empresa_id: string; +} + +export interface UserDTO { + id_usuario: string; + nome: string; + email: string; +} + +export interface EnterpriseDTO { + id_empresa: string; + nome: string; +} + +/** + * View enriched de Assessment com dados de usuário e empresa + * Útil quando backend retorna dados relacionados + */ +export interface AssessmentEnrichedDTO extends AssessmentDTO { + usuario?: UserDTO; + empresa?: EnterpriseDTO; +} + +/** + * Estatísticas de avaliações por tipo + * Possível endpoint futuro: GET /assessments/stats/enterprise/{id} + */ +export interface AssessmentStatsDTO { + total: number; + positiva: number; + negativa: number; + neutra: number; + sugestao: number; + duvida: number; + avgSentiment?: number; // -1 a 1 ou percentual + recentCount: number; // últimos 7 dias +} diff --git a/types/index.ts b/types/index.ts index ed5504d..6a1526a 100644 --- a/types/index.ts +++ b/types/index.ts @@ -1 +1 @@ -export type ReviewSentiment = 'elogios' | 'dicas' | 'duvidas'; +export type ReviewSentiment = 'positiva' | 'negativa' | 'neutra' | 'sugestao' | 'duvida'; From 9f1b26aed2bf002b2afc53ab92eee0a37cc4762e Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 24 Apr 2026 20:26:53 -0300 Subject: [PATCH 5/9] Scrum-56 feat: ajuste no tema dos sentimentos --- constants/theme.ts | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/constants/theme.ts b/constants/theme.ts index 6ac358e..f724be4 100644 --- a/constants/theme.ts +++ b/constants/theme.ts @@ -54,19 +54,29 @@ export const Fonts = Platform.select({ }); export const SENTIMENT_CONFIG = { - elogios: { - label: 'Elogio', - bgColor: '#F4EBE8', - textColor: '#AD3C00', + positiva: { + label: 'Positiva', + bgColor: '#E0F9F0', + textColor: '#00896B', }, - dicas: { - label: 'Dicas', - bgColor: '#F9F3E9', - textColor: '#D19E49', + negativa: { + label: 'Negativa', + bgColor: '#FDE8E8', + textColor: '#C62828', }, - duvidas: { - label: 'Dúvidas', - bgColor: '#DBEAFE', - textColor: '#004BDA', + neutra: { + label: 'Neutra', + bgColor: '#F5F5F5', + textColor: '#424242', + }, + sugestao: { + label: 'Sugestão', + bgColor: '#FFF3E0', + textColor: '#E65100', + }, + duvida: { + label: 'Dúvida', + bgColor: '#E1F5FE', + textColor: '#0D47A1', }, }; From 477d70d8080bc03e571709746abd4475de79c2b0 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 24 Apr 2026 21:30:55 -0300 Subject: [PATCH 6/9] Scrum-205 fix: deteccao dos topicos da transcricao --- app/(mybusiness)/editStory.tsx | 21 +++++++++++--------- components/editStory/audioBox.tsx | 32 ++++++++++++++++++++---------- components/editStory/checklist.tsx | 24 +++++++++++++++++----- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/app/(mybusiness)/editStory.tsx b/app/(mybusiness)/editStory.tsx index 9365181..afc7fda 100644 --- a/app/(mybusiness)/editStory.tsx +++ b/app/(mybusiness)/editStory.tsx @@ -17,9 +17,10 @@ export default function EditStory() { const [toggle, setToggle] = useState<'WRITE' | 'AUDIO'>('WRITE'); const [text, setText] = useState(''); const [audio, setAudio] = useState(''); - const [saving, setSaving] = useState(false); + const [detectedTopics, setDetectedTopics] = useState([]); + const [isSaving, setIsSaving] = useState(false); - const handlePress = async () => { + const handleSave = async () => { try { if (toggle === 'WRITE' && !text.trim()) { Alert.alert('Atenção', 'Digite uma história antes de continuar.'); @@ -31,7 +32,7 @@ export default function EditStory() { return; } - setSaving(true); + setIsSaving(true); await axios.put(`${API_URL}/enterprises/${ENTERPRISE_ID}`, { historia: toggle === 'WRITE' ? text.trim() : audio, @@ -39,6 +40,7 @@ export default function EditStory() { setText(''); setAudio(''); + setDetectedTopics([]); router.navigate('/(mybusiness)/manageImages'); @@ -46,7 +48,7 @@ export default function EditStory() { console.error(error); Alert.alert('Erro', 'Não foi possível salvar a história. Tente novamente.'); } finally { - setSaving(false); + setIsSaving(false); } }; @@ -56,7 +58,7 @@ export default function EditStory() {
- Conte história do seu restaurante. + Conte a história do seu restaurante. @@ -71,15 +73,16 @@ export default function EditStory() { setAudio={setAudio} setText={setText} setToggle={setToggle} + setDetectedTopics={setDetectedTopics} /> )} - + diff --git a/components/editStory/audioBox.tsx b/components/editStory/audioBox.tsx index 7508875..2bdd70a 100644 --- a/components/editStory/audioBox.tsx +++ b/components/editStory/audioBox.tsx @@ -21,15 +21,16 @@ type Props = { setAudio: (s: string) => void setText: (s: string) => void setToggle: (t: 'WRITE' | 'AUDIO') => void + setDetectedTopics?: (topics: string[]) => void } const USER_ID = '453df15b-61ce-4571-8bdb-cdbedf0ff041' -export default function AudioBox({ audio, setAudio, setText, setToggle }: Props) { +export default function AudioBox({ audio, setAudio, setText, setToggle, setDetectedTopics }: Props) { const recorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY) - const [loading, setLoading] = useState(false) + const [isLoading, setIsLoading] = useState(false) const scale = useSharedValue(1) @@ -62,7 +63,7 @@ export default function AudioBox({ audio, setAudio, setText, setToggle }: Props) const stopRecording = async () => { try { - setLoading(true) + setIsLoading(true) await recorder.stop() @@ -104,16 +105,27 @@ export default function AudioBox({ audio, setAudio, setText, setToggle }: Props) } const data = JSON.parse(result.body) - const texto = data?.historia || '' + const text = data?.historia || '' + + const detectedTopics: string[] = [] + if (data?.nome) detectedTopics.push('Nome do negócio') + if (data?.especialidade) detectedTopics.push('Sua especialidade (produtos/serviços)') + if (data?.endereco) detectedTopics.push('Localização / Bairro') + if (data?.historia) detectedTopics.push('Sua história ou o diferencial') + + setText(text) + setAudio(text) + + if (setDetectedTopics) { + setDetectedTopics(detectedTopics) + } - setText(texto) - setAudio(texto) setToggle('WRITE') } catch (error: any) { console.error('Erro ao enviar áudio:', error?.message) } finally { - setLoading(false) + setIsLoading(false) } } @@ -130,7 +142,7 @@ export default function AudioBox({ audio, setAudio, setText, setToggle }: Props) - {loading ? ( + {isLoading ? ( Processando áudio... @@ -139,8 +151,8 @@ export default function AudioBox({ audio, setAudio, setText, setToggle }: Props) {recorder.isRecording ? 'Gravando... solte para enviar' : audio - ? 'Áudio convertido com sucesso ✅' - : 'Toque e segure para gravar'} + ? 'Áudio convertido com sucesso ✅' + : 'Toque e segure para gravar'} )} diff --git a/components/editStory/checklist.tsx b/components/editStory/checklist.tsx index d4719f1..d9d5c27 100644 --- a/components/editStory/checklist.tsx +++ b/components/editStory/checklist.tsx @@ -1,14 +1,28 @@ import { Text, View } from 'react-native'; import ChecklistButton from './checklistButton'; -export default function Checklist() { +type Props = { + detectedTopics?: string[]; +}; + +export default function Checklist({ detectedTopics = [] }: Props) { + const topics = [ + 'Nome do negócio', + 'Sua especialidade (produtos/serviços)', + 'Localização / Bairro', + 'Sua história ou o diferencial', + ]; + return ( Dicas para uma boa descrição - - - - + {topics.map((topic) => ( + + ))} ); } From 0a330f28ee7017e02fefea542275f72f2176b19d Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 24 Apr 2026 21:36:48 -0300 Subject: [PATCH 7/9] scrum-205 fix: lint fix --- .github/workflows/eas-release-android.yml | 2 +- app/(help)/help.tsx | 10 +- app/(help)/helpDetail.tsx | 54 ++-- app/(mybusiness)/businessOverview.tsx | 95 +++--- app/(mybusiness)/editStory.tsx | 148 ++++----- app/(mybusiness)/manageImages.tsx | 20 +- app/(mybusiness)/myBusiness.tsx | 109 ++++--- app/(report)/negativePoints.tsx | 22 +- app/(report)/positivePoints.tsx | 22 +- app/(report)/recomendations.tsx | 18 +- app/(report)/report.tsx | 20 +- app/home.tsx | 2 +- app/index.tsx | 10 +- .../Home/completeProfile/completeProfile.tsx | 135 ++++---- components/Home/header/header.tsx | 140 ++++---- components/MyBusiness/overviewCard/main.tsx | 1 - components/editStory/audioBox.tsx | 301 +++++++++--------- components/editStory/checklist.tsx | 40 +-- components/editStory/checklistButton.tsx | 80 +++-- components/editStory/inputBox.tsx | 63 ++-- components/editStory/toggleWrite.tsx | 40 +-- components/editStory/toggleWriteButton.tsx | 68 ++-- components/general/generalButton.tsx | 50 ++- components/manageImages/fileUpload.tsx | 117 ++++--- components/manageImages/imageItem.tsx | 123 +++---- components/manageImages/imagesList.tsx | 92 +++--- components/overview/address.tsx | 12 +- components/overview/carousel.tsx | 20 +- components/overview/editButton.tsx | 2 +- components/report/CardItem.tsx | 109 ++++--- components/report/CardList.tsx | 92 +++--- components/report/cardItemSkeleton.tsx | 44 ++- components/report/cardListSkeleton.tsx | 20 +- components/report/chatButton.tsx | 33 +- constants/api.ts | 2 +- scripts/reset-project.js | 4 +- services/imagesEnterprise.ts | 52 +-- types/enterprise.ts | 12 +- types/imageEnterprise.ts | 8 +- types/user.ts | 14 +- 40 files changed, 1102 insertions(+), 1104 deletions(-) diff --git a/.github/workflows/eas-release-android.yml b/.github/workflows/eas-release-android.yml index b198495..afeeea0 100644 --- a/.github/workflows/eas-release-android.yml +++ b/.github/workflows/eas-release-android.yml @@ -2,7 +2,7 @@ name: EAS Release Android (Weekly) on: schedule: - - cron: '0 8 * * 0' + - cron: "0 8 * * 0" workflow_dispatch: jobs: diff --git a/app/(help)/help.tsx b/app/(help)/help.tsx index a49c5ea..fc8118f 100644 --- a/app/(help)/help.tsx +++ b/app/(help)/help.tsx @@ -15,7 +15,6 @@ import { } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; - type FAQItem = { id: number; title: string; @@ -33,7 +32,6 @@ const HelpScreen: React.FC = () => { { id: 4, title: 'Formas de pagamento', icon: 'credit-card' }, ]; - const handleSendEmail = async () => { const email = 'suporte@mandaca.com.br'; const subject = encodeURIComponent('Suporte - Central de Ajuda Mandacá'); @@ -53,7 +51,8 @@ const HelpScreen: React.FC = () => { // Handle phone call const handleCallSupport = async () => { - const phoneNumber = Platform.OS === 'ios' ? 'telprompt:08001234567' : 'tel:08001234567'; + const phoneNumber = + Platform.OS === 'ios' ? 'telprompt:08001234567' : 'tel:08001234567'; try { const canOpen = await Linking.canOpenURL(phoneNumber); @@ -79,7 +78,10 @@ const HelpScreen: React.FC = () => { /> - + {/* FAQ Section */} Perguntas Frequentes diff --git a/app/(help)/helpDetail.tsx b/app/(help)/helpDetail.tsx index ca96a03..bdd98ff 100644 --- a/app/(help)/helpDetail.tsx +++ b/app/(help)/helpDetail.tsx @@ -3,13 +3,7 @@ import Ionicons from '@expo/vector-icons/Ionicons'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import { useRouter } from 'expo-router'; import React from 'react'; -import { - Platform, - ScrollView, - StyleSheet, - Text, - View, -} from 'react-native'; +import { Platform, ScrollView, StyleSheet, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; /** @@ -23,16 +17,16 @@ const FaqDetailScreen: React.FC = () => { {/* Standard Header with standard back button logic */} -
router.back()} /> - {/* Visual Hierarchy: Icon and Centered Title */} @@ -46,20 +40,24 @@ const FaqDetailScreen: React.FC = () => { {/* Content Section: Focused on Readability (lineHeight: 26) */} - Para cadastrar seu negócio no Mandacá e começar a atrair turistas da região, - siga este guia passo a passo:{'\n\n'} - 1. Na sua tela inicial, navegue até a seção de ferramentas e clique no card{' '} - "Minha Empresa".{'\n\n'} - 2. Toque no botão de adição ou editar. Preencha todas as informações básicas: - nome do local, descrição detalhada e o tipo de serviço que você oferece - (ex: Gastronomia, Artesanato).{'\n\n'} - 3. Fotos são fundamentais! Adicione imagens - bem iluminadas da fachada e do interior para gerar confiança nos visitantes.{'\n\n'} - 4. Localização: Verifique se o ponto no mapa está correto para que o GPS leve - os turistas exatamente até a sua porta.{'\n\n'} - 5. Finalize clicando em "Salvar". - Nossa equipe analisará os dados em no máximo 24 horas para garantir - a qualidade da plataforma. + Para cadastrar seu negócio no Mandacá e começar a atrair turistas da + região, siga este guia passo a passo:{'\n\n'} + 1. Na sua tela inicial, navegue até a seção de ferramentas e clique + no card{' '} + "Minha Empresa". + {'\n\n'} + 2. Toque no botão de adição ou editar. Preencha todas as informações + básicas: nome do local, descrição detalhada e o tipo de serviço que + você oferece (ex: Gastronomia, Artesanato).{'\n\n'} + 3. Fotos são fundamentais!{' '} + Adicione imagens bem iluminadas da fachada e do interior para gerar + confiança nos visitantes.{'\n\n'} + 4. Localização: Verifique se o ponto no mapa está correto para que o + GPS leve os turistas exatamente até a sua porta.{'\n\n'} + 5. Finalize clicando em{' '} + "Salvar". Nossa + equipe analisará os dados em no máximo 24 horas para garantir a + qualidade da plataforma. @@ -70,8 +68,8 @@ const FaqDetailScreen: React.FC = () => { Dica de Especialista - Negócios com descrições ricas em detalhes e que respondem rápido - às avaliações tendem a aparecer no topo das buscas dos turistas! + Negócios com descrições ricas em detalhes e que respondem rápido às + avaliações tendem a aparecer no topo das buscas dos turistas! diff --git a/app/(mybusiness)/businessOverview.tsx b/app/(mybusiness)/businessOverview.tsx index 5c62828..785e589 100644 --- a/app/(mybusiness)/businessOverview.tsx +++ b/app/(mybusiness)/businessOverview.tsx @@ -13,54 +13,49 @@ import { API_URL } from '@/constants/api'; const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; export default function Overview() { - const [story, setStory] = useState(''); - const [address, setAddress] = useState(''); - const [loading, setLoading] = useState(false); - - const loadOverview = useCallback(async () => { - try { - setLoading(true); - - const response = await axios.get( - `${API_URL}/enterprises/overview`, - { - params: { enterprise_id: ENTERPRISE_ID }, - }, - ); - - setStory(response.data.historia ?? ''); - setAddress(response.data.endereco ?? ''); - - } catch (error) { - console.error('Erro ao buscar visão geral:', error); - Alert.alert('Erro', 'Não foi possível carregar os dados.'); - } finally { - setLoading(false); - } - }, []); - - useFocusEffect( - useCallback(() => { - loadOverview(); - }, [loadOverview]), - ); - - return ( - -
- - {loading ? ( - - - - ) : ( - <> - -
- - - )} - - - ); + const [story, setStory] = useState(''); + const [address, setAddress] = useState(''); + const [loading, setLoading] = useState(false); + + const loadOverview = useCallback(async () => { + try { + setLoading(true); + + const response = await axios.get(`${API_URL}/enterprises/overview`, { + params: { enterprise_id: ENTERPRISE_ID }, + }); + + setStory(response.data.historia ?? ''); + setAddress(response.data.endereco ?? ''); + } catch (error) { + console.error('Erro ao buscar visão geral:', error); + Alert.alert('Erro', 'Não foi possível carregar os dados.'); + } finally { + setLoading(false); + } + }, []); + + useFocusEffect( + useCallback(() => { + loadOverview(); + }, [loadOverview]), + ); + + return ( + +
+ + {loading ? ( + + + + ) : ( + <> + +
+ + + )} + + ); } diff --git a/app/(mybusiness)/editStory.tsx b/app/(mybusiness)/editStory.tsx index afc7fda..b756505 100644 --- a/app/(mybusiness)/editStory.tsx +++ b/app/(mybusiness)/editStory.tsx @@ -14,77 +14,77 @@ import { API_URL } from '@/constants/api'; const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; export default function EditStory() { - const [toggle, setToggle] = useState<'WRITE' | 'AUDIO'>('WRITE'); - const [text, setText] = useState(''); - const [audio, setAudio] = useState(''); - const [detectedTopics, setDetectedTopics] = useState([]); - const [isSaving, setIsSaving] = useState(false); - - const handleSave = async () => { - try { - if (toggle === 'WRITE' && !text.trim()) { - Alert.alert('Atenção', 'Digite uma história antes de continuar.'); - return; - } - - if (toggle === 'AUDIO' && !audio) { - Alert.alert('Atenção', 'Grave um áudio antes de continuar.'); - return; - } - - setIsSaving(true); - - await axios.put(`${API_URL}/enterprises/${ENTERPRISE_ID}`, { - historia: toggle === 'WRITE' ? text.trim() : audio, - }); - - setText(''); - setAudio(''); - setDetectedTopics([]); - - router.navigate('/(mybusiness)/manageImages'); - - } catch (error) { - console.error(error); - Alert.alert('Erro', 'Não foi possível salvar a história. Tente novamente.'); - } finally { - setIsSaving(false); - } - }; - - return ( - - -
- - - Conte a história do seu restaurante. - - - - - {toggle === 'WRITE' && ( - - )} - - {toggle === 'AUDIO' && ( - - )} - - - - - - - ); -} \ No newline at end of file + const [toggle, setToggle] = useState<'WRITE' | 'AUDIO'>('WRITE'); + const [text, setText] = useState(''); + const [audio, setAudio] = useState(''); + const [detectedTopics, setDetectedTopics] = useState([]); + const [isSaving, setIsSaving] = useState(false); + + const handleSave = async () => { + try { + if (toggle === 'WRITE' && !text.trim()) { + Alert.alert('Atenção', 'Digite uma história antes de continuar.'); + return; + } + + if (toggle === 'AUDIO' && !audio) { + Alert.alert('Atenção', 'Grave um áudio antes de continuar.'); + return; + } + + setIsSaving(true); + + await axios.put(`${API_URL}/enterprises/${ENTERPRISE_ID}`, { + historia: toggle === 'WRITE' ? text.trim() : audio, + }); + + setText(''); + setAudio(''); + setDetectedTopics([]); + + router.navigate('/(mybusiness)/manageImages'); + } catch (error) { + console.error(error); + Alert.alert( + 'Erro', + 'Não foi possível salvar a história. Tente novamente.', + ); + } finally { + setIsSaving(false); + } + }; + + return ( + + +
+ + + Conte a história do seu restaurante. + + + + + {toggle === 'WRITE' && } + + {toggle === 'AUDIO' && ( + + )} + + + + + + + ); +} diff --git a/app/(mybusiness)/manageImages.tsx b/app/(mybusiness)/manageImages.tsx index e305408..7759594 100644 --- a/app/(mybusiness)/manageImages.tsx +++ b/app/(mybusiness)/manageImages.tsx @@ -3,13 +3,13 @@ import { Header } from '@/components/general/header'; import ImagesList from '@/components/manageImages/imagesList'; import { View } from 'react-native'; -export default function ManageImages () { - return( - - -
- - - - ) -} \ No newline at end of file +export default function ManageImages() { + return ( + + +
+ + + + ); +} diff --git a/app/(mybusiness)/myBusiness.tsx b/app/(mybusiness)/myBusiness.tsx index cae21df..12d020d 100644 --- a/app/(mybusiness)/myBusiness.tsx +++ b/app/(mybusiness)/myBusiness.tsx @@ -1,60 +1,59 @@ -import { useFocusEffect } from '@react-navigation/native' -import { router } from 'expo-router' -import { useCallback, useState } from 'react' -import { View } from 'react-native' +import { useFocusEffect } from '@react-navigation/native'; +import { router } from 'expo-router'; +import { useCallback, useState } from 'react'; +import { View } from 'react-native'; -import { Container } from '@/components/general/container' -import { Header } from '@/components/general/header' -import { AccessGrid } from '@/components/MyBusiness/accessGrid/main' -import { OverviewCard } from '@/components/MyBusiness/overviewCard/main' +import { Container } from '@/components/general/container'; +import { Header } from '@/components/general/header'; +import { AccessGrid } from '@/components/MyBusiness/accessGrid/main'; +import { OverviewCard } from '@/components/MyBusiness/overviewCard/main'; -import { getImagesByEnterprise } from '@/services/imagesEnterprise' -import { ImageEnterprise } from '@/types/imageEnterprise' +import { getImagesByEnterprise } from '@/services/imagesEnterprise'; +import { ImageEnterprise } from '@/types/imageEnterprise'; -const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2' +const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; export default function MyBusiness() { - - const [image, setImage] = useState(undefined) - - const loadImages = useCallback(async () => { - try { - const data: ImageEnterprise[] = await getImagesByEnterprise(ENTERPRISE_ID) - - if (data.length > 0) { - setImage(data[0].url_foto_empresa) - } - - } catch (error) { - console.error('Erro ao carregar imagem:', error) - } - }, []) - - useFocusEffect( - useCallback(() => { - loadImages() - }, [loadImages]), - ) - - return ( - -
- - - router.navigate('/businessOverview')} - /> - - - - - ) -} \ No newline at end of file + const [image, setImage] = useState(undefined); + + const loadImages = useCallback(async () => { + try { + const data: ImageEnterprise[] = + await getImagesByEnterprise(ENTERPRISE_ID); + + if (data.length > 0) { + setImage(data[0].url_foto_empresa); + } + } catch (error) { + console.error('Erro ao carregar imagem:', error); + } + }, []); + + useFocusEffect( + useCallback(() => { + loadImages(); + }, [loadImages]), + ); + + return ( + +
+ + + router.navigate('/businessOverview')} + /> + + + + + ); +} diff --git a/app/(report)/negativePoints.tsx b/app/(report)/negativePoints.tsx index 91f9bca..eb1f651 100644 --- a/app/(report)/negativePoints.tsx +++ b/app/(report)/negativePoints.tsx @@ -2,12 +2,16 @@ import { Container } from '@/components/general/container'; import { Header } from '@/components/general/header'; import { View } from 'react-native'; -export default function NegativePoints () { - return( - - -
- - - ) -} \ No newline at end of file +export default function NegativePoints() { + return ( + + +
+ + + ); +} diff --git a/app/(report)/positivePoints.tsx b/app/(report)/positivePoints.tsx index 49f2e5c..fb283fd 100644 --- a/app/(report)/positivePoints.tsx +++ b/app/(report)/positivePoints.tsx @@ -2,12 +2,16 @@ import { Container } from '@/components/general/container'; import { Header } from '@/components/general/header'; import { View } from 'react-native'; -export default function PositivePoints () { - return( - - -
- - - ) -} \ No newline at end of file +export default function PositivePoints() { + return ( + + +
+ + + ); +} diff --git a/app/(report)/recomendations.tsx b/app/(report)/recomendations.tsx index 146b0b2..8e63e8a 100644 --- a/app/(report)/recomendations.tsx +++ b/app/(report)/recomendations.tsx @@ -2,12 +2,12 @@ import { Container } from '@/components/general/container'; import { Header } from '@/components/general/header'; import { View } from 'react-native'; -export default function Recomendations () { - return( - - -
- - - ) -} \ No newline at end of file +export default function Recomendations() { + return ( + + +
+ + + ); +} diff --git a/app/(report)/report.tsx b/app/(report)/report.tsx index 5112d90..da083cc 100644 --- a/app/(report)/report.tsx +++ b/app/(report)/report.tsx @@ -7,22 +7,18 @@ import { useState } from 'react'; import { View } from 'react-native'; export default function Report() { - const [loading, setLoading] = useState(false) + const [loading, setLoading] = useState(false); - const handlePress = ()=> { - setLoading //to do - } - - return ( + const handlePress = () => { + setLoading; //to do + }; + + return (
- - {loading ? ( - - ) : ( - - )} + + {loading ? : } ); diff --git a/app/home.tsx b/app/home.tsx index b143082..78c37fc 100644 --- a/app/home.tsx +++ b/app/home.tsx @@ -7,7 +7,7 @@ export default function Home() { return (
- + ); diff --git a/app/index.tsx b/app/index.tsx index a50b026..0bf95a1 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -7,18 +7,14 @@ export default function App() { return ( - - Hello Mandacá! - + Hello Mandacá! router.push('/home')} // ✅ usar push > - - ir para Home - + ir para Home ); -} \ No newline at end of file +} diff --git a/components/Home/completeProfile/completeProfile.tsx b/components/Home/completeProfile/completeProfile.tsx index 44d4a05..e3bdcb4 100644 --- a/components/Home/completeProfile/completeProfile.tsx +++ b/components/Home/completeProfile/completeProfile.tsx @@ -5,81 +5,84 @@ import { useEffect, useState } from 'react'; import { ActivityIndicator, StyleSheet, Text, View } from 'react-native'; export const CompleteProfile = () => { - const [enterprise, setEnterprise] = useState(null); - const [loading, setLoading] = useState(true); + const [enterprise, setEnterprise] = useState(null); + const [loading, setLoading] = useState(true); - const getEnterprise = async () => { - try { - const enterpriseId = - 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; + const getEnterprise = async () => { + try { + const enterpriseId = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; - const response = await axios.get( - `${API_URL}/enterprises/percentage/${enterpriseId}`, - ); + const response = await axios.get( + `${API_URL}/enterprises/percentage/${enterpriseId}`, + ); - if (response.data && typeof response.data.porcentagem === 'number') { - setEnterprise(response.data); - } else { - console.warn('Resposta inesperada:', response.data); - } + if (response.data && typeof response.data.porcentagem === 'number') { + setEnterprise(response.data); + } else { + console.warn('Resposta inesperada:', response.data); + } + } catch (error) { + console.error('Erro ao buscar empresa:', error); + } finally { + setLoading(false); + } + }; - } catch (error) { - console.error('Erro ao buscar empresa:', error); - } finally { - setLoading(false); - } - }; + useEffect(() => { + getEnterprise(); + }, []); - useEffect(() => { - getEnterprise(); - }, []); + if (loading) { + return ( + + + + ); + } - if (loading) { - return ( - - - - ); - } + const porcentagem = enterprise?.porcentagem ?? 0; - const porcentagem = enterprise?.porcentagem ?? 0; + return ( + + + Complete seu Perfil + {porcentagem}% + - return ( - - - Complete seu Perfil - {porcentagem}% - - - - Preencha as informações para atrair mais turistas - + + Preencha as informações para atrair mais turistas + - - - - - ); + + + + + ); }; const style = StyleSheet.create({ - card: { - padding: 24, - backgroundColor: '#FFF', - borderRadius: 24, - gap: 16, - }, - row: { flexDirection: 'row', justifyContent: 'space-between' }, - title: { fontWeight: 'bold', fontSize: 24 }, - percentageText: { fontWeight: 'bold', fontSize: 24, color: '#C34342' }, // Substitua pela cor do seu primary - subtitle: { fontSize: 18 }, - progressBg: { width: '100%', height: 8, backgroundColor: '#EEE', borderRadius: 99 }, - progressBar: { height: '100%', backgroundColor: '#C34342', borderRadius: 99 }, - cardShadow: { - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.25, - shadowRadius: 3.8, - elevation: 5, - }, -}); \ No newline at end of file + card: { + padding: 24, + backgroundColor: '#FFF', + borderRadius: 24, + gap: 16, + }, + row: { flexDirection: 'row', justifyContent: 'space-between' }, + title: { fontWeight: 'bold', fontSize: 24 }, + percentageText: { fontWeight: 'bold', fontSize: 24, color: '#C34342' }, // Substitua pela cor do seu primary + subtitle: { fontSize: 18 }, + progressBg: { + width: '100%', + height: 8, + backgroundColor: '#EEE', + borderRadius: 99, + }, + progressBar: { height: '100%', backgroundColor: '#C34342', borderRadius: 99 }, + cardShadow: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.25, + shadowRadius: 3.8, + elevation: 5, + }, +}); diff --git a/components/Home/header/header.tsx b/components/Home/header/header.tsx index 41b112a..204c388 100644 --- a/components/Home/header/header.tsx +++ b/components/Home/header/header.tsx @@ -5,93 +5,85 @@ import axios from 'axios'; import { router } from 'expo-router'; import { useEffect, useState } from 'react'; import { - ActivityIndicator, - Image, - Pressable, - StyleSheet, - Text, - View, + ActivityIndicator, + Image, + Pressable, + StyleSheet, + Text, + View, } from 'react-native'; - - export const Header = () => { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); - const getUser = async () => { - try { - const userId = '453df15b-61ce-4571-8bdb-cdbedf0ff041'; + const getUser = async () => { + try { + const userId = '453df15b-61ce-4571-8bdb-cdbedf0ff041'; - const responseUser = await axios.get( - `${API_URL}/users/${userId}`, - ); + const responseUser = await axios.get(`${API_URL}/users/${userId}`); - setUser(responseUser.data); - } catch (error) { - console.error('Erro ao buscar usuário:', error); - } finally { - setLoading(false); - } - }; + setUser(responseUser.data); + } catch (error) { + console.error('Erro ao buscar usuário:', error); + } finally { + setLoading(false); + } + }; - useEffect(() => { - getUser(); - }, []); + useEffect(() => { + getUser(); + }, []); - if (loading) { - return ; - } + if (loading) { + return ; + } - const userName = user?.nome || 'Usuário'; - const userInitial = userName.charAt(0).toUpperCase(); + const userName = user?.nome || 'Usuário'; + const userInitial = userName.charAt(0).toUpperCase(); - return ( - - router.push('/profile')} - > - {user?.url_foto_usuario ? ( - - ) : ( - {userInitial} - )} - + return ( + + router.push('/profile')} + > + {user?.url_foto_usuario ? ( + + ) : ( + {userInitial} + )} + - - - Bem-vindo de volta, - - - {userName} - - + + Bem-vindo de volta, + {userName} + - router.push('/notifications')} - > - - - - ); + router.push('/notifications')} + > + + + + ); }; const style = StyleSheet.create({ - cardShadow: { - shadowColor: '#000', - shadowOffset: { - width: 0, - height: 2, - }, - shadowOpacity: 1, - shadowRadius: 3.8, - elevation: 5, + cardShadow: { + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, }, -}); \ No newline at end of file + shadowOpacity: 1, + shadowRadius: 3.8, + elevation: 5, + }, +}); diff --git a/components/MyBusiness/overviewCard/main.tsx b/components/MyBusiness/overviewCard/main.tsx index 1400e62..59e556c 100644 --- a/components/MyBusiness/overviewCard/main.tsx +++ b/components/MyBusiness/overviewCard/main.tsx @@ -7,7 +7,6 @@ type Props = { }; export const OverviewCard = ({ imageUrl, onPress }: Props) => { - return ( void - setText: (s: string) => void - setToggle: (t: 'WRITE' | 'AUDIO') => void - setDetectedTopics?: (topics: string[]) => void -} - -const USER_ID = '453df15b-61ce-4571-8bdb-cdbedf0ff041' - -export default function AudioBox({ audio, setAudio, setText, setToggle, setDetectedTopics }: Props) { - - const recorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY) - - const [isLoading, setIsLoading] = useState(false) - - const scale = useSharedValue(1) - - const animatedStyle = useAnimatedStyle(() => ({ - transform: [{ scale: scale.value }], - })) - - const startRecording = async () => { - try { - const permission = await requestRecordingPermissionsAsync() - - if (!permission.granted) { - alert('Permissão de microfone necessária!') - return - } - - await recorder.prepareToRecordAsync() - recorder.record() - - scale.value = withRepeat( - withTiming(1.2, { duration: 600 }), - -1, - true, - ) - - } catch (error) { - console.error('Erro ao iniciar gravação:', error) - } + audio: string; + setAudio: (s: string) => void; + setText: (s: string) => void; + setToggle: (t: 'WRITE' | 'AUDIO') => void; + setDetectedTopics?: (topics: string[]) => void; +}; + +const USER_ID = '453df15b-61ce-4571-8bdb-cdbedf0ff041'; + +export default function AudioBox({ + audio, + setAudio, + setText, + setToggle, + setDetectedTopics, +}: Props) { + const recorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY); + + const [isLoading, setIsLoading] = useState(false); + + const scale = useSharedValue(1); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: scale.value }], + })); + + const startRecording = async () => { + try { + const permission = await requestRecordingPermissionsAsync(); + + if (!permission.granted) { + alert('Permissão de microfone necessária!'); + return; + } + + await recorder.prepareToRecordAsync(); + recorder.record(); + + scale.value = withRepeat(withTiming(1.2, { duration: 600 }), -1, true); + } catch (error) { + console.error('Erro ao iniciar gravação:', error); } - - const stopRecording = async () => { - try { - setIsLoading(true) - - await recorder.stop() - - const uri = recorder.uri - - scale.value = withTiming(1) - - if (!uri) return - - const extension = uri.split('.').pop()?.toLowerCase() ?? 'm4a' - const mimeTypeMap: Record = { - 'm4a': 'audio/mp4', - 'mp4': 'audio/mp4', - 'mp3': 'audio/mpeg', - '3gp': 'audio/3gpp', - 'aac': 'audio/aac', - 'wav': 'audio/wav', - } - const mimeType = mimeTypeMap[extension] ?? 'audio/mp4' - - console.log('[AudioBox] URI gravada:', uri, '| MIME:', mimeType) - - const result = await FileSystem.uploadAsync( - `${API_URL}/transcriptions/`, - uri, - { - httpMethod: 'POST', - uploadType: FileSystem.FileSystemUploadType.MULTIPART, - fieldName: 'audio', - mimeType, - parameters: { - usuario_id: USER_ID, - }, - }, - ) - - if (result.status < 200 || result.status >= 300) { - throw new Error(`HTTP ${result.status}: ${result.body}`) - } - - const data = JSON.parse(result.body) - const text = data?.historia || '' - - const detectedTopics: string[] = [] - if (data?.nome) detectedTopics.push('Nome do negócio') - if (data?.especialidade) detectedTopics.push('Sua especialidade (produtos/serviços)') - if (data?.endereco) detectedTopics.push('Localização / Bairro') - if (data?.historia) detectedTopics.push('Sua história ou o diferencial') - - setText(text) - setAudio(text) - - if (setDetectedTopics) { - setDetectedTopics(detectedTopics) - } - - setToggle('WRITE') - - } catch (error: any) { - console.error('Erro ao enviar áudio:', error?.message) - } finally { - setIsLoading(false) - } + }; + + const stopRecording = async () => { + try { + setIsLoading(true); + + await recorder.stop(); + + const uri = recorder.uri; + + scale.value = withTiming(1); + + if (!uri) return; + + const extension = uri.split('.').pop()?.toLowerCase() ?? 'm4a'; + const mimeTypeMap: Record = { + m4a: 'audio/mp4', + mp4: 'audio/mp4', + mp3: 'audio/mpeg', + '3gp': 'audio/3gpp', + aac: 'audio/aac', + wav: 'audio/wav', + }; + const mimeType = mimeTypeMap[extension] ?? 'audio/mp4'; + + console.log('[AudioBox] URI gravada:', uri, '| MIME:', mimeType); + + const result = await FileSystem.uploadAsync( + `${API_URL}/transcriptions/`, + uri, + { + httpMethod: 'POST', + uploadType: FileSystem.FileSystemUploadType.MULTIPART, + fieldName: 'audio', + mimeType, + parameters: { + usuario_id: USER_ID, + }, + }, + ); + + if (result.status < 200 || result.status >= 300) { + throw new Error(`HTTP ${result.status}: ${result.body}`); + } + + const data = JSON.parse(result.body); + const text = data?.historia || ''; + + const detectedTopics: string[] = []; + if (data?.nome) detectedTopics.push('Nome do negócio'); + if (data?.especialidade) + detectedTopics.push('Sua especialidade (produtos/serviços)'); + if (data?.endereco) detectedTopics.push('Localização / Bairro'); + if (data?.historia) detectedTopics.push('Sua história ou o diferencial'); + + setText(text); + setAudio(text); + + if (setDetectedTopics) { + setDetectedTopics(detectedTopics); + } + + setToggle('WRITE'); + } catch (error: any) { + console.error('Erro ao enviar áudio:', error?.message); + } finally { + setIsLoading(false); } - - return ( - - - - - - - - - {isLoading ? ( - - Processando áudio... - - ) : ( - - {recorder.isRecording - ? 'Gravando... solte para enviar' - : audio - ? 'Áudio convertido com sucesso ✅' - : 'Toque e segure para gravar'} - - )} - - ) + }; + + return ( + + + + + + + + {isLoading ? ( + Processando áudio... + ) : ( + + {recorder.isRecording + ? 'Gravando... solte para enviar' + : audio + ? 'Áudio convertido com sucesso ✅' + : 'Toque e segure para gravar'} + + )} + + ); } diff --git a/components/editStory/checklist.tsx b/components/editStory/checklist.tsx index d9d5c27..d305a27 100644 --- a/components/editStory/checklist.tsx +++ b/components/editStory/checklist.tsx @@ -2,27 +2,29 @@ import { Text, View } from 'react-native'; import ChecklistButton from './checklistButton'; type Props = { - detectedTopics?: string[]; + detectedTopics?: string[]; }; export default function Checklist({ detectedTopics = [] }: Props) { - const topics = [ - 'Nome do negócio', - 'Sua especialidade (produtos/serviços)', - 'Localização / Bairro', - 'Sua história ou o diferencial', - ]; + const topics = [ + 'Nome do negócio', + 'Sua especialidade (produtos/serviços)', + 'Localização / Bairro', + 'Sua história ou o diferencial', + ]; - return ( - - Dicas para uma boa descrição - {topics.map((topic) => ( - - ))} - - ); + return ( + + + Dicas para uma boa descrição + + {topics.map((topic) => ( + + ))} + + ); } diff --git a/components/editStory/checklistButton.tsx b/components/editStory/checklistButton.tsx index e125c04..ca900a2 100644 --- a/components/editStory/checklistButton.tsx +++ b/components/editStory/checklistButton.tsx @@ -1,51 +1,47 @@ -import { useEffect } from 'react' -import { Pressable, Text } from 'react-native' +import { useEffect } from 'react'; +import { Pressable, Text } from 'react-native'; import Animated, { - interpolateColor, - useAnimatedStyle, - useSharedValue, - withTiming, -} from 'react-native-reanimated' + interpolateColor, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; type Props = { - text: string - check: boolean -} + text: string; + check: boolean; +}; export default function ChecklistButton({ text, check }: Props) { + const progress = useSharedValue(check ? 1 : 0); - const progress = useSharedValue(check ? 1 : 0) - - useEffect(() => { - progress.value = withTiming(check ? 1 : 0, { duration: 250 }) - }, [check]) + useEffect(() => { + progress.value = withTiming(check ? 1 : 0, { duration: 250 }); + }, [check]); - const animatedStyle = useAnimatedStyle(() => { - return { - backgroundColor: interpolateColor( - progress.value, - [0, 1], - ['transparent', '#C34342'], // verde - ), - borderColor: interpolateColor( - progress.value, - [0, 1], - ['#000', '#C34342'], - ), - } - }) + const animatedStyle = useAnimatedStyle(() => { + return { + backgroundColor: interpolateColor( + progress.value, + [0, 1], + ['transparent', '#C34342'], // verde + ), + borderColor: interpolateColor( + progress.value, + [0, 1], + ['#000', '#C34342'], + ), + }; + }); - return ( - - - + return ( + + - - {text} - - - ) -} \ No newline at end of file + {text} + + ); +} diff --git a/components/editStory/inputBox.tsx b/components/editStory/inputBox.tsx index c16edce..7aa3668 100644 --- a/components/editStory/inputBox.tsx +++ b/components/editStory/inputBox.tsx @@ -1,41 +1,40 @@ -import { useState } from 'react' -import { Text, TextInput, View } from 'react-native' +import { useState } from 'react'; +import { Text, TextInput, View } from 'react-native'; -const MAX_LENGTH = 500 +const MAX_LENGTH = 500; type Props = { - text: string, - setText: (s: string) => void -} + text: string; + setText: (s: string) => void; +}; export default function InputBox({ text, setText }: Props) { + const [isFocused, setIsFocused] = useState(false); - const [isFocused, setIsFocused] = useState(false) - - return ( - - + - setIsFocused(true)} - onBlur={() => setIsFocused(false)} - className="text-base text-black" - /> - - - {text.length}/{MAX_LENGTH} - - - ) -} \ No newline at end of file + > + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + className="text-base text-black" + /> + + + {text.length}/{MAX_LENGTH} + + + ); +} diff --git a/components/editStory/toggleWrite.tsx b/components/editStory/toggleWrite.tsx index f5ef1f1..7a01ddf 100644 --- a/components/editStory/toggleWrite.tsx +++ b/components/editStory/toggleWrite.tsx @@ -2,24 +2,24 @@ import { View } from 'react-native'; import ToggleWriteButton from './toggleWriteButton'; type Props = { - toggle: 'WRITE' | 'AUDIO' - setToggle: (s: 'WRITE' | 'AUDIO')=> void + toggle: 'WRITE' | 'AUDIO'; + setToggle: (s: 'WRITE' | 'AUDIO') => void; +}; +export default function ToggleWrite({ toggle, setToggle }: Props) { + return ( + + setToggle('WRITE')} + /> + setToggle('AUDIO')} + /> + + ); } -export default function ToggleWrite ({toggle, setToggle}: Props) { - return( - - setToggle('WRITE')} - /> - setToggle('AUDIO')} - /> - - ) -} \ No newline at end of file diff --git a/components/editStory/toggleWriteButton.tsx b/components/editStory/toggleWriteButton.tsx index 919abf8..31f3ad5 100644 --- a/components/editStory/toggleWriteButton.tsx +++ b/components/editStory/toggleWriteButton.tsx @@ -1,42 +1,42 @@ -import { Pressable, Text } from 'react-native' +import { Pressable, Text } from 'react-native'; import Animated, { - useAnimatedStyle, - withTiming, -} from 'react-native-reanimated' + useAnimatedStyle, + withTiming, +} from 'react-native-reanimated'; type Props = { - text: string - tag: 'WRITE' | 'AUDIO' - toggle: 'WRITE' | 'AUDIO' - handlePress: (s: 'WRITE' | 'AUDIO') => void -} + text: string; + tag: 'WRITE' | 'AUDIO'; + toggle: 'WRITE' | 'AUDIO'; + handlePress: (s: 'WRITE' | 'AUDIO') => void; +}; export default function ToggleWriteButton({ - text, - handlePress, - toggle, - tag, + text, + handlePress, + toggle, + tag, }: Props) { + const animatedStyle = useAnimatedStyle(() => { + const isActive = toggle === tag; - const animatedStyle = useAnimatedStyle(() => { - const isActive = toggle === tag - - return { - backgroundColor: withTiming( - isActive ? '#ffffff' : 'transparent', - { duration: 300 }, - ), - } - }) + return { + backgroundColor: withTiming(isActive ? '#ffffff' : 'transparent', { + duration: 300, + }), + }; + }); - return ( - - handlePress(tag)} - > - {text} - - - ) -} \ No newline at end of file + return ( + + handlePress(tag)} + > + {text} + + + ); +} diff --git a/components/general/generalButton.tsx b/components/general/generalButton.tsx index 1f5ce6c..af15600 100644 --- a/components/general/generalButton.tsx +++ b/components/general/generalButton.tsx @@ -1,33 +1,31 @@ import { ActivityIndicator, Pressable, Text } from 'react-native'; type Props = { - text: string; - handlePress: () => void; - disabled?: boolean; - loading?: boolean; + text: string; + handlePress: () => void; + disabled?: boolean; + loading?: boolean; }; export default function GeneralButton({ - text, - handlePress, - disabled = false, - loading = false, + text, + handlePress, + disabled = false, + loading = false, }: Props) { - return ( - - {loading ? ( - - ) : ( - - {text} - - )} - - ); -} \ No newline at end of file + return ( + + {loading ? ( + + ) : ( + {text} + )} + + ); +} diff --git a/components/manageImages/fileUpload.tsx b/components/manageImages/fileUpload.tsx index c59ceae..82c4c5d 100644 --- a/components/manageImages/fileUpload.tsx +++ b/components/manageImages/fileUpload.tsx @@ -6,83 +6,76 @@ import { Pressable, Text, View } from 'react-native'; import { uploadImage } from '@/services/imagesEnterprise'; type Props = { - onUploadSuccess?: () => void; + onUploadSuccess?: () => void; }; type Status = 'idle' | 'loading' | 'success'; export default function FileUpload({ onUploadSuccess }: Props) { - const [status, setStatus] = useState('idle'); + const [status, setStatus] = useState('idle'); - const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; + const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; - const pickImage = async () => { - const permission = await ImagePicker.requestMediaLibraryPermissionsAsync(); + const pickImage = async () => { + const permission = await ImagePicker.requestMediaLibraryPermissionsAsync(); - if (!permission.granted) { - alert('Permissão para acessar a galeria é necessária!'); - return; - } + if (!permission.granted) { + alert('Permissão para acessar a galeria é necessária!'); + return; + } - const result = await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ['images'], - quality: 1, - }); + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ['images'], + quality: 1, + }); - if (!result.canceled) { - const uri = result.assets[0].uri; + if (!result.canceled) { + const uri = result.assets[0].uri; - setStatus('loading'); + setStatus('loading'); - try { - await uploadImage(uri, ENTERPRISE_ID); + try { + await uploadImage(uri, ENTERPRISE_ID); - setStatus('success'); + setStatus('success'); - onUploadSuccess?.(); + onUploadSuccess?.(); - setTimeout(() => { - setStatus('idle'); - }, 3000); + setTimeout(() => { + setStatus('idle'); + }, 3000); + } catch (error) { + console.error('Erro ao fazer upload:', error); + setStatus('idle'); + } + } + }; - } catch (error) { - console.error('Erro ao fazer upload:', error); - setStatus('idle'); - } - } - }; + return ( + + Adicionar Imagem - return ( - - - Adicionar Imagem - + + {status === 'loading' && Enviando...} - - - {status === 'loading' && ( - Enviando... - )} - - {status === 'success' && ( - <> - - - Imagem Adicionada - - - )} - - {status === 'idle' && ( - <> - - - Adicionar Imagem - - - )} - - - - ); -} \ No newline at end of file + {status === 'success' && ( + <> + + + Imagem Adicionada + + + )} + + {status === 'idle' && ( + <> + + + Adicionar Imagem + + + )} + + + ); +} diff --git a/components/manageImages/imageItem.tsx b/components/manageImages/imageItem.tsx index c6c825d..9ffa562 100644 --- a/components/manageImages/imageItem.tsx +++ b/components/manageImages/imageItem.tsx @@ -1,74 +1,79 @@ import * as ImagePicker from 'expo-image-picker'; -import { ActivityIndicator, Alert, Image, Pressable, Text, View } from 'react-native'; +import { + ActivityIndicator, + Alert, + Image, + Pressable, + Text, + View, +} from 'react-native'; type Props = { - id: string; - uri: string; - onDelete: () => void; - onReplace: (newUri: string) => void; - isLoading?: boolean; + id: string; + uri: string; + onDelete: () => void; + onReplace: (newUri: string) => void; + isLoading?: boolean; }; export default function ImageItem({ - uri, - onDelete, - onReplace, - isLoading = false, + uri, + onDelete, + onReplace, + isLoading = false, }: Props) { + async function handlePickImage() { + const permission = await ImagePicker.requestMediaLibraryPermissionsAsync(); - async function handlePickImage() { - const permission = await ImagePicker.requestMediaLibraryPermissionsAsync(); - - if (!permission.granted) { - Alert.alert('Permissão necessária', 'Você precisa permitir acesso à galeria.'); - return; - } + if (!permission.granted) { + Alert.alert( + 'Permissão necessária', + 'Você precisa permitir acesso à galeria.', + ); + return; + } - const result = await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ['images'], - quality: 1, - }); + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ['images'], + quality: 1, + }); - if (!result.canceled) { - const newUri = result.assets[0].uri; + if (!result.canceled) { + const newUri = result.assets[0].uri; - onReplace(newUri); - } + onReplace(newUri); } + } - return ( - - + return ( + + - - - {isLoading ? ( - - ) : ( - - Substituir - - )} - + + + {isLoading ? ( + + ) : ( + Substituir + )} + - - - Deletar - - - - - ); -} \ No newline at end of file + + Deletar + + + + ); +} diff --git a/components/manageImages/imagesList.tsx b/components/manageImages/imagesList.tsx index 9f7ae13..5857472 100644 --- a/components/manageImages/imagesList.tsx +++ b/components/manageImages/imagesList.tsx @@ -3,64 +3,66 @@ import { Text, View } from 'react-native'; import FileUpload from './fileUpload'; import ImageItem from './imageItem'; -import { deleteImage, getImagesByEnterprise } from '@/services/imagesEnterprise'; +import { + deleteImage, + getImagesByEnterprise, +} from '@/services/imagesEnterprise'; import { ImageEnterprise } from '@/types/imageEnterprise'; type ImageType = { - id: string; - uri: string; + id: string; + uri: string; }; export default function ImagesList() { - const [images, setImages] = useState([]); + const [images, setImages] = useState([]); - const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; + const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; - const loadImages = async () => { - try { - const data: ImageEnterprise[] = await getImagesByEnterprise(ENTERPRISE_ID); + const loadImages = async () => { + try { + const data: ImageEnterprise[] = + await getImagesByEnterprise(ENTERPRISE_ID); - const formattedImages: ImageType[] = data.map((item) => ({ - id: item.id_foto, - uri: item.url_foto_empresa, - })); + const formattedImages: ImageType[] = data.map((item) => ({ + id: item.id_foto, + uri: item.url_foto_empresa, + })); - setImages(formattedImages); - } catch (error) { - console.error(error); - } - }; + setImages(formattedImages); + } catch (error) { + console.error(error); + } + }; - useEffect(() => { - loadImages(); - }, []); + useEffect(() => { + loadImages(); + }, []); - async function handleDelete(id: string) { - try { - await deleteImage(id); - loadImages(); - } catch (error) { - console.error('Erro ao deletar imagem:', error); - } + async function handleDelete(id: string) { + try { + await deleteImage(id); + loadImages(); + } catch (error) { + console.error('Erro ao deletar imagem:', error); } + } - return ( - - + return ( + + - - Imagens cadastradas - + Imagens cadastradas - {images.map((img) => ( - handleDelete(img.id)} - onReplace={() => {}} - /> - ))} - - ); -} \ No newline at end of file + {images.map((img) => ( + handleDelete(img.id)} + onReplace={() => {}} + /> + ))} + + ); +} diff --git a/components/overview/address.tsx b/components/overview/address.tsx index 9f8e7ae..dcd98b6 100644 --- a/components/overview/address.tsx +++ b/components/overview/address.tsx @@ -1,12 +1,18 @@ import Ionicons from '@expo/vector-icons/Ionicons'; -import { Alert, Linking, Pressable, StyleSheet, Text, View } from 'react-native'; +import { + Alert, + Linking, + Pressable, + StyleSheet, + Text, + View, +} from 'react-native'; type Props = { address: string; }; export default function Address({ address }: Props) { - const handleMapsbutton = async () => { if (!address) { Alert.alert('Erro', 'Endereço não disponível.'); @@ -49,4 +55,4 @@ const style = StyleSheet.create({ shadowRadius: 3.8, elevation: 2, }, -}); \ No newline at end of file +}); diff --git a/components/overview/carousel.tsx b/components/overview/carousel.tsx index b172127..6860a3a 100644 --- a/components/overview/carousel.tsx +++ b/components/overview/carousel.tsx @@ -51,7 +51,10 @@ const PaginationDot = ({ active }: PaginationDotProps) => { }); return ( - + ); }; @@ -61,7 +64,9 @@ export default function Carousel() { const [activeIndex, setActiveIndex] = useState(0); const [images, setImages] = useState([]); - const [loadingImages, setLoadingImages] = useState>({}); + const [loadingImages, setLoadingImages] = useState>( + {}, + ); const ENTERPRISE_ID = 'caa68f64-b68e-4327-90f0-264ca1bb73e2'; @@ -70,7 +75,7 @@ export default function Carousel() { const data = await getImagesByEnterprise(ENTERPRISE_ID); const initialLoadingState: Record = {}; - data.forEach(item => { + data.forEach((item) => { initialLoadingState[item.id_foto] = true; }); @@ -97,7 +102,7 @@ export default function Carousel() { }; const handleImageLoad = (id: string) => { - setLoadingImages(prev => ({ + setLoadingImages((prev) => ({ ...prev, [id]: false, })); @@ -137,12 +142,9 @@ export default function Carousel() { {images.map((_, index) => ( - + ))} ); -} \ No newline at end of file +} diff --git a/components/overview/editButton.tsx b/components/overview/editButton.tsx index bcec225..9a3c4cd 100644 --- a/components/overview/editButton.tsx +++ b/components/overview/editButton.tsx @@ -6,7 +6,7 @@ export default function EditButton() { return ( router.navigate('/(mybusiness)/editStory')} + onPress={() => router.navigate('/(mybusiness)/editStory')} className="flex-row justify-between items-center" > diff --git a/components/report/CardItem.tsx b/components/report/CardItem.tsx index 13906bc..be948ae 100644 --- a/components/report/CardItem.tsx +++ b/components/report/CardItem.tsx @@ -1,57 +1,64 @@ -import Ionicons from '@expo/vector-icons/Ionicons' -import { Pressable, Text, View } from 'react-native' +import Ionicons from '@expo/vector-icons/Ionicons'; +import { Pressable, Text, View } from 'react-native'; type topic = { - id: number, - text: string -} + id: number; + text: string; +}; type Props = { - icon: any, - typeCard: 'positive' | 'negative' | 'recomendation', - topics: topic[] - handlePress: ()=> void -} + icon: any; + typeCard: 'positive' | 'negative' | 'recomendation'; + topics: topic[]; + handlePress: () => void; +}; -export const CardItem = ({icon, typeCard, topics, handlePress}: Props)=> { - return( - { + return ( + + + + {typeCard === 'positive' && ( + <> + + Pontos positivos + + + + )} + {typeCard === 'negative' && ( + <> + + Pontos negativos + + + + )} + {typeCard === 'recomendation' && ( + <> + + Recomendações + + + + )} + + + {topics.map((item) => ( + - - - {typeCard === 'positive' && - <> - Pontos positivos - - - } - {typeCard === 'negative' && - <> - Pontos negativos - - - } - {typeCard === 'recomendation' && - <> - Recomendações - - - } - - - {topics.map(item=> ( - - - {item.text} - - ))} - - Ver Sugestões - - + + {item.text} - ) -} \ No newline at end of file + ))} + + Ver Sugestões + + + + ); +}; diff --git a/components/report/CardList.tsx b/components/report/CardList.tsx index 7a2db84..e6d7a59 100644 --- a/components/report/CardList.tsx +++ b/components/report/CardList.tsx @@ -1,45 +1,49 @@ -import { router } from 'expo-router' -import { View } from 'react-native' -import { CardItem } from './CardItem' +import { router } from 'expo-router'; +import { View } from 'react-native'; +import { CardItem } from './CardItem'; -export const CardList = ()=> { - const handlePresPositive = ()=> { - router.navigate('/(report)/positivePoints') - } - const handlePresNegative = ()=> { - router.navigate('/(report)/negativePoints') - } - const handlePresRecomendation = ()=> { - router.navigate('/(report)/recomendations') - } - return( - - - - - - ) -} \ No newline at end of file +export const CardList = () => { + const handlePresPositive = () => { + router.navigate('/(report)/positivePoints'); + }; + const handlePresNegative = () => { + router.navigate('/(report)/negativePoints'); + }; + const handlePresRecomendation = () => { + router.navigate('/(report)/recomendations'); + }; + return ( + + + + + + ); +}; diff --git a/components/report/cardItemSkeleton.tsx b/components/report/cardItemSkeleton.tsx index c174b1b..7422d8c 100644 --- a/components/report/cardItemSkeleton.tsx +++ b/components/report/cardItemSkeleton.tsx @@ -1,27 +1,25 @@ -import { View } from 'react-native' +import { View } from 'react-native'; export const CardItemSkeleton = () => { - return ( - - - {/* Header */} - - - - - - - {/* Topics */} - {[1,2,3].map((_, index) => ( - - - - - ))} - - {/* Button */} - + return ( + + {/* Header */} + + + + + + {/* Topics */} + {[1, 2, 3].map((_, index) => ( + + + - ) -} \ No newline at end of file + ))} + + {/* Button */} + + + ); +}; diff --git a/components/report/cardListSkeleton.tsx b/components/report/cardListSkeleton.tsx index 5377f78..d5d6edf 100644 --- a/components/report/cardListSkeleton.tsx +++ b/components/report/cardListSkeleton.tsx @@ -1,12 +1,12 @@ -import { View } from 'react-native' -import { CardItemSkeleton } from './cardItemSkeleton' +import { View } from 'react-native'; +import { CardItemSkeleton } from './cardItemSkeleton'; export const CardListSkeleton = () => { - return ( - - - - - - ) -} \ No newline at end of file + return ( + + + + + + ); +}; diff --git a/components/report/chatButton.tsx b/components/report/chatButton.tsx index 14c29c3..aa26236 100644 --- a/components/report/chatButton.tsx +++ b/components/report/chatButton.tsx @@ -1,20 +1,19 @@ -import Ionicons from '@expo/vector-icons/Ionicons' -import { Pressable, Text } from 'react-native' +import Ionicons from '@expo/vector-icons/Ionicons'; +import { Pressable, Text } from 'react-native'; type Props = { - text: string - handlePress: ()=> void -} + text: string; + handlePress: () => void; +}; -export const ChatButton = ({text, handlePress}: Props)=> { - return( - - - {text} - - - ) -} \ No newline at end of file +export const ChatButton = ({ text, handlePress }: Props) => { + return ( + + {text} + + + ); +}; diff --git a/constants/api.ts b/constants/api.ts index b85468d..be19e3d 100644 --- a/constants/api.ts +++ b/constants/api.ts @@ -1,2 +1,2 @@ export const API_URL = - process.env.EXPO_PUBLIC_API_URL ?? 'http://localhost:8000'; + process.env.EXPO_PUBLIC_API_URL ?? 'http://localhost:8000'; diff --git a/scripts/reset-project.js b/scripts/reset-project.js index 8edba94..9678791 100644 --- a/scripts/reset-project.js +++ b/scripts/reset-project.js @@ -2,10 +2,10 @@ /** * This script is used to reset the project to a blank state. - * It deletes or moves the /app, /components, /hooks, /scripts, + * It deletes or moves the /app, /components, /hooks, /scripts, * and /constants directories to /app-example based on user inpu * and creates a new /app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json + * You can remove the `reset-project` script from package.json * and safely delete this file after running it. */ diff --git a/services/imagesEnterprise.ts b/services/imagesEnterprise.ts index 5cc2768..1799bc7 100644 --- a/services/imagesEnterprise.ts +++ b/services/imagesEnterprise.ts @@ -5,35 +5,37 @@ import axios from 'axios'; import { API_URL } from '@/constants/api'; -export const getImagesByEnterprise = async (enterpriseId: string): Promise => { - const response = await axios.get( - `${API_URL}/photos/enterprise/${enterpriseId}`, - ); - - return response.data; +export const getImagesByEnterprise = async ( + enterpriseId: string, +): Promise => { + const response = await axios.get( + `${API_URL}/photos/enterprise/${enterpriseId}`, + ); + + return response.data; }; export const deleteImage = async (photoId: string): Promise => { - await axios.delete(`${API_URL}/photos/${photoId}`); + await axios.delete(`${API_URL}/photos/${photoId}`); }; export const uploadImage = async ( - imageUri: string, - empresa_id: string, + imageUri: string, + empresa_id: string, ): Promise => { - const formData = new FormData(); - - formData.append('empresa_id', empresa_id); - - formData.append('files', { - uri: imageUri, - name: 'photo.jpg', - type: 'image/jpeg', - } as any); - - await axios.post(`${API_URL}/photos/`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); -}; \ No newline at end of file + const formData = new FormData(); + + formData.append('empresa_id', empresa_id); + + formData.append('files', { + uri: imageUri, + name: 'photo.jpg', + type: 'image/jpeg', + } as any); + + await axios.post(`${API_URL}/photos/`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); +}; diff --git a/types/enterprise.ts b/types/enterprise.ts index 312b97c..7463472 100644 --- a/types/enterprise.ts +++ b/types/enterprise.ts @@ -1,7 +1,7 @@ export type Enterprise = { - id_empresa: string; - nome: string; - porcentagem: number; - campos_preenchidos: string[]; - campos_faltando: string[]; -}; \ No newline at end of file + id_empresa: string; + nome: string; + porcentagem: number; + campos_preenchidos: string[]; + campos_faltando: string[]; +}; diff --git a/types/imageEnterprise.ts b/types/imageEnterprise.ts index 4f55954..461dad5 100644 --- a/types/imageEnterprise.ts +++ b/types/imageEnterprise.ts @@ -1,5 +1,5 @@ export type ImageEnterprise = { - id_foto: string; - url_foto_empresa: string; - empresa_id: string; -}; \ No newline at end of file + id_foto: string; + url_foto_empresa: string; + empresa_id: string; +}; diff --git a/types/user.ts b/types/user.ts index a9f6ca3..f02e171 100644 --- a/types/user.ts +++ b/types/user.ts @@ -1,8 +1,8 @@ export type User = { - id_usuario: string; - tipo_usuario: string; - nome: string; - cpf: string; - url_foto_usuario: string; - empresa_id: string; -}; \ No newline at end of file + id_usuario: string; + tipo_usuario: string; + nome: string; + cpf: string; + url_foto_usuario: string; + empresa_id: string; +}; From 2380e83996a68b1d7cd4dea63e4cea1eda362a87 Mon Sep 17 00:00:00 2001 From: degoNDL Date: Sun, 26 Apr 2026 21:10:23 -0300 Subject: [PATCH 8/9] SCRUM-41: atualiza layout do CardItem --- components/report/CardItem.tsx | 72 ++++++++-------------------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/components/report/CardItem.tsx b/components/report/CardItem.tsx index a4a49c0..646c3d8 100644 --- a/components/report/CardItem.tsx +++ b/components/report/CardItem.tsx @@ -1,52 +1,21 @@ -import Ionicons from '@expo/vector-icons/Ionicons'; -import { Pressable, Text, View } from 'react-native'; +import Ionicons from '@expo/vector-icons/Ionicons' +import { Pressable, Text, View } from 'react-native' type topic = { - id: number; - text: string; -}; + id: number, + text: string +} type Props = { - icon: any; - typeCard: 'positive' | 'negative' | 'recomendation'; - topics: topic[]; - handlePress: () => void; -}; + icon: any, + typeCard: 'positive' | 'negative' | 'recomendation', + topics: topic[] + handlePress: ()=> void +} -export const CardItem = ({ icon, typeCard, topics, handlePress }: Props) => { - return ( - - - - {typeCard === 'positive' && ( - <> - - Pontos positivos - - - - )} - {typeCard === 'negative' && ( - <> - - Pontos negativos - - - - )} - {typeCard === 'recomendation' && ( - <> - - Recomendações - - - - )} - - - {topics.map((item) => ( - { + return( + @@ -84,14 +53,5 @@ export const CardItem = ({ icon, typeCard, topics, handlePress }: Props) => { - ))} - - Ver Sugestões - - - - ); -}; + ) +} \ No newline at end of file From 88715e3d4d185b6a997205cdda41707feedd636f Mon Sep 17 00:00:00 2001 From: Daniel Santana Date: Sun, 26 Apr 2026 21:28:39 -0300 Subject: [PATCH 9/9] SCRUM-27: grid adjustment --- components/Home/routeGrid/gridBox.tsx | 2 +- components/Home/routeGrid/main.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/components/Home/routeGrid/gridBox.tsx b/components/Home/routeGrid/gridBox.tsx index aed95a6..e6e1dfa 100644 --- a/components/Home/routeGrid/gridBox.tsx +++ b/components/Home/routeGrid/gridBox.tsx @@ -1,6 +1,6 @@ import Ionicons from '@expo/vector-icons/Ionicons'; -import { Pressable, StyleSheet, Text } from 'react-native'; import { ComponentProps } from 'react'; +import { Pressable, StyleSheet, Text } from 'react-native'; type Props = { icon: ComponentProps['name']; diff --git a/components/Home/routeGrid/main.tsx b/components/Home/routeGrid/main.tsx index a414358..f44d701 100644 --- a/components/Home/routeGrid/main.tsx +++ b/components/Home/routeGrid/main.tsx @@ -14,8 +14,7 @@ export const RouteGrid = () => { router.navigate('/(report)/report')} />