230 lines
7.1 KiB
TypeScript
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>
|
|
);
|
|
};
|