diff --git a/App.tsx b/App.tsx index dfb5517..30d539a 100644 --- a/App.tsx +++ b/App.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { StyleSheet } from 'react-native'; +import { LogBox, StyleSheet } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { PaperProvider } from 'react-native-paper'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; @@ -18,6 +18,8 @@ import { import { ThemeProvider } from './src/theme/ThemeProvider'; + +LogBox.ignoreAllLogs(true); const queryClient = new QueryClient({ defaultOptions: { queries: { diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs index e479558..59d7197 100644 --- a/android/.settings/org.eclipse.buildship.core.prefs +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -1,13 +1,13 @@ -arguments= +arguments=--init-script C\:\\Users\\Lincpay\\AppData\\Roaming\\Code\\User\\globalStorage\\redhat.java\\1.53.0\\config_win\\org.eclipse.osgi\\58\\0\\.cp\\gradle\\init\\init.gradle --init-script C\:\\Users\\Lincpay\\AppData\\Roaming\\Code\\User\\globalStorage\\redhat.java\\1.53.0\\config_win\\org.eclipse.osgi\\58\\0\\.cp\\gradle\\protobuf\\init.gradle --init-script C\:\\Users\\Lincpay\\AppData\\Roaming\\Code\\User\\globalStorage\\redhat.java\\1.53.0\\config_win\\org.eclipse.osgi\\58\\0\\.cp\\gradle\\kotlin\\init.gradle --init-script C\:\\Users\\Lincpay\\AppData\\Roaming\\Code\\User\\globalStorage\\redhat.java\\1.53.0\\config_win\\org.eclipse.osgi\\58\\0\\.cp\\gradle\\groovy\\init.gradle auto.sync=false build.scans.enabled=false connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.project.dir= eclipse.preferences.version=1 gradle.user.home= -java.home= +java.home=C\:/Program Files/Microsoft/jdk-17.0.14.7-hotspot jvm.arguments= offline.mode=false -override.workspace.settings=false -show.console.view=false -show.executions.view=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/src/api/client.ts b/src/api/client.ts index 902d806..cf59cf3 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -81,6 +81,7 @@ apiClient.interceptors.request.use( async (config) => { try { const credentials = await Keychain.getGenericPassword(); + console.log(`[HOOK_API] Token`, credentials); if (credentials) { config.headers.Authorization = `Bearer ${credentials.password}`; } diff --git a/src/components/JoinedGroupCard.tsx b/src/components/JoinedGroupCard.tsx index 2041022..6f65e47 100644 --- a/src/components/JoinedGroupCard.tsx +++ b/src/components/JoinedGroupCard.tsx @@ -22,7 +22,7 @@ interface JoinedGroup { currentParticipants: number; contributionAmount: number; cycleType: 'MONTHLY' | 'WEEKLY' | 'QUARTERLY' | 'CUSTOM'; - status: 'ACTIVE' | 'PENDING' | 'COMPLETED'; + status: 'ACTIVE' | 'PENDING' | 'COMPLETED'| 'APPROVED'; } interface JoinedGroupCardProps { @@ -42,9 +42,10 @@ const formatDate = (dateStr: string | null): string => { return `${dayNum} ${months[monthIndex]} ${year}`; }; -export const JoinedGroupCard: FC = ({ item }) => { +export const JoinedGroupCard: FC = ({ item }:any) => { const theme = useTheme(); const navigation = useNavigation(); + console.log("item",item) const { data: payableRes } = useFetch(`${CONTRIBUTE}${item.id}/calculate-contribution`); const payable = payableRes?.data; @@ -101,8 +102,8 @@ export const JoinedGroupCard: FC = ({ item }) => { - Type - {item.cycleType} + Duration + {`${item?.cycleType} (${item?.customCycleDays} days)`} diff --git a/src/components/MyGroupCard.tsx b/src/components/MyGroupCard.tsx index 5474389..1b91a66 100644 --- a/src/components/MyGroupCard.tsx +++ b/src/components/MyGroupCard.tsx @@ -74,7 +74,7 @@ export const MyGroupCard: React.FC = ({ item, onManage }) => { - ₹{item.contributionAmount} + UGX{item.contributionAmount} diff --git a/src/screens/ContributionScreen.tsx b/src/screens/ContributionScreen.tsx index 542e8a5..0bb4858 100644 --- a/src/screens/ContributionScreen.tsx +++ b/src/screens/ContributionScreen.tsx @@ -65,14 +65,14 @@ const ContributionDetails: React.FC = ({ - ₹{data?.totalPayable || data?.baseAmount} + UGX{data?.totalPayable || data?.baseAmount} - + - + diff --git a/src/screens/GroupCreation.tsx b/src/screens/GroupCreation.tsx index 9603a48..c16d47f 100644 --- a/src/screens/GroupCreation.tsx +++ b/src/screens/GroupCreation.tsx @@ -217,7 +217,7 @@ export default function GroupCreationForm() {
handleChange('contributionAmount', val)} @@ -226,7 +226,7 @@ export default function GroupCreationForm() { error={errors.contributionAmount} /> handleChange('entryFee', val)} @@ -272,7 +272,7 @@ export default function GroupCreationForm() { /> handleChange('lateFeeValue', val)} diff --git a/src/screens/GroupReview.tsx b/src/screens/GroupReview.tsx index 6e08035..323374e 100644 --- a/src/screens/GroupReview.tsx +++ b/src/screens/GroupReview.tsx @@ -189,8 +189,8 @@ export default function GroupReview({ route, navigation }: any) { - {renderRow('Contribution', `₹${form.contributionAmount}`)} - {renderRow('Entry Fee', `₹${form.entryFee}`)} + {renderRow('Contribution', `UGX${form.contributionAmount}`)} + {renderRow('Entry Fee', `UGX${form.entryFee}`)} {renderRow('Cycle', getCycleLabel(form.cycleType, form.customCycleDays))} {form.cycleType === 'CUSTOM' && !['1', '7'].includes(String(form.customCycleDays)) && renderRow('Cycle Days', `${form.customCycleDays} days`)} @@ -198,7 +198,7 @@ export default function GroupReview({ route, navigation }: any) { {renderRow('Fee Mode', form.lateFeeMode)} - {renderRow('Fee Value', `₹${form.lateFeeValue}`)} + {renderRow('Fee Value', `UGX${form.lateFeeValue}`)} - ₹{item.entryFee} + UGX{item.entryFee} ) : null} diff --git a/src/screens/InviteScreen.tsx b/src/screens/InviteScreen.tsx index f2679a5..341c125 100644 --- a/src/screens/InviteScreen.tsx +++ b/src/screens/InviteScreen.tsx @@ -139,16 +139,16 @@ const InviteScreen: React.FC = ({ route, navigation }) => { Circle Overview - + - + diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx index dbc8d98..ef42894 100644 --- a/src/screens/LoginScreen.tsx +++ b/src/screens/LoginScreen.tsx @@ -43,8 +43,8 @@ export default function LoginScreen() { }, [fadeAnim, slideAnim]); const { refetch: sendOtp, isFetching: loading } = useFetch( - LOGIN_URL, - { mobile }, + LOGIN_URL + mobile, + {}, { enabled: false, retry: false, @@ -100,6 +100,7 @@ export default function LoginScreen() { value={mobile} onChangeText={(text) => { setMobile(text.replace(/\D/g, '')); + if (error) setError(''); }} keyboardType="phone-pad" diff --git a/src/screens/OtpVarificationScreen.tsx b/src/screens/OtpVarificationScreen.tsx index d8380ca..74dbe37 100644 --- a/src/screens/OtpVarificationScreen.tsx +++ b/src/screens/OtpVarificationScreen.tsx @@ -97,6 +97,30 @@ export default function OtpVerificationScreen() { }); const handleTextChange = (text: string, index: number) => { + // If text length > 1, it's likely a paste operation + if (text.length > 1) { + const pastedCode = text.trim().slice(0, CELL_COUNT).split(''); + const newCode = [...code]; + + pastedCode.forEach((char, i) => { + if (i < CELL_COUNT) { + newCode[i] = char; + } + }); + + setCode(newCode); + + // Focus the last filled cell or the next empty one + const focusIndex = Math.min(pastedCode.length, CELL_COUNT - 1); + inputs.current[focusIndex]?.focus(); + + // Trigger verification if OTP complete + if (newCode.every((cell) => cell !== '')) { + verifyOtp({ mobile, otp: newCode.join('') }); + } + return; + } + const newCode = [...code]; newCode[index] = text.slice(-1); setCode(newCode); @@ -153,13 +177,14 @@ export default function OtpVerificationScreen() { }, ]} keyboardType="number-pad" - maxLength={1} + maxLength={CELL_COUNT} value={digit} placeholder="0" placeholderTextColor={theme.colors.textMuted} onChangeText={(text) => handleTextChange(text, index)} onKeyPress={(e) => handleKeyPress(e, index)} selectionColor={theme.colors.primary} + textContentType="oneTimeCode" /> ))} diff --git a/src/screens/home/GroupList.tsx b/src/screens/home/GroupList.tsx index 4ea711d..24d616d 100644 --- a/src/screens/home/GroupList.tsx +++ b/src/screens/home/GroupList.tsx @@ -72,7 +72,7 @@ const GroupCard = ({ item }: { item: any }) => { {item.baseAmount && ( Contribution - ₹{item.baseAmount} + UGX{item.baseAmount} )} diff --git a/src/screens/home/UserProfileStatus.tsx b/src/screens/home/UserProfileStatus.tsx index dc1af69..341665c 100644 --- a/src/screens/home/UserProfileStatus.tsx +++ b/src/screens/home/UserProfileStatus.tsx @@ -117,7 +117,7 @@ const UserProfileStatus: React.FC = ({ Total Outstanding - ₹{totalDue} + UGX{totalDue} = ({ {group.groupName} - Amount: ₹{group.baseAmount} {group.lateFee > 0 ? `+ ₹${group.lateFee} fee` : ''} + Amount: UGX{group.baseAmount} {group.lateFee > 0 ? `+ UGX${group.lateFee} fee` : ''} - ₹{group.totalDue} + UGX{group.totalDue} ))} Payable - ₹{totalDueSelected} + UGX{totalDueSelected} { Total Balance - ₹{cycleDashboardData?.contributionAmount * memberData.length || group.goal} + UGX{cycleDashboardData?.contributionAmount * memberData.length || group.goal} @@ -248,7 +248,7 @@ const GroupDetails = () => { - ₹{cycleDashboardData?.contributionAmount || group.collected} + UGX{cycleDashboardData?.contributionAmount || group.collected} Per Head diff --git a/src/screens/management/EditGroupScreen.tsx b/src/screens/management/EditGroupScreen.tsx index 5e1968a..5fc096c 100644 --- a/src/screens/management/EditGroupScreen.tsx +++ b/src/screens/management/EditGroupScreen.tsx @@ -425,7 +425,7 @@ const EditGroupScreen: React.FC = () => { - + {selectedImage && ( @@ -452,7 +452,7 @@ const EditGroupScreen: React.FC = () => { - ₹{groupDetails.contributionAmount} + UGX{groupDetails.contributionAmount} Contribution @@ -474,7 +474,7 @@ const EditGroupScreen: React.FC = () => { const getDisplayValue = (field: string) => { const val = groupDetails[field as keyof typeof groupDetails]; - if (field.includes('Amount') || field.includes('Fee') || field.includes('Charge') || field.includes('Value')) return `₹${val ?? 0}`; + if (field.includes('Amount') || field.includes('Fee') || field.includes('Charge') || field.includes('Value')) return `UGX${val ?? 0}`; return String(val ?? '-'); }; @@ -592,9 +592,9 @@ const EditGroupScreen: React.FC = () => { ) : ( <> - + Activate Group - + {/* */} )} @@ -682,8 +682,8 @@ const getStyles = (theme: any) => StyleSheet.create({ descContainer: { backgroundColor: 'rgba(255,255,255,0.15)', padding: 10, borderRadius: 8, borderLeftWidth: 3, borderLeftColor: '#fff', marginBottom: 12 }, descText: { color: '#eee', fontSize: 14, fontFamily: FontsFamily.nunitoRegular }, badgesRow: { flexDirection: 'row', gap: 10 }, - badge: { backgroundColor: 'rgba(0,0,0,0.5)', paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12 }, - badgeText: { color: '#fff', fontSize: 12, fontWeight: 'bold' }, + badge: { backgroundColor: theme.colors.background, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12 }, + badgeText: { color: theme.colors.textPrimary, fontSize: 12, fontWeight: 'bold' }, cameraBtn: { position: 'absolute', bottom: -20, right: 30, width: 44, height: 44, borderRadius: 22, backgroundColor: '#fff', elevation: 4, justifyContent: 'center', alignItems: 'center' }, uploadFloatBtn: { position: 'absolute', bottom: 40, right: 30, width: 50, height: 50, borderRadius: 25, elevation: 5, justifyContent: 'center', alignItems: 'center' }, uploadFloatWrapper: { position: 'absolute', bottom: 40, right: 30, alignItems: 'center' }, @@ -792,7 +792,7 @@ const getStyles = (theme: any) => StyleSheet.create({ borderBottomColor: '#334155', }, floatingBtnText: { - color: '#fff', + color: theme.colors.reverseText, fontSize: 18, fontFamily: FontsFamily.nunitoBold, letterSpacing: 0.5, @@ -800,15 +800,15 @@ const getStyles = (theme: any) => StyleSheet.create({ modalContent: { flex: 1 }, modalFieldContainer: { marginBottom: 16 }, modalLabel: { fontSize: 14, color: theme.colors.textSecondary, marginBottom: 8, fontFamily: FontsFamily.nunitoBold }, - modalInput: { borderWidth: 1, borderColor: '#e0e0e0', borderRadius: 12, padding: 14, fontSize: 16, color: theme.colors.textPrimary, backgroundColor: '#f9f9f9', fontFamily: FontsFamily.nunitoMedium }, + modalInput: { borderWidth: 1, borderColor: '#e0e0e0', borderRadius: 12, padding: 14, fontSize: 16, color: theme.colors.black, backgroundColor: '#f9f9f9', fontFamily: FontsFamily.nunitoMedium }, chipContainer: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 }, chip: { paddingHorizontal: 16, paddingVertical: 10, borderRadius: 20, backgroundColor: '#f0f0f0', borderWidth: 1, borderColor: '#e0e0e0' }, activeChip: { backgroundColor: theme.colors.primary, borderColor: theme.colors.primary }, - chipText: { fontSize: 14, fontFamily: FontsFamily.nunitoMedium, color: theme.colors.textSecondary }, - activeChipText: { color: theme.colors.white, fontFamily: FontsFamily.nunitoBold }, + chipText: { fontSize: 14, fontFamily: FontsFamily.nunitoMedium, color: theme.colors.black }, + activeChipText: { color: theme.colors.reverseText, fontFamily: FontsFamily.nunitoBold }, modalFooter: { borderTopWidth: 1, borderTopColor: '#eee', paddingTop: 16 }, saveBtn: { backgroundColor: theme.colors.primary, padding: 16, borderRadius: 12, alignItems: 'center' }, - saveBtnText: { color: theme.colors.white, fontSize: 16, fontFamily: FontsFamily.nunitoBold } + saveBtnText: { color: theme.colors.reverseText, fontSize: 16, fontFamily: FontsFamily.nunitoBold } }); export default EditGroupScreen; diff --git a/src/screens/management/ManagementScreen.tsx b/src/screens/management/ManagementScreen.tsx index 72c8291..e0f8e1f 100644 --- a/src/screens/management/ManagementScreen.tsx +++ b/src/screens/management/ManagementScreen.tsx @@ -52,6 +52,7 @@ const JOINED_GROUP_STATUS_FILTERS: GroupStatusFilter[] = [ 'ACTIVE', 'DELAY', 'COMPLETED', + 'APPROVED' ]; const getListFromResponse = (response: any): T[] => { diff --git a/src/screens/management/Payments.tsx b/src/screens/management/Payments.tsx index fac4ed0..e030d27 100644 --- a/src/screens/management/Payments.tsx +++ b/src/screens/management/Payments.tsx @@ -133,7 +133,7 @@ const Payments: React.FC = () => { } if (currentParticipant && amountToWaive > currentParticipant.lateFee) { - ToastAndroid.show(`Waiver cannot exceed ₹${currentParticipant.lateFee}`, ToastAndroid.SHORT); + ToastAndroid.show(`Waiver cannot exceed UGX${currentParticipant.lateFee}`, ToastAndroid.SHORT); return; } @@ -174,13 +174,13 @@ const Payments: React.FC = () => { {participant.status} - ₹{participant.status === 'SUCCESS' ? participant.totalPaidAmount : participant.expectedAmount} + UGX{participant.status === 'SUCCESS' ? participant.totalPaidAmount : participant.expectedAmount} {participant.lateFee > 0 && ( - Late Fee: ₹{participant.lateFee} + Late Fee: UGX{participant.lateFee} )} {participant.lateFeeWaived && ( - Waived: ₹{participant.waivedAmount} + Waived: UGX{participant.waivedAmount} )} @@ -212,7 +212,7 @@ const Payments: React.FC = () => { confirmText={isWaiving ? 'Applying...' : 'Apply Waiver'} > - For {currentParticipant?.firstName}. Current late fee: ₹{currentParticipant?.lateFee} + For {currentParticipant?.firstName}. Current late fee: UGX{currentParticipant?.lateFee} { Contribution - ₹{paymentDetails.contributionAmount} + UGX{paymentDetails.contributionAmount} Total Amount - ₹{paymentDetails.totalDueWithLateFee} + UGX{paymentDetails.totalDueWithLateFee} diff --git a/src/screens/notification/Notification.tsx b/src/screens/notification/Notification.tsx index 4f76790..2e46046 100644 --- a/src/screens/notification/Notification.tsx +++ b/src/screens/notification/Notification.tsx @@ -1,4 +1,4 @@ -// import React from 'react'; +import React from 'react'; import { View, Text, @@ -6,274 +6,265 @@ import { FlatList, StyleSheet, } from 'react-native'; + import { useDispatch, useSelector } from 'react-redux'; -import { - removeNotification, -} from '../../redux/notificationSlice'; +import { removeNotification } from '../../redux/notificationSlice'; + import LinearGradient from 'react-native-linear-gradient'; import LucideIcon from '../../components/common/LucideIcon'; import AppView from '../../components/AppView'; import Header from '../../components/common/Header'; import { useNotificationHandler } from '../hook/useNotificationHandler'; -import colors from '../../components/colors'; +import { useTheme } from '../../theme/ThemeProvider'; -type FilterType = 'all' | 'JOIN_REQUEST' | 'CONTRIBUTION' | 'CONTRIBUTION_ACK' | - 'CYCLE_COMPLETED' | 'WITHDRAWAL' | 'WITHDRAWAL_NOTIFY' | - 'CYCLE_WAIVER' | 'WAIVER_RECEIVED' | 'USER_SELECTED' | - 'GROUP_STARTED' | 'JOIN_APPROVED' | 'JOIN_VIA_INVITE'; +import { Swipeable } from 'react-native-gesture-handler'; +import Animated, { + useSharedValue, + useAnimatedStyle, + withSpring, +} from 'react-native-reanimated'; + +type FilterType = + | 'all' + | 'JOIN_REQUEST' + | 'CONTRIBUTION' + | 'CONTRIBUTION_ACK' + | 'CYCLE_COMPLETED' + | 'WITHDRAWAL' + | 'WITHDRAWAL_NOTIFY' + | 'CYCLE_WAIVER' + | 'WAIVER_RECEIVED' + | 'USER_SELECTED' + | 'GROUP_STARTED' + | 'JOIN_APPROVED' + | 'JOIN_VIA_INVITE'; const Notification = () => { const dispatch = useDispatch(); const { handleMarkAsReadAndNavigate } = useNotificationHandler(); + const { theme } = useTheme(); + const notifications = useSelector( - (state: any) => state.notifications.notifications + (state: any) => state.notifications.notifications ?? [] ); - - console.log("notifications ==>", notifications); - const handleDelete = (messageId: string) => { dispatch(removeNotification(messageId)); }; - const handleMarkAsRead = (messageId: string, screen: string, groupId: string, cycleNumber: string, contributionId: string) => { - console.log("screen type : ", screen); - handleMarkAsReadAndNavigate( - messageId, - screen, - { - groupId: groupId, - cycleNumber, - contributionId, - actionTab3: 'Your Requests', - actionTab1: 'Joined Groups', - GroupDetails: { - item: {} - }, - } - ); + const handleMarkAsRead = ( + messageId: string, + screen: string, + groupId: string, + cycleNumber: string, + contributionId: string + ) => { + handleMarkAsReadAndNavigate(messageId, screen, { + groupId, + cycleNumber, + contributionId, + actionTab3: 'Your Requests', + actionTab1: 'Joined Groups', + GroupDetails: { item: {} }, + }); }; const formatTime = (timestamp: number) => { const date = new Date(timestamp); const now = new Date(); - const diffInHours = (now.getTime() - date.getTime()) / (1000 * 60 * 60); + const diff = (now.getTime() - date.getTime()) / (1000 * 60 * 60); - if (diffInHours < 1) { - return 'Just now'; - } else if (diffInHours < 24) { - return `${Math.floor(diffInHours)}h ago`; - } else { - return date.toLocaleDateString(); - } + if (diff < 1) return 'Just now'; + if (diff < 24) return `${Math.floor(diff)}h ago`; + return date.toLocaleDateString(); }; - const getNotificationIcon = (msgType: string) => { - switch (msgType) { - case 'JOIN_REQUEST': - return 'person-add-outline'; - case 'CONTRIBUTION': - return 'cash-outline'; - case 'CONTRIBUTION_ACK': - return 'checkmark-done-outline'; - case 'CYCLE_COMPLETED': - return 'trophy-outline'; - case 'WITHDRAWAL': - return 'card-outline'; - case 'WITHDRAWAL_NOTIFY': - return 'notifications-outline'; - case 'CYCLE_WAIVER': - return 'document-text-outline'; - case 'WAIVER_RECEIVED': - return 'download-outline'; - case 'USER_SELECTED': - return 'people-outline'; - case 'GROUP_STARTED': - return 'rocket-outline'; - case 'JOIN_APPROVED': - return 'checkmark-circle-outline'; - case 'JOIN_VIA_INVITE': - return 'gift-outline'; - default: - return 'notifications-outline'; - } + const getNotificationIcon = (type: string) => { + const map: any = { + JOIN_REQUEST: 'person-add-outline', + CONTRIBUTION: 'cash-outline', + CONTRIBUTION_ACK: 'checkmark-done-outline', + CYCLE_COMPLETED: 'trophy-outline', + WITHDRAWAL: 'card-outline', + WITHDRAWAL_NOTIFY: 'notifications-outline', + GROUP_STARTED: 'rocket-outline', + }; + return map[type] || 'notifications-outline'; }; - - const getNotificationColor = (msgType: string) => { - switch (msgType) { - case 'JOIN_REQUEST': - return '#FFB6C1'; // Pastel Pink - case 'CONTRIBUTION': - return '#98FB98'; // Pastel Green - case 'CONTRIBUTION_ACK': - return '#ADD8E6'; // Pastel Blue - case 'CYCLE_COMPLETED': - return '#D8BFD8'; // Pastel Purple - case 'WITHDRAWAL': - return '#FFD700'; // Pastel Yellow - case 'WITHDRAWAL_NOTIFY': - return '#FFA07A'; // Pastel Orange - case 'CYCLE_WAIVER': - return '#B0C4DE'; // Pastel Steel Blue - case 'WAIVER_RECEIVED': - return '#AFEEEE'; // Pastel Turquoise - case 'USER_SELECTED': - return '#DDA0DD'; // Pastel Plum - case 'GROUP_STARTED': - return '#87CEFA'; // Pastel Sky Blue - case 'JOIN_APPROVED': - return '#98FB98'; // Pastel Green - case 'JOIN_VIA_INVITE': - return '#FFFACD'; // Pastel Lemon - default: - return '#D3D3D3'; // Pastel Gray - } + const getNotificationColor = (type: string) => { + const map: any = { + JOIN_REQUEST: '#FFB6C1', + CONTRIBUTION: '#98FB98', + CONTRIBUTION_ACK: '#ADD8E6', + CYCLE_COMPLETED: '#D8BFD8', + WITHDRAWAL: '#FFD700', + WITHDRAWAL_NOTIFY: '#FFA07A', + }; + return map[type] || '#D3D3D3'; }; const getFilterDisplayName = (filter: FilterType) => { - switch (filter) { - case 'JOIN_REQUEST': - return 'Requests'; - case 'CONTRIBUTION': - return 'Contributions'; - case 'CONTRIBUTION_ACK': - return 'Contributions Ack'; - case 'CYCLE_COMPLETED': - return 'Cycle Completed'; - case 'WITHDRAWAL': - return 'Withdrawals'; - case 'WITHDRAWAL_NOTIFY': - return 'Withdrawal Notify'; - case 'CYCLE_WAIVER': - return 'Waivers'; - case 'WAIVER_RECEIVED': - return 'Waiver Received'; - case 'USER_SELECTED': - return 'User Selected'; - case 'GROUP_STARTED': - return 'Group Started'; - case 'JOIN_APPROVED': - return 'Join Approved'; - case 'JOIN_VIA_INVITE': - return 'Invites'; - default: - return 'All'; - } + return filter.replace(/_/g, ' '); }; - const unreadCount = notifications.filter((item: any) => !item.read).length; + const unreadCount = notifications.filter((i: any) => !i.read).length; + + // 🔥 Swipe Delete UI + const renderRightActions = (dragX: any, messageId: string) => { + const scale = dragX.interpolate({ + inputRange: [-100, 0], + outputRange: [1, 0], + extrapolate: 'clamp', + }); + + return ( + handleDelete(messageId)} + style={styles(theme).deleteSwipe} + > + + + Delete + + + ); + }; + + const renderItem = ({ item }: any) => { + const scale = useSharedValue(1); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: scale.value }], + })); + + return ( + + renderRightActions(dragX, item.messageId) + } + > + + (scale.value = withSpring(0.96))} + onPressOut={() => (scale.value = withSpring(1))} + onPress={() => + handleMarkAsRead( + item.messageId, + item?.data?.msgType, + item?.data?.groupId, + item?.data?.cycleNumber, + item?.data?.contributionId + ) + } + > + + + {/* ICON */} + + + + + {/* TEXT */} + + + + {item.data.title} + + + {formatTime(item.sentTime)} + + + + + {item.data.body} + + + + + {getFilterDisplayName( + item.data.msgType + )} + + + + + {/* DOT */} + {!item.read && ( + + )} + + + + + + ); + }; return ( - {/* Header with actions */}
0 ? `(${unreadCount})` : ''}`} + title={ + 'Notifications' + + (unreadCount ? ` (${unreadCount})` : '') + } /> - {/* - {unreadCount > 0 && ( - - - Mark All Read - - - )} - {notifications.length > 0 && ( - - - Clear All - - - )} - */} - {/* Notification List */} item.messageId} + renderItem={renderItem} showsVerticalScrollIndicator={false} - contentContainerStyle={styles.listContainer} - renderItem={({ item }) => { - console.log("noti items : ", item) - return ( - handleMarkAsRead(item.messageId, item?.data?.msgType, item?.data?.groupId, item?.data?.cycleNumber, item?.data?.contributionId)} - activeOpacity={0.7} - > - - - {/* Icon */} - - - - - {/* Content */} - - - - {item.data.title} - - - {formatTime(item.sentTime)} - - - - - {item.data.body} - - - {item.data.msgType && ( - - - {getFilterDisplayName(item.data.msgType as FilterType)} - - - )} - - - {/* Actions */} - - {!item.read && ( - - )} - handleDelete(item.messageId)} - style={styles.deleteButton} - > - {/* Pastel Pink */} - - - - - - ) - }} + contentContainerStyle={styles(theme).container} ListEmptyComponent={ - - {/* Pastel Gray */} - No notifications yet - - When you get notifications, they'll appear here + + + + No notifications } @@ -284,130 +275,119 @@ const Notification = () => { export default Notification; -const styles = StyleSheet.create({ - headerActions: { - flexDirection: 'row', - gap: 8, - justifyContent: 'flex-end', - paddingHorizontal: 16, - paddingVertical: 10, - }, - headerButton: { - borderRadius: 8, - paddingVertical: 6, - paddingHorizontal: 12, - minWidth: 80, - alignItems: 'center', - }, - markReadButton: { - minWidth: 100, - }, - headerButtonText: { - color: '#FFFFFF', // White text for better contrast on pastel backgrounds - fontWeight: '600', - fontSize: 12, - }, - listContainer: { - paddingHorizontal: 12, - paddingBottom: 30, - paddingTop: 8, - }, - notificationCard: { - borderRadius: 12, - padding: 16, - marginBottom: 10, - shadowColor: '#000', - shadowOpacity: 0.05, // Reduced opacity for softer shadow - shadowRadius: 4, - elevation: 2, - borderLeftWidth: 4, - borderLeftColor: 'transparent', - }, - unreadNotification: { - backgroundColor: '#F8F8FF', // Pastel Lavender - }, - notificationContent: { - flexDirection: 'row', - alignItems: 'flex-start', - gap: 12, - }, - iconContainer: { - paddingTop: 2, - }, - textContent: { - flex: 1, - gap: 4, - }, - headerRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - marginBottom: 4, - }, - title: { - fontWeight: '600', - fontSize: 16, - color: '#696969', // Dim Gray - flex: 1, - marginRight: 8, - }, - unreadTitle: { - fontWeight: '700', - color: '#2F4F4F', // Dark Slate Gray - }, - message: { - fontSize: 14, - color: '#808080', // Gray - lineHeight: 20, - }, - time: { - fontSize: 12, - color: '#A9A9A9', // Dark Gray - fontWeight: '500', - }, - tagContainer: { - marginTop: 6, - }, - tag: { - fontSize: 10, - color: '#FFFFFF', // White text for better contrast - paddingHorizontal: 8, - paddingVertical: 2, - borderRadius: 4, - overflow: 'hidden', - alignSelf: 'flex-start', - fontWeight: '600', - }, - actionsContainer: { - alignItems: 'center', - gap: 8, - }, - unreadIndicator: { - width: 8, - height: 8, - borderRadius: 4, - backgroundColor: '#87CEEB', // Pastel Sky Blue - }, - deleteButton: { - padding: 4, - }, - emptyContainer: { - alignItems: 'center', - justifyContent: 'center', - marginTop: 100, - paddingHorizontal: 40, - }, - emptyText: { - fontSize: 18, - fontWeight: '600', - color: '#A9A9A9', // Dark Gray - marginTop: 16, - marginBottom: 8, - }, - emptySubtext: { - fontSize: 14, - color: '#C0C0C0', // Silver - textAlign: 'center', - lineHeight: 20, - }, -}); +const styles = (theme: any) => + StyleSheet.create({ + container: { + padding: 12, + paddingBottom: 40, + }, + + card: { + borderRadius: 16, + padding: 16, + marginBottom: 12, + backgroundColor: 'rgba(255,255,255,0.95)', + shadowColor: '#000', + shadowOpacity: 0.08, + shadowRadius: 10, + elevation: 4, + borderLeftWidth: 5, + }, + + unread: { + borderWidth: 1, + borderColor: '#E0E7FF', + shadowColor: '#6366F1', + shadowOpacity: 0.15, + }, + + row: { + flexDirection: 'row', + gap: 12, + }, + + iconBox: { + backgroundColor: '#F1F5F9', + padding: 10, + borderRadius: 12, + }, + + content: { + flex: 1, + }, + + topRow: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + + title: { + fontWeight: '700', + fontSize: 15, + color: theme.colors.textPrimary, + flex: 1, + }, + + unreadTitle: { + color: theme.colors.primary, + }, + + message: { + fontSize: 13, + color: theme.colors.textSecondary, + marginTop: 4, + }, + + time: { + fontSize: 11, + color: '#6B7280', + }, + + tagWrap: { + marginTop: 6, + }, + + tag: { + fontSize: 10, + color: '#fff', + paddingHorizontal: 10, + paddingVertical: 4, + borderRadius: 20, + fontWeight: '700', + alignSelf: 'flex-start', + }, + + dot: { + width: 8, + height: 8, + borderRadius: 4, + backgroundColor: theme.colors.primary, + marginTop: 6, + }, + + deleteSwipe: { + backgroundColor: '#FF3B30', + justifyContent: 'center', + alignItems: 'center', + width: 80, + borderRadius: 16, + marginBottom: 12, + }, + + deleteText: { + color: '#fff', + fontSize: 12, + marginTop: 4, + }, + + empty: { + alignItems: 'center', + marginTop: 120, + }, + + emptyText: { + marginTop: 12, + color: '#999', + fontSize: 16, + }, + }); \ No newline at end of file diff --git a/src/screens/payment/PaymentModeScreen.tsx b/src/screens/payment/PaymentModeScreen.tsx index b342da0..b0d110b 100644 --- a/src/screens/payment/PaymentModeScreen.tsx +++ b/src/screens/payment/PaymentModeScreen.tsx @@ -319,7 +319,9 @@ const PaymentModeScreen: React.FC = () => { `${BASE_URL}payments/pg/entry-fee/${groupId}?groupId=${groupId}&paymentId=${selectedMode}&mobileNumber=`, 'POST', payload - ) as any; + ) as any; + + console.log("payment res",res) if (res?.status === 'success' || res?.statusCode === 200) { const pgOrderNo = res?.data?.pgOrderNo; @@ -356,7 +358,8 @@ const PaymentModeScreen: React.FC = () => { 'POST', payload ) as any; - + + console.log("payment payload ",payload) if (res?.status === 'success' || res?.statusCode === 200) { const pgOrderNo = res?.data?.pgOrderNo; if (pgOrderNo) { @@ -467,8 +470,8 @@ const PaymentModeScreen: React.FC = () => { ) : ( <> - - Confirm & Pay + + Confirm & Pay )} @@ -685,7 +688,6 @@ const styles = StyleSheet.create({ borderRadius: 14, }, confirmText: { - color: '#fff', fontSize: responsiveFontSize(1.8), fontFamily: FontsFamily.nunitoBold, }, diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx index 87035af..9ac97a1 100644 --- a/src/screens/profile/Profile.tsx +++ b/src/screens/profile/Profile.tsx @@ -9,6 +9,8 @@ import { Alert, RefreshControl, ToastAndroid, + Modal, + ActivityIndicator, } from "react-native"; import * as Keychain from "react-native-keychain"; import LucideIcon from '../../components/common/LucideIcon'; @@ -118,6 +120,7 @@ const Profile = () => { countryCode: "", }); const [mobileMoneyErrors, setMobileMoneyErrors] = useState({}); + const [isLoggingOut, setIsLoggingOut] = useState(false); // --- API HOOKS --- const { @@ -181,6 +184,7 @@ const Profile = () => { }, [refetchProfile, refetchStatus]); const handleLogout = async () => { + setIsLoggingOut(true); try { const fcmToken = await getFcmToken(user?.userId?.toString()); if (fcmToken) { @@ -199,6 +203,7 @@ const Profile = () => { navigation.replace("Login"); } catch (error) { console.error("Logout error:", error); + setIsLoggingOut(false); } }; @@ -671,6 +676,19 @@ const Profile = () => { )} + + + + + + Signing you out... + + + ); }; @@ -883,7 +901,33 @@ const styles = StyleSheet.create({ skeletonContainer: { paddingHorizontal: 14, marginTop: 14, - } + }, + loadingOverlay: { + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.4)', + justifyContent: 'center', + alignItems: 'center', + padding: 20, + }, + loadingBox: { + padding: 24, + borderRadius: 20, + alignItems: 'center', + width: '70%', + maxWidth: 280, + borderWidth: 1, + elevation: 5, + shadowColor: '#000', + shadowOpacity: 0.1, + shadowRadius: 10, + shadowOffset: { width: 0, height: 4 }, + }, + loadingText: { + marginTop: 16, + fontSize: responsiveFontSize(1.8), + fontFamily: FontsFamily.nunitoBold, + textAlign: 'center', + }, }); export default Profile; diff --git a/src/screens/profile/SwitchNavigator.tsx b/src/screens/profile/SwitchNavigator.tsx index a0a7cc3..f1c893b 100644 --- a/src/screens/profile/SwitchNavigator.tsx +++ b/src/screens/profile/SwitchNavigator.tsx @@ -26,7 +26,8 @@ const SwitchNavigator: React.FC = ({ role, status }) => { return ( navigation.navigate("BecomeOrg")} + onPress={() => navigation.navigate("BecomeOrg")} + disabled={prettyStatus === "PENDING"} style={[styles.container, { backgroundColor: theme.colors.surface, borderColor: theme.colors.border }]} > @@ -42,7 +43,7 @@ const SwitchNavigator: React.FC = ({ role, status }) => { {secondaryText} - { prettyStatus !== "PENDING" && + {prettyStatus !== "PENDING" && } diff --git a/src/screens/transaction/TransactionList.tsx b/src/screens/transaction/TransactionList.tsx index 067013a..f229093 100644 --- a/src/screens/transaction/TransactionList.tsx +++ b/src/screens/transaction/TransactionList.tsx @@ -52,7 +52,7 @@ const formatINR = (amount: number) => { try { return new Intl.NumberFormat("en-IN", { style: "currency", currency: "INR" }).format(amount); } catch (e) { - return `₹${amount.toFixed(2)}`; + return `UGX${amount.toFixed(2)}`; } }; diff --git a/src/utils/ApiUrls.tsx b/src/utils/ApiUrls.tsx index 5e75c3e..d1ef603 100644 --- a/src/utils/ApiUrls.tsx +++ b/src/utils/ApiUrls.tsx @@ -1,7 +1,7 @@ export const BASE_URL = "https://11.godemo.in/FundCollect/api/" // export const BASE_URL = "http://192.168.29.113:9060/api/" -export const LOGIN_URL = `${BASE_URL}user/generate/otp?mobiles=` +export const LOGIN_URL = `${BASE_URL}user/generate/otp?mobile=` export const VARIFY_OTP_URL = `${BASE_URL}user/otp/verify` export const ORGRAGISTRATION = `${BASE_URL}organizer/registration/apply` export const GROUP_CREATION_URL = `${BASE_URL}fund/groups/{organizerId}`