update
This commit is contained in:
@@ -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: {
|
||||||
|
|||||||
@@ -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' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -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,9 +208,25 @@ const ManagementScreen: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<AppView paddingHorizontal={0}>
|
<AppView paddingHorizontal={0}>
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
|
<View style={styles.headerTitleRow}>
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>Account Hub</Text>
|
<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>
|
<Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>Manage your circles and requests</Text>
|
||||||
</View>
|
</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 style={[styles.tabContainer, { backgroundColor: theme.colors.borderLight }]}>
|
<View style={[styles.tabContainer, { backgroundColor: theme.colors.borderLight }]}>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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" },
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user