This commit is contained in:
Abin Toppo
2026-03-27 13:16:17 +05:30
parent ac9e7772b9
commit c6ba774167
4 changed files with 302 additions and 102 deletions
+6 -6
View File
@@ -42,19 +42,19 @@ const BottomTabs: React.FC = () => {
tabBarStyle: { tabBarStyle: {
position: "absolute", position: "absolute",
bottom: Math.max(insets.bottom, 8), bottom: Math.max(insets.bottom, 8),
left: 12, left: 40,
right: 12, right: 40,
height: 64, height: 64,
borderRadius: 18, borderRadius: 22,
backgroundColor: theme.colors.surface, backgroundColor: theme.colors.surface,
borderWidth: 1, borderWidth: 1,
borderColor: theme.colors.border, borderColor: theme.colors.border,
borderTopWidth: 0, borderTopWidth: 0,
elevation: 8, elevation: 10,
shadowColor: "#000", shadowColor: "#000",
shadowOffset: { width: 0, height: 4 }, shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12, shadowOpacity: 0.15,
shadowRadius: 8, shadowRadius: 10,
paddingBottom: 0, paddingBottom: 0,
}, },
tabBarItemStyle: { tabBarItemStyle: {
+1 -1
View File
@@ -38,7 +38,7 @@ const organizerActions: ActionItem[] = [
{ id: '2', title: 'Joined Groups', subtitle: 'Circles you belong to', action: 'NAVIGATE_MANAGEMENT', tab: 'Joined Groups', iconName: 'people-outline' }, { id: '2', title: 'Joined Groups', subtitle: 'Circles you belong to', action: 'NAVIGATE_MANAGEMENT', tab: 'Joined Groups', iconName: 'people-outline' },
{ id: '3', title: 'Requests', subtitle: 'Pending join requests', action: 'NAVIGATE_MANAGEMENT', tab: 'Requests', iconName: 'notifications-outline' }, { id: '3', title: 'Requests', subtitle: 'Pending join requests', action: 'NAVIGATE_MANAGEMENT', tab: 'Requests', iconName: 'notifications-outline' },
{ id: '4', title: 'Create Group', subtitle: 'Start a new circle', action: 'NAVIGATE_CREATE_GROUP', iconName: 'add-circle-outline' }, { id: '4', title: 'Create Group', subtitle: 'Start a new circle', action: 'NAVIGATE_CREATE_GROUP', iconName: 'add-circle-outline' },
{ id: '5', title: 'Your Requests', subtitle: 'Status of your applications', action: 'NAVIGATE_MANAGEMENT', tab: 'Your Requests', iconName: 'mail-open-outline' }, { id: '5', title: 'Your Requests', subtitle: 'Status of your applications', action: 'NAVIGATE_MANAGEMENT', tab: 'Requests', iconName: 'mail-open-outline' },
{ id: '6', title: 'Transactions', subtitle: 'View payment history', action: 'TRANSACTION', iconName: 'receipt-outline' }, { id: '6', title: 'Transactions', subtitle: 'View payment history', action: 'TRANSACTION', iconName: 'receipt-outline' },
]; ];
+91 -12
View File
@@ -35,7 +35,8 @@ import FontsFamily from '../../constents/FontsFamily';
import { RootTabParamList } from '../../navigation/BottomTabs'; import { RootTabParamList } from '../../navigation/BottomTabs';
type ManagementRouteProp = RouteProp<RootTabParamList, 'Management'>; type ManagementRouteProp = RouteProp<RootTabParamList, 'Management'>;
type ActiveTab = 'My Groups' | 'Joined Groups' | 'Requests' | 'Your Requests'; type ActiveTab = 'My Groups' | 'Joined Groups' | 'Requests' | 'My Requests';
type RequestSubTab = 'Join Requests' | 'My Requests';
type GroupStatusFilter = 'ALL' | 'PENDING' | 'APPROVED' | 'ACTIVE' | 'DELAY' | 'COMPLETED' | 'REJECTED' | 'CANCELLED'; type GroupStatusFilter = 'ALL' | 'PENDING' | 'APPROVED' | 'ACTIVE' | 'DELAY' | 'COMPLETED' | 'REJECTED' | 'CANCELLED';
const GROUP_STATUS_FILTERS: GroupStatusFilter[] = [ const GROUP_STATUS_FILTERS: GroupStatusFilter[] = [
@@ -74,7 +75,8 @@ const ManagementScreen: React.FC = () => {
user.role === 'ORGANIZER' ? 'My Groups' : 'Joined Groups' user.role === 'ORGANIZER' ? 'My Groups' : 'Joined Groups'
); );
const [myGroupStatusFilter, setMyGroupStatusFilter] = useState<GroupStatusFilter>('APPROVED'); const [myGroupStatusFilter, setMyGroupStatusFilter] = useState<GroupStatusFilter>('APPROVED');
const [joinedGroupStatusFilter, setJoinedGroupStatusFilter] = useState<GroupStatusFilter>('ACTIVE'); const [joinedGroupStatusFilter, setJoinedGroupStatusFilter] = useState<GroupStatusFilter>('ALL');
const [requestSubTab, setRequestSubTab] = useState<RequestSubTab>('Join Requests');
useEffect(() => { useEffect(() => {
if (!route.params?.actionTab) return; if (!route.params?.actionTab) return;
@@ -102,6 +104,9 @@ const ManagementScreen: React.FC = () => {
}; };
}, [joinedGroupStatusFilter]); }, [joinedGroupStatusFilter]);
const requestsParams = useMemo(() => ({ page: 0, size: 10 }), []);
const myRequestsParams = useMemo(() => ({ page: 0, size: 10 }), []);
console.log('joinedGroupsParams:', joinedGroupsParams); console.log('joinedGroupsParams:', joinedGroupsParams);
@@ -117,13 +122,12 @@ const ManagementScreen: React.FC = () => {
); );
const { data: requestsRes, isLoading: requestsLoading, refetch: refetchRequests } = useFetch<any>( const { data: requestsRes, isLoading: requestsLoading, refetch: refetchRequests } = useFetch<any>(
REQUESTS_URL, REQUESTS_URL,
undefined, requestsParams,
{ enabled: user.role === 'ORGANIZER' } { enabled: user.role === 'ORGANIZER' }
); );
const { data: userReqRes, isLoading: userReqLoading, refetch: refetchUserReq } = useFetch<any>( const { data: userReqRes, isLoading: userReqLoading, refetch: refetchUserReq } = useFetch<any>(
USER_REQUEST, USER_REQUEST,
undefined, myRequestsParams
{ enabled: user.role !== 'ORGANIZER' }
); );
const { mutate: updateRequest } = useUpdate<any, any>(''); const { mutate: updateRequest } = useUpdate<any, any>('');
@@ -155,7 +159,7 @@ const ManagementScreen: React.FC = () => {
const tabs: ActiveTab[] = useMemo(() => const tabs: ActiveTab[] = useMemo(() =>
user.role === 'ORGANIZER' user.role === 'ORGANIZER'
? ['My Groups', 'Joined Groups', 'Requests'] ? ['My Groups', 'Joined Groups', 'Requests']
: ['Joined Groups', 'Your Requests'], : ['Joined Groups', 'My Requests'],
[user.role]); [user.role]);
const handleApprove = (req: JoinRequest) => { const handleApprove = (req: JoinRequest) => {
@@ -192,8 +196,9 @@ const ManagementScreen: React.FC = () => {
switch (activeTab) { switch (activeTab) {
case 'My Groups': return myGroups; case 'My Groups': return myGroups;
case 'Joined Groups': return joinedGroups; case 'Joined Groups': return joinedGroups;
case 'Requests': return incomingRequests; case 'Requests':
case 'Your Requests': return myRequests; return requestSubTab === 'Join Requests' ? incomingRequests : myRequests;
case 'My Requests': return myRequests;
default: return []; default: return [];
} }
}; };
@@ -203,8 +208,24 @@ const ManagementScreen: React.FC = () => {
return ( return (
<AppView paddingHorizontal={0}> <AppView paddingHorizontal={0}>
<View style={styles.header}> <View style={styles.header}>
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>Account Hub</Text> <View style={styles.headerTitleRow}>
<Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>Manage your circles and requests</Text> <View style={{ flex: 1 }}>
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>Account Hub</Text>
<Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>Manage your circles and requests</Text>
</View>
<TouchableOpacity
onPress={onRefresh}
disabled={refreshing}
style={styles.refreshBtn}
>
<LucideIcon
name="sync-outline"
size={20}
color={theme.colors.textSecondary}
style={refreshing ? { opacity: 0.5 } : {}}
/>
</TouchableOpacity>
</View>
</View> </View>
<View style={[styles.tabContainer, { backgroundColor: theme.colors.borderLight }]}> <View style={[styles.tabContainer, { backgroundColor: theme.colors.borderLight }]}>
@@ -306,6 +327,47 @@ const ManagementScreen: React.FC = () => {
</View> </View>
)} )}
{activeTab === 'Requests' && user.role === 'ORGANIZER' && (
<View style={styles.statusFilterContainer}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.statusFilterRow}
>
{(['Join Requests', 'My Requests'] as RequestSubTab[]).map((tab, index) => {
const isActive = requestSubTab === tab;
return (
<TouchableOpacity
key={tab}
onPress={() => setRequestSubTab(tab)}
style={[
styles.statusFilterChip,
index !== (['Join Requests', 'My Requests'] as RequestSubTab[]).length - 1 && styles.statusFilterChipSpaced,
{
borderColor: isActive ? theme.colors.primary : theme.colors.border,
backgroundColor: isActive ? theme.colors.primarySoft : theme.colors.surface,
},
]}
>
<Text
style={[
styles.statusFilterText,
{
color: isActive ? theme.colors.textPrimary : theme.colors.textSecondary,
},
]}
>
{tab}
</Text>
</TouchableOpacity>
);
})}
</ScrollView>
</View>
)}
<FlatList <FlatList
data={getFilteredData()} data={getFilteredData()}
contentContainerStyle={styles.listContent} contentContainerStyle={styles.listContent}
@@ -313,8 +375,14 @@ const ManagementScreen: React.FC = () => {
renderItem={({ item }) => { renderItem={({ item }) => {
if (activeTab === 'My Groups') return <MyGroupCard item={item} onManage={handleManageGroup} />; if (activeTab === 'My Groups') return <MyGroupCard item={item} onManage={handleManageGroup} />;
if (activeTab === 'Joined Groups') return <JoinedGroupCard item={item} />; if (activeTab === 'Joined Groups') return <JoinedGroupCard item={item} />;
if (activeTab === 'Requests') return <RequestCard item={item} onApprove={handleApprove} onDecline={handleDecline} />; if (activeTab === 'Requests') {
if (activeTab === 'Your Requests') return <UserRequested item={item} />; if (requestSubTab === 'Join Requests') {
return <RequestCard item={item} onApprove={handleApprove} onDecline={handleDecline} />;
} else {
return <UserRequested item={item} />;
}
}
if (activeTab === 'My Requests') return <UserRequested item={item} />;
return null; return null;
}} }}
refreshControl={ refreshControl={
@@ -344,6 +412,17 @@ const styles = StyleSheet.create({
paddingBottom: 12, paddingBottom: 12,
zIndex: 10, zIndex: 10,
}, },
headerTitleRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
refreshBtn: {
padding: 8,
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
},
title: { title: {
fontSize: responsiveFontSize(3.2), fontSize: responsiveFontSize(3.2),
fontFamily: FontsFamily.nunitoBold, fontFamily: FontsFamily.nunitoBold,
+204 -83
View File
@@ -13,93 +13,139 @@ import { TRANSACTION } from "../../utils/ApiUrls";
import colors from "../../components/colors"; import colors from "../../components/colors";
import AppView from "../../components/AppView"; import AppView from "../../components/AppView";
import Header from "../../components/common/Header"; import Header from "../../components/common/Header";
import LucideIcon from "../../components/common/LucideIcon";
import { useTheme } from "../../hooks/ui/useTheme";
import FontsFamily from "../../constents/FontsFamily";
import { responsiveFontSize } from "react-native-responsive-dimensions";
type Transaction = { type Transaction = {
id: string; txnId: string;
fundGroupId: string; fundGroupId: number;
fundGroupName: string; fundGroupName: string;
amount: number; amount: number;
status: string; status: string;
payerMobile?: string; payerMobile: string;
transactionDate: number[]; transactionDate: string;
currency: string;
orderNo: string;
}; };
type ApiResponse = { type ApiResponse = {
message: string; message: string;
status: string; status: string;
statusCode: number; statusCode: number;
data: Transaction[]; data: {
content: Transaction[];
};
}; };
// Helper: convert the array-style date into a JS Date
const parseTransactionDate = (arr: number[] | undefined): Date | null => {
if (!arr || arr.length < 6) return null;
const [year, month, day, hour = 0, minute = 0, second = 0, sub = 0] = arr;
let milli = 0;
if (sub > 999999999) {
milli = Math.floor(sub / 1e6);
} else if (sub > 1000) {
milli = Math.floor(sub / 1e6);
} else {
milli = sub;
}
return new Date(year, month - 1, day, hour, minute, second, milli); const formatCurrency = (amount: number, currency: string = "UGX") => {
};
const formatINR = (amount: number) => {
try { try {
return new Intl.NumberFormat("en-IN", { style: "currency", currency: "INR" }).format(amount); return new Intl.NumberFormat("en-UG", {
style: "currency",
currency: currency || "UGX",
minimumFractionDigits: 0,
}).format(amount);
} catch (e) { } catch (e) {
return `UGX${amount.toFixed(2)}`; return `${currency || "UGX"} ${amount.toLocaleString()}`;
} }
}; };
const StatusBadge: React.FC<{ status?: string }> = ({ status }) => { const StatusBadge: React.FC<{ status?: string }> = ({ status }) => {
const theme = useTheme();
const text = status ?? "FAILED"; const text = status ?? "FAILED";
const color =
text === "SUCCESS" ? styles.statusSuccess : text === "PENDING" ? styles.statusPending : styles.statusFailed; const getStatusColors = (s: string) => {
switch (s) {
case 'SUCCESS':
return { bg: theme.colors.success + '15', text: theme.colors.success };
case 'SENT_TO_PROCESSOR':
case 'PENDING':
return { bg: theme.colors.warning + '15', text: theme.colors.warning };
default:
return { bg: theme.colors.error + '15', text: theme.colors.error };
}
};
const colors = getStatusColors(text);
return ( return (
<View style={[styles.statusBadge, color]}> <View style={[styles.statusBadge, { backgroundColor: colors.bg }]}>
<Text style={styles.statusText}>{text}</Text> <Text style={[styles.statusText, { color: colors.text }]}>{text}</Text>
</View> </View>
); );
}; };
const TransactionItem: React.FC<{ item: Transaction }> = ({ item }) => { const TransactionItem: React.FC<{ item: Transaction }> = ({ item }) => {
const theme = useTheme();
const formatDate = (dateStr: string | null): string => { const formatDate = (dateStr: string | null): string => {
if (!dateStr) return 'N/A'; if (!dateStr) return 'N/A';
try {
const date = new Date(dateStr); const date = new Date(dateStr);
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0'); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const year = date.getFullYear(); return `${day} ${months[date.getMonth()]} ${date.getFullYear()}`;
} catch (e) {
return `${day}-${month}-${year}`; return 'N/A';
}
}; };
const dateStr = item.transactionDate ? formatDate(item.transactionDate) : "N/A"; const isSuccess = item.status === 'SUCCESS';
console.log("item of the transaction : ", item)
return ( return (
<TouchableOpacity activeOpacity={0.8} style={styles.card}> <TouchableOpacity
<View style={styles.cardLeft}> activeOpacity={0.7}
<Text style={styles.fundName}>{item.fundGroupName}</Text> style={[styles.card, { backgroundColor: theme.colors.card, borderColor: theme.colors.border }]}
<Text style={styles.payer}>Payer: {item.payerMobile ?? "-"}</Text> >
<Text style={styles.date}>{dateStr}</Text> <View style={styles.cardHeader}>
<View style={styles.titleContainer}>
<Text style={[styles.fundName, { color: theme.colors.textPrimary }]}>{item.fundGroupName}</Text>
<Text style={[styles.payer, { color: theme.colors.textSecondary }]}>
by {item.payerMobile ?? "-"}
</Text>
</View>
<StatusBadge status={item.status} />
</View> </View>
<View style={styles.cardRight}> <View style={[styles.infoRow, { borderColor: theme.colors.border }]}>
<Text style={styles.amount}>{formatINR(item.amount)}</Text> <View style={styles.infoItem}>
<StatusBadge status={item.status} /> <Text style={[styles.infoLabel, { color: theme.colors.textSecondary }]}>Amount</Text>
<Text style={[styles.infoValue, { color: theme.colors.textPrimary }]}>
{formatCurrency(item.amount, item.currency)}
</Text>
</View>
<View style={styles.infoItem}>
<Text style={[styles.infoLabel, { color: theme.colors.textSecondary }]}>Order No</Text>
<Text style={[styles.infoValue, { color: theme.colors.textPrimary }]} numberOfLines={1}>
{item.orderNo}
</Text>
</View>
</View>
<View style={styles.cardFooter}>
<Text style={[styles.dateText, { color: theme.colors.textSecondary }]}>
{formatDate(item.transactionDate)}
</Text>
{isSuccess ? (
<View style={[styles.successBadge, { backgroundColor: theme.colors.success + '18' }]}>
<Text style={[styles.successText, { color: theme.colors.success }]}>✓ SUCCESS</Text>
</View>
) : (
<View style={[styles.typeBadge, { backgroundColor: theme.colors.primarySoft }]}>
<Text style={[styles.typeText, { color: theme.colors.primary }]}>CONTRIBUTION</Text>
</View>
)}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); );
}; };
const TransactionsList: React.FC = () => { const TransactionsList: React.FC = () => {
const theme = useTheme();
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const [transactions, setTransactions] = useState<Transaction[]>([]); const [transactions, setTransactions] = useState<Transaction[]>([]);
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
@@ -109,7 +155,7 @@ const TransactionsList: React.FC = () => {
const res = (await apiCall(TRANSACTION, "GET")) as ApiResponse; const res = (await apiCall(TRANSACTION, "GET")) as ApiResponse;
console.log("transaction list response : ", res); console.log("transaction list response : ", res);
if (res.statusCode === 200) { if (res.statusCode === 200) {
setTransactions(res.data); setTransactions(res.data?.content || []);
setMessage(res.message); setMessage(res.message);
} }
} catch (error) { } catch (error) {
@@ -134,18 +180,23 @@ const TransactionsList: React.FC = () => {
const renderEmpty = () => ( const renderEmpty = () => (
<View style={styles.emptyContainer}> <View style={styles.emptyContainer}>
<Text style={styles.emptyTitle}>No transactions</Text> <LucideIcon name="receipt-outline" size={64} color={theme.colors.textMuted} />
<Text style={styles.emptySubtitle}>Transactions will appear here when available.</Text> <Text style={[styles.emptyTitle, { color: theme.colors.textPrimary }]}>No transactions</Text>
<Text style={[styles.emptySubtitle, { color: theme.colors.textSecondary }]}>Transactions will appear here when available.</Text>
</View> </View>
); );
const isLoading = false; // Add placeholder or hook if needed
return ( return (
<AppView> <AppView paddingHorizontal={0}>
<Header title="transactions" titleColor={colors.Home} arrowColor={colors.Home} /> <Header title="Transactions" titleColor={theme.colors.textPrimary} arrowColor={theme.colors.textPrimary} />
<Text style={styles.headerTitle}>{message}</Text> <View style={{ paddingHorizontal: 16, marginBottom: 8 }}>
<Text style={[styles.headerTitle, { color: theme.colors.textPrimary }]}>{message}</Text>
</View>
<FlatList <FlatList
data={transactions} data={transactions}
keyExtractor={(item) => item.id} keyExtractor={(item) => item.txnId || item.orderNo}
renderItem={({ item }) => <TransactionItem item={item} />} renderItem={({ item }) => <TransactionItem item={item} />}
ListEmptyComponent={renderEmpty} ListEmptyComponent={renderEmpty}
contentContainerStyle={transactions.length === 0 ? { flex: 1 } : undefined} contentContainerStyle={transactions.length === 0 ? { flex: 1 } : undefined}
@@ -158,44 +209,114 @@ const TransactionsList: React.FC = () => {
export default TransactionsList; export default TransactionsList;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "#f7f7fb" }, headerTitle: {
headerTitle: { fontSize: 18, fontWeight: "500", color: colors.primary }, fontSize: 18,
headerSubtitle: { marginTop: 4, color: "#6b7280" }, fontFamily: FontsFamily.nunitoSemiBold,
color: colors.primary
},
card: { card: {
flexDirection: "row", borderRadius: 16,
justifyContent: "space-between", padding: 14,
padding: 16, marginBottom: 12,
// marginHorizontal: 12, marginHorizontal: 14,
marginVertical: 8, borderWidth: 1,
backgroundColor: "#fff", shadowColor: '#000',
borderRadius: 12, shadowOffset: { width: 0, height: 2 },
shadowColor: "#000", shadowOpacity: 0.05,
shadowOpacity: 0.03,
shadowRadius: 8, shadowRadius: 8,
elevation: 2, elevation: 2,
}, },
cardHeader: {
cardLeft: { flex: 1 }, flexDirection: 'row',
fundName: { fontSize: 16, fontWeight: "600" }, justifyContent: 'space-between',
payer: { marginTop: 6, color: "#6b7280" }, alignItems: 'flex-start',
date: { marginTop: 6, color: "#9ca3af" }, marginBottom: 10,
},
cardRight: { alignItems: "flex-end", justifyContent: "space-between" }, titleContainer: {
amount: { fontSize: 16, fontWeight: "700" }, flex: 1,
},
fundName: {
fontSize: responsiveFontSize(2),
fontFamily: FontsFamily.nunitoExtraBold,
marginBottom: 2,
},
payer: {
fontSize: responsiveFontSize(1.4),
fontFamily: FontsFamily.nunitoRegular,
},
statusBadge: { statusBadge: {
marginTop: 8,
paddingHorizontal: 8, paddingHorizontal: 8,
paddingVertical: 4, paddingVertical: 4,
borderRadius: 999, borderRadius: 6,
},
statusText: {
fontSize: responsiveFontSize(1.2),
fontFamily: FontsFamily.nunitoBold,
},
infoRow: {
flexDirection: 'row',
paddingVertical: 10,
borderTopWidth: 1,
borderBottomWidth: 1,
marginBottom: 10,
},
infoItem: {
flex: 1,
},
infoLabel: {
fontSize: responsiveFontSize(1.3),
fontFamily: FontsFamily.nunitoRegular,
marginBottom: 2,
},
infoValue: {
fontSize: responsiveFontSize(1.6),
fontFamily: FontsFamily.nunitoBold,
},
cardFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 4,
},
dateText: {
fontSize: responsiveFontSize(1.3),
fontFamily: FontsFamily.nunitoMedium,
},
typeBadge: {
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 6,
},
typeText: {
fontSize: responsiveFontSize(1.15),
fontFamily: FontsFamily.nunitoBold,
letterSpacing: 0.5,
},
successBadge: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 6,
},
successText: {
fontSize: responsiveFontSize(1.3),
fontFamily: FontsFamily.nunitoBold,
letterSpacing: 0.5,
},
emptyContainer: {
alignItems: "center",
justifyContent: "center",
marginTop: 100
},
emptyTitle: {
fontSize: responsiveFontSize(2),
fontFamily: FontsFamily.nunitoBold,
marginTop: 16
},
emptySubtitle: {
marginTop: 8,
fontSize: responsiveFontSize(1.6),
fontFamily: FontsFamily.nunitoRegular,
textAlign: 'center',
paddingHorizontal: 40
}, },
statusText: { fontSize: 12, fontWeight: "600", color: "#fff" },
statusSuccess: { backgroundColor: "#10b981" },
statusPending: { backgroundColor: "#f59e0b" },
statusFailed: { backgroundColor: "#ef4444" },
emptyContainer: { alignItems: "center", justifyContent: "center", },
emptyTitle: { fontSize: 18, fontWeight: "600", color: "#374151" },
emptySubtitle: { marginTop: 8, color: "#6b7280" },
}); });