Add 'Programs' tab to TabLayout and refactor ServicesScreen to use FlatList for service cards
- Introduced a new 'Programs' tab in the TabLayout with localized title and icon. - Refactored ServicesScreen to replace ScrollView with FlatList for improved performance and layout, displaying service cards with icons. - Updated localization files to include new keys for services and programs.
This commit is contained in:
@@ -42,6 +42,13 @@ export default function TabLayout() {
|
|||||||
tabBarIcon: ({ color }) => <TabBarIcon name="th-large" color={color} />,
|
tabBarIcon: ({ color }) => <TabBarIcon name="th-large" color={color} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="programs"
|
||||||
|
options={{
|
||||||
|
title: i18n.t('programs'),
|
||||||
|
tabBarIcon: ({ color }) => <TabBarIcon name="briefcase" color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
190
app/(tabs)/programs.tsx
Normal file
190
app/(tabs)/programs.tsx
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { StyleSheet, SafeAreaView, Text, View, TouchableOpacity, ScrollView } from 'react-native';
|
||||||
|
import Colors from '@/constants/Colors';
|
||||||
|
import { FontAwesome, FontAwesome5 } from '@expo/vector-icons';
|
||||||
|
|
||||||
|
type Activity = {
|
||||||
|
time: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
transport?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Activities = {
|
||||||
|
[key: string]: Activity[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Programs() {
|
||||||
|
const colorScheme = 'dark';
|
||||||
|
const [activeDay, setActiveDay] = useState('Day 1');
|
||||||
|
|
||||||
|
const activities: Activities = {
|
||||||
|
'Day 1': [
|
||||||
|
{
|
||||||
|
time: '16:30 - 19:15',
|
||||||
|
title: 'Depart for Mecca',
|
||||||
|
description: 'From your location to Mecca',
|
||||||
|
icon: <FontAwesome name="plane" size={24} color="black" />,
|
||||||
|
transport: 'Transport to hotel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: '21:00 - 23:00',
|
||||||
|
title: 'Dinner at Al-Baik',
|
||||||
|
description: 'Masjid al-',
|
||||||
|
icon: <FontAwesome name="cutlery" size={24} color="black" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: '00:00 - 04:00',
|
||||||
|
title: 'Tawaf',
|
||||||
|
description: 'Kaaba',
|
||||||
|
icon: <FontAwesome5 name="kaaba" size={24} color="black" />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'Day 2': [
|
||||||
|
{
|
||||||
|
time: '10:00 - 12:00',
|
||||||
|
title: 'Shopping',
|
||||||
|
description: 'Zamzam Tower',
|
||||||
|
icon: <FontAwesome name="shopping-bag" size={24} color="black" />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'Day 3': [],
|
||||||
|
'Day 4': [],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={[styles.container, { backgroundColor: Colors[colorScheme].background }]}>
|
||||||
|
<View style={styles.header}>
|
||||||
|
<Text style={[styles.title, { color: Colors[colorScheme].text }]}>Umrah Pilgrimage</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.daysScroll}>
|
||||||
|
{Object.keys(activities).map((day) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={day}
|
||||||
|
style={[styles.dayButton, activeDay === day && styles.activeDayButton]}
|
||||||
|
onPress={() => setActiveDay(day)}
|
||||||
|
>
|
||||||
|
<Text style={[styles.dayText, activeDay === day && styles.activeDayText]}>{day}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<ScrollView style={styles.content}>
|
||||||
|
{activities[activeDay].map((activity, index) => (
|
||||||
|
<View key={index}>
|
||||||
|
<View style={styles.activityCard}>
|
||||||
|
<View style={styles.timeContainer}>
|
||||||
|
<Text style={styles.timeText}>{activity.time}</Text>
|
||||||
|
<Text style={styles.activityTitle}>{activity.title}</Text>
|
||||||
|
<Text style={styles.activityDescription}>{activity.description}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.iconContainer}>{activity.icon}</View>
|
||||||
|
</View>
|
||||||
|
{activity.transport && (
|
||||||
|
<View style={styles.transportContainer}>
|
||||||
|
<View style={styles.dot} />
|
||||||
|
<View style={styles.dot} />
|
||||||
|
<View style={styles.dot} />
|
||||||
|
<Text style={styles.transportText}>{activity.transport}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: 10,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
daysScroll: {
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
paddingVertical: 10,
|
||||||
|
},
|
||||||
|
dayButton: {
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
borderRadius: 20,
|
||||||
|
backgroundColor: '#333',
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
activeDayButton: {
|
||||||
|
backgroundColor: '#555',
|
||||||
|
},
|
||||||
|
dayText: {
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
activeDayText: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flex: 1,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
},
|
||||||
|
activityCard: {
|
||||||
|
backgroundColor: '#222',
|
||||||
|
borderRadius: 15,
|
||||||
|
padding: 20,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
timeContainer: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
timeText: {
|
||||||
|
color: 'gray',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
activityTitle: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginVertical: 4,
|
||||||
|
},
|
||||||
|
activityDescription: {
|
||||||
|
color: 'gray',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
borderRadius: 25,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
transportContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginVertical: 10,
|
||||||
|
},
|
||||||
|
dot: {
|
||||||
|
width: 6,
|
||||||
|
height: 6,
|
||||||
|
borderRadius: 3,
|
||||||
|
backgroundColor: 'gray',
|
||||||
|
marginHorizontal: 3,
|
||||||
|
},
|
||||||
|
transportText: {
|
||||||
|
color: 'gray',
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,40 +1,39 @@
|
|||||||
import { StyleSheet, SafeAreaView, ScrollView } from 'react-native';
|
import { StyleSheet, SafeAreaView, FlatList } from 'react-native';
|
||||||
import { Text, View } from '@/components/Themed';
|
import { Text } from '@/components/Themed';
|
||||||
import AdhkarCard from '@/components/AdhkarCard';
|
|
||||||
import SupplicationListItem from '@/components/SupplicationListItem';
|
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
import { FontAwesome5 } from '@expo/vector-icons';
|
||||||
|
import ServiceCard from '@/components/ServiceCard';
|
||||||
|
|
||||||
const adhkarCategories = [
|
const services = [
|
||||||
'morningEveningThikr',
|
{
|
||||||
'beforeSleepingThikr',
|
key: 'sarToTmt',
|
||||||
'afterSalamThikr',
|
icon: <FontAwesome5 name="exchange-alt" size={24} color="#D4AF37" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'hotelBusinessCard',
|
||||||
|
icon: <FontAwesome5 name="vcard" size={24} color="#D4AF37" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'masterkeyBox',
|
||||||
|
icon: <FontAwesome5 name="key" size={24} color="#D4AF37" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'translator',
|
||||||
|
icon: <FontAwesome5 name="language" size={24} color="#D4AF37" />,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const supplications = [
|
|
||||||
'breakingFastSupplication',
|
|
||||||
'fastingPersonSupplication',
|
|
||||||
'insultedWhileFasting',
|
|
||||||
'seeingFruitSupplication',
|
|
||||||
'sneezingSupplication',
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function ServicesScreen() {
|
export default function ServicesScreen() {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
<ScrollView>
|
|
||||||
<View style={styles.innerContainer}>
|
|
||||||
<Text style={styles.title}>{i18n.t('services')}</Text>
|
<Text style={styles.title}>{i18n.t('services')}</Text>
|
||||||
<Text style={styles.sectionTitle}>{i18n.t('adhkar')}</Text>
|
<FlatList
|
||||||
{adhkarCategories.map((titleKey, index) => (
|
data={services}
|
||||||
<AdhkarCard key={index} title={i18n.t(titleKey)} />
|
renderItem={({ item }) => <ServiceCard title={i18n.t(item.key)} icon={item.icon} />}
|
||||||
))}
|
keyExtractor={(item) => item.key}
|
||||||
|
numColumns={2}
|
||||||
<Text style={styles.sectionTitle}>{i18n.t('hisnAlMuslim')}</Text>
|
columnWrapperStyle={styles.row}
|
||||||
{supplications.map((textKey, index) => (
|
/>
|
||||||
<SupplicationListItem key={index} text={i18n.t(textKey)} onPress={() => {}} />
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -42,8 +41,6 @@ export default function ServicesScreen() {
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
|
||||||
innerContainer: {
|
|
||||||
paddingHorizontal: 15,
|
paddingHorizontal: 15,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
@@ -51,10 +48,8 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginVertical: 15,
|
marginVertical: 15,
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
row: {
|
||||||
fontSize: 20,
|
flex: 1,
|
||||||
fontWeight: 'bold',
|
justifyContent: 'space-around',
|
||||||
color: '#fff',
|
},
|
||||||
marginVertical: 10,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, StyleSheet, Image, ImageSourcePropType } from 'react-native';
|
import { View, Text, StyleSheet, Image, ImageSourcePropType } from 'react-native';
|
||||||
import Colors from '@/constants/Colors';
|
import Colors from '@/constants/Colors';
|
||||||
import i18n from '@/i18n';
|
|
||||||
|
|
||||||
type FeatureCardProps = {
|
type FeatureCardProps = {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
46
components/ServiceCard.tsx
Normal file
46
components/ServiceCard.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import Colors from '@/constants/Colors';
|
||||||
|
|
||||||
|
type ServiceCardProps = {
|
||||||
|
title: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ServiceCard({ title, icon }: ServiceCardProps) {
|
||||||
|
const colorScheme = 'dark';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: Colors[colorScheme].secondary }]}>
|
||||||
|
<View style={styles.content}>
|
||||||
|
<View style={styles.iconContainer}>{icon}</View>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
borderRadius: 15,
|
||||||
|
padding: 15,
|
||||||
|
marginVertical: 10,
|
||||||
|
width: '45%',
|
||||||
|
aspectRatio: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: Colors.dark.text,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"services": "Services",
|
"services": "Services",
|
||||||
|
"supplications": "Supplications",
|
||||||
"yourJourneyToHajj": "Your Journey to Hajj",
|
"yourJourneyToHajj": "Your Journey to Hajj",
|
||||||
"hajjEssentials": "Everything you need for Hajj essentials.",
|
"hajjEssentials": "Everything you need for Hajj essentials.",
|
||||||
"umrah": "Umrah",
|
"umrah": "Umrah",
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
"nobleRawdah": "Noble Rawdah",
|
"nobleRawdah": "Noble Rawdah",
|
||||||
"newExperience": "New Experience",
|
"newExperience": "New Experience",
|
||||||
"prayerTimes": "Prayer Times",
|
"prayerTimes": "Prayer Times",
|
||||||
|
"programs": "Programs",
|
||||||
"leftOnPrayer": "Left on {{prayerName}} prayer",
|
"leftOnPrayer": "Left on {{prayerName}} prayer",
|
||||||
"servicesToEnrich": "Services to Enrich Your Spiritual Experience",
|
"servicesToEnrich": "Services to Enrich Your Spiritual Experience",
|
||||||
"quran": "Qur'an",
|
"quran": "Qur'an",
|
||||||
@@ -22,5 +24,9 @@
|
|||||||
"fastingPersonSupplication": "Supplication said by one fasting when presented with food and does not break his fast",
|
"fastingPersonSupplication": "Supplication said by one fasting when presented with food and does not break his fast",
|
||||||
"insultedWhileFasting": "When insulted while fasting",
|
"insultedWhileFasting": "When insulted while fasting",
|
||||||
"seeingFruitSupplication": "Supplication upon seeing the early or premature fruit",
|
"seeingFruitSupplication": "Supplication upon seeing the early or premature fruit",
|
||||||
"sneezingSupplication": "Supplication upon sneezing"
|
"sneezingSupplication": "Supplication upon sneezing",
|
||||||
|
"sarToTmt": "SAR to TMT",
|
||||||
|
"hotelBusinessCard": "Hotel Business Card",
|
||||||
|
"masterkeyBox": "Masterkey Box",
|
||||||
|
"translator": "Translator"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"home": "Главная",
|
"home": "Главная",
|
||||||
"services": "Услуги",
|
"services": "Сервисы",
|
||||||
"yourJourneyToHajj": "Ваше путешествие в Хадж",
|
"yourJourneyToHajj": "Ваше путешествие в Хадж",
|
||||||
"hajjEssentials": "Все, что вам нужно для Хаджа.",
|
"hajjEssentials": "Все, что вам нужно для Хаджа.",
|
||||||
"umrah": "Умра",
|
"umrah": "Умра",
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
"nobleRawdah": "Благородная Равда",
|
"nobleRawdah": "Благородная Равда",
|
||||||
"newExperience": "Новый опыт",
|
"newExperience": "Новый опыт",
|
||||||
"prayerTimes": "Время молитв",
|
"prayerTimes": "Время молитв",
|
||||||
|
"programs": "Программы",
|
||||||
"leftOnPrayer": "Осталось до молитвы {{prayerName}}",
|
"leftOnPrayer": "Осталось до молитвы {{prayerName}}",
|
||||||
"servicesToEnrich": "Услуги для обогащения вашего духовного опыта",
|
"servicesToEnrich": "Услуги для обогащения вашего духовного опыта",
|
||||||
"quran": "Коран",
|
"quran": "Коран",
|
||||||
@@ -22,5 +23,9 @@
|
|||||||
"fastingPersonSupplication": "Мольба, произносимая постящимся, когда ему преподносят еду, и он не прерывает свой пост",
|
"fastingPersonSupplication": "Мольба, произносимая постящимся, когда ему преподносят еду, и он не прерывает свой пост",
|
||||||
"insultedWhileFasting": "Когда оскорбляют во время поста",
|
"insultedWhileFasting": "Когда оскорбляют во время поста",
|
||||||
"seeingFruitSupplication": "Мольба при виде ранних или незрелых плодов",
|
"seeingFruitSupplication": "Мольба при виде ранних или незрелых плодов",
|
||||||
"sneezingSupplication": "Мольба при чихании"
|
"sneezingSupplication": "Мольба при чихании",
|
||||||
|
"sarToTmt": "SAR в TMT",
|
||||||
|
"hotelBusinessCard": "Визитная карточка отеля",
|
||||||
|
"masterkeyBox": "Ящик для мастер-ключей",
|
||||||
|
"translator": "Переводчик"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"home": "Baş sahypa",
|
"home": "Baş sahypa",
|
||||||
"services": "Hyzmatlar",
|
"services": "Hyzmatlar",
|
||||||
|
"supplications": "Dogalar",
|
||||||
"yourJourneyToHajj": "Haj syýahatyňyz",
|
"yourJourneyToHajj": "Haj syýahatyňyz",
|
||||||
"hajjEssentials": "Haj üçin zerur zatlar.",
|
"hajjEssentials": "Haj üçin zerur zatlar.",
|
||||||
"umrah": "Umra",
|
"umrah": "Umra",
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
"nobleRawdah": "Asylly Rawdah",
|
"nobleRawdah": "Asylly Rawdah",
|
||||||
"newExperience": "Täze tejribe",
|
"newExperience": "Täze tejribe",
|
||||||
"prayerTimes": "Namaz wagtlary",
|
"prayerTimes": "Namaz wagtlary",
|
||||||
|
"programs": "Programmalar",
|
||||||
"leftOnPrayer": "{{prayerName}} namazyna çenli galdy",
|
"leftOnPrayer": "{{prayerName}} namazyna çenli galdy",
|
||||||
"servicesToEnrich": "Ruhy tejribäňizi baýlaşdyrmak üçin hyzmatlar",
|
"servicesToEnrich": "Ruhy tejribäňizi baýlaşdyrmak üçin hyzmatlar",
|
||||||
"quran": "Kuran",
|
"quran": "Kuran",
|
||||||
@@ -31,5 +33,9 @@
|
|||||||
"fastingPersonSupplication": "Agzy bekli adama iýmit hödürlenende we orazasyny bozmasa aýdylýan doga",
|
"fastingPersonSupplication": "Agzy bekli adama iýmit hödürlenende we orazasyny bozmasa aýdylýan doga",
|
||||||
"insultedWhileFasting": "Orazaly wagtyň kemsidilende",
|
"insultedWhileFasting": "Orazaly wagtyň kemsidilende",
|
||||||
"seeingFruitSupplication": "Irki ýa-da bişmedik miwäni göreniňde aýdylýan doga",
|
"seeingFruitSupplication": "Irki ýa-da bişmedik miwäni göreniňde aýdylýan doga",
|
||||||
"sneezingSupplication": "Asgyranyňda aýdylýan doga"
|
"sneezingSupplication": "Asgyranyňda aýdylýan doga",
|
||||||
|
"sarToTmt": "SAR-dan TMT-a",
|
||||||
|
"hotelBusinessCard": "Myhmanhananyň wizit karty",
|
||||||
|
"masterkeyBox": "Masterkey gutusy",
|
||||||
|
"translator": "Terjimeçi"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user