feat: redesenho premium mobile-first dos ecras ShopDetails e Profile
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ import { Button } from '../components/ui/Button';
|
||||
import { currency } from '../lib/format';
|
||||
import { RootStackParamList } from '../navigation/types';
|
||||
import { colors, radius, shadows } from '../lib/theme';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
|
||||
type Tab = 'servicos' | 'barbeiros' | 'produtos' | 'detalhes';
|
||||
|
||||
@@ -34,6 +35,7 @@ export default function ShopDetails() {
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<View style={styles.centerState}>
|
||||
<Ionicons name="sync-outline" size={40} color={colors.primary} style={styles.loadingSpinner} />
|
||||
<Text style={styles.centerTitle}>Carregando...</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
@@ -44,6 +46,7 @@ export default function ShopDetails() {
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<View style={styles.centerState}>
|
||||
<Ionicons name="alert-circle-outline" size={48} color={colors.danger} />
|
||||
<Text style={styles.centerTitle}>Não encontrado</Text>
|
||||
<Button onPress={() => navigation.navigate('Explore')}>Voltar</Button>
|
||||
</View>
|
||||
@@ -69,62 +72,97 @@ export default function ShopDetails() {
|
||||
|
||||
const schedule = shop.schedule || defaultSchedule;
|
||||
const currentDayIndex = new Date().getDay() === 0 ? 6 : new Date().getDay() - 1;
|
||||
const isFav = isFavorite(shop.id);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView bounces={false} contentContainerStyle={styles.scrollContent}>
|
||||
<ScrollView bounces={false} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
||||
|
||||
{/* Cover Hero Banner */}
|
||||
<View style={styles.hero}>
|
||||
{shop.imageUrl ? (
|
||||
<Image source={{ uri: shop.imageUrl }} style={styles.heroImage} />
|
||||
) : (
|
||||
<View style={styles.heroPlaceholder}><Text style={styles.heroPlaceholderText}>💈</Text></View>
|
||||
<View style={styles.heroPlaceholder}>
|
||||
<Ionicons name="storefront-outline" size={64} color={colors.primary} style={{ opacity: 0.6 }} />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.heroOverlay} />
|
||||
|
||||
<SafeAreaView style={styles.heroHeader}>
|
||||
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backBtn}>
|
||||
<Text style={styles.backIcon}>←</Text>
|
||||
<SafeAreaView style={styles.heroHeader} edges={['top']}>
|
||||
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backBtn} activeOpacity={0.8}>
|
||||
<Ionicons name="chevron-back" size={24} color={colors.textInverse} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={() => toggleFavorite(shop.id)} style={styles.favBtn}>
|
||||
<Text style={[styles.favIcon, isFavorite(shop.id) && styles.favActive]}>
|
||||
{isFavorite(shop.id) ? '♥' : '♡'}
|
||||
</Text>
|
||||
<TouchableOpacity onPress={() => toggleFavorite(shop.id)} style={styles.favBtn} activeOpacity={0.8}>
|
||||
<Ionicons
|
||||
name={isFav ? "heart" : "heart-outline"}
|
||||
size={24}
|
||||
color={isFav ? colors.danger : colors.textInverse}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
|
||||
{/* Overlapping Info Summary Sheet */}
|
||||
<View style={styles.summaryWrap}>
|
||||
<View style={styles.summaryCard}>
|
||||
<View style={styles.summaryTop}>
|
||||
<View style={styles.ratingBox}>
|
||||
<Text style={styles.ratingValue}>★ {shop.rating.toFixed(1)}</Text>
|
||||
<View style={styles.ratingBadge}>
|
||||
<Ionicons name="star" size={14} color={colors.star} />
|
||||
<Text style={styles.ratingValue}>{(shop.rating || 0).toFixed(1)}</Text>
|
||||
</View>
|
||||
<View style={styles.servicesCountBadge}>
|
||||
<Text style={styles.summaryMeta}>{shop.services.length} serviços</Text>
|
||||
</View>
|
||||
<Text style={styles.summaryMeta}>{shop.services.length} serviços</Text>
|
||||
</View>
|
||||
|
||||
<Text style={styles.shopName}>{shop.name}</Text>
|
||||
<TouchableOpacity onPress={openMap} style={styles.addrBox}>
|
||||
<Text style={styles.shopAddr} numberOfLines={2}>📍 {shop.address}</Text>
|
||||
|
||||
<TouchableOpacity onPress={openMap} style={styles.addrBox} activeOpacity={0.7}>
|
||||
<Ionicons name="location-outline" size={16} color={colors.primary} />
|
||||
<Text style={styles.shopAddr} numberOfLines={2}>
|
||||
{shop.address && shop.address !== 'Endereço a definir' ? shop.address : 'Endereço por definir'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* Quick Stats Grid */}
|
||||
<View style={styles.quickStats}>
|
||||
<View style={styles.quickStat}>
|
||||
<Text style={styles.quickValue}>{shop.barbers.length}</Text>
|
||||
<Text style={styles.quickLabel}>Equipa</Text>
|
||||
<Ionicons name="people-outline" size={18} color={colors.primary} />
|
||||
<View>
|
||||
<Text style={styles.quickValue}>{shop.barbers.length}</Text>
|
||||
<Text style={styles.quickLabel}>Equipa</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.quickStat}>
|
||||
<Text style={styles.quickValue}>{shop.products.length}</Text>
|
||||
<Text style={styles.quickLabel}>Produtos</Text>
|
||||
<Ionicons name="basket-outline" size={18} color={colors.primary} />
|
||||
<View>
|
||||
<Text style={styles.quickValue}>{shop.products.length}</Text>
|
||||
<Text style={styles.quickLabel}>Produtos</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.quickStat}>
|
||||
<Text style={styles.quickValue}>{schedule[currentDayIndex]?.closed ? 'Fechado' : 'Aberto'}</Text>
|
||||
<Text style={styles.quickLabel}>Hoje</Text>
|
||||
<Ionicons name="time-outline" size={18} color={colors.primary} />
|
||||
<View>
|
||||
<Text style={[styles.quickValue, schedule[currentDayIndex]?.closed && styles.closedText]}>
|
||||
{schedule[currentDayIndex]?.closed ? 'Fechado' : 'Aberto'}
|
||||
</Text>
|
||||
<Text style={styles.quickLabel}>Hoje</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Button onPress={() => reserveService(shop.services[0]?.id)} disabled={shop.services.length === 0}>
|
||||
Agendar agora
|
||||
|
||||
<Button
|
||||
onPress={() => reserveService(shop.services[0]?.id)}
|
||||
disabled={shop.services.length === 0}
|
||||
style={styles.reserveBtn}
|
||||
>
|
||||
Agendar agora ⚡
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Tab Selector section */}
|
||||
<View style={styles.tabSection}>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.tabsScroll}>
|
||||
{[
|
||||
@@ -137,6 +175,7 @@ export default function ShopDetails() {
|
||||
key={id}
|
||||
onPress={() => setTab(id as Tab)}
|
||||
style={[styles.tabItem, tab === id && styles.tabItemActive]}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text style={[styles.tabText, tab === id && styles.tabTextActive]}>{label}</Text>
|
||||
</TouchableOpacity>
|
||||
@@ -144,71 +183,135 @@ export default function ShopDetails() {
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* Listagem de Conteúdo */}
|
||||
{/* Content Tabs Area */}
|
||||
<View style={styles.contentArea}>
|
||||
{tab === 'servicos' && (
|
||||
<View style={styles.grid}>
|
||||
{shop.services.map((s) => (
|
||||
<Card key={s.id} style={styles.serviceCard}>
|
||||
<View style={styles.svcInfo}>
|
||||
<Text style={styles.svcName}>{s.name}</Text>
|
||||
<Text style={styles.svcMeta}>{s.duration} min · {currency(s.price)}</Text>
|
||||
</View>
|
||||
<Button size="sm" onPress={() => reserveService(s.id)}>Agendar</Button>
|
||||
</Card>
|
||||
))}
|
||||
{shop.services.length > 0 ? (
|
||||
shop.services.map((s) => (
|
||||
<Card key={s.id} style={styles.serviceCard}>
|
||||
<View style={styles.svcInfo}>
|
||||
<Text style={styles.svcName}>{s.name}</Text>
|
||||
<View style={styles.svcMetaRow}>
|
||||
<Ionicons name="time-outline" size={13} color={colors.textMuted} />
|
||||
<Text style={styles.svcMeta}>{s.duration} min</Text>
|
||||
<Text style={styles.bulletSeparator}>•</Text>
|
||||
<Text style={styles.svcPrice}>{currency(s.price)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Button size="sm" onPress={() => reserveService(s.id)} style={styles.svcBookBtn}>
|
||||
Agendar
|
||||
</Button>
|
||||
</Card>
|
||||
))
|
||||
) : (
|
||||
<View style={styles.emptyContentBlock}>
|
||||
<Ionicons name="cut-outline" size={32} color={colors.textSubtle} />
|
||||
<Text style={styles.emptyContentText}>Sem serviços disponíveis de momento.</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{tab === 'barbeiros' && (
|
||||
<View style={styles.barberList}>
|
||||
{shop.barbers.map((b) => (
|
||||
<Card key={b.id} style={styles.barberCard}>
|
||||
<View style={styles.barberAvatar}>
|
||||
<Text style={styles.avatarTxt}>{b.name.charAt(0)}</Text>
|
||||
</View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={styles.barberName}>{b.name}</Text>
|
||||
<Text style={styles.barberSpecs}>{b.specialties.join(', ')}</Text>
|
||||
</View>
|
||||
</Card>
|
||||
))}
|
||||
{shop.barbers.length > 0 ? (
|
||||
shop.barbers.map((b) => (
|
||||
<Card key={b.id} style={styles.barberCard}>
|
||||
<View style={styles.barberAvatar}>
|
||||
<Text style={styles.avatarTxt}>{b.name.charAt(0).toUpperCase()}</Text>
|
||||
</View>
|
||||
<View style={styles.barberInfo}>
|
||||
<Text style={styles.barberName}>{b.name}</Text>
|
||||
<Text style={styles.barberSpecs} numberOfLines={1}>
|
||||
{b.specialties.length > 0 ? b.specialties.join(', ') : 'Barbeiro especialista'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.barberBadge}>
|
||||
<Ionicons name="shield-checkmark" size={14} color={colors.primary} />
|
||||
</View>
|
||||
</Card>
|
||||
))
|
||||
) : (
|
||||
<View style={styles.emptyContentBlock}>
|
||||
<Ionicons name="people-outline" size={32} color={colors.textSubtle} />
|
||||
<Text style={styles.emptyContentText}>Sem profissionais disponíveis de momento.</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{tab === 'produtos' && (
|
||||
<View style={styles.grid}>
|
||||
{shop.products.map((p) => (
|
||||
<Card key={p.id} style={styles.productCard}>
|
||||
<View style={styles.prodHeader}>
|
||||
<Text style={styles.prodName}>{p.name}</Text>
|
||||
<Text style={styles.prodPrice}>{currency(p.price)}</Text>
|
||||
</View>
|
||||
<Text style={styles.prodStock}>{p.stock} em stock</Text>
|
||||
<Button size="sm" variant="outline" onPress={() => addProduct(p.id)} disabled={p.stock <= 0}>
|
||||
{p.stock > 0 ? 'Adicionar' : 'Esgotado'}
|
||||
</Button>
|
||||
</Card>
|
||||
))}
|
||||
{shop.products.length > 0 ? (
|
||||
shop.products.map((p) => (
|
||||
<Card key={p.id} style={styles.productCard}>
|
||||
<View style={styles.prodHeader}>
|
||||
<Text style={styles.prodName} numberOfLines={1}>{p.name}</Text>
|
||||
<Text style={styles.prodPrice}>{currency(p.price)}</Text>
|
||||
</View>
|
||||
<View style={styles.productMetaRow}>
|
||||
<View style={[styles.stockBadge, p.stock <= 0 && styles.outOfStockBadge]}>
|
||||
<Text style={[styles.stockText, p.stock <= 0 && styles.outOfStockText]}>
|
||||
{p.stock > 0 ? `${p.stock} em stock` : 'Esgotado'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={p.stock > 0 ? "outline" : "ghost"}
|
||||
onPress={() => addProduct(p.id)}
|
||||
disabled={p.stock <= 0}
|
||||
style={styles.productBtn}
|
||||
>
|
||||
{p.stock > 0 ? 'Adicionar 🛒' : 'Indisponível'}
|
||||
</Button>
|
||||
</Card>
|
||||
))
|
||||
) : (
|
||||
<View style={styles.emptyContentBlock}>
|
||||
<Ionicons name="basket-outline" size={32} color={colors.textSubtle} />
|
||||
<Text style={styles.emptyContentText}>Sem produtos disponíveis de momento.</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{tab === 'detalhes' && (
|
||||
<Card style={styles.detailsBox}>
|
||||
<Text style={styles.detailTitle}>Horário</Text>
|
||||
{schedule.map((s, idx) => (
|
||||
<View key={s.day} style={styles.schedRow}>
|
||||
<Text style={[styles.schedDay, idx === currentDayIndex && styles.today]}>{s.day}</Text>
|
||||
<Text style={[styles.schedTime, idx === currentDayIndex && styles.today]}>
|
||||
{s.closed ? 'Fechado' : `${s.open} - ${s.close}`}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
<View style={styles.detailTitleRow}>
|
||||
<Ionicons name="calendar-outline" size={18} color={colors.primary} />
|
||||
<Text style={styles.detailTitle}>Horário de Funcionamento</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.scheduleList}>
|
||||
{schedule.map((s, idx) => (
|
||||
<View key={s.day} style={[styles.schedRow, idx === currentDayIndex && styles.schedRowToday]}>
|
||||
<Text style={[styles.schedDay, idx === currentDayIndex && styles.today]}>
|
||||
{s.day} {idx === currentDayIndex && '• Hoje'}
|
||||
</Text>
|
||||
<Text style={[styles.schedTime, idx === currentDayIndex && styles.today]}>
|
||||
{s.closed ? 'Fechado' : `${s.open} - ${s.close}`}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<View style={styles.divider} />
|
||||
<Text style={styles.detailTitle}>Contacto</Text>
|
||||
<TouchableOpacity onPress={() => Linking.openURL(`tel:${shop.contacts?.phone1}`)}>
|
||||
<Text style={styles.contactLink}>{shop.contacts?.phone1 || 'Não disponível'}</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.detailTitleRow}>
|
||||
<Ionicons name="call-outline" size={18} color={colors.primary} />
|
||||
<Text style={styles.detailTitle}>Contacto Telefónico</Text>
|
||||
</View>
|
||||
|
||||
{shop.contacts?.phone1 ? (
|
||||
<TouchableOpacity onPress={() => Linking.openURL(`tel:${shop.contacts?.phone1}`)} style={styles.phoneLinkBox} activeOpacity={0.7}>
|
||||
<Ionicons name="phone-portrait-outline" size={18} color={colors.primaryDark} />
|
||||
<Text style={styles.contactLink}>{shop.contacts?.phone1}</Text>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<Text style={styles.noContactText}>Contacto telefónico não disponível.</Text>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</View>
|
||||
@@ -226,7 +329,7 @@ const styles = StyleSheet.create({
|
||||
paddingBottom: 40,
|
||||
},
|
||||
hero: {
|
||||
height: 230,
|
||||
height: 250,
|
||||
position: 'relative',
|
||||
},
|
||||
heroImage: {
|
||||
@@ -240,12 +343,9 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
heroPlaceholderText: {
|
||||
fontSize: 64,
|
||||
},
|
||||
heroOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: colors.overlay,
|
||||
backgroundColor: 'rgba(12,20,17,0.3)',
|
||||
},
|
||||
heroHeader: {
|
||||
position: 'absolute',
|
||||
@@ -258,116 +358,135 @@ const styles = StyleSheet.create({
|
||||
paddingTop: 10,
|
||||
},
|
||||
backBtn: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: radius.pill,
|
||||
width: 42,
|
||||
height: 42,
|
||||
borderRadius: 21,
|
||||
backgroundColor: 'rgba(20,33,29,0.72)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1,
|
||||
borderWidth: 0.5,
|
||||
borderColor: 'rgba(255,255,255,0.16)',
|
||||
},
|
||||
backIcon: {
|
||||
color: colors.textInverse,
|
||||
fontSize: 20,
|
||||
fontWeight: '700',
|
||||
},
|
||||
favBtn: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: radius.pill,
|
||||
width: 42,
|
||||
height: 42,
|
||||
borderRadius: 21,
|
||||
backgroundColor: 'rgba(20,33,29,0.72)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1,
|
||||
borderWidth: 0.5,
|
||||
borderColor: 'rgba(255,255,255,0.16)',
|
||||
},
|
||||
favIcon: {
|
||||
color: colors.textInverse,
|
||||
fontSize: 22,
|
||||
},
|
||||
favActive: {
|
||||
color: colors.danger,
|
||||
},
|
||||
summaryWrap: {
|
||||
paddingHorizontal: 20,
|
||||
marginTop: -36,
|
||||
marginTop: -46,
|
||||
zIndex: 2,
|
||||
},
|
||||
summaryCard: {
|
||||
backgroundColor: colors.surface,
|
||||
borderRadius: radius.sm,
|
||||
padding: 18,
|
||||
gap: 12,
|
||||
borderRadius: radius.lg,
|
||||
padding: 20,
|
||||
gap: 14,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
...shadows.card,
|
||||
borderColor: 'rgba(15,118,110,0.06)',
|
||||
shadowColor: '#102018',
|
||||
shadowOffset: { width: 0, height: 8 },
|
||||
shadowOpacity: 0.08,
|
||||
shadowRadius: 16,
|
||||
elevation: 4,
|
||||
},
|
||||
summaryTop: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
summaryMeta: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 12,
|
||||
fontWeight: '800',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
ratingBox: {
|
||||
alignSelf: 'flex-start',
|
||||
backgroundColor: colors.accent,
|
||||
ratingBadge: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.accentSoft,
|
||||
borderRadius: radius.pill,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
gap: 4,
|
||||
},
|
||||
ratingValue: {
|
||||
color: colors.textInverse,
|
||||
color: '#7a4310',
|
||||
fontSize: 12,
|
||||
fontWeight: '900',
|
||||
},
|
||||
servicesCountBadge: {
|
||||
backgroundColor: colors.primarySoft,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
borderRadius: radius.pill,
|
||||
},
|
||||
summaryMeta: {
|
||||
color: colors.primaryDark,
|
||||
fontSize: 11,
|
||||
fontWeight: '800',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
shopName: {
|
||||
color: colors.text,
|
||||
fontSize: 28,
|
||||
lineHeight: 32,
|
||||
fontSize: 26,
|
||||
fontWeight: '900',
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
addrBox: {
|
||||
opacity: 0.8,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
},
|
||||
shopAddr: {
|
||||
flex: 1,
|
||||
color: colors.textMuted,
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
quickStats: {
|
||||
flexDirection: 'row',
|
||||
gap: 10,
|
||||
gap: 8,
|
||||
marginTop: 2,
|
||||
},
|
||||
quickStat: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
backgroundColor: colors.surfaceMuted,
|
||||
borderRadius: radius.sm,
|
||||
borderRadius: radius.md,
|
||||
padding: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
borderColor: 'rgba(15,118,110,0.05)',
|
||||
},
|
||||
quickValue: {
|
||||
color: colors.text,
|
||||
fontSize: 16,
|
||||
fontSize: 14,
|
||||
fontWeight: '900',
|
||||
},
|
||||
closedText: {
|
||||
color: colors.danger,
|
||||
},
|
||||
quickLabel: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 11,
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
marginTop: 2,
|
||||
textTransform: 'uppercase',
|
||||
marginTop: 1,
|
||||
},
|
||||
reserveBtn: {
|
||||
marginTop: 4,
|
||||
borderRadius: radius.md,
|
||||
shadowColor: colors.primary,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 3,
|
||||
},
|
||||
tabSection: {
|
||||
backgroundColor: colors.background,
|
||||
paddingVertical: 10,
|
||||
marginTop: 6,
|
||||
paddingVertical: 12,
|
||||
marginTop: 8,
|
||||
},
|
||||
tabsScroll: {
|
||||
paddingHorizontal: 20,
|
||||
@@ -375,11 +494,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
tabItem: {
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 14,
|
||||
paddingHorizontal: 16,
|
||||
borderRadius: radius.pill,
|
||||
backgroundColor: colors.surfaceMuted,
|
||||
backgroundColor: colors.surface,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
borderColor: 'rgba(15,118,110,0.05)',
|
||||
},
|
||||
tabItemActive: {
|
||||
backgroundColor: colors.primary,
|
||||
@@ -394,7 +513,7 @@ const styles = StyleSheet.create({
|
||||
color: colors.textInverse,
|
||||
},
|
||||
contentArea: {
|
||||
padding: 20,
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
grid: {
|
||||
gap: 12,
|
||||
@@ -404,19 +523,48 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
gap: 12,
|
||||
borderRadius: radius.md,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(15,118,110,0.06)',
|
||||
backgroundColor: colors.surface,
|
||||
shadowColor: '#102018',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.02,
|
||||
shadowRadius: 6,
|
||||
elevation: 1,
|
||||
},
|
||||
svcInfo: {
|
||||
flex: 1,
|
||||
gap: 4,
|
||||
gap: 6,
|
||||
},
|
||||
svcName: {
|
||||
color: colors.text,
|
||||
fontSize: 16,
|
||||
fontWeight: '800',
|
||||
},
|
||||
svcMetaRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
},
|
||||
svcMeta: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
},
|
||||
bulletSeparator: {
|
||||
color: colors.borderStrong,
|
||||
fontSize: 12,
|
||||
marginHorizontal: 2,
|
||||
},
|
||||
svcPrice: {
|
||||
color: colors.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: '800',
|
||||
},
|
||||
svcBookBtn: {
|
||||
borderRadius: radius.md,
|
||||
paddingHorizontal: 14,
|
||||
},
|
||||
barberList: {
|
||||
gap: 12,
|
||||
@@ -426,20 +574,30 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
padding: 14,
|
||||
gap: 14,
|
||||
borderRadius: radius.md,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(15,118,110,0.06)',
|
||||
backgroundColor: colors.surface,
|
||||
},
|
||||
barberAvatar: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: radius.md,
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 24,
|
||||
backgroundColor: colors.primarySoft,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(15,118,110,0.1)',
|
||||
},
|
||||
avatarTxt: {
|
||||
color: colors.primary,
|
||||
fontSize: 20,
|
||||
fontSize: 18,
|
||||
fontWeight: '900',
|
||||
},
|
||||
barberInfo: {
|
||||
flex: 1,
|
||||
gap: 2,
|
||||
},
|
||||
barberName: {
|
||||
color: colors.text,
|
||||
fontSize: 16,
|
||||
@@ -448,15 +606,29 @@ const styles = StyleSheet.create({
|
||||
barberSpecs: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
},
|
||||
barberBadge: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: 12,
|
||||
backgroundColor: colors.primarySoft,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
productCard: {
|
||||
padding: 16,
|
||||
gap: 8,
|
||||
gap: 10,
|
||||
borderRadius: radius.md,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(15,118,110,0.06)',
|
||||
backgroundColor: colors.surface,
|
||||
},
|
||||
prodHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
gap: 12,
|
||||
},
|
||||
prodName: {
|
||||
color: colors.text,
|
||||
@@ -469,28 +641,69 @@ const styles = StyleSheet.create({
|
||||
fontSize: 16,
|
||||
fontWeight: '900',
|
||||
},
|
||||
prodStock: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 12,
|
||||
marginBottom: 4,
|
||||
productMetaRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
stockBadge: {
|
||||
backgroundColor: colors.successSoft,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
borderRadius: radius.xs,
|
||||
},
|
||||
outOfStockBadge: {
|
||||
backgroundColor: colors.dangerSoft,
|
||||
},
|
||||
stockText: {
|
||||
color: colors.success,
|
||||
fontSize: 10,
|
||||
fontWeight: '800',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
outOfStockText: {
|
||||
color: colors.danger,
|
||||
},
|
||||
productBtn: {
|
||||
borderRadius: radius.md,
|
||||
marginTop: 2,
|
||||
},
|
||||
detailsBox: {
|
||||
padding: 20,
|
||||
gap: 12,
|
||||
gap: 14,
|
||||
borderRadius: radius.lg,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(15,118,110,0.06)',
|
||||
backgroundColor: colors.surface,
|
||||
},
|
||||
detailTitleRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
marginBottom: 4,
|
||||
},
|
||||
detailTitle: {
|
||||
color: colors.text,
|
||||
fontSize: 18,
|
||||
fontSize: 16,
|
||||
fontWeight: '800',
|
||||
marginBottom: 8,
|
||||
},
|
||||
scheduleList: {
|
||||
gap: 8,
|
||||
},
|
||||
schedRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
schedRowToday: {
|
||||
backgroundColor: colors.primarySoft,
|
||||
borderRadius: radius.sm,
|
||||
paddingVertical: 6,
|
||||
},
|
||||
schedDay: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
schedTime: {
|
||||
color: colors.text,
|
||||
@@ -498,28 +711,61 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '600',
|
||||
},
|
||||
today: {
|
||||
color: colors.primary,
|
||||
color: colors.primaryDark,
|
||||
fontWeight: '800',
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: colors.border,
|
||||
marginVertical: 12,
|
||||
backgroundColor: 'rgba(15,118,110,0.08)',
|
||||
marginVertical: 6,
|
||||
},
|
||||
phoneLinkBox: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
backgroundColor: colors.primarySoft,
|
||||
alignSelf: 'flex-start',
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 14,
|
||||
borderRadius: radius.sm,
|
||||
borderWidth: 0.5,
|
||||
borderColor: 'rgba(15,118,110,0.1)',
|
||||
},
|
||||
contactLink: {
|
||||
color: colors.primaryDark,
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontSize: 15,
|
||||
fontWeight: '800',
|
||||
},
|
||||
noContactText: {
|
||||
color: colors.textSubtle,
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
centerState: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 40,
|
||||
gap: 12,
|
||||
},
|
||||
centerTitle: {
|
||||
color: colors.text,
|
||||
fontSize: 18,
|
||||
fontSize: 16,
|
||||
fontWeight: '800',
|
||||
},
|
||||
loadingSpinner: {
|
||||
marginBottom: 4,
|
||||
},
|
||||
emptyContentBlock: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 40,
|
||||
gap: 10,
|
||||
},
|
||||
emptyContentText: {
|
||||
color: colors.textMuted,
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user