Skip to content

Commit

Permalink
feat(#61): trainee preference/settings (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian-Ishimwe authored Nov 1, 2024
1 parent c017178 commit a735812
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 30 deletions.
27 changes: 3 additions & 24 deletions app/dashboard/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import {
lightNotifyIcon,
menu,
} from '@/assets/Icons/dashboard/Icons';
import ProfileAvatar from '@/components/ProfileAvatar';
import Sidebar from '@/components/sidebar';
import { GET_PROFILE } from '@/graphql/queries/user';
import { useQuery } from '@apollo/client';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Slot, useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
Expand All @@ -18,7 +15,7 @@ import {
ScrollView,
TouchableOpacity,
View,
useColorScheme
useColorScheme,
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { SvgXml } from 'react-native-svg';
Expand Down Expand Up @@ -56,23 +53,16 @@ export type ProfileType = {
};
};

import ProfileDropdown from '@/components/ProfileDropdown';
export default function AuthLayout() {
const router = useRouter();
const insets = useSafeAreaInsets();
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(false);
const colorScheme = useColorScheme();
const [authToken, setAuthToken] = useState<string | null>(null);
const [profile, setProfile] = useState<ProfileType | null>(null);

const toggleSidebar = () => setIsSidebarOpen(!isSidebarOpen);

const { data: profileData } = useQuery(GET_PROFILE, {
context: {
headers: { Authorization: `Bearer ${authToken}` },
},
skip: !authToken,
});

useEffect(() => {
(async function () {
const cachedToken = await AsyncStorage.getItem('authToken');
Expand All @@ -86,15 +76,6 @@ export default function AuthLayout() {
})();
}, [authToken]);

useEffect(() => {
(async function () {
if (profileData != undefined) {
setProfile(profileData.getProfile);
await AsyncStorage.setItem('userProfile', JSON.stringify(profileData.getProfile));
}
})();
}, [profileData]);

return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
Expand Down Expand Up @@ -129,9 +110,7 @@ export default function AuthLayout() {
height={40}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => router.push('/dashboard/trainee/profile')}>
<ProfileAvatar name={profile?.name} src={profile?.avatar} size='xs' />
</TouchableOpacity>
<ProfileDropdown />
</View>
</View>
</View>
Expand Down
9 changes: 9 additions & 0 deletions app/dashboard/trainee/preference.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Text, View } from 'react-native';
import Settings from '@/components/settingPreference/SettingPreference';
export default function PreferenceDashboard() {
return (
<View>
<Settings />
</View>
);
}
15 changes: 9 additions & 6 deletions components/LanguagePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const LANGUAGES = [
{ code: 'fr', labelKey: 'languages.french', flagCode: 'FR' },
];

export default function LanguagePicker() {
export default function LanguagePicker({ showFlag = true }) {
const { i18n, t } = useTranslation();
const [modalVisible, setModalVisible] = useState(false);
const colorScheme = useColorScheme();
Expand All @@ -35,11 +35,14 @@ export default function LanguagePicker() {
activeOpacity={0.6}
className="p-2 bg-white rounded dark:bg-primary-dark"
>
<View className="flex-row items-center p-4">
<CountryFlag
isoCode={LANGUAGES.find((lang) => lang.code === i18n.language)?.flagCode || 'GB'}
size={24}
/>
<View className="flex-row items-center ">
{showFlag ? (
<CountryFlag
isoCode={LANGUAGES.find((lang) => lang.code === i18n.language)?.flagCode || 'GB'}
size={24}
/>
) : null}

<Text className="flex-1 ml-3 text-lg font-medium text-gray-900 dark:text-white">
{getCurrentLanguageLabel()}
</Text>
Expand Down
102 changes: 102 additions & 0 deletions components/ProfileDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { useState, useEffect } from 'react';
import { View, TouchableOpacity, Image, Text, StyleSheet, useColorScheme } from 'react-native';
import Popover from 'react-native-popover-view';
import { useRouter } from 'expo-router';
import { ProfileType } from '@/app/dashboard/_layout';
import { useQuery } from '@apollo/client';
import { GET_PROFILE } from '@/graphql/queries/user';
import ProfileAvatar from './ProfileAvatar';
import AsyncStorage from '@react-native-async-storage/async-storage';
const ProfileDropdown = () => {
const theme = useColorScheme();
const [profile, setProfile] = useState<ProfileType | null>(null);
const [authToken, setAuthToken] = useState<string | null>(null);

const [isVisible, setIsVisible] = useState(false);
const router = useRouter();

const togglePopover = () => {
setIsVisible(!isVisible);
};

const handleNavigate = (route: any) => {
setIsVisible(false);
router.push(route);
};

const { data: profileData } = useQuery(GET_PROFILE, {
context: {
headers: { Authorization: `Bearer ${authToken}` },
},
skip: !authToken,
});

useEffect(() => {
(async function () {
const cachedToken = await AsyncStorage.getItem('authToken');
if (cachedToken != authToken) {
setAuthToken(cachedToken);
}

if (cachedToken === null) {
router.push('/auth/login');
}
})();
}, [authToken]);

useEffect(() => {
(async function () {
if (profileData != undefined) {
setProfile(profileData.getProfile);
await AsyncStorage.setItem('userProfile', JSON.stringify(profileData.getProfile));
}
})();
}, [profileData]);
return (
<View className="flex flex-row items-center">
<Popover
isVisible={isVisible}
onRequestClose={togglePopover}
from={
<TouchableOpacity onPress={togglePopover}>
<ProfileAvatar name={profile?.name} src={profile?.avatar} size="xs" />
</TouchableOpacity>
}
>
<View
className={`py-2 px-8 border border-gray-300 shadow-lg shadow-black/30 w-48
${theme === 'light' ? 'bg-[#f9f9f9] text-[#333] border-[#ccc]' : 'bg-[#2b2b2b] text-[#f5f5f5] border-[#444]'}
`}
>
<TouchableOpacity
className="border-b border-gray-300 py-2"
onPress={() => handleNavigate('/dashboard/trainee/profile')}
>
<Text
className={`
${theme === 'light' ? 'text-[#333]' : 'text-[#f5f5f5]'}
`}
>
Profile
</Text>
</TouchableOpacity>

<TouchableOpacity
className="py-2"
onPress={() => handleNavigate('/dashboard/trainee/preference')}
>
<Text
className={`
${theme === 'light' ? 'text-[#333]' : 'text-[#f5f5f5]'}
`}
>
Settings
</Text>
</TouchableOpacity>
</View>
</Popover>
</View>
);
};

export default ProfileDropdown;
142 changes: 142 additions & 0 deletions components/settingPreference/SettingPreference.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React, { useState } from 'react';
import { View, Switch, Text, useColorScheme, TouchableOpacity, StyleSheet } from 'react-native';
import { router } from 'expo-router';
import LanguagePicker from '../LanguagePicker';
import { Dropdown } from 'react-native-element-dropdown';

const Settings = () => {
const [emailNotifications, setEmailNotifications] = useState(false);
const [pushNotifications, setPushNotifications] = useState(false);
const [selectedTheme, setSelectedTheme] = useState('system');

const colorScheme = selectedTheme === 'system' ? useColorScheme() : selectedTheme;
const textStyle = colorScheme === 'dark' ? 'text-gray-100' : 'text-gray-800';
const borderColor = colorScheme === 'dark' ? 'border-gray-700' : 'border-gray-300';
const containerStyle = colorScheme === 'dark' ? 'bg-primary-dark' : 'bg-primary-light';

const themeData = [
{ label: 'System', value: 'system' },
{ label: 'Light', value: 'light' },
{ label: 'Dark', value: 'dark' },
];

return (
<View className={`p-4 mb-8 ${containerStyle}`}>
<Text className={`text-2xl font-extrabold ml-4 mb-4 ${textStyle}`}>Settings</Text>

{/* Profile Section */}
<View className="mb-6 p-4 rounded-lg flex flex-row justify-center">
<View className="flex-1 mr-4">
<Text className={`text-xl font-bold ${textStyle}`}>Profile</Text>
<Text className={`text-lg mt-2 ${textStyle}`}>Edit profile, export account, data…</Text>
</View>
<TouchableOpacity className="flex items-end">
<Text
className={`${textStyle} mt-2`}
onPress={() => router.push('/dashboard/trainee/profile')}
>
Change
</Text>
</TouchableOpacity>
</View>

{/* Theme Picker */}
<View className={`mb-6 p-4 border-t ${borderColor} rounded-lg flex flex-row`}>
<View className="flex-1 mr-4">
<Text className={`text-xl font-bold ${textStyle}`}>Appearance</Text>
<Text className={`text-lg mt-2 ${textStyle}`}>Theme preferences</Text>
</View>
<View className="flex-row items-center gap-5">
<Dropdown
labelField="label"
valueField="value"
data={themeData}
value={selectedTheme}
onChange={(item) => {}}
placeholder="Select Theme"
selectedTextStyle={{ color: colorScheme === 'dark' ? '#fff' : '#000' }}
//@ts-ignore
style={styles.picker(colorScheme)}
/>
</View>
</View>

{/* Language Picker */}
<View className={`mb-6 p-4 border-t ${borderColor} rounded-lg justify-between`}>
<Text className={`text-xl font-bold ${textStyle}`}>Language</Text>
<View className="flex-row justify-between w-full mr-4">
<Text className={`${textStyle} mt-2 text-lg flex-1`}>Language Preference</Text>
<View className={`border ${borderColor} flex-1 rounded-md `}>
<LanguagePicker showFlag={false} />
</View>
</View>
</View>

{/* Email Notifications */}
<View className={`mb-6 p-4 border-t ${borderColor} rounded-lg flex-row justify-between`}>
<View className="flex-1 mr-4">
<Text className={`text-xl font-bold ${textStyle}`}>Email Notifications</Text>
<Text className={`text-lg mt-2 ${textStyle}`}>
Feedback emails, reminder emails, news emails
</Text>
</View>
<Switch
value={emailNotifications}
onValueChange={() => setEmailNotifications((prev) => !prev)}
thumbColor={emailNotifications ? '#6200ee' : '#f4f3f4'}
trackColor={{ false: '#767577', true: '#81b0ff' }}
/>
</View>

{/* Push Notifications */}
<View className={`mb-6 p-4 border-t ${borderColor} rounded-lg flex-row justify-between`}>
<View className="flex-1 mr-4">
<Text className={`text-xl font-bold ${textStyle}`}>Push Notifications</Text>
<Text className={`text-lg mt-2 ${textStyle}`}>
Grade updates, session reminders, performance comments
</Text>
</View>
<Switch
value={pushNotifications}
onValueChange={() => setPushNotifications((prev) => !prev)}
thumbColor={pushNotifications ? '#6200ee' : '#f4f3f4'}
trackColor={{ false: '#767577', true: '#81b0ff' }}
/>
</View>

{/* Privacy and Security */}
<View className={`mb-6 p-4 border-t ${borderColor} rounded-lg flex-row justify-center`}>
<View className="flex-1 mr-4">
<Text className={`text-xl font-bold ${textStyle}`}>Privacy and Security</Text>
<Text className={`text-lg mt-2 ${textStyle}`}>Privacy and Security</Text>
</View>
<Text className={`mt-2 ${textStyle}`}>Change</Text>
</View>

{/* Login Activity */}
<View className={`p-4 mb-7 border-t ${borderColor} rounded-lg flex flex-row justify-center`}>
<View className="flex-1 mr-4">
<Text className={`text-xl font-bold ${textStyle}`}>Login Activity</Text>
<Text className={`text-lg ${textStyle}`}>History of Your login session</Text>
</View>
<Text className={`mt-2 ${textStyle}`}>View</Text>
</View>
</View>
);
};

const styles = StyleSheet.create({
//@ts-ignore
picker: (colorScheme) => ({
height: 50,
width: 178,
backgroundColor: colorScheme === 'dark' ? '#070E1C' : '#fff',
color: colorScheme === 'dark' ? '#fff' : '#000',
borderColor: colorScheme === 'dark' ? '#374151' : '#ccc',
borderWidth: 1,
borderRadius: 8,
paddingHorizontal: 10,
}),
});

export default Settings;
Loading

0 comments on commit a735812

Please sign in to comment.