native_convex_CRM/app/navigation/customDrawerContent.tsx

230 lines
7.1 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
Text,
View,
TouchableOpacity,
ScrollView,
Image,
Alert,
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { DrawerContentComponentProps } from '@react-navigation/drawer';
import Icon from 'react-native-vector-icons/Ionicons';
import { menuItems } from '@mock-data';
import { useTheme } from '@theme';
import { DrawerItemProps } from '@interfaces';
import { route } from '@utils';
import { getStyles } from './customDrawerContent.style';
import { useAppDispatch, useAppSelector, logout, RootState } from '@store';
import { NotificationService } from '@services';
const DrawerItem = ({ label, iconName, focused, onPress }: DrawerItemProps) => {
const { theme: colors } = useTheme();
const styles = getStyles(colors);
return (
<TouchableOpacity
style={[styles.itemWrapper, focused && styles.itemWrapperActive]}
activeOpacity={0.7}
onPress={onPress}>
{focused && <View style={styles.activeIndicatorBar} />}
<Icon
name={iconName}
size={20}
color={focused ? colors.icon : colors.textSecondary}
style={styles.itemIcon}
/>
<Text style={[styles.itemLabel, focused && styles.itemLabelActive]}>
{label}
</Text>
</TouchableOpacity>
);
};
export const CustomDrawerContent = (props: DrawerContentComponentProps) => {
const { state, navigation } = props;
const { theme: colors } = useTheme();
const styles = getStyles(colors);
const dispatch = useAppDispatch();
const userData = useAppSelector((state: RootState) => state.auth.user_data);
const handleLogout = async () => {
try {
const fcmToken = await NotificationService.getFCMToken();
await dispatch(
logout({
id: userData?.staffid ?? '',
fcm_token: fcmToken ?? '',
}),
).unwrap();
navigation.reset({
index: 0,
routes: [{ name: 'AuthStack' }],
});
} catch (e: any) {
const message =
typeof e === 'string'
? e
: e?.message || 'Logout failed. Please try again.';
Alert.alert('Logout Failed', message);
}
};
const activeRouteName = state.routeNames[state.index];
// User details
const fullName =
userData?.full_name ||
`${userData?.firstname || ''} ${userData?.lastname || ''}`.trim() ||
'User';
const email = userData?.email || 'N/A';
const isAdmin = userData?.admin === '1';
const roleText = isAdmin ? 'Administrator' : 'Staff Member';
const isActive = userData?.active === '1';
const [profileImageUrl, setProfileImageUrl] = useState('');
useEffect(() => {
const resolveImageUrl = async () => {
if (!userData?.profile_image) {
setProfileImageUrl('');
return;
}
const rawImage = userData.profile_image.replace(/"/g, '').trim();
if (!rawImage) {
setProfileImageUrl('');
return;
}
if (
rawImage.startsWith('http') ||
rawImage.startsWith('file://') ||
rawImage.startsWith('content://')
) {
setProfileImageUrl(rawImage);
return;
}
const storedBaseUrl = await AsyncStorage.getItem('base_url');
const baseUrl = storedBaseUrl || 'https://demo-convexcrm.convexsol.co';
const cleanBase = baseUrl.replace(/\/+$/, '');
const cleanPath = rawImage.replace(/^\/+/, '');
setProfileImageUrl(`${cleanBase}/${cleanPath}`);
};
resolveImageUrl();
}, [userData?.profile_image]);
const hasProfileImage = !!profileImageUrl;
const initials =
fullName
.split(' ')
.filter(Boolean)
.map(n => n[0])
.join('')
.substring(0, 2)
.toUpperCase() || 'U';
return (
<View style={styles.container}>
{/* ── Drawer Header (User Profile) ── */}
<View style={styles.headerContainer}>
<TouchableOpacity
style={styles.userHeaderTouchable}
activeOpacity={0.8}
onPress={() => {
navigation.navigate('home', { screen: route.profile });
navigation.closeDrawer();
}}>
<View style={styles.avatarWrapper}>
{hasProfileImage ? (
<Image
source={{ uri: profileImageUrl }}
style={styles.avatarImage}
/>
) : (
<View style={styles.avatarCircle}>
<Text style={styles.avatarInitial}>{initials}</Text>
</View>
)}
{isActive && <View style={styles.onlineDot} />}
</View>
<View style={styles.headerDetails}>
<Text style={styles.userName} numberOfLines={1}>
{fullName}
</Text>
<Text style={styles.userEmail} numberOfLines={1}>
{email}
</Text>
<View style={styles.roleBadge}>
<Text style={styles.roleBadgeText}>{roleText}</Text>
</View>
</View>
<Icon name="chevron-forward" size={18} color="#94A3B8" />
</TouchableOpacity>
</View>
{/* ── Navigation Menu List ── */}
<ScrollView contentContainerStyle={styles.scrollContent}>
<View style={styles.menuContainer}>
{menuItems.map(item => {
// Find currently active nested tab route
let activeTabName = '';
try {
let currentRoute = state.routes[state.index];
if (currentRoute.name === 'home' && currentRoute.state) {
const nestedIndex = currentRoute.state.index ?? 0;
activeTabName = currentRoute.state.routes[nestedIndex].name;
} else {
activeTabName = currentRoute.name;
}
} catch (e) {
activeTabName = '';
}
const isFocused =
(item.route === 'home' && activeTabName === route.dashboard) ||
(item.route !== 'home' && activeTabName === item.route);
return (
<DrawerItem
key={item.route}
label={item.label}
iconName={item.icon}
focused={isFocused}
onPress={() => {
if (item.route === 'home') {
navigation.navigate('home', { screen: route.dashboard });
} else {
navigation.navigate('home', { screen: item.route });
}
navigation.closeDrawer();
}}
/>
);
})}
</View>
</ScrollView>
{/* ── Drawer Footer (Logout) ── */}
<View style={styles.footer}>
<TouchableOpacity
style={styles.logoutButton}
activeOpacity={0.7}
onPress={handleLogout}>
<Icon
name="log-out-outline"
size={20}
color="#EF4444"
style={styles.logoutIcon}
/>
<Text style={styles.logoutText}>Sign Out</Text>
</TouchableOpacity>
<Text style={styles.versionText}>App Version: 1.0.0</Text>
</View>
</View>
);
};