fixed some ui
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect } from 'react';
|
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 { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
import { PaperProvider } from 'react-native-paper';
|
import { PaperProvider } from 'react-native-paper';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
@@ -18,6 +18,8 @@ import {
|
|||||||
|
|
||||||
import { ThemeProvider } from './src/theme/ThemeProvider';
|
import { ThemeProvider } from './src/theme/ThemeProvider';
|
||||||
|
|
||||||
|
|
||||||
|
LogBox.ignoreAllLogs(true);
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
|
|||||||
@@ -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
|
auto.sync=false
|
||||||
build.scans.enabled=false
|
build.scans.enabled=false
|
||||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||||
connection.project.dir=
|
connection.project.dir=
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
gradle.user.home=
|
gradle.user.home=
|
||||||
java.home=
|
java.home=C\:/Program Files/Microsoft/jdk-17.0.14.7-hotspot
|
||||||
jvm.arguments=
|
jvm.arguments=
|
||||||
offline.mode=false
|
offline.mode=false
|
||||||
override.workspace.settings=false
|
override.workspace.settings=true
|
||||||
show.console.view=false
|
show.console.view=true
|
||||||
show.executions.view=false
|
show.executions.view=true
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ apiClient.interceptors.request.use(
|
|||||||
async (config) => {
|
async (config) => {
|
||||||
try {
|
try {
|
||||||
const credentials = await Keychain.getGenericPassword();
|
const credentials = await Keychain.getGenericPassword();
|
||||||
|
console.log(`[HOOK_API] Token`, credentials);
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
config.headers.Authorization = `Bearer ${credentials.password}`;
|
config.headers.Authorization = `Bearer ${credentials.password}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ interface JoinedGroup {
|
|||||||
currentParticipants: number;
|
currentParticipants: number;
|
||||||
contributionAmount: number;
|
contributionAmount: number;
|
||||||
cycleType: 'MONTHLY' | 'WEEKLY' | 'QUARTERLY' | 'CUSTOM';
|
cycleType: 'MONTHLY' | 'WEEKLY' | 'QUARTERLY' | 'CUSTOM';
|
||||||
status: 'ACTIVE' | 'PENDING' | 'COMPLETED';
|
status: 'ACTIVE' | 'PENDING' | 'COMPLETED'| 'APPROVED';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JoinedGroupCardProps {
|
interface JoinedGroupCardProps {
|
||||||
@@ -42,9 +42,10 @@ const formatDate = (dateStr: string | null): string => {
|
|||||||
return `${dayNum} ${months[monthIndex]} ${year}`;
|
return `${dayNum} ${months[monthIndex]} ${year}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const JoinedGroupCard: FC<JoinedGroupCardProps> = ({ item }) => {
|
export const JoinedGroupCard: FC<JoinedGroupCardProps> = ({ item }:any) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const navigation = useNavigation<any>();
|
const navigation = useNavigation<any>();
|
||||||
|
console.log("item",item)
|
||||||
|
|
||||||
const { data: payableRes } = useFetch<any>(`${CONTRIBUTE}${item.id}/calculate-contribution`);
|
const { data: payableRes } = useFetch<any>(`${CONTRIBUTE}${item.id}/calculate-contribution`);
|
||||||
const payable = payableRes?.data;
|
const payable = payableRes?.data;
|
||||||
@@ -101,8 +102,8 @@ export const JoinedGroupCard: FC<JoinedGroupCardProps> = ({ item }) => {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.infoItem}>
|
<View style={styles.infoItem}>
|
||||||
<Text style={[styles.infoLabel, { color: theme.colors.textMuted }]}>Type</Text>
|
<Text style={[styles.infoLabel, { color: theme.colors.textMuted }]}>Duration</Text>
|
||||||
<Text style={[styles.infoValue, { color: theme.colors.textPrimary }]}>{item.cycleType}</Text>
|
<Text style={[styles.infoValue, { color: theme.colors.textPrimary }]}>{`${item?.cycleType} (${item?.customCycleDays} days)`}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export const MyGroupCard: React.FC<Props> = ({ item, onManage }) => {
|
|||||||
<View style={styles.metaItem}>
|
<View style={styles.metaItem}>
|
||||||
<LucideIcon name="cash-outline" size={14} color={theme.colors.textSecondary} />
|
<LucideIcon name="cash-outline" size={14} color={theme.colors.textSecondary} />
|
||||||
<Text style={[styles.cardMetaText, { color: theme.colors.textSecondary }]}>
|
<Text style={[styles.cardMetaText, { color: theme.colors.textSecondary }]}>
|
||||||
₹{item.contributionAmount}
|
UGX{item.contributionAmount}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.metaItem}>
|
<View style={styles.metaItem}>
|
||||||
|
|||||||
@@ -65,14 +65,14 @@ const ContributionDetails: React.FC<ContributionProps> = ({
|
|||||||
<View style={[styles.card, { backgroundColor: theme.colors.background, borderColor: theme.colors.border }]}>
|
<View style={[styles.card, { backgroundColor: theme.colors.background, borderColor: theme.colors.border }]}>
|
||||||
<View style={styles.headerRow}>
|
<View style={styles.headerRow}>
|
||||||
<LucideIcon name="wallet-outline" size={24} color={theme.colors.primary} />
|
<LucideIcon name="wallet-outline" size={24} color={theme.colors.primary} />
|
||||||
<Text style={[styles.amount, { color: theme.colors.textPrimary }]}>₹{data?.totalPayable || data?.baseAmount}</Text>
|
<Text style={[styles.amount, { color: theme.colors.textPrimary }]}>UGX{data?.totalPayable || data?.baseAmount}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.infoGrid}>
|
<View style={styles.infoGrid}>
|
||||||
<InfoItem label="Base Amount" value={`₹${data?.baseAmount}`} />
|
<InfoItem label="Base Amount" value={`UGX${data?.baseAmount}`} />
|
||||||
<InfoItem label="Due Date" value={data?.dueDate} />
|
<InfoItem label="Due Date" value={data?.dueDate} />
|
||||||
<InfoItem label="Cycle End" value={data?.cycleEnd} />
|
<InfoItem label="Cycle End" value={data?.cycleEnd} />
|
||||||
<InfoItem label="Late Fee" value={`₹${data?.effectiveLateFee || 0}`} />
|
<InfoItem label="Late Fee" value={`UGX${data?.effectiveLateFee || 0}`} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ export default function GroupCreationForm() {
|
|||||||
|
|
||||||
<Section title="Financial Details">
|
<Section title="Financial Details">
|
||||||
<CustomInput
|
<CustomInput
|
||||||
label="Contribution Amount (₹)"
|
label="Contribution Amount (UGX)"
|
||||||
placeholder="e.g., 5000.00"
|
placeholder="e.g., 5000.00"
|
||||||
value={form.contributionAmount}
|
value={form.contributionAmount}
|
||||||
onChangeText={(val) => handleChange('contributionAmount', val)}
|
onChangeText={(val) => handleChange('contributionAmount', val)}
|
||||||
@@ -226,7 +226,7 @@ export default function GroupCreationForm() {
|
|||||||
error={errors.contributionAmount}
|
error={errors.contributionAmount}
|
||||||
/>
|
/>
|
||||||
<CustomInput
|
<CustomInput
|
||||||
label="Entry Fee (₹)"
|
label="Entry Fee (UGX)"
|
||||||
placeholder="e.g., 0 for free"
|
placeholder="e.g., 0 for free"
|
||||||
value={form.entryFee}
|
value={form.entryFee}
|
||||||
onChangeText={(val) => handleChange('entryFee', val)}
|
onChangeText={(val) => handleChange('entryFee', val)}
|
||||||
@@ -272,7 +272,7 @@ export default function GroupCreationForm() {
|
|||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<CustomInput
|
<CustomInput
|
||||||
label="Fee Amount (₹)"
|
label="Late Fee Amount (UGX)"
|
||||||
placeholder="e.g., 50"
|
placeholder="e.g., 50"
|
||||||
value={form.lateFeeValue}
|
value={form.lateFeeValue}
|
||||||
onChangeText={(val) => handleChange('lateFeeValue', val)}
|
onChangeText={(val) => handleChange('lateFeeValue', val)}
|
||||||
|
|||||||
@@ -189,8 +189,8 @@ export default function GroupReview({ route, navigation }: any) {
|
|||||||
</TimelineSection>
|
</TimelineSection>
|
||||||
|
|
||||||
<TimelineSection icon="wallet-outline" title="Financials">
|
<TimelineSection icon="wallet-outline" title="Financials">
|
||||||
{renderRow('Contribution', `₹${form.contributionAmount}`)}
|
{renderRow('Contribution', `UGX${form.contributionAmount}`)}
|
||||||
{renderRow('Entry Fee', `₹${form.entryFee}`)}
|
{renderRow('Entry Fee', `UGX${form.entryFee}`)}
|
||||||
{renderRow('Cycle', getCycleLabel(form.cycleType, form.customCycleDays))}
|
{renderRow('Cycle', getCycleLabel(form.cycleType, form.customCycleDays))}
|
||||||
{form.cycleType === 'CUSTOM' && !['1', '7'].includes(String(form.customCycleDays)) &&
|
{form.cycleType === 'CUSTOM' && !['1', '7'].includes(String(form.customCycleDays)) &&
|
||||||
renderRow('Cycle Days', `${form.customCycleDays} days`)}
|
renderRow('Cycle Days', `${form.customCycleDays} days`)}
|
||||||
@@ -198,7 +198,7 @@ export default function GroupReview({ route, navigation }: any) {
|
|||||||
|
|
||||||
<TimelineSection icon="alert-circle-outline" title="Policies">
|
<TimelineSection icon="alert-circle-outline" title="Policies">
|
||||||
{renderRow('Fee Mode', form.lateFeeMode)}
|
{renderRow('Fee Mode', form.lateFeeMode)}
|
||||||
{renderRow('Fee Value', `₹${form.lateFeeValue}`)}
|
{renderRow('Fee Value', `UGX${form.lateFeeValue}`)}
|
||||||
</TimelineSection>
|
</TimelineSection>
|
||||||
|
|
||||||
<CustomButton
|
<CustomButton
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ const GroupCard = ({
|
|||||||
<View style={styles.metaItem}>
|
<View style={styles.metaItem}>
|
||||||
<LucideIcon name="ticket-outline" size={14} color={theme.colors.textMuted} />
|
<LucideIcon name="ticket-outline" size={14} color={theme.colors.textMuted} />
|
||||||
<Text style={[styles.metaText, { color: theme.colors.textSecondary }]}>
|
<Text style={[styles.metaText, { color: theme.colors.textSecondary }]}>
|
||||||
₹{item.entryFee}
|
UGX{item.entryFee}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -139,16 +139,16 @@ const InviteScreen: React.FC<InviteScreenProps> = ({ route, navigation }) => {
|
|||||||
<View style={[styles.card, { backgroundColor: theme.colors.surface, borderColor: theme.colors.border }]}>
|
<View style={[styles.card, { backgroundColor: theme.colors.surface, borderColor: theme.colors.border }]}>
|
||||||
<Text style={[styles.cardTitle, { color: theme.colors.textPrimary }]}>Circle Overview</Text>
|
<Text style={[styles.cardTitle, { color: theme.colors.textPrimary }]}>Circle Overview</Text>
|
||||||
<InfoRow label="Organizer" value={organizerName} />
|
<InfoRow label="Organizer" value={organizerName} />
|
||||||
<InfoRow label="Contribution" value={`₹${groupDetails.contributionAmount.toLocaleString('en-IN')}`} />
|
<InfoRow label="Contribution" value={`UGX${groupDetails.contributionAmount.toLocaleString('en-IN')}`} />
|
||||||
<InfoRow
|
<InfoRow
|
||||||
label="Frequency"
|
label="Frequency"
|
||||||
value={groupDetails.cycleType === 'CUSTOM' ? `${groupDetails.customCycleDays} Day(s)` : groupDetails.cycleType}
|
value={groupDetails.cycleType === 'CUSTOM' ? `${groupDetails.customCycleDays} Day(s)` : groupDetails.cycleType}
|
||||||
/>
|
/>
|
||||||
<InfoRow label="Min/Max Participants" value={`${groupDetails.minParticipants} / ${groupDetails.maxParticipants}`} />
|
<InfoRow label="Min/Max Participants" value={`${groupDetails.minParticipants} / ${groupDetails.maxParticipants}`} />
|
||||||
<InfoRow label="Entry Fee" value={`₹${groupDetails.entryFee}`} />
|
<InfoRow label="Entry Fee" value={`UGX${groupDetails.entryFee}`} />
|
||||||
<InfoRow
|
<InfoRow
|
||||||
label="Late Payment Charge"
|
label="Late Payment Charge"
|
||||||
value={`₹${groupDetails.latePaymentCharge} (${groupDetails.lateFeeMode || 'DAILY'})`}
|
value={`UGX${groupDetails.latePaymentCharge} (${groupDetails.lateFeeMode || 'DAILY'})`}
|
||||||
/>
|
/>
|
||||||
<InfoRow label="Withdrawal Method" value={groupDetails.withdrawalMethod || 'RANDOM'} />
|
<InfoRow label="Withdrawal Method" value={groupDetails.withdrawalMethod || 'RANDOM'} />
|
||||||
<InfoRow label="Status" value={groupDetails.status} />
|
<InfoRow label="Status" value={groupDetails.status} />
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ export default function LoginScreen() {
|
|||||||
}, [fadeAnim, slideAnim]);
|
}, [fadeAnim, slideAnim]);
|
||||||
|
|
||||||
const { refetch: sendOtp, isFetching: loading } = useFetch<any>(
|
const { refetch: sendOtp, isFetching: loading } = useFetch<any>(
|
||||||
LOGIN_URL,
|
LOGIN_URL + mobile,
|
||||||
{ mobile },
|
{},
|
||||||
{
|
{
|
||||||
enabled: false,
|
enabled: false,
|
||||||
retry: false,
|
retry: false,
|
||||||
@@ -100,6 +100,7 @@ export default function LoginScreen() {
|
|||||||
value={mobile}
|
value={mobile}
|
||||||
onChangeText={(text) => {
|
onChangeText={(text) => {
|
||||||
setMobile(text.replace(/\D/g, ''));
|
setMobile(text.replace(/\D/g, ''));
|
||||||
|
|
||||||
if (error) setError('');
|
if (error) setError('');
|
||||||
}}
|
}}
|
||||||
keyboardType="phone-pad"
|
keyboardType="phone-pad"
|
||||||
|
|||||||
@@ -97,6 +97,30 @@ export default function OtpVerificationScreen() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleTextChange = (text: string, index: number) => {
|
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];
|
const newCode = [...code];
|
||||||
newCode[index] = text.slice(-1);
|
newCode[index] = text.slice(-1);
|
||||||
setCode(newCode);
|
setCode(newCode);
|
||||||
@@ -153,13 +177,14 @@ export default function OtpVerificationScreen() {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
keyboardType="number-pad"
|
keyboardType="number-pad"
|
||||||
maxLength={1}
|
maxLength={CELL_COUNT}
|
||||||
value={digit}
|
value={digit}
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
placeholderTextColor={theme.colors.textMuted}
|
placeholderTextColor={theme.colors.textMuted}
|
||||||
onChangeText={(text) => handleTextChange(text, index)}
|
onChangeText={(text) => handleTextChange(text, index)}
|
||||||
onKeyPress={(e) => handleKeyPress(e, index)}
|
onKeyPress={(e) => handleKeyPress(e, index)}
|
||||||
selectionColor={theme.colors.primary}
|
selectionColor={theme.colors.primary}
|
||||||
|
textContentType="oneTimeCode"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const GroupCard = ({ item }: { item: any }) => {
|
|||||||
{item.baseAmount && (
|
{item.baseAmount && (
|
||||||
<View style={styles.detailRow}>
|
<View style={styles.detailRow}>
|
||||||
<Text style={[styles.detailLabel, { color: theme.colors.textSecondary }]}>Contribution</Text>
|
<Text style={[styles.detailLabel, { color: theme.colors.textSecondary }]}>Contribution</Text>
|
||||||
<Text style={[styles.detailValue, { color: theme.colors.textPrimary }]}>₹{item.baseAmount}</Text>
|
<Text style={[styles.detailValue, { color: theme.colors.textPrimary }]}>UGX{item.baseAmount}</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
|
|||||||
<View style={[styles.dueBar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }]}>
|
<View style={[styles.dueBar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }]}>
|
||||||
<View>
|
<View>
|
||||||
<Text style={[styles.dueBarLabel, { color: theme.colors.textSecondary }]}>Total Outstanding</Text>
|
<Text style={[styles.dueBarLabel, { color: theme.colors.textSecondary }]}>Total Outstanding</Text>
|
||||||
<Text style={[styles.dueBarValue, { color: theme.colors.textPrimary }]}>₹{totalDue}</Text>
|
<Text style={[styles.dueBarValue, { color: theme.colors.textPrimary }]}>UGX{totalDue}</Text>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.payButton, { backgroundColor: theme.colors.primary }]}
|
style={[styles.payButton, { backgroundColor: theme.colors.primary }]}
|
||||||
@@ -201,17 +201,17 @@ const UserProfileStatus: React.FC<UserProfileStatusProps> = ({
|
|||||||
<View style={styles.groupInfo}>
|
<View style={styles.groupInfo}>
|
||||||
<Text style={[styles.groupName, { color: theme.colors.textPrimary }]}>{group.groupName}</Text>
|
<Text style={[styles.groupName, { color: theme.colors.textPrimary }]}>{group.groupName}</Text>
|
||||||
<Text style={[styles.groupSub, { color: theme.colors.textSecondary }]}>
|
<Text style={[styles.groupSub, { color: theme.colors.textSecondary }]}>
|
||||||
Amount: ₹{group.baseAmount} {group.lateFee > 0 ? `+ ₹${group.lateFee} fee` : ''}
|
Amount: UGX{group.baseAmount} {group.lateFee > 0 ? `+ UGX${group.lateFee} fee` : ''}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={[styles.groupAmount, { color: theme.colors.textPrimary }]}>₹{group.totalDue}</Text>
|
<Text style={[styles.groupAmount, { color: theme.colors.textPrimary }]}>UGX{group.totalDue}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<View style={[styles.modalFooter, { borderTopColor: theme.colors.border }]}>
|
<View style={[styles.modalFooter, { borderTopColor: theme.colors.border }]}>
|
||||||
<View>
|
<View>
|
||||||
<Text style={[styles.totalLabel, { color: theme.colors.textSecondary }]}>Payable</Text>
|
<Text style={[styles.totalLabel, { color: theme.colors.textSecondary }]}>Payable</Text>
|
||||||
<Text style={[styles.totalValue, { color: theme.colors.textPrimary }]}>₹{totalDueSelected}</Text>
|
<Text style={[styles.totalValue, { color: theme.colors.textPrimary }]}>UGX{totalDueSelected}</Text>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.confirmButton, { backgroundColor: theme.colors.primary, opacity: submitting ? 0.7 : 1 }]}
|
style={[styles.confirmButton, { backgroundColor: theme.colors.primary, opacity: submitting ? 0.7 : 1 }]}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ const GroupDetails = () => {
|
|||||||
<View>
|
<View>
|
||||||
<Text style={[styles.cardLabel, { fontFamily: FontsFamily.nunitoSemiBold }]}>Total Balance</Text>
|
<Text style={[styles.cardLabel, { fontFamily: FontsFamily.nunitoSemiBold }]}>Total Balance</Text>
|
||||||
<Text style={[styles.cardBalance, { fontFamily: FontsFamily.nunitoExtraBold }]}>
|
<Text style={[styles.cardBalance, { fontFamily: FontsFamily.nunitoExtraBold }]}>
|
||||||
₹{cycleDashboardData?.contributionAmount * memberData.length || group.goal}
|
UGX{cycleDashboardData?.contributionAmount * memberData.length || group.goal}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
@@ -248,7 +248,7 @@ const GroupDetails = () => {
|
|||||||
<LucideIcon name="cash" size={18} color={theme.colors.success} />
|
<LucideIcon name="cash" size={18} color={theme.colors.success} />
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<Text style={[styles.miniStatValue, { color: theme.colors.textPrimary, fontFamily: FontsFamily.nunitoBold }]}>₹{cycleDashboardData?.contributionAmount || group.collected}</Text>
|
<Text style={[styles.miniStatValue, { color: theme.colors.textPrimary, fontFamily: FontsFamily.nunitoBold }]}>UGX{cycleDashboardData?.contributionAmount || group.collected}</Text>
|
||||||
<Text style={[styles.miniStatLabel, { color: theme.colors.textMuted, fontFamily: FontsFamily.nunitoRegular }]}>Per Head</Text>
|
<Text style={[styles.miniStatLabel, { color: theme.colors.textMuted, fontFamily: FontsFamily.nunitoRegular }]}>Per Head</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ const EditGroupScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<TouchableOpacity onPress={pickImage} style={styles.cameraBtn}>
|
<TouchableOpacity onPress={pickImage} style={styles.cameraBtn}>
|
||||||
<LucideIcon name="camera" size={20} color={theme.colors.primary} />
|
<LucideIcon name="camera" size={20} color={theme.colors.black} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
{selectedImage && (
|
{selectedImage && (
|
||||||
@@ -452,7 +452,7 @@ const EditGroupScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={styles.statDivider} />
|
<View style={styles.statDivider} />
|
||||||
<View style={styles.statBox}>
|
<View style={styles.statBox}>
|
||||||
<Text style={styles.statValue}>₹{groupDetails.contributionAmount}</Text>
|
<Text style={styles.statValue}>UGX{groupDetails.contributionAmount}</Text>
|
||||||
<Text style={styles.statLabel}>Contribution</Text>
|
<Text style={styles.statLabel}>Contribution</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.statDivider} />
|
<View style={styles.statDivider} />
|
||||||
@@ -474,7 +474,7 @@ const EditGroupScreen: React.FC = () => {
|
|||||||
|
|
||||||
const getDisplayValue = (field: string) => {
|
const getDisplayValue = (field: string) => {
|
||||||
const val = groupDetails[field as keyof typeof groupDetails];
|
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 ?? '-');
|
return String(val ?? '-');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -592,9 +592,9 @@ const EditGroupScreen: React.FC = () => {
|
|||||||
<ActivityIndicator color="#fff" />
|
<ActivityIndicator color="#fff" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<LucideIcon name="power" size={24} color="#fff" style={{ marginRight: 8 }} />
|
<LucideIcon name="power" size={24} color={ theme.colors.reverseText} style={{ marginRight: 8 }} />
|
||||||
<Text style={styles.floatingBtnText}>Activate Group</Text>
|
<Text style={styles.floatingBtnText}>Activate Group</Text>
|
||||||
<LucideIcon name="arrow-forward" size={20} color="#fff" style={{ marginLeft: 8, opacity: 0.8 }} />
|
{/* <LucideIcon name="arrow-forward" size={20} color={ theme.colors.reverseText} style={{ marginLeft: 8, opacity: 0.8 }} /> */}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -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 },
|
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 },
|
descText: { color: '#eee', fontSize: 14, fontFamily: FontsFamily.nunitoRegular },
|
||||||
badgesRow: { flexDirection: 'row', gap: 10 },
|
badgesRow: { flexDirection: 'row', gap: 10 },
|
||||||
badge: { backgroundColor: 'rgba(0,0,0,0.5)', paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12 },
|
badge: { backgroundColor: theme.colors.background, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12 },
|
||||||
badgeText: { color: '#fff', fontSize: 12, fontWeight: 'bold' },
|
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' },
|
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' },
|
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' },
|
uploadFloatWrapper: { position: 'absolute', bottom: 40, right: 30, alignItems: 'center' },
|
||||||
@@ -792,7 +792,7 @@ const getStyles = (theme: any) => StyleSheet.create({
|
|||||||
borderBottomColor: '#334155',
|
borderBottomColor: '#334155',
|
||||||
},
|
},
|
||||||
floatingBtnText: {
|
floatingBtnText: {
|
||||||
color: '#fff',
|
color: theme.colors.reverseText,
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontFamily: FontsFamily.nunitoBold,
|
fontFamily: FontsFamily.nunitoBold,
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
@@ -800,15 +800,15 @@ const getStyles = (theme: any) => StyleSheet.create({
|
|||||||
modalContent: { flex: 1 },
|
modalContent: { flex: 1 },
|
||||||
modalFieldContainer: { marginBottom: 16 },
|
modalFieldContainer: { marginBottom: 16 },
|
||||||
modalLabel: { fontSize: 14, color: theme.colors.textSecondary, marginBottom: 8, fontFamily: FontsFamily.nunitoBold },
|
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 },
|
chipContainer: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 },
|
||||||
chip: { paddingHorizontal: 16, paddingVertical: 10, borderRadius: 20, backgroundColor: '#f0f0f0', borderWidth: 1, borderColor: '#e0e0e0' },
|
chip: { paddingHorizontal: 16, paddingVertical: 10, borderRadius: 20, backgroundColor: '#f0f0f0', borderWidth: 1, borderColor: '#e0e0e0' },
|
||||||
activeChip: { backgroundColor: theme.colors.primary, borderColor: theme.colors.primary },
|
activeChip: { backgroundColor: theme.colors.primary, borderColor: theme.colors.primary },
|
||||||
chipText: { fontSize: 14, fontFamily: FontsFamily.nunitoMedium, color: theme.colors.textSecondary },
|
chipText: { fontSize: 14, fontFamily: FontsFamily.nunitoMedium, color: theme.colors.black },
|
||||||
activeChipText: { color: theme.colors.white, fontFamily: FontsFamily.nunitoBold },
|
activeChipText: { color: theme.colors.reverseText, fontFamily: FontsFamily.nunitoBold },
|
||||||
modalFooter: { borderTopWidth: 1, borderTopColor: '#eee', paddingTop: 16 },
|
modalFooter: { borderTopWidth: 1, borderTopColor: '#eee', paddingTop: 16 },
|
||||||
saveBtn: { backgroundColor: theme.colors.primary, padding: 16, borderRadius: 12, alignItems: 'center' },
|
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;
|
export default EditGroupScreen;
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ const JOINED_GROUP_STATUS_FILTERS: GroupStatusFilter[] = [
|
|||||||
'ACTIVE',
|
'ACTIVE',
|
||||||
'DELAY',
|
'DELAY',
|
||||||
'COMPLETED',
|
'COMPLETED',
|
||||||
|
'APPROVED'
|
||||||
];
|
];
|
||||||
|
|
||||||
const getListFromResponse = <T,>(response: any): T[] => {
|
const getListFromResponse = <T,>(response: any): T[] => {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ const Payments: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentParticipant && amountToWaive > currentParticipant.lateFee) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,13 +174,13 @@ const Payments: React.FC = () => {
|
|||||||
{participant.status}
|
{participant.status}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.amountText, { color: theme.colors.textPrimary }]}>
|
<Text style={[styles.amountText, { color: theme.colors.textPrimary }]}>
|
||||||
₹{participant.status === 'SUCCESS' ? participant.totalPaidAmount : participant.expectedAmount}
|
UGX{participant.status === 'SUCCESS' ? participant.totalPaidAmount : participant.expectedAmount}
|
||||||
</Text>
|
</Text>
|
||||||
{participant.lateFee > 0 && (
|
{participant.lateFee > 0 && (
|
||||||
<Text style={[styles.lateFeeText, { color: theme.colors.error }]}>Late Fee: ₹{participant.lateFee}</Text>
|
<Text style={[styles.lateFeeText, { color: theme.colors.error }]}>Late Fee: UGX{participant.lateFee}</Text>
|
||||||
)}
|
)}
|
||||||
{participant.lateFeeWaived && (
|
{participant.lateFeeWaived && (
|
||||||
<Text style={[styles.waivedText, { color: theme.colors.success }]}>Waived: ₹{participant.waivedAmount}</Text>
|
<Text style={[styles.waivedText, { color: theme.colors.success }]}>Waived: UGX{participant.waivedAmount}</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ const Payments: React.FC = () => {
|
|||||||
confirmText={isWaiving ? 'Applying...' : 'Apply Waiver'}
|
confirmText={isWaiving ? 'Applying...' : 'Apply Waiver'}
|
||||||
>
|
>
|
||||||
<Text style={[styles.modalSubtitle, { color: theme.colors.textSecondary }]}>
|
<Text style={[styles.modalSubtitle, { color: theme.colors.textSecondary }]}>
|
||||||
For {currentParticipant?.firstName}. Current late fee: ₹{currentParticipant?.lateFee}
|
For {currentParticipant?.firstName}. Current late fee: UGX{currentParticipant?.lateFee}
|
||||||
</Text>
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[
|
style={[
|
||||||
@@ -282,14 +282,14 @@ const Payments: React.FC = () => {
|
|||||||
<View style={styles.summaryBox}>
|
<View style={styles.summaryBox}>
|
||||||
<Text style={[styles.summaryLabel, { color: theme.colors.textSecondary }]}>Contribution</Text>
|
<Text style={[styles.summaryLabel, { color: theme.colors.textSecondary }]}>Contribution</Text>
|
||||||
<Text style={[styles.summaryValue, { color: theme.colors.textPrimary }]}>
|
<Text style={[styles.summaryValue, { color: theme.colors.textPrimary }]}>
|
||||||
₹{paymentDetails.contributionAmount}
|
UGX{paymentDetails.contributionAmount}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.divider} />
|
<View style={styles.divider} />
|
||||||
<View style={styles.summaryBox}>
|
<View style={styles.summaryBox}>
|
||||||
<Text style={[styles.summaryLabel, { color: theme.colors.textSecondary }]}>Total Amount</Text>
|
<Text style={[styles.summaryLabel, { color: theme.colors.textSecondary }]}>Total Amount</Text>
|
||||||
<Text style={[styles.summaryValue, { color: theme.colors.primary }]}>
|
<Text style={[styles.summaryValue, { color: theme.colors.primary }]}>
|
||||||
₹{paymentDetails.totalDueWithLateFee}
|
UGX{paymentDetails.totalDueWithLateFee}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
@@ -6,274 +6,265 @@ import {
|
|||||||
FlatList,
|
FlatList,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import {
|
import { removeNotification } from '../../redux/notificationSlice';
|
||||||
removeNotification,
|
|
||||||
} from '../../redux/notificationSlice';
|
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import LucideIcon from '../../components/common/LucideIcon';
|
import LucideIcon from '../../components/common/LucideIcon';
|
||||||
import AppView from '../../components/AppView';
|
import AppView from '../../components/AppView';
|
||||||
import Header from '../../components/common/Header';
|
import Header from '../../components/common/Header';
|
||||||
import { useNotificationHandler } from '../hook/useNotificationHandler';
|
import { useNotificationHandler } from '../hook/useNotificationHandler';
|
||||||
import colors from '../../components/colors';
|
import { useTheme } from '../../theme/ThemeProvider';
|
||||||
|
|
||||||
type FilterType = 'all' | 'JOIN_REQUEST' | 'CONTRIBUTION' | 'CONTRIBUTION_ACK' |
|
import { Swipeable } from 'react-native-gesture-handler';
|
||||||
'CYCLE_COMPLETED' | 'WITHDRAWAL' | 'WITHDRAWAL_NOTIFY' |
|
import Animated, {
|
||||||
'CYCLE_WAIVER' | 'WAIVER_RECEIVED' | 'USER_SELECTED' |
|
useSharedValue,
|
||||||
'GROUP_STARTED' | 'JOIN_APPROVED' | 'JOIN_VIA_INVITE';
|
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 Notification = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { handleMarkAsReadAndNavigate } = useNotificationHandler();
|
const { handleMarkAsReadAndNavigate } = useNotificationHandler();
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
const notifications = useSelector(
|
const notifications = useSelector(
|
||||||
(state: any) => state.notifications.notifications
|
(state: any) => state.notifications.notifications ?? []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
console.log("notifications ==>", notifications);
|
|
||||||
|
|
||||||
const handleDelete = (messageId: string) => {
|
const handleDelete = (messageId: string) => {
|
||||||
dispatch(removeNotification(messageId));
|
dispatch(removeNotification(messageId));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMarkAsRead = (messageId: string, screen: string, groupId: string, cycleNumber: string, contributionId: string) => {
|
const handleMarkAsRead = (
|
||||||
console.log("screen type : ", screen);
|
messageId: string,
|
||||||
handleMarkAsReadAndNavigate(
|
screen: string,
|
||||||
messageId,
|
groupId: string,
|
||||||
screen,
|
cycleNumber: string,
|
||||||
{
|
contributionId: string
|
||||||
groupId: groupId,
|
) => {
|
||||||
cycleNumber,
|
handleMarkAsReadAndNavigate(messageId, screen, {
|
||||||
contributionId,
|
groupId,
|
||||||
actionTab3: 'Your Requests',
|
cycleNumber,
|
||||||
actionTab1: 'Joined Groups',
|
contributionId,
|
||||||
GroupDetails: {
|
actionTab3: 'Your Requests',
|
||||||
item: {}
|
actionTab1: 'Joined Groups',
|
||||||
},
|
GroupDetails: { item: {} },
|
||||||
}
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatTime = (timestamp: number) => {
|
const formatTime = (timestamp: number) => {
|
||||||
const date = new Date(timestamp);
|
const date = new Date(timestamp);
|
||||||
const now = new Date();
|
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) {
|
if (diff < 1) return 'Just now';
|
||||||
return 'Just now';
|
if (diff < 24) return `${Math.floor(diff)}h ago`;
|
||||||
} else if (diffInHours < 24) {
|
return date.toLocaleDateString();
|
||||||
return `${Math.floor(diffInHours)}h ago`;
|
|
||||||
} else {
|
|
||||||
return date.toLocaleDateString();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNotificationIcon = (msgType: string) => {
|
const getNotificationIcon = (type: string) => {
|
||||||
switch (msgType) {
|
const map: any = {
|
||||||
case 'JOIN_REQUEST':
|
JOIN_REQUEST: 'person-add-outline',
|
||||||
return 'person-add-outline';
|
CONTRIBUTION: 'cash-outline',
|
||||||
case 'CONTRIBUTION':
|
CONTRIBUTION_ACK: 'checkmark-done-outline',
|
||||||
return 'cash-outline';
|
CYCLE_COMPLETED: 'trophy-outline',
|
||||||
case 'CONTRIBUTION_ACK':
|
WITHDRAWAL: 'card-outline',
|
||||||
return 'checkmark-done-outline';
|
WITHDRAWAL_NOTIFY: 'notifications-outline',
|
||||||
case 'CYCLE_COMPLETED':
|
GROUP_STARTED: 'rocket-outline',
|
||||||
return 'trophy-outline';
|
};
|
||||||
case 'WITHDRAWAL':
|
return map[type] || 'notifications-outline';
|
||||||
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 getNotificationColor = (type: string) => {
|
||||||
const getNotificationColor = (msgType: string) => {
|
const map: any = {
|
||||||
switch (msgType) {
|
JOIN_REQUEST: '#FFB6C1',
|
||||||
case 'JOIN_REQUEST':
|
CONTRIBUTION: '#98FB98',
|
||||||
return '#FFB6C1'; // Pastel Pink
|
CONTRIBUTION_ACK: '#ADD8E6',
|
||||||
case 'CONTRIBUTION':
|
CYCLE_COMPLETED: '#D8BFD8',
|
||||||
return '#98FB98'; // Pastel Green
|
WITHDRAWAL: '#FFD700',
|
||||||
case 'CONTRIBUTION_ACK':
|
WITHDRAWAL_NOTIFY: '#FFA07A',
|
||||||
return '#ADD8E6'; // Pastel Blue
|
};
|
||||||
case 'CYCLE_COMPLETED':
|
return map[type] || '#D3D3D3';
|
||||||
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 getFilterDisplayName = (filter: FilterType) => {
|
const getFilterDisplayName = (filter: FilterType) => {
|
||||||
switch (filter) {
|
return filter.replace(/_/g, ' ');
|
||||||
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';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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 (
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => handleDelete(messageId)}
|
||||||
|
style={styles(theme).deleteSwipe}
|
||||||
|
>
|
||||||
|
<Animated.View style={{ transform: [{ scale }] }}>
|
||||||
|
<LucideIcon name="trash-outline" size={22} color="#fff" />
|
||||||
|
<Text style={styles(theme).deleteText}>Delete</Text>
|
||||||
|
</Animated.View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderItem = ({ item }: any) => {
|
||||||
|
const scale = useSharedValue(1);
|
||||||
|
|
||||||
|
const animatedStyle = useAnimatedStyle(() => ({
|
||||||
|
transform: [{ scale: scale.value }],
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Swipeable
|
||||||
|
renderRightActions={(_, dragX) =>
|
||||||
|
renderRightActions(dragX, item.messageId)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Animated.View style={animatedStyle}>
|
||||||
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.9}
|
||||||
|
onPressIn={() => (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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<LinearGradient
|
||||||
|
colors={
|
||||||
|
item.read
|
||||||
|
? [theme.colors.white, '#F8FAFF']
|
||||||
|
: ['#FFF7ED', '#EEF2FF']
|
||||||
|
}
|
||||||
|
style={[
|
||||||
|
styles(theme).card,
|
||||||
|
!item.read && styles(theme).unread,
|
||||||
|
{
|
||||||
|
borderLeftColor: getNotificationColor(
|
||||||
|
item.data.msgType
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View style={styles(theme).row}>
|
||||||
|
{/* ICON */}
|
||||||
|
<View style={styles(theme).iconBox}>
|
||||||
|
<LucideIcon
|
||||||
|
name={getNotificationIcon(
|
||||||
|
item.data.msgType
|
||||||
|
)}
|
||||||
|
size={22}
|
||||||
|
color={getNotificationColor(
|
||||||
|
item.data.msgType
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* TEXT */}
|
||||||
|
<View style={styles(theme).content}>
|
||||||
|
<View style={styles(theme).topRow}>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles(theme).title,
|
||||||
|
!item.read &&
|
||||||
|
styles(theme).unreadTitle,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{item.data.title}
|
||||||
|
</Text>
|
||||||
|
<Text style={styles(theme).time}>
|
||||||
|
{formatTime(item.sentTime)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text style={styles(theme).message}>
|
||||||
|
{item.data.body}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={styles(theme).tagWrap}>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles(theme).tag,
|
||||||
|
{
|
||||||
|
backgroundColor:
|
||||||
|
getNotificationColor(
|
||||||
|
item.data.msgType
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{getFilterDisplayName(
|
||||||
|
item.data.msgType
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* DOT */}
|
||||||
|
{!item.read && (
|
||||||
|
<View style={styles(theme).dot} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
|
</Swipeable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppView>
|
<AppView>
|
||||||
{/* Header with actions */}
|
|
||||||
<Header
|
<Header
|
||||||
title={`Notifications ${unreadCount > 0 ? `(${unreadCount})` : ''}`}
|
title={
|
||||||
|
'Notifications' +
|
||||||
|
(unreadCount ? ` (${unreadCount})` : '')
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{/* <View style={styles.headerActions}>
|
|
||||||
{unreadCount > 0 && (
|
|
||||||
<LinearGradient
|
|
||||||
colors={[colors.primary, colors.primary]} // Pastel Green gradient
|
|
||||||
style={[styles.headerButton, styles.markReadButton]}>
|
|
||||||
<TouchableOpacity onPress={handleMarkAllAsRead}>
|
|
||||||
<Text style={styles.headerButtonText}>Mark All Read</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</LinearGradient>
|
|
||||||
)}
|
|
||||||
{notifications.length > 0 && (
|
|
||||||
<LinearGradient
|
|
||||||
colors={[colors.pastelPink, colors.primary]} // Pastel Pink to Pastel Orange gradient
|
|
||||||
style={styles.headerButton}>
|
|
||||||
<TouchableOpacity onPress={handleClearAll}>
|
|
||||||
<Text style={styles.headerButtonText}>Clear All</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</LinearGradient>
|
|
||||||
)}
|
|
||||||
</View> */}
|
|
||||||
|
|
||||||
{/* Notification List */}
|
|
||||||
<FlatList
|
<FlatList
|
||||||
data={notifications}
|
data={notifications}
|
||||||
keyExtractor={(item) => item.messageId}
|
keyExtractor={(item) => item.messageId}
|
||||||
|
renderItem={renderItem}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
contentContainerStyle={styles.listContainer}
|
contentContainerStyle={styles(theme).container}
|
||||||
renderItem={({ item }) => {
|
|
||||||
console.log("noti items : ", item)
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => handleMarkAsRead(item.messageId, item?.data?.msgType, item?.data?.groupId, item?.data?.cycleNumber, item?.data?.contributionId)}
|
|
||||||
activeOpacity={0.7}
|
|
||||||
>
|
|
||||||
<LinearGradient
|
|
||||||
colors={item.read ? ['#F5F5F5', '#E8E8E8'] : ['#FFFFFF', '#F8F8FF']} // Pastel grays and whites
|
|
||||||
style={[
|
|
||||||
styles.notificationCard,
|
|
||||||
!item.read && styles.unreadNotification,
|
|
||||||
{ borderLeftColor: getNotificationColor(item.data.msgType) }
|
|
||||||
]}>
|
|
||||||
<View style={styles.notificationContent}>
|
|
||||||
{/* Icon */}
|
|
||||||
<View style={styles.iconContainer}>
|
|
||||||
<LucideIcon
|
|
||||||
name={getNotificationIcon(item.data.msgType)}
|
|
||||||
size={24}
|
|
||||||
color={getNotificationColor(item.data.msgType)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
<View style={styles.textContent}>
|
|
||||||
<View style={styles.headerRow}>
|
|
||||||
<Text style={[
|
|
||||||
styles.title,
|
|
||||||
!item.read && styles.unreadTitle
|
|
||||||
]}>
|
|
||||||
{item.data.title}
|
|
||||||
</Text>
|
|
||||||
<Text style={styles.time}>
|
|
||||||
{formatTime(item.sentTime)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Text style={styles.message}>
|
|
||||||
{item.data.body}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{item.data.msgType && (
|
|
||||||
<View style={styles.tagContainer}>
|
|
||||||
<Text style={[
|
|
||||||
styles.tag,
|
|
||||||
{ backgroundColor: getNotificationColor(item.data.msgType) }
|
|
||||||
]}>
|
|
||||||
{getFilterDisplayName(item.data.msgType as FilterType)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Actions */}
|
|
||||||
<View style={styles.actionsContainer}>
|
|
||||||
{!item.read && (
|
|
||||||
<View style={styles.unreadIndicator} />
|
|
||||||
)}
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => handleDelete(item.messageId)}
|
|
||||||
style={styles.deleteButton}
|
|
||||||
>
|
|
||||||
<LucideIcon name="trash-outline" size={20} color={colors.pastelPink} /> {/* Pastel Pink */}
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</LinearGradient>
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
<View style={styles.emptyContainer}>
|
<View style={styles(theme).empty}>
|
||||||
<LucideIcon name="notifications-off-outline" size={64} color="#D3D3D3" /> {/* Pastel Gray */}
|
<LucideIcon
|
||||||
<Text style={styles.emptyText}>No notifications yet</Text>
|
name="notifications-off-outline"
|
||||||
<Text style={styles.emptySubtext}>
|
size={64}
|
||||||
When you get notifications, they'll appear here
|
color="#ccc"
|
||||||
|
/>
|
||||||
|
<Text style={styles(theme).emptyText}>
|
||||||
|
No notifications
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
@@ -284,130 +275,119 @@ const Notification = () => {
|
|||||||
|
|
||||||
export default Notification;
|
export default Notification;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = (theme: any) =>
|
||||||
headerActions: {
|
StyleSheet.create({
|
||||||
flexDirection: 'row',
|
container: {
|
||||||
gap: 8,
|
padding: 12,
|
||||||
justifyContent: 'flex-end',
|
paddingBottom: 40,
|
||||||
paddingHorizontal: 16,
|
},
|
||||||
paddingVertical: 10,
|
|
||||||
},
|
card: {
|
||||||
headerButton: {
|
borderRadius: 16,
|
||||||
borderRadius: 8,
|
padding: 16,
|
||||||
paddingVertical: 6,
|
marginBottom: 12,
|
||||||
paddingHorizontal: 12,
|
backgroundColor: 'rgba(255,255,255,0.95)',
|
||||||
minWidth: 80,
|
shadowColor: '#000',
|
||||||
alignItems: 'center',
|
shadowOpacity: 0.08,
|
||||||
},
|
shadowRadius: 10,
|
||||||
markReadButton: {
|
elevation: 4,
|
||||||
minWidth: 100,
|
borderLeftWidth: 5,
|
||||||
},
|
},
|
||||||
headerButtonText: {
|
|
||||||
color: '#FFFFFF', // White text for better contrast on pastel backgrounds
|
unread: {
|
||||||
fontWeight: '600',
|
borderWidth: 1,
|
||||||
fontSize: 12,
|
borderColor: '#E0E7FF',
|
||||||
},
|
shadowColor: '#6366F1',
|
||||||
listContainer: {
|
shadowOpacity: 0.15,
|
||||||
paddingHorizontal: 12,
|
},
|
||||||
paddingBottom: 30,
|
|
||||||
paddingTop: 8,
|
row: {
|
||||||
},
|
flexDirection: 'row',
|
||||||
notificationCard: {
|
gap: 12,
|
||||||
borderRadius: 12,
|
},
|
||||||
padding: 16,
|
|
||||||
marginBottom: 10,
|
iconBox: {
|
||||||
shadowColor: '#000',
|
backgroundColor: '#F1F5F9',
|
||||||
shadowOpacity: 0.05, // Reduced opacity for softer shadow
|
padding: 10,
|
||||||
shadowRadius: 4,
|
borderRadius: 12,
|
||||||
elevation: 2,
|
},
|
||||||
borderLeftWidth: 4,
|
|
||||||
borderLeftColor: 'transparent',
|
content: {
|
||||||
},
|
flex: 1,
|
||||||
unreadNotification: {
|
},
|
||||||
backgroundColor: '#F8F8FF', // Pastel Lavender
|
|
||||||
},
|
topRow: {
|
||||||
notificationContent: {
|
flexDirection: 'row',
|
||||||
flexDirection: 'row',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'flex-start',
|
},
|
||||||
gap: 12,
|
|
||||||
},
|
title: {
|
||||||
iconContainer: {
|
fontWeight: '700',
|
||||||
paddingTop: 2,
|
fontSize: 15,
|
||||||
},
|
color: theme.colors.textPrimary,
|
||||||
textContent: {
|
flex: 1,
|
||||||
flex: 1,
|
},
|
||||||
gap: 4,
|
|
||||||
},
|
unreadTitle: {
|
||||||
headerRow: {
|
color: theme.colors.primary,
|
||||||
flexDirection: 'row',
|
},
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'flex-start',
|
message: {
|
||||||
marginBottom: 4,
|
fontSize: 13,
|
||||||
},
|
color: theme.colors.textSecondary,
|
||||||
title: {
|
marginTop: 4,
|
||||||
fontWeight: '600',
|
},
|
||||||
fontSize: 16,
|
|
||||||
color: '#696969', // Dim Gray
|
time: {
|
||||||
flex: 1,
|
fontSize: 11,
|
||||||
marginRight: 8,
|
color: '#6B7280',
|
||||||
},
|
},
|
||||||
unreadTitle: {
|
|
||||||
fontWeight: '700',
|
tagWrap: {
|
||||||
color: '#2F4F4F', // Dark Slate Gray
|
marginTop: 6,
|
||||||
},
|
},
|
||||||
message: {
|
|
||||||
fontSize: 14,
|
tag: {
|
||||||
color: '#808080', // Gray
|
fontSize: 10,
|
||||||
lineHeight: 20,
|
color: '#fff',
|
||||||
},
|
paddingHorizontal: 10,
|
||||||
time: {
|
paddingVertical: 4,
|
||||||
fontSize: 12,
|
borderRadius: 20,
|
||||||
color: '#A9A9A9', // Dark Gray
|
fontWeight: '700',
|
||||||
fontWeight: '500',
|
alignSelf: 'flex-start',
|
||||||
},
|
},
|
||||||
tagContainer: {
|
|
||||||
marginTop: 6,
|
dot: {
|
||||||
},
|
width: 8,
|
||||||
tag: {
|
height: 8,
|
||||||
fontSize: 10,
|
borderRadius: 4,
|
||||||
color: '#FFFFFF', // White text for better contrast
|
backgroundColor: theme.colors.primary,
|
||||||
paddingHorizontal: 8,
|
marginTop: 6,
|
||||||
paddingVertical: 2,
|
},
|
||||||
borderRadius: 4,
|
|
||||||
overflow: 'hidden',
|
deleteSwipe: {
|
||||||
alignSelf: 'flex-start',
|
backgroundColor: '#FF3B30',
|
||||||
fontWeight: '600',
|
justifyContent: 'center',
|
||||||
},
|
alignItems: 'center',
|
||||||
actionsContainer: {
|
width: 80,
|
||||||
alignItems: 'center',
|
borderRadius: 16,
|
||||||
gap: 8,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
unreadIndicator: {
|
|
||||||
width: 8,
|
deleteText: {
|
||||||
height: 8,
|
color: '#fff',
|
||||||
borderRadius: 4,
|
fontSize: 12,
|
||||||
backgroundColor: '#87CEEB', // Pastel Sky Blue
|
marginTop: 4,
|
||||||
},
|
},
|
||||||
deleteButton: {
|
|
||||||
padding: 4,
|
empty: {
|
||||||
},
|
alignItems: 'center',
|
||||||
emptyContainer: {
|
marginTop: 120,
|
||||||
alignItems: 'center',
|
},
|
||||||
justifyContent: 'center',
|
|
||||||
marginTop: 100,
|
emptyText: {
|
||||||
paddingHorizontal: 40,
|
marginTop: 12,
|
||||||
},
|
color: '#999',
|
||||||
emptyText: {
|
fontSize: 16,
|
||||||
fontSize: 18,
|
},
|
||||||
fontWeight: '600',
|
});
|
||||||
color: '#A9A9A9', // Dark Gray
|
|
||||||
marginTop: 16,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
emptySubtext: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#C0C0C0', // Silver
|
|
||||||
textAlign: 'center',
|
|
||||||
lineHeight: 20,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -321,6 +321,8 @@ const PaymentModeScreen: React.FC = () => {
|
|||||||
payload
|
payload
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
|
console.log("payment res",res)
|
||||||
|
|
||||||
if (res?.status === 'success' || res?.statusCode === 200) {
|
if (res?.status === 'success' || res?.statusCode === 200) {
|
||||||
const pgOrderNo = res?.data?.pgOrderNo;
|
const pgOrderNo = res?.data?.pgOrderNo;
|
||||||
if (pgOrderNo) {
|
if (pgOrderNo) {
|
||||||
@@ -357,6 +359,7 @@ const PaymentModeScreen: React.FC = () => {
|
|||||||
payload
|
payload
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
|
console.log("payment payload ",payload)
|
||||||
if (res?.status === 'success' || res?.statusCode === 200) {
|
if (res?.status === 'success' || res?.statusCode === 200) {
|
||||||
const pgOrderNo = res?.data?.pgOrderNo;
|
const pgOrderNo = res?.data?.pgOrderNo;
|
||||||
if (pgOrderNo) {
|
if (pgOrderNo) {
|
||||||
@@ -467,8 +470,8 @@ const PaymentModeScreen: React.FC = () => {
|
|||||||
<ActivityIndicator color="#fff" size="small" />
|
<ActivityIndicator color="#fff" size="small" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<LucideIcon name="shield-checkmark-outline" size={20} color="#fff" style={{ marginRight: 8 }} />
|
<LucideIcon name="shield-checkmark-outline" size={20} color={theme.colors.reverseText} style={{ marginRight: 8 }} />
|
||||||
<Text style={styles.confirmText}>Confirm & Pay</Text>
|
<Text style={[styles.confirmText, { color: theme.colors.black }]}>Confirm & Pay</Text>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -685,7 +688,6 @@ const styles = StyleSheet.create({
|
|||||||
borderRadius: 14,
|
borderRadius: 14,
|
||||||
},
|
},
|
||||||
confirmText: {
|
confirmText: {
|
||||||
color: '#fff',
|
|
||||||
fontSize: responsiveFontSize(1.8),
|
fontSize: responsiveFontSize(1.8),
|
||||||
fontFamily: FontsFamily.nunitoBold,
|
fontFamily: FontsFamily.nunitoBold,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
ToastAndroid,
|
ToastAndroid,
|
||||||
|
Modal,
|
||||||
|
ActivityIndicator,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import * as Keychain from "react-native-keychain";
|
import * as Keychain from "react-native-keychain";
|
||||||
import LucideIcon from '../../components/common/LucideIcon';
|
import LucideIcon from '../../components/common/LucideIcon';
|
||||||
@@ -118,6 +120,7 @@ const Profile = () => {
|
|||||||
countryCode: "",
|
countryCode: "",
|
||||||
});
|
});
|
||||||
const [mobileMoneyErrors, setMobileMoneyErrors] = useState<MobileMoneyErrors>({});
|
const [mobileMoneyErrors, setMobileMoneyErrors] = useState<MobileMoneyErrors>({});
|
||||||
|
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||||
|
|
||||||
// --- API HOOKS ---
|
// --- API HOOKS ---
|
||||||
const {
|
const {
|
||||||
@@ -181,6 +184,7 @@ const Profile = () => {
|
|||||||
}, [refetchProfile, refetchStatus]);
|
}, [refetchProfile, refetchStatus]);
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
|
setIsLoggingOut(true);
|
||||||
try {
|
try {
|
||||||
const fcmToken = await getFcmToken(user?.userId?.toString());
|
const fcmToken = await getFcmToken(user?.userId?.toString());
|
||||||
if (fcmToken) {
|
if (fcmToken) {
|
||||||
@@ -199,6 +203,7 @@ const Profile = () => {
|
|||||||
navigation.replace("Login");
|
navigation.replace("Login");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Logout error:", error);
|
console.error("Logout error:", error);
|
||||||
|
setIsLoggingOut(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -671,6 +676,19 @@ const Profile = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
visible={isLoggingOut}
|
||||||
|
transparent={true}
|
||||||
|
animationType="fade"
|
||||||
|
>
|
||||||
|
<View style={styles.loadingOverlay}>
|
||||||
|
<View style={[styles.loadingBox, { backgroundColor: theme.colors.surface, borderColor: theme.colors.border }]}>
|
||||||
|
<ActivityIndicator size="large" color={theme.colors.primary} />
|
||||||
|
<Text style={[styles.loadingText, { color: theme.colors.textPrimary }]}>Signing you out...</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
</AppView>
|
</AppView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -883,7 +901,33 @@ const styles = StyleSheet.create({
|
|||||||
skeletonContainer: {
|
skeletonContainer: {
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
marginTop: 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;
|
export default Profile;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const SwitchNavigator: React.FC<SwitchNavigatorProps> = ({ role, status }) => {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.88}
|
activeOpacity={0.88}
|
||||||
onPress={() => navigation.navigate("BecomeOrg")}
|
onPress={() => navigation.navigate("BecomeOrg")}
|
||||||
|
disabled={prettyStatus === "PENDING"}
|
||||||
style={[styles.container, { backgroundColor: theme.colors.surface, borderColor: theme.colors.border }]}
|
style={[styles.container, { backgroundColor: theme.colors.surface, borderColor: theme.colors.border }]}
|
||||||
>
|
>
|
||||||
<View style={[styles.leadingIcon, { backgroundColor: theme.colors.primarySoft }]}>
|
<View style={[styles.leadingIcon, { backgroundColor: theme.colors.primarySoft }]}>
|
||||||
@@ -42,7 +43,7 @@ const SwitchNavigator: React.FC<SwitchNavigatorProps> = ({ role, status }) => {
|
|||||||
<Text style={[styles.secondaryText, { color: theme.colors.textSecondary }]}>{secondaryText}</Text>
|
<Text style={[styles.secondaryText, { color: theme.colors.textSecondary }]}>{secondaryText}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{ prettyStatus !== "PENDING" && <View style={[styles.arrowBox, { borderColor: theme.colors.border, backgroundColor: theme.colors.background }]}>
|
{prettyStatus !== "PENDING" && <View style={[styles.arrowBox, { borderColor: theme.colors.border, backgroundColor: theme.colors.background }]}>
|
||||||
<LucideIcon name="chevron-forward" size={18} color={theme.colors.textSecondary} />
|
<LucideIcon name="chevron-forward" size={18} color={theme.colors.textSecondary} />
|
||||||
</View>}
|
</View>}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const formatINR = (amount: number) => {
|
|||||||
try {
|
try {
|
||||||
return new Intl.NumberFormat("en-IN", { style: "currency", currency: "INR" }).format(amount);
|
return new Intl.NumberFormat("en-IN", { style: "currency", currency: "INR" }).format(amount);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return `₹${amount.toFixed(2)}`;
|
return `UGX${amount.toFixed(2)}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
export const BASE_URL = "https://11.godemo.in/FundCollect/api/"
|
export const BASE_URL = "https://11.godemo.in/FundCollect/api/"
|
||||||
// export const BASE_URL = "http://192.168.29.113:9060/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 VARIFY_OTP_URL = `${BASE_URL}user/otp/verify`
|
||||||
export const ORGRAGISTRATION = `${BASE_URL}organizer/registration/apply`
|
export const ORGRAGISTRATION = `${BASE_URL}organizer/registration/apply`
|
||||||
export const GROUP_CREATION_URL = `${BASE_URL}fund/groups/{organizerId}`
|
export const GROUP_CREATION_URL = `${BASE_URL}fund/groups/{organizerId}`
|
||||||
|
|||||||
Reference in New Issue
Block a user