Skip to content

Commit 7926941

Browse files
Merge pull request #111 from ICEI-PUC-Minas-PMV-ADS/clients-b
feat: máscaras CPF/telefone, correções de clientes e ajustes mobile
2 parents 45e085b + e640ab5 commit 7926941

12 files changed

Lines changed: 77 additions & 44 deletions

File tree

src/mobile/app/(tabs)/clientes/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export default function ClientesScreen() {
5757
}
5858

5959
function confirmarDeletar(id: number) {
60+
console.log('🔍 deletar id:', id, typeof id);
6061
const nome = clientes.find((c) => c.id === id)?.nome ?? 'este cliente';
6162
Alert.alert('Remover cliente', `Deseja remover ${nome}?`, [
6263
{ text: 'Cancelar', style: 'cancel' },

src/mobile/app/(tabs)/index.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const acoes = [
4848

4949
export default function DashboardScreen() {
5050
const router = useRouter();
51-
const { user, logout } = useAuth();
51+
const { user, logout, isLoading } = useAuth();
5252
const nome = user?.nome?.split(' ')[0] ?? 'Usuário';
5353

5454
const [stats, setStats] = useState<Stats | null>(null);
@@ -57,26 +57,25 @@ export default function DashboardScreen() {
5757
const tabBarHeight = useBottomTabBarHeight();
5858

5959
useEffect(() => {
60+
if (isLoading) return;
61+
if (!user) return;
62+
6063
async function carregar() {
6164
const cobrador = user?.nome ?? '';
62-
6365

6466
let clientes: unknown[] = [];
6567
let lista: Emprestimo[] = [];
6668

6769
try {
6870
const resClientes = await api.get(CLIENTES);
6971
clientes = Array.isArray(resClientes.data) ? resClientes.data : [];
70-
7172
} catch (err) {
7273
console.log('❌ Erro clientes:', err);
7374
}
7475

7576
try {
7677
const resCarteira = await api.get(`${EMPRESTIMOS}/carteira`);
77-
7878
lista = Array.isArray(resCarteira.data) ? resCarteira.data : [];
79-
8079
} catch (err) {
8180
console.log('❌ Erro empréstimos:', err);
8281
}
@@ -92,7 +91,6 @@ export default function DashboardScreen() {
9291
let lucro = null;
9392
try {
9493
const resLucro = await api.get(`${EMPRESTIMOS}/relatorio-lucro`);
95-
9694
lucro = resLucro.data;
9795
} catch {
9896
console.log('Report indisponível, continuando sem dados financeiros.');
@@ -116,7 +114,7 @@ export default function DashboardScreen() {
116114
}
117115
}
118116
carregar();
119-
}, [user]);
117+
}, [user, isLoading]);
120118

121119
return (
122120
<SafeAreaView style={s.safe} edges={['top', 'left', 'right']}>

src/mobile/babel.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,18 @@ module.exports = function (api) {
22
api.cache(true);
33
return {
44
presets: ['babel-preset-expo'],
5+
plugins: [
6+
['module-resolver', {
7+
root: ['.'],
8+
alias: {
9+
'@components': './components',
10+
'@services': './services',
11+
'@hooks': './hooks',
12+
'@typings': './types', // ← era @types
13+
'@constants': './constants',
14+
'@contexts': './contexts',
15+
},
16+
}],
17+
],
518
};
619
};

src/mobile/components/clientes/ClienteCard.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import React from 'react';
22
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
33
import { Card } from '@components/common';
4-
import { fmtCpf, fmtTelefone } from '@types/cliente';
5-
import type { Cliente } from '@types/cliente';
6-
4+
import { fmtCpf, fmtTelefone, type Cliente } from '@typings/cliente';
75
interface ClienteCardProps {
86
cliente: Cliente;
97
onEditar: (cliente: Cliente) => void;
108
onDeletar: (id: number) => void;
119
}
1210

1311
export function ClienteCard({ cliente, onEditar, onDeletar }: ClienteCardProps) {
14-
const inicial = cliente.nome.charAt(0).toUpperCase();
15-
12+
const inicial = (cliente.nome ?? '?').charAt(0).toUpperCase();
1613
return (
1714
<Card style={s.card}>
1815
<View style={s.row}>

src/mobile/components/clientes/ClienteForm.tsx

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import {
1111
ActivityIndicator,
1212
} from 'react-native';
1313
import { Input } from '@components/common';
14-
import type { Cliente, ClientePayload } from '@types/cliente';
15-
14+
import type { Cliente, ClientePayload } from '@typings/cliente';
1615
interface ClienteFormProps {
1716
visivel: boolean;
1817
clienteParaEditar?: Cliente | null;
@@ -33,6 +32,22 @@ export function ClienteForm({ visivel, clienteParaEditar, onSalvar, onFechar }:
3332
const [form, setForm] = useState<ClientePayload>(VAZIO);
3433
const [salvando, setSalvando] = useState(false);
3534
const [erros, setErros] = useState<Partial<Record<keyof ClientePayload, string>>>({});
35+
function mascaraCPF(value: string): string {
36+
return value
37+
.replace(/\D/g, '')
38+
.slice(0, 11)
39+
.replace(/(\d{3})(\d)/, '$1.$2')
40+
.replace(/(\d{3})(\d)/, '$1.$2')
41+
.replace(/(\d{3})(\d{1,2})$/, '$1-$2');
42+
}
43+
44+
function mascaraTelefone(value: string): string {
45+
return value
46+
.replace(/\D/g, '')
47+
.slice(0, 11)
48+
.replace(/(\d{2})(\d)/, '($1)$2')
49+
.replace(/(\d{5})(\d{1,4})$/, '$1-$2');
50+
}
3651

3752
useEffect(() => {
3853
if (clienteParaEditar) {
@@ -48,8 +63,7 @@ export function ClienteForm({ visivel, clienteParaEditar, onSalvar, onFechar }:
4863
setForm(VAZIO);
4964
}
5065
setErros({});
51-
}, [clienteParaEditar, visivel]);
52-
66+
}, [clienteParaEditar]);
5367
function set(field: keyof ClientePayload, value: string) {
5468
setForm((prev) => ({ ...prev, [field]: value }));
5569
setErros((prev) => ({ ...prev, [field]: undefined }));
@@ -100,20 +114,22 @@ export function ClienteForm({ visivel, clienteParaEditar, onSalvar, onFechar }:
100114
placeholder="Nome completo"
101115
/>
102116
<Input
103-
label="CPF *"
104-
value={form.cpf}
105-
onChangeText={(v) => set('cpf', v)}
106-
error={erros.cpf}
107-
placeholder="000.000.000-00"
117+
label="CPF *"
118+
value={form.cpf}
119+
onChangeText={(v) => set('cpf', mascaraCPF(v))}
120+
error={erros.cpf}
121+
placeholder="000.000.000-00"
122+
keyboardType="numeric"
108123
/>
109-
<Input
110-
label="Telefone *"
111-
value={form.telefone}
112-
onChangeText={(v) => set('telefone', v)}
113-
error={erros.telefone}
114-
placeholder="(00) 00000-0000"
124+
<Input
125+
label="Telefone *"
126+
value={form.telefone}
127+
onChangeText={(v) => set('telefone', mascaraTelefone(v))}
128+
error={erros.telefone}
129+
placeholder="(00)90000-0000"
130+
keyboardType="numeric"
115131
/>
116-
<Input
132+
<Input
117133
label="E-mail *"
118134
value={form.email}
119135
onChangeText={(v) => set('email', v)}

src/mobile/components/common/Input.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ interface InputProps {
1010
secureTextEntry?: boolean;
1111
error?: string;
1212
placeholder?: string;
13+
keyboardType?: 'default' | 'numeric' | 'email-address' | 'phone-pad';
1314
}
1415

15-
export function Input({ label, value, onChangeText, secureTextEntry = false, error, placeholder }: InputProps) {
16+
export function Input({ label, value, onChangeText, secureTextEntry = false, error, placeholder, keyboardType = 'default' }: InputProps) {
1617
return (
1718
<View style={styles.container}>
1819
<Text style={styles.label}>{label}</Text>
@@ -24,6 +25,7 @@ export function Input({ label, value, onChangeText, secureTextEntry = false, err
2425
placeholder={placeholder}
2526
placeholderTextColor="#aaa"
2627
autoCapitalize="none"
28+
keyboardType={keyboardType} // ← adiciona aqui
2729
/>
2830
{error ? <Text style={styles.error}>{error}</Text> : null}
2931
</View>

src/mobile/contexts/AuthContext.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React, { createContext, useContext, useEffect, useState } from 'react';
22
import AsyncStorage from '@react-native-async-storage/async-storage';
33
import { TOKEN_KEY } from '@services/api';
4-
import type { Usuario } from '@types/usuario';
4+
import type { Usuario } from '@typings/usuario';
5+
56

67
interface AuthContextData {
78
user: Usuario | null;
@@ -17,6 +18,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
1718
const [user, setUser] = useState<Usuario | null>(null);
1819
const [token, setToken] = useState<string | null>(null);
1920
const [isLoading, setIsLoading] = useState(true);
21+
2022

2123
useEffect(() => {
2224
async function loadStoredAuth() {

src/mobile/hooks/useClientes.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState, useCallback } from 'react';
2-
import type { Cliente, ClientePayload } from '@types/cliente';
2+
import type { Cliente, ClientePayload } from '@typings/cliente';
33
import {
44
getClientes,
55
createCliente,
@@ -36,7 +36,6 @@ export function useClientes() {
3636
setClientes((prev) => prev.map((c) => (c.id === id ? atualizado : c)));
3737
return atualizado;
3838
}, []);
39-
4039
const deletar = useCallback(async (id: number) => {
4140
await deleteCliente(id);
4241
setClientes((prev) => prev.filter((c) => c.id !== id));

src/mobile/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@expo/vector-icons": "^15.0.2",
1313
"@react-native-async-storage/async-storage": "2.2.0",
1414
"@react-native-community/datetimepicker": "^8.6.0",
15-
"@react-native-picker/picker": "^2.11.4",
15+
"@react-native-picker/picker": "2.11.4",
1616
"@react-navigation/bottom-tabs": "^7.16.2",
1717
"axios": "^1.7.0",
1818
"expo": "~55.0.0",
@@ -34,6 +34,7 @@
3434
"devDependencies": {
3535
"@types/react": "~19.2.10",
3636
"@types/react-navigation": "^3.0.8",
37+
"babel-plugin-module-resolver": "^5.0.3",
3738
"babel-preset-expo": "~55.0.21",
3839
"typescript": "~5.9.2"
3940
}

src/mobile/services/clientes.service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import api from './api';
22
import { CLIENTES } from '@constants/endpoints';
3-
import type { Cliente, ClientePayload } from '@types/cliente';
3+
import type { Cliente, ClientePayload } from '@typings/cliente';
4+
45

56
export async function getClientes(): Promise<Cliente[]> {
67
const { data } = await api.get<Cliente[]>(CLIENTES);
@@ -18,7 +19,8 @@ export async function createCliente(payload: ClientePayload): Promise<Cliente> {
1819
}
1920

2021
export async function updateCliente(id: number, payload: Partial<ClientePayload>): Promise<Cliente> {
21-
const { data } = await api.put<Cliente>(`${CLIENTES}/${id}`, payload);
22+
await api.put(`${CLIENTES}/${id}`, payload);
23+
const { data } = await api.get<Cliente>(`${CLIENTES}/${id}`);
2224
return data;
2325
}
2426

0 commit comments

Comments
 (0)