From 3988535f16306265f2fd2d520397008343871861 Mon Sep 17 00:00:00 2001
From: 230417 <230417@epvc.pt>
Date: Thu, 14 May 2026 09:58:39 +0100
Subject: [PATCH] refactor: add logout section with version display to profile
and dashboard, and implement conflict check for appointment confirmation
---
src/context/AppContext.tsx | 25 ++++++++-
src/pages/Dashboard.tsx | 43 ++++++++++++++++
src/pages/Profile.tsx | 101 ++++++++++++++-----------------------
3 files changed, 105 insertions(+), 64 deletions(-)
diff --git a/src/context/AppContext.tsx b/src/context/AppContext.tsx
index 1b1e9b3..66e5f48 100644
--- a/src/context/AppContext.tsx
+++ b/src/context/AppContext.tsx
@@ -522,9 +522,30 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
const updateAppointmentStatus = async (id: string, status: Appointment['status']) => {
const apt = appointments.find((a) => a.id === id);
- await supabase.from('appointments').update({ status }).eq('id', id);
+ if (!apt) return;
- if (status === 'cancelado' && apt) {
+ if (status === 'confirmado') {
+ // Check if there is already a confirmed appointment for this barber at this time
+ const conflict = appointments.find(a =>
+ a.barberId === apt.barberId &&
+ a.date === apt.date &&
+ a.status === 'confirmado' &&
+ a.id !== id
+ );
+
+ if (conflict) {
+ Alert.alert('Conflito de Horário', 'Já existe uma marcação confirmada para este horário com este profissional.');
+ return;
+ }
+ }
+
+ const { error } = await supabase.from('appointments').update({ status }).eq('id', id);
+ if (error) {
+ Alert.alert('Erro', 'Não foi possível atualizar o estado da marcação.');
+ return;
+ }
+
+ if (status === 'cancelado') {
const waitlistDate = apt.date.split(' ')[0]; // Extract YYYY-MM-DD
const waitingUsers = waitlists.filter(w => w.barberId === apt.barberId && w.date === waitlistDate && w.status === 'pending');
diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx
index 83ad53b..77cd6c8 100644
--- a/src/pages/Dashboard.tsx
+++ b/src/pages/Dashboard.tsx
@@ -440,6 +440,14 @@ export default function Dashboard() {
))}
+
+
+
+ Sair da Conta
+ ⎗
+
+ Smart Agenda v1.0.4
+
)}
@@ -813,4 +821,39 @@ const styles = StyleSheet.create({
width: 55,
textAlign: 'center',
},
+ logoutSection: {
+ marginTop: 40,
+ paddingTop: 20,
+ borderTopWidth: 1,
+ borderTopColor: 'rgba(255,255,255,0.06)',
+ alignItems: 'center',
+ },
+ logoutButtonPremium: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: 'rgba(239,68,68,0.1)',
+ paddingVertical: 16,
+ paddingHorizontal: 24,
+ borderRadius: 16,
+ width: '100%',
+ borderWidth: 1,
+ borderColor: 'rgba(239,68,68,0.2)',
+ gap: 12,
+ },
+ logoutButtonText: {
+ color: '#ef4444',
+ fontSize: 16,
+ fontWeight: '800',
+ },
+ logoutButtonIcon: {
+ color: '#ef4444',
+ fontSize: 20,
+ },
+ versionText: {
+ color: '#334155',
+ fontSize: 12,
+ marginTop: 16,
+ fontWeight: '600',
+ },
});
diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx
index 459351d..4e9c01b 100644
--- a/src/pages/Profile.tsx
+++ b/src/pages/Profile.tsx
@@ -126,11 +126,7 @@ export default function Profile() {
{isBarber ? 'Gestor de Espaço' : 'Cliente'}
-
-
- ⎗
-
-
+
{/* Notificações (Horizontal) */}
@@ -296,6 +292,14 @@ export default function Profile() {
)}
+
+
+
+ Sair da Conta
+ ⎗
+
+ Smart Agenda v1.0.4
+
);
@@ -312,67 +316,40 @@ const styles = StyleSheet.create({
profileEmail: { color: '#64748b', fontSize: 14, fontWeight: '500' },
roleBadge: { alignSelf: 'flex-start', backgroundColor: 'rgba(99,102,241,0.1)', borderRadius: 8, paddingHorizontal: 8, paddingVertical: 3, marginTop: 4 },
roleText: { color: '#818cf8', fontSize: 10, fontWeight: '800', textTransform: 'uppercase' },
- logoutBtn: {
- padding: 4,
+ reviewActions: { flexDirection: 'row', gap: 14 },
+ logoutSection: {
+ marginTop: 60,
+ paddingTop: 30,
+ borderTopWidth: 1,
+ borderTopColor: 'rgba(255,255,255,0.06)',
+ alignItems: 'center',
},
- logoutIconBg: {
- width: 40,
- height: 40,
- borderRadius: 12,
- backgroundColor: 'rgba(239,68,68,0.1)',
+ logoutButtonPremium: {
+ flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
+ backgroundColor: 'rgba(239,68,68,0.1)',
+ paddingVertical: 18,
+ paddingHorizontal: 24,
+ borderRadius: 20,
+ width: '100%',
borderWidth: 1,
borderColor: 'rgba(239,68,68,0.2)',
+ gap: 12,
+ },
+ logoutButtonText: {
+ color: '#ef4444',
+ fontSize: 16,
+ fontWeight: '800',
+ },
+ logoutButtonIcon: {
+ color: '#ef4444',
+ fontSize: 20,
+ },
+ versionText: {
+ color: '#334155',
+ fontSize: 12,
+ marginTop: 16,
+ fontWeight: '600',
},
- logoutIcon: { color: '#ef4444', fontSize: 20, fontWeight: '700' },
- sectionTitle: { color: '#f8fafc', fontSize: 18, fontWeight: '800', marginBottom: 12 },
- notifSection: { marginBottom: 32 },
- notifScroll: { marginHorizontal: -20, paddingHorizontal: 20 },
- notifCard: { backgroundColor: '#141420', width: 240, borderRadius: 20, padding: 16, marginRight: 12, borderWidth: 1, borderColor: 'rgba(255,255,255,0.06)', gap: 8, elevation: 2 },
- notifUnread: { borderColor: 'rgba(99,102,241,0.3)', backgroundColor: '#18182b' },
- notifMsg: { color: '#94a3b8', fontSize: 13, lineHeight: 18, fontWeight: '500' },
- markRead: { color: '#818cf8', fontSize: 12, fontWeight: '800', textTransform: 'uppercase', letterSpacing: 0.5 },
- tabBar: { flexDirection: 'row', marginBottom: 24, borderBottomWidth: 1, borderBottomColor: 'rgba(255,255,255,0.06)' },
- tabItem: { paddingVertical: 14, marginRight: 24, position: 'relative' },
- tabActive: {},
- tabText: { color: '#475569', fontSize: 15, fontWeight: '700' },
- tabTextActive: { color: '#f8fafc' },
- tabIndicator: { position: 'absolute', bottom: -1, left: 0, right: 0, height: 3, backgroundColor: '#6366f1', borderRadius: 4 },
- tabContent: { minHeight: 200 },
- listContainer: { gap: 16 },
- emptyTxt: { color: '#475569', textAlign: 'center', marginTop: 20, fontWeight: '600' },
- subTitle: { color: '#64748b', fontSize: 13, fontWeight: '700', textTransform: 'uppercase', marginBottom: 8, letterSpacing: 1 },
- agendaCard: { padding: 16, gap: 12, backgroundColor: '#141420', borderRadius: 20, borderWidth: 1, borderColor: 'rgba(255,255,255,0.03)' },
- agendaTop: { flexDirection: 'row', alignItems: 'center', gap: 14 },
- dateBox: { backgroundColor: '#1c1c2e', padding: 10, borderRadius: 14, alignItems: 'center', minWidth: 55, borderWidth: 1, borderColor: 'rgba(255,255,255,0.05)' },
- dateDay: { color: '#f8fafc', fontSize: 20, fontWeight: '900' },
- dateMonth: { color: '#818cf8', fontSize: 10, fontWeight: '800', textTransform: 'uppercase' },
- agendaMain: { flex: 1, gap: 3 },
- agendaShop: { color: '#f8fafc', fontSize: 17, fontWeight: '800' },
- agendaTime: { color: '#64748b', fontSize: 13, fontWeight: '500' },
- statusTag: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 10 },
- statusText: { fontSize: 10, fontWeight: '900', textTransform: 'uppercase', letterSpacing: 0.5 },
- reviewBtn: { marginTop: 8, borderRadius: 12 },
- shopCard: { flexDirection: 'row', alignItems: 'center', gap: 14, padding: 16, backgroundColor: '#141420', borderRadius: 20, marginBottom: 4 },
- shopIcon: { width: 48, height: 48, borderRadius: 14, backgroundColor: 'rgba(99,102,241,0.05)', alignItems: 'center', justifyContent: 'center', borderWidth: 1, borderColor: 'rgba(99,102,241,0.1)' },
- shopIconText: { color: '#818cf8', fontSize: 20, fontWeight: '900' },
- shopName: { color: '#f8fafc', fontSize: 16, fontWeight: '800' },
- shopAddr: { color: '#475569', fontSize: 12, fontWeight: '500' },
- shopRating: { color: '#fbbf24', fontWeight: '900', fontSize: 14 },
- reviewCard: { padding: 18, gap: 10, backgroundColor: '#141420', borderRadius: 20 },
- reviewHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
- reviewStars: { color: '#fbbf24', fontSize: 15 },
- reviewDate: { color: '#475569', fontSize: 12, fontWeight: '600' },
- reviewComment: { color: '#f8fafc', fontSize: 14, lineHeight: 20, fontWeight: '400' },
- statCard: { padding: 24, alignItems: 'center', gap: 6, backgroundColor: '#141420', borderRadius: 24, borderWidth: 1, borderColor: 'rgba(99,102,241,0.1)' },
- statLabel: { color: '#64748b', fontSize: 12, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 1 },
- statValue: { color: '#f8fafc', fontSize: 28, fontWeight: '900' },
- reviewModal: { marginTop: 24, padding: 24, gap: 16, borderColor: 'rgba(99,102,241,0.3)', borderRadius: 24 },
- reviewTitle: { color: '#f8fafc', fontSize: 18, fontWeight: '800', textAlign: 'center' },
- stars: { flexDirection: 'row', justifyContent: 'center', gap: 12 },
- star: { fontSize: 36, color: '#1c1c2e' },
- starActive: { color: '#fbbf24' },
- reviewInput: { backgroundColor: '#1c1c2e', borderRadius: 16, padding: 16, color: '#f8fafc', height: 100, textAlignVertical: 'top', borderWidth: 1, borderColor: 'rgba(255,255,255,0.05)' },
- reviewActions: { flexDirection: 'row', gap: 14 },
});