payment added
This commit is contained in:
@@ -15,6 +15,8 @@ export interface AlertButton {
|
|||||||
text: string;
|
text: string;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
style?: "cancel" | "destructive" | "default" | "primary" | "secondary" | "outline";
|
style?: "cancel" | "destructive" | "default" | "primary" | "secondary" | "outline";
|
||||||
|
buttonColor?: string;
|
||||||
|
textColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CustomAlertProps {
|
export interface CustomAlertProps {
|
||||||
@@ -58,7 +60,7 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getButtonStyle = (styleType:string|undefined) => {
|
const getButtonStyle = (styleType: string | undefined) => {
|
||||||
switch (styleType) {
|
switch (styleType) {
|
||||||
case "primary":
|
case "primary":
|
||||||
return [styles.button, styles.primaryButton];
|
return [styles.button, styles.primaryButton];
|
||||||
@@ -75,7 +77,7 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getButtonTextStyle = (styleType:string|undefined) => {
|
const getButtonTextStyle = (styleType: string | undefined) => {
|
||||||
switch (styleType) {
|
switch (styleType) {
|
||||||
case "primary":
|
case "primary":
|
||||||
return [styles.buttonText, styles.primaryButtonText];
|
return [styles.buttonText, styles.primaryButtonText];
|
||||||
@@ -110,7 +112,7 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
|
|||||||
<View style={styles.faintOverlay} />
|
<View style={styles.faintOverlay} />
|
||||||
<View style={styles.contentContainer}>
|
<View style={styles.contentContainer}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<Ionicons name={icon.name} size={48} color={icon.color} />
|
<Ionicons name={icon.name} size={36} color={icon.color} /> {/* smaller icon for vertical compression */}
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.alertTitle}>{title}</Text>
|
<Text style={styles.alertTitle}>{title}</Text>
|
||||||
<Text style={styles.alertMessage}>{message}</Text>
|
<Text style={styles.alertMessage}>{message}</Text>
|
||||||
@@ -119,10 +121,17 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
|
|||||||
{buttons.map((button, index) => (
|
{buttons.map((button, index) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={index}
|
key={index}
|
||||||
style={getButtonStyle(button.style)}
|
style={[
|
||||||
|
...getButtonStyle(button.style),
|
||||||
|
button.style !== "cancel" && button.buttonColor ? { backgroundColor: button.buttonColor } : null,
|
||||||
|
button.style === "outline" && button.buttonColor ? { borderColor: button.buttonColor } : null,
|
||||||
|
]}
|
||||||
onPress={button.onPress}
|
onPress={button.onPress}
|
||||||
>
|
>
|
||||||
<Text style={getButtonTextStyle(button.style)}>
|
<Text style={[
|
||||||
|
...getButtonTextStyle(button.style),
|
||||||
|
button.style !== "cancel" && button.textColor ? { color: button.textColor } : null,
|
||||||
|
]}>
|
||||||
{button.text}
|
{button.text}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -150,13 +159,13 @@ const styles = StyleSheet.create({
|
|||||||
right: 0,
|
right: 0,
|
||||||
},
|
},
|
||||||
box: {
|
box: {
|
||||||
paddingHorizontal: 4,
|
paddingHorizontal: 2, // reduce horizontal padding
|
||||||
padding: BORDER_WIDTH + 4,
|
padding: BORDER_WIDTH + 2, // reduce overall padding
|
||||||
borderRadius: RADIUS + BORDER_WIDTH,
|
borderRadius: RADIUS + BORDER_WIDTH,
|
||||||
width: '90%',
|
width: '90%',
|
||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
margin: 20,
|
margin: 10, // reduce margin for vertical compression
|
||||||
backgroundColor: colors.pastelPink,
|
backgroundColor: colors.pastelPink,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
@@ -169,40 +178,40 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
contentContainer: {
|
contentContainer: {
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
padding: 24,
|
padding: 12, // reduce container padding
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
iconContainer: {
|
iconContainer: {
|
||||||
marginBottom: 16,
|
marginBottom: 8, // reduce vertical space below icon
|
||||||
},
|
},
|
||||||
alertTitle: {
|
alertTitle: {
|
||||||
fontSize: 22,
|
fontSize: 18, // smaller font size
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
color: colors.textDark,
|
color: colors.textDark,
|
||||||
marginBottom: 8,
|
marginBottom: 4, // less space below title
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
},
|
},
|
||||||
alertMessage: {
|
alertMessage: {
|
||||||
fontSize: 16,
|
fontSize: 14, // smaller font size
|
||||||
color: colors.textLight,
|
color: colors.textLight,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
marginBottom: 24,
|
marginBottom: 12, // tighter spacing
|
||||||
lineHeight: 22,
|
lineHeight: 18, // reduced line height
|
||||||
},
|
},
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
width: "100%",
|
width: "100%",
|
||||||
gap: 12,
|
gap: 8, // reduce gap between buttons
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
paddingVertical: 14,
|
paddingVertical: 8, // less vertical padding
|
||||||
paddingHorizontal: 24,
|
paddingHorizontal: 16, // less horizontal padding
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
minWidth: 80,
|
minWidth: 72, // slightly smaller minimum width
|
||||||
borderWidth: 0, // default border, override for outline/cancel
|
borderWidth: 0,
|
||||||
},
|
},
|
||||||
// === BUTTON STYLE VARIANTS ===
|
// === BUTTON STYLE VARIANTS ===
|
||||||
defaultButton: {
|
defaultButton: {
|
||||||
@@ -229,7 +238,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
// === BUTTON TEXT COLOR VARIANTS ===
|
// === BUTTON TEXT COLOR VARIANTS ===
|
||||||
buttonText: {
|
buttonText: {
|
||||||
fontSize: 16,
|
fontSize: 14, // smaller text
|
||||||
fontWeight: "700",
|
fontWeight: "700",
|
||||||
},
|
},
|
||||||
defaultButtonText: {
|
defaultButtonText: {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// src/components/UserRequested.tsx
|
|
||||||
|
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { View, Text, StyleSheet } from "react-native";
|
import { View, Text, StyleSheet } from "react-native";
|
||||||
import {Ionicons} from "@react-native-vector-icons/ionicons";
|
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
||||||
import colors from "./colors";
|
import colors from "./colors";
|
||||||
import { UserJoinRequest } from "../types";
|
import { UserJoinRequest } from "../types";
|
||||||
import { formatDate } from "../utils/HelperFunction";
|
import { formatDate } from "../utils/HelperFunction";
|
||||||
@@ -15,24 +13,23 @@ const getStatusStyle = (status: UserJoinRequest["status"]) => {
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case "APPROVED":
|
case "APPROVED":
|
||||||
return {
|
return {
|
||||||
backgroundColor: "#D1FAE5", // light green
|
backgroundColor: "#D1FAE5",
|
||||||
textColor: "#065F46", // dark green
|
textColor: "#065F46",
|
||||||
label: "Approved",
|
label: "Approved",
|
||||||
icon: "checkmark-circle-outline",
|
icon: "checkmark-circle-outline",
|
||||||
|
|
||||||
};
|
};
|
||||||
case "PENDING":
|
case "PENDING":
|
||||||
return {
|
return {
|
||||||
backgroundColor: "#FEF3C7", // light yellow
|
backgroundColor: "#FEF3C7",
|
||||||
textColor: "#92400E", // dark yellow/brown
|
textColor: "#92400E",
|
||||||
label: "Pending",
|
label: "Pending",
|
||||||
icon: "time-outline",
|
icon: "time-outline",
|
||||||
};
|
};
|
||||||
case "REJECT":
|
case "REJECT":
|
||||||
return {
|
return {
|
||||||
backgroundColor: "#E5E7EB", // light gray
|
backgroundColor: "#FEE2E2",
|
||||||
textColor: "#4B5563", // dark gray
|
textColor: "#B91C1C",
|
||||||
label: "Reject",
|
label: "Rejected",
|
||||||
icon: "close-circle-outline",
|
icon: "close-circle-outline",
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
@@ -48,38 +45,39 @@ const getStatusStyle = (status: UserJoinRequest["status"]) => {
|
|||||||
export const UserRequested: FC<UserRequestedProps> = ({ item }) => {
|
export const UserRequested: FC<UserRequestedProps> = ({ item }) => {
|
||||||
const { backgroundColor, textColor, label, icon } = getStatusStyle(item.status);
|
const { backgroundColor, textColor, label, icon } = getStatusStyle(item.status);
|
||||||
|
|
||||||
|
// Optionally, initials avatar (remove/comment if you want):
|
||||||
|
const initials = item.createdBy?.trim().split(" ").map((w) => w[0])?.join("").toUpperCase();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
{/* Top row: User info + Status */}
|
|
||||||
<View style={styles.topRow}>
|
<View style={styles.topRow}>
|
||||||
|
<View style={styles.avatarContainer}>
|
||||||
|
<View style={styles.avatarCircle}>
|
||||||
|
<Text style={styles.avatarText}>{initials}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
<View style={styles.userInfoSection}>
|
<View style={styles.userInfoSection}>
|
||||||
|
|
||||||
<Text style={styles.groupName}>{item.groupName}</Text>
|
<Text style={styles.groupName}>{item.groupName}</Text>
|
||||||
|
|
||||||
<Text style={styles.userMeta}>
|
<Text style={styles.userMeta}>
|
||||||
<Ionicons name="time-outline" size={14} color={colors.textLight} />{" "}
|
<Ionicons name="time-outline" size={13} color={colors.textLight} />{" "}
|
||||||
{formatDate(item.requestDate)}
|
{formatDate(item.requestDate)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Status Badge */}
|
|
||||||
<View style={[styles.statusBadge, { backgroundColor }]}>
|
<View style={[styles.statusBadge, { backgroundColor }]}>
|
||||||
<Ionicons name={icon as any} size={16} color={textColor} />
|
<Ionicons name={icon as any} size={17} color={textColor} />
|
||||||
<Text style={[styles.statusText, { color: textColor }]}>{label}</Text>
|
<Text style={[styles.statusText, { color: textColor }]}>{label}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Group Info */}
|
|
||||||
<View style={styles.groupInfoSection}>
|
<View style={styles.groupInfoSection}>
|
||||||
<Text style={styles.requestText}>
|
<Text style={styles.requestText}>
|
||||||
Group Created By :{" "}
|
<Ionicons name="person-outline" size={13} color={colors.textLight} />
|
||||||
<Text style={styles.userName} numberOfLines={1}>
|
{" "}Group Created By:{" "}
|
||||||
{item.createdBy}
|
<Text style={styles.userName} numberOfLines={1}>{item.createdBy}</Text>
|
||||||
</Text>
|
|
||||||
</Text>
|
</Text>
|
||||||
{item.reason ? (
|
{item.reason ? (
|
||||||
<Text style={styles.reasonText} numberOfLines={2}>
|
<Text style={styles.reasonText} numberOfLines={2}>
|
||||||
Reason: {item.reason}
|
<Ionicons name="chatbubbles-outline" size={13} color={colors.textLight} /> {item.reason}
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
@@ -91,64 +89,95 @@ const styles = StyleSheet.create({
|
|||||||
card: {
|
card: {
|
||||||
backgroundColor: colors.white,
|
backgroundColor: colors.white,
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
marginVertical: 8,
|
marginVertical: 14, // More space
|
||||||
elevation: 3,
|
marginHorizontal: 10,
|
||||||
|
elevation: 4,
|
||||||
shadowColor: "#000",
|
shadowColor: "#000",
|
||||||
shadowOpacity: 0.1,
|
shadowOpacity: 0.12,
|
||||||
shadowRadius: 6,
|
shadowRadius: 8,
|
||||||
shadowOffset: { width: 0, height: 3 },
|
shadowOffset: { width: 0, height: 2 },
|
||||||
padding: 12,
|
padding: 16,
|
||||||
},
|
},
|
||||||
topRow: {
|
topRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: colors.Settings,
|
borderBottomColor: "#e5e7eb",
|
||||||
paddingBottom: 8,
|
paddingBottom: 10,
|
||||||
|
marginBottom: 6
|
||||||
|
},
|
||||||
|
avatarContainer: {
|
||||||
|
marginRight: 10,
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
avatarCircle: {
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
borderRadius: 18,
|
||||||
|
backgroundColor: colors.pastelBlue,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center"
|
||||||
|
},
|
||||||
|
avatarText: {
|
||||||
|
fontWeight: "700",
|
||||||
|
fontSize: 15,
|
||||||
|
color: colors.white,
|
||||||
},
|
},
|
||||||
userInfoSection: {
|
userInfoSection: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingRight: 8,
|
paddingRight: 8,
|
||||||
},
|
},
|
||||||
userName: {
|
userName: {
|
||||||
// fontSize: 16,
|
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
color: colors.textDark,
|
color: colors.textDark,
|
||||||
|
fontSize: 15,
|
||||||
},
|
},
|
||||||
userMeta: {
|
userMeta: {
|
||||||
fontSize: 13,
|
fontSize: 12,
|
||||||
color: colors.textLight,
|
color: colors.textLight,
|
||||||
marginTop: 2,
|
marginTop: 3,
|
||||||
|
marginBottom: -3,
|
||||||
},
|
},
|
||||||
groupInfoSection: {
|
groupInfoSection: {
|
||||||
paddingVertical: 12,
|
paddingTop: 10,
|
||||||
|
paddingBottom: 6,
|
||||||
},
|
},
|
||||||
requestText: {
|
requestText: {
|
||||||
// fontSize: 15,
|
|
||||||
color: colors.textDark,
|
color: colors.textDark,
|
||||||
lineHeight: 20,
|
fontSize: 15,
|
||||||
|
marginBottom: 2,
|
||||||
|
lineHeight: 22,
|
||||||
},
|
},
|
||||||
groupName: {
|
groupName: {
|
||||||
fontWeight: "600",
|
fontWeight: "600",
|
||||||
color: colors.Settings,
|
color: colors.Settings,
|
||||||
fontSize:18
|
fontSize: 18,
|
||||||
|
marginBottom: 2,
|
||||||
},
|
},
|
||||||
reasonText: {
|
reasonText: {
|
||||||
fontSize: 13,
|
fontSize: 12,
|
||||||
color: colors.textLight,
|
color: colors.textLight,
|
||||||
marginTop: 4,
|
marginTop: 6,
|
||||||
|
paddingLeft: 1,
|
||||||
|
lineHeight: 18,
|
||||||
|
fontStyle: "italic",
|
||||||
},
|
},
|
||||||
statusBadge: {
|
statusBadge: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
borderRadius: 12,
|
borderRadius: 10,
|
||||||
paddingHorizontal: 8,
|
paddingHorizontal: 10,
|
||||||
paddingVertical: 4,
|
paddingVertical: 5,
|
||||||
|
marginLeft: 4,
|
||||||
|
minWidth: 90,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignSelf: "flex-start",
|
||||||
|
marginTop: 2
|
||||||
},
|
},
|
||||||
statusText: {
|
statusText: {
|
||||||
fontSize: 13,
|
fontSize: 14,
|
||||||
fontWeight: "600",
|
fontWeight: "700",
|
||||||
marginLeft: 4,
|
marginLeft: 6,
|
||||||
|
letterSpacing: 0.5
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import Notification from "../screens/notification/Notification";
|
|||||||
import ManagementScreen from "../screens/management/ManagementScreen";
|
import ManagementScreen from "../screens/management/ManagementScreen";
|
||||||
import { navigationRef } from "../utils/navigationService";
|
import { navigationRef } from "../utils/navigationService";
|
||||||
import NotificationRedirection from "../screens/NotificationRedirection";
|
import NotificationRedirection from "../screens/NotificationRedirection";
|
||||||
|
// import screen from "../components/test";
|
||||||
|
|
||||||
export type RootStackParamList = {
|
export type RootStackParamList = {
|
||||||
Splash: undefined;
|
Splash: undefined;
|
||||||
@@ -54,6 +55,7 @@ export type RootStackParamList = {
|
|||||||
| { actionTab3: "Your Requests" }
|
| { actionTab3: "Your Requests" }
|
||||||
| { groupId: string | number; }
|
| { groupId: string | number; }
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<RootStackParamList>();
|
const Stack = createNativeStackNavigator<RootStackParamList>();
|
||||||
@@ -121,3 +123,4 @@ export default function AppNavigator() {
|
|||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
;
|
||||||
+132
-65
@@ -36,7 +36,7 @@ export default function GroupCreationForm() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [errors, setErrors] = useState<any>({});
|
const [errors, setErrors] = useState<any>({});
|
||||||
const [selectedImage, setSelectedImage] = useState()
|
const [selectedImage, setSelectedImage] = useState<any>();
|
||||||
|
|
||||||
const handleChange = (key: string, value: string) => {
|
const handleChange = (key: string, value: string) => {
|
||||||
setForm({ ...form, [key]: value });
|
setForm({ ...form, [key]: value });
|
||||||
@@ -45,10 +45,37 @@ export default function GroupCreationForm() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cycleTypes = ["CUSTOM", "MONTHLY", "QUARTERLY", "YEARLY", "HALF_YEARLY"];
|
// ---- Custom logic for Payment Cycle ----
|
||||||
|
const cycleTypes = [
|
||||||
|
"CUSTOM",
|
||||||
|
"DAILY",
|
||||||
|
"WEEKLY",
|
||||||
|
"MONTHLY",
|
||||||
|
"QUARTERLY",
|
||||||
|
"HALF_YEARLY",
|
||||||
|
"YEARLY",
|
||||||
|
];
|
||||||
const distributionTypes = ["SEQUENTIAL", "RANDOM"];
|
const distributionTypes = ["SEQUENTIAL", "RANDOM"];
|
||||||
const lateFeeModes = ["DAILY", "MONTHLY"];
|
const lateFeeModes = ["DAILY", "MONTHLY"];
|
||||||
|
|
||||||
|
// The core: handle payment cycle options
|
||||||
|
const handleCycleChange = (val: string) => {
|
||||||
|
let newForm = { ...form, cycleType: val };
|
||||||
|
if (val === "DAILY") {
|
||||||
|
newForm = { ...newForm, cycleType: "CUSTOM", customCycleDays: "1" };
|
||||||
|
} else if (val === "WEEKLY") {
|
||||||
|
newForm = { ...newForm, cycleType: "CUSTOM", customCycleDays: "7" };
|
||||||
|
} else if (
|
||||||
|
val !== "CUSTOM"
|
||||||
|
) {
|
||||||
|
newForm = { ...newForm, customCycleDays: "" }; // Clear custom days
|
||||||
|
}
|
||||||
|
setForm(newForm);
|
||||||
|
if (errors["customCycleDays"]) {
|
||||||
|
setErrors({ ...errors, customCycleDays: null });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors: any = {};
|
const newErrors: any = {};
|
||||||
if (!form.groupName.trim()) newErrors.groupName = "Group name is required.";
|
if (!form.groupName.trim()) newErrors.groupName = "Group name is required.";
|
||||||
@@ -58,7 +85,6 @@ export default function GroupCreationForm() {
|
|||||||
if (!form.contributionAmount || Number(form.contributionAmount) <= 0) newErrors.contributionAmount = "Amount must be greater than 0.";
|
if (!form.contributionAmount || Number(form.contributionAmount) <= 0) newErrors.contributionAmount = "Amount must be greater than 0.";
|
||||||
if (form.cycleType === "CUSTOM" && (!form.customCycleDays || Number(form.customCycleDays) <= 0)) newErrors.customCycleDays = "Custom days must be greater than 0.";
|
if (form.cycleType === "CUSTOM" && (!form.customCycleDays || Number(form.customCycleDays) <= 0)) newErrors.customCycleDays = "Custom days must be greater than 0.";
|
||||||
if (!form.lateFeeMode && (!form.lateFeeValue || Number(form.lateFeeValue) <= 0)) newErrors.lateFeeValue = "Late fee value is required.";
|
if (!form.lateFeeMode && (!form.lateFeeValue || Number(form.lateFeeValue) <= 0)) newErrors.lateFeeValue = "Late fee value is required.";
|
||||||
console.log("new errors : ", newErrors)
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors);
|
||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0;
|
||||||
};
|
};
|
||||||
@@ -73,26 +99,39 @@ export default function GroupCreationForm() {
|
|||||||
maxParticipants: Number(form.maxParticipants),
|
maxParticipants: Number(form.maxParticipants),
|
||||||
minParticipants: Number(form.minParticipants),
|
minParticipants: Number(form.minParticipants),
|
||||||
};
|
};
|
||||||
console.log("Reviewing Payload:", payload);
|
console.log('review payload',payload)
|
||||||
navigation.navigate("GroupReview", { form: payload });
|
navigation.navigate("GroupReview", { form: payload });
|
||||||
} else {
|
} else {
|
||||||
ToastAndroid.show('Please fill out all required fields correctly.', ToastAndroid.SHORT);
|
ToastAndroid.show('Please fill out all required fields correctly.', ToastAndroid.SHORT);
|
||||||
setErrors('');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
style={{ flex: 1, backgroundColor: "#f5f6fa", paddingTop: Platform.OS === "android" ? 40 : 0 }}
|
style={{ flex: 1, backgroundColor: "#f5f6fa", paddingTop: Platform.OS === "android" ? 40 : 0 }}
|
||||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||||
>
|
>
|
||||||
<ScrollView contentContainerStyle={styles.container} showsVerticalScrollIndicator={false}>
|
<ScrollView contentContainerStyle={styles.container} showsVerticalScrollIndicator={false}>
|
||||||
<View style={styles.titleContainer}><Text style={styles.title}>Create New Group</Text></View>
|
<View style={styles.titleContainer}>
|
||||||
|
<Text style={styles.title}>Create New Group</Text>
|
||||||
<CustomInput label="Group Name" placeholder="e.g., 'Family Savings'" value={form.groupName} onChangeText={(val) => handleChange("groupName", val)} icon="people" error={errors.groupName} />
|
</View>
|
||||||
<CustomInput label="Description" placeholder="e.g., 'Monthly fund for family needs'" value={form.description} onChangeText={(val) => handleChange("description", val)} multiline icon="document-text" error={errors.description} />
|
<CustomInput
|
||||||
|
label="Group Name"
|
||||||
|
placeholder="e.g., 'Family Savings'"
|
||||||
|
value={form.groupName}
|
||||||
|
onChangeText={(val) => handleChange("groupName", val)}
|
||||||
|
icon="people"
|
||||||
|
error={errors.groupName}
|
||||||
|
/>
|
||||||
|
<CustomInput
|
||||||
|
label="Description"
|
||||||
|
placeholder="e.g., 'Monthly fund for family needs'"
|
||||||
|
value={form.description}
|
||||||
|
onChangeText={(val) => handleChange("description", val)}
|
||||||
|
multiline
|
||||||
|
icon="document-text"
|
||||||
|
error={errors.description}
|
||||||
|
/>
|
||||||
<Section title="Group Settings">
|
<Section title="Group Settings">
|
||||||
<SettingRow label="Visibility">
|
<SettingRow label="Visibility">
|
||||||
<ToggleButtons
|
<ToggleButtons
|
||||||
@@ -109,27 +148,70 @@ export default function GroupCreationForm() {
|
|||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Participant Rules">
|
<Section title="Participant Rules">
|
||||||
<View style={styles.participantsRow}>
|
<View style={styles.participantsRow}>
|
||||||
<View style={styles.participantInput}><CustomInput label="Min" placeholder="e.g., 5" value={form.minParticipants} onChangeText={(val) => handleChange("minParticipants", val)} keyboardType="numeric" icon="person-add" error={errors.minParticipants} /></View>
|
<View style={styles.participantInput}>
|
||||||
<View style={styles.participantInput}><CustomInput label="Max" placeholder="e.g., 10" value={form.maxParticipants} onChangeText={(val) => handleChange("maxParticipants", val)} keyboardType="numeric" icon="people-circle" error={errors.maxParticipants} /></View>
|
<CustomInput
|
||||||
|
label="Min"
|
||||||
|
placeholder="e.g., 5"
|
||||||
|
value={form.minParticipants}
|
||||||
|
onChangeText={(val) => handleChange("minParticipants", val)}
|
||||||
|
keyboardType="numeric"
|
||||||
|
icon="person-add"
|
||||||
|
error={errors.minParticipants}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.participantInput}>
|
||||||
|
<CustomInput
|
||||||
|
label="Max"
|
||||||
|
placeholder="e.g., 10"
|
||||||
|
value={form.maxParticipants}
|
||||||
|
onChangeText={(val) => handleChange("maxParticipants", val)}
|
||||||
|
keyboardType="numeric"
|
||||||
|
icon="people-circle"
|
||||||
|
error={errors.maxParticipants}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Financial Details">
|
<Section title="Financial Details">
|
||||||
<CustomInput label="Contribution Amount (INR)" placeholder="e.g., 5000" value={form.contributionAmount} onChangeText={(val) => handleChange("contributionAmount", val)} keyboardType="numeric" icon="cash" error={errors.contributionAmount} />
|
<CustomInput
|
||||||
|
label="Contribution Amount (INR)"
|
||||||
|
placeholder="e.g., 5000"
|
||||||
|
value={form.contributionAmount}
|
||||||
|
onChangeText={(val) => handleChange("contributionAmount", val)}
|
||||||
|
keyboardType="numeric"
|
||||||
|
icon="cash"
|
||||||
|
error={errors.contributionAmount}
|
||||||
|
/>
|
||||||
<SettingRow label="Payment Cycle">
|
<SettingRow label="Payment Cycle">
|
||||||
<ToggleButtons
|
<ToggleButtons
|
||||||
options={cycleTypes}
|
options={cycleTypes}
|
||||||
selectedValue={form.cycleType}
|
selectedValue={
|
||||||
onSelect={(val) => handleChange("cycleType", val)}
|
form.cycleType === "CUSTOM" && form.customCycleDays === "1"
|
||||||
|
? "DAILY"
|
||||||
|
: form.cycleType === "CUSTOM" && form.customCycleDays === "7"
|
||||||
|
? "WEEKLY"
|
||||||
|
: form.cycleType
|
||||||
|
}
|
||||||
|
onSelect={handleCycleChange}
|
||||||
isWrap={true}
|
isWrap={true}
|
||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
{form.cycleType === "CUSTOM" && (<CustomInput label="Custom Cycle (Days)" placeholder="Enter number of days" value={form.customCycleDays} onChangeText={(val) => handleChange("customCycleDays", val)} keyboardType="numeric" icon="calendar" error={errors.customCycleDays} />)}
|
{form.cycleType === "CUSTOM" &&
|
||||||
|
form.customCycleDays !== "1" &&
|
||||||
|
form.customCycleDays !== "7" && (
|
||||||
|
<CustomInput
|
||||||
|
label="Custom Cycle (Days)"
|
||||||
|
placeholder="Enter number of days"
|
||||||
|
value={form.customCycleDays}
|
||||||
|
onChangeText={(val) => handleChange("customCycleDays", val)}
|
||||||
|
keyboardType="numeric"
|
||||||
|
icon="calendar"
|
||||||
|
error={errors.customCycleDays}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Late Fee Policy">
|
<Section title="Late Fee Policy">
|
||||||
<SettingRow label="Fee Mode">
|
<SettingRow label="Fee Mode">
|
||||||
<ToggleButtons
|
<ToggleButtons
|
||||||
@@ -138,9 +220,16 @@ export default function GroupCreationForm() {
|
|||||||
onSelect={(val) => handleChange("lateFeeMode", val)}
|
onSelect={(val) => handleChange("lateFeeMode", val)}
|
||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<CustomInput label="Late Fee Value (INR)" placeholder="e.g., 50" value={form.lateFeeValue} onChangeText={(val) => handleChange("lateFeeValue", val)} keyboardType="numeric" icon="alert-circle" error={errors.lateFeeValue} />
|
<CustomInput
|
||||||
|
label="Late Fee Value (INR)"
|
||||||
|
placeholder="e.g., 50"
|
||||||
|
value={form.lateFeeValue}
|
||||||
|
onChangeText={(val) => handleChange("lateFeeValue", val)}
|
||||||
|
keyboardType="numeric"
|
||||||
|
icon="alert-circle"
|
||||||
|
error={errors.lateFeeValue}
|
||||||
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<CustomButton title="Review Group" onPress={handleReview} style={{ marginTop: 20 }} />
|
<CustomButton title="Review Group" onPress={handleReview} style={{ marginTop: 20 }} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
@@ -148,28 +237,36 @@ export default function GroupCreationForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Helper Components for a cleaner layout ---
|
// --- Helper Components for a cleaner layout ---
|
||||||
|
const Section: React.FC<{ title: string; children: React.ReactNode }> = ({ title, children }) => (
|
||||||
const Section: React.FC<{ title: string, children: React.ReactNode }> = ({ title, children }) => (
|
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<Text style={styles.sectionTitle}>{title}</Text>
|
<Text style={styles.sectionTitle}>{title}</Text>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
const SettingRow: React.FC<{ label: string, children: React.ReactNode }> = ({ label, children }) => (
|
const SettingRow: React.FC<{ label: string; children: React.ReactNode }> = ({ label, children }) => (
|
||||||
<View style={styles.settingRow}>
|
<View style={styles.settingRow}>
|
||||||
<Text style={styles.label}>{label}</Text>
|
<Text style={styles.label}>{label}</Text>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ToggleButtons: React.FC<{ options: string[], selectedValue: string, onSelect: (value: string) => void, isWrap?: boolean }> = ({ options, selectedValue, onSelect, isWrap = false }) => (
|
const ToggleButtons: React.FC<{
|
||||||
<View style={[styles.toggleContainer, isWrap && { flexWrap: 'wrap' }]}>
|
options: string[];
|
||||||
|
selectedValue: string;
|
||||||
|
onSelect: (value: string) => void;
|
||||||
|
isWrap?: boolean;
|
||||||
|
}> = ({ options, selectedValue, onSelect, isWrap = false }) => (
|
||||||
|
<View style={[styles.toggleContainer, isWrap && { flexWrap: "wrap" }]}>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={option}
|
key={option}
|
||||||
onPress={() => onSelect(option)}
|
onPress={() => onSelect(option)}
|
||||||
style={[styles.toggleButton, selectedValue === option && styles.selectedButton, isWrap && { flex: 0, paddingHorizontal: 15 }]}
|
style={[
|
||||||
|
styles.toggleButton,
|
||||||
|
selectedValue === option && styles.selectedButton,
|
||||||
|
isWrap && { flex: 0, paddingHorizontal: 15 },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Text style={[styles.toggleButtonText, selectedValue === option && styles.selectedButtonText]}>
|
<Text style={[styles.toggleButtonText, selectedValue === option && styles.selectedButtonText]}>
|
||||||
{option.charAt(0) + option.slice(1).toLowerCase()}
|
{option.charAt(0) + option.slice(1).toLowerCase()}
|
||||||
@@ -183,58 +280,29 @@ const styles = StyleSheet.create({
|
|||||||
container: { flexGrow: 1, padding: 16, backgroundColor: "#f5f6fa" },
|
container: { flexGrow: 1, padding: 16, backgroundColor: "#f5f6fa" },
|
||||||
titleContainer: { marginBottom: 20, paddingHorizontal: 4 },
|
titleContainer: { marginBottom: 20, paddingHorizontal: 4 },
|
||||||
title: { fontSize: 28, fontWeight: "bold", color: "#1F2937" },
|
title: { fontSize: 28, fontWeight: "bold", color: "#1F2937" },
|
||||||
|
|
||||||
section: {
|
section: {
|
||||||
backgroundColor: '#fff',
|
backgroundColor: "#fff",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
shadowColor: '#4A5568',
|
shadowColor: "#4A5568",
|
||||||
shadowOpacity: 0.06,
|
shadowOpacity: 0.06,
|
||||||
shadowRadius: 10,
|
shadowRadius: 10,
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: '600',
|
fontWeight: "600",
|
||||||
color: '#374151',
|
color: "#374151",
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: '#F3F4F6',
|
borderBottomColor: "#F3F4F6",
|
||||||
paddingBottom: 10,
|
paddingBottom: 10,
|
||||||
},
|
},
|
||||||
|
|
||||||
avatarContainer: {
|
|
||||||
position: "relative",
|
|
||||||
marginBottom: 16,
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
borderRadius: 60,
|
|
||||||
borderWidth: 4,
|
|
||||||
borderColor: "#e2e8f0",
|
|
||||||
},
|
|
||||||
cameraIcon: {
|
|
||||||
position: "absolute",
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
backgroundColor: colors.pastelBlue,
|
|
||||||
borderRadius: 20,
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
borderWidth: 3,
|
|
||||||
borderColor: "#fff",
|
|
||||||
elevation: 2,
|
|
||||||
},
|
|
||||||
settingRow: {
|
settingRow: {
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
|
|
||||||
label: { fontSize: 14, fontWeight: "600", marginBottom: 8, color: "#4B5563" },
|
label: { fontSize: 14, fontWeight: "600", marginBottom: 8, color: "#4B5563" },
|
||||||
|
|
||||||
toggleContainer: { flexDirection: "row", gap: 10 },
|
toggleContainer: { flexDirection: "row", gap: 10 },
|
||||||
toggleButton: {
|
toggleButton: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -243,12 +311,11 @@ const styles = StyleSheet.create({
|
|||||||
borderColor: "#D1D5DB",
|
borderColor: "#D1D5DB",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
backgroundColor: "#F9FAFB"
|
backgroundColor: "#F9FAFB",
|
||||||
},
|
},
|
||||||
selectedButton: { backgroundColor: colors.pastelGreen, borderColor: "#62b59a" },
|
selectedButton: { backgroundColor: colors.pastelGreen, borderColor: "#62b59a" },
|
||||||
toggleButtonText: { color: "#374151", fontWeight: "600", textTransform: 'capitalize' },
|
toggleButtonText: { color: "#374151", fontWeight: "600", textTransform: "capitalize" },
|
||||||
selectedButtonText: { color: "#1F2937" },
|
selectedButtonText: { color: "#1F2937" },
|
||||||
|
|
||||||
participantsRow: { flexDirection: "row", gap: 15 },
|
participantsRow: { flexDirection: "row", gap: 15 },
|
||||||
participantInput: { flex: 1 },
|
participantInput: { flex: 1 },
|
||||||
});
|
});
|
||||||
|
|||||||
+95
-36
@@ -6,23 +6,47 @@ import { apiCall } from "../utils/ApiHelper";
|
|||||||
import { GROUP_CREATION_URL } from "../utils/ApiUrls";
|
import { GROUP_CREATION_URL } from "../utils/ApiUrls";
|
||||||
import colors from "../components/colors";
|
import colors from "../components/colors";
|
||||||
|
|
||||||
|
// --- Helper: Get Cycle Label ---
|
||||||
|
const getCycleLabel = (cycleType: string, customCycleDays: string | number) => {
|
||||||
|
if (cycleType === "CUSTOM") {
|
||||||
|
if (String(customCycleDays) === "1") return "Daily";
|
||||||
|
if (String(customCycleDays) === "7") return "Weekly";
|
||||||
|
return `Custom (${customCycleDays} days)`;
|
||||||
|
}
|
||||||
|
return cycleType.charAt(0).toUpperCase() + cycleType.slice(1).toLowerCase().replace("_", " ");
|
||||||
|
};
|
||||||
|
|
||||||
// --- Themed Dialog Component ---
|
// --- Themed Dialog Component ---
|
||||||
const ThemedDialog = ({ visible, isSuccess, message, onClose, onNavigate }: { visible: boolean; isSuccess: boolean; message: string; onClose: () => void; onNavigate: () => void; }) => {
|
const ThemedDialog = ({
|
||||||
|
visible,
|
||||||
|
isSuccess,
|
||||||
|
message,
|
||||||
|
onClose,
|
||||||
|
onNavigate,
|
||||||
|
}: {
|
||||||
|
visible: boolean;
|
||||||
|
isSuccess: boolean;
|
||||||
|
message: string;
|
||||||
|
onClose: () => void;
|
||||||
|
onNavigate: () => void;
|
||||||
|
}) => {
|
||||||
if (!visible) return null;
|
if (!visible) return null;
|
||||||
|
|
||||||
const config = isSuccess ? {
|
const config = isSuccess
|
||||||
icon: "checkmark-circle" as const,
|
? {
|
||||||
color: "#2ecc71",
|
icon: "checkmark-circle" as const,
|
||||||
title: "Success!",
|
color: "#2ecc71",
|
||||||
buttonText: "Go Home",
|
title: "Success!",
|
||||||
action: onNavigate,
|
buttonText: "Go Home",
|
||||||
} : {
|
action: onNavigate,
|
||||||
icon: "close-circle" as const,
|
}
|
||||||
color: "#e74c3c",
|
: {
|
||||||
title: "Oops!",
|
icon: "close-circle" as const,
|
||||||
buttonText: "Try Again",
|
color: "#e74c3c",
|
||||||
action: onClose,
|
title: "Oops!",
|
||||||
};
|
buttonText: "Try Again",
|
||||||
|
action: onClose,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal animationType="fade" transparent={true} visible={visible}>
|
<Modal animationType="fade" transparent={true} visible={visible}>
|
||||||
@@ -49,13 +73,16 @@ export default function GroupReview({ route, navigation }: any) {
|
|||||||
|
|
||||||
const handleNavigateHome = () => {
|
const handleNavigateHome = () => {
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
navigation.reset({ index: 0, routes: [{ name: 'Home' }] });
|
navigation.reset({ index: 0, routes: [{ name: "Home" }] });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await apiCall(GROUP_CREATION_URL, "POST", form) as { statusCode?: number, message?: string };
|
const res = (await apiCall(GROUP_CREATION_URL, "POST", form)) as {
|
||||||
|
statusCode?: number;
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
if (res?.statusCode === 201) {
|
if (res?.statusCode === 201) {
|
||||||
setIsSuccess(true);
|
setIsSuccess(true);
|
||||||
setFeedbackMessage(res.message || "Your group has been created successfully.");
|
setFeedbackMessage(res.message || "Your group has been created successfully.");
|
||||||
@@ -71,10 +98,16 @@ export default function GroupReview({ route, navigation }: any) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const TimelineSection: React.FC<{ icon: any; title: string; children: React.ReactNode }> = ({ icon, title, children }) => (
|
const TimelineSection: React.FC<{ icon: any; title: string; children: React.ReactNode }> = ({
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
}) => (
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<View style={styles.timeline}>
|
<View style={styles.timeline}>
|
||||||
<View style={styles.timelineIcon}><Ionicons name={icon} size={22} color="#fff" /></View>
|
<View style={styles.timelineIcon}>
|
||||||
|
<Ionicons name={icon} size={22} color="#fff" />
|
||||||
|
</View>
|
||||||
<View style={styles.timelineLine} />
|
<View style={styles.timelineLine} />
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
@@ -121,8 +154,10 @@ export default function GroupReview({ route, navigation }: any) {
|
|||||||
|
|
||||||
<TimelineSection icon="wallet-outline" title="Financial Details">
|
<TimelineSection icon="wallet-outline" title="Financial Details">
|
||||||
{renderRow("Contribution", `₹${form.contributionAmount}`)}
|
{renderRow("Contribution", `₹${form.contributionAmount}`)}
|
||||||
{renderRow("Cycle", form.cycleType)}
|
{renderRow("Cycle", getCycleLabel(form.cycleType, form.customCycleDays))}
|
||||||
{form.cycleType === "CUSTOM" && renderRow("Cycle Days", `${form.customCycleDays} days`)}
|
{/* Show days only if neither Daily nor Weekly */}
|
||||||
|
{form.cycleType === "CUSTOM" && !["1", "7"].includes(String(form.customCycleDays))
|
||||||
|
&& renderRow("Cycle Days", `${form.customCycleDays} days`)}
|
||||||
</TimelineSection>
|
</TimelineSection>
|
||||||
|
|
||||||
<TimelineSection icon="alert-circle-outline" title="Late Fee Policy">
|
<TimelineSection icon="alert-circle-outline" title="Late Fee Policy">
|
||||||
@@ -131,7 +166,9 @@ export default function GroupReview({ route, navigation }: any) {
|
|||||||
</TimelineSection>
|
</TimelineSection>
|
||||||
|
|
||||||
<TouchableOpacity onPress={handleSubmit} style={styles.submitBtn} disabled={isLoading}>
|
<TouchableOpacity onPress={handleSubmit} style={styles.submitBtn} disabled={isLoading}>
|
||||||
{isLoading ? <ActivityIndicator color="#333" /> : (
|
{isLoading ? (
|
||||||
|
<ActivityIndicator color="#333" />
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
<Ionicons name="rocket-outline" size={22} color="#333" />
|
<Ionicons name="rocket-outline" size={22} color="#333" />
|
||||||
<Text style={styles.btnText}>Create Group</Text>
|
<Text style={styles.btnText}>Create Group</Text>
|
||||||
@@ -156,24 +193,46 @@ const styles = StyleSheet.create({
|
|||||||
header: { marginBottom: 30 },
|
header: { marginBottom: 30 },
|
||||||
title: { fontSize: 28, fontWeight: "bold", color: "#333" },
|
title: { fontSize: 28, fontWeight: "bold", color: "#333" },
|
||||||
subtitle: { fontSize: 16, color: "#666", marginTop: 4 },
|
subtitle: { fontSize: 16, color: "#666", marginTop: 4 },
|
||||||
section: { flexDirection: 'row', marginBottom: 10 },
|
section: { flexDirection: "row", marginBottom: 10 },
|
||||||
timeline: { alignItems: 'center', width: 40 },
|
timeline: { alignItems: "center", width: 40 },
|
||||||
timelineIcon: { width: 40, height: 40, borderRadius: 20, backgroundColor: colors.pastelGreen, justifyContent: 'center', alignItems: 'center', elevation: 3 },
|
timelineIcon: {
|
||||||
timelineLine: { flex: 1, width: 2, backgroundColor: '#e0e0e0' },
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: 20,
|
||||||
|
backgroundColor: colors.pastelGreen,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
elevation: 3,
|
||||||
|
},
|
||||||
|
timelineLine: { flex: 1, width: 2, backgroundColor: "#e0e0e0" },
|
||||||
content: { flex: 1, paddingLeft: 10, paddingBottom: 20 },
|
content: { flex: 1, paddingLeft: 10, paddingBottom: 20 },
|
||||||
sectionTitle: { fontSize: 18, fontWeight: 'bold', color: '#444', marginBottom: 10 },
|
sectionTitle: { fontSize: 18, fontWeight: "bold", color: "#444", marginBottom: 10 },
|
||||||
card: { backgroundColor: "#fff", borderRadius: 12, padding: 15, elevation: 2 },
|
card: { backgroundColor: "#fff", borderRadius: 12, padding: 15, elevation: 2 },
|
||||||
row: { flexDirection: "row", justifyContent: "space-between", paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#f5f5f5' },
|
row: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#f5f5f5",
|
||||||
|
},
|
||||||
label: { fontSize: 15, color: "#666", fontWeight: "500" },
|
label: { fontSize: 15, color: "#666", fontWeight: "500" },
|
||||||
value: { fontSize: 15, color: "#333", fontWeight: "600", textTransform: 'capitalize' },
|
value: { fontSize: 15, color: "#333", fontWeight: "600", textTransform: "capitalize" },
|
||||||
descriptionContainer: { paddingVertical: 10 },
|
descriptionContainer: { paddingVertical: 10 },
|
||||||
descriptionText: { fontSize: 15, color: '#333', marginTop: 4, lineHeight: 22 },
|
descriptionText: { fontSize: 15, color: "#333", marginTop: 4, lineHeight: 22 },
|
||||||
submitBtn: { backgroundColor: colors.pastelGreen, flexDirection: 'row', justifyContent: 'center', alignItems: "center", paddingVertical: 16, borderRadius: 12, marginTop: 20 },
|
submitBtn: {
|
||||||
|
backgroundColor: colors.pastelGreen,
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingVertical: 16,
|
||||||
|
borderRadius: 12,
|
||||||
|
marginTop: 20,
|
||||||
|
},
|
||||||
btnText: { color: "#333", fontWeight: "bold", fontSize: 18, marginLeft: 10 },
|
btnText: { color: "#333", fontWeight: "bold", fontSize: 18, marginLeft: 10 },
|
||||||
modalBackdrop: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.6)', justifyContent: 'center', alignItems: 'center' },
|
modalBackdrop: { flex: 1, backgroundColor: "rgba(0, 0, 0, 0.6)", justifyContent: "center", alignItems: "center" },
|
||||||
dialogBox: { width: '85%', backgroundColor: 'white', borderRadius: 16, padding: 25, alignItems: 'center', elevation: 10 },
|
dialogBox: { width: "85%", backgroundColor: "white", borderRadius: 16, padding: 25, alignItems: "center", elevation: 10 },
|
||||||
dialogTitle: { fontSize: 22, fontWeight: 'bold', color: '#333', marginTop: 15, marginBottom: 10 },
|
dialogTitle: { fontSize: 22, fontWeight: "bold", color: "#333", marginTop: 15, marginBottom: 10 },
|
||||||
dialogMessage: { fontSize: 16, color: '#666', textAlign: 'center', marginBottom: 25 },
|
dialogMessage: { fontSize: 16, color: "#666", textAlign: "center", marginBottom: 25 },
|
||||||
dialogButton: { backgroundColor: '#f0f0f0', paddingVertical: 12, paddingHorizontal: 40, borderRadius: 10 },
|
dialogButton: { backgroundColor: "#f0f0f0", paddingVertical: 12, paddingHorizontal: 40, borderRadius: 10 },
|
||||||
dialogButtonText: { color: '#333', fontWeight: 'bold', fontSize: 16 },
|
dialogButtonText: { color: "#333", fontWeight: "bold", fontSize: 16 },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { BASE_URL, GET_JOINED_GROUP_URL, JOIN_GROUP_URL, PUBLIC_GROUPS_URL, PUT_
|
|||||||
import colors from "../components/colors";
|
import colors from "../components/colors";
|
||||||
import { ApiResponse } from "./home/UserProfileStatus";
|
import { ApiResponse } from "./home/UserProfileStatus";
|
||||||
import * as Keychain from "react-native-keychain";
|
import * as Keychain from "react-native-keychain";
|
||||||
|
import CustomAlert from "../components/CostomAlart";
|
||||||
const { width } = Dimensions.get('window');
|
const { width } = Dimensions.get('window');
|
||||||
|
|
||||||
// --- Types ---
|
// --- Types ---
|
||||||
@@ -113,6 +114,13 @@ export default function GroupScreen() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [role, setRole] = useState<string | null>(null);
|
const [role, setRole] = useState<string | null>(null);
|
||||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||||
|
const [alertInfo, setAlertInfo] = useState({
|
||||||
|
visible: false,
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
buttons: [] as { text: string; onPress: () => void; style?: 'cancel' | 'destructive' | 'default' }[],
|
||||||
|
alertType: 'info' as 'info' | 'success' | 'error',
|
||||||
|
});
|
||||||
|
|
||||||
const fetchGroups = async () => {
|
const fetchGroups = async () => {
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
@@ -177,7 +185,15 @@ export default function GroupScreen() {
|
|||||||
console.log("JOIN_GROUP_URL response:", res);
|
console.log("JOIN_GROUP_URL response:", res);
|
||||||
|
|
||||||
if (res.statusCode === 200) {
|
if (res.statusCode === 200) {
|
||||||
ToastAndroid.show('You requested to join the group.', ToastAndroid.SHORT);
|
setAlertInfo({
|
||||||
|
visible: true,
|
||||||
|
title: 'Success',
|
||||||
|
message: 'Your request to join the group has been sent.',
|
||||||
|
buttons: [
|
||||||
|
{ text: 'OK', onPress: () => setAlertInfo({ ...alertInfo, visible: false }), buttonColor: "#28a745", textColor: "#fff" }
|
||||||
|
],
|
||||||
|
alertType: 'success',
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ToastAndroid.show(res.message || "Failed to join group.", ToastAndroid.SHORT);
|
ToastAndroid.show(res.message || "Failed to join group.", ToastAndroid.SHORT);
|
||||||
}
|
}
|
||||||
@@ -327,6 +343,15 @@ export default function GroupScreen() {
|
|||||||
{role === "ORGANIZER" && (
|
{role === "ORGANIZER" && (
|
||||||
<FAB style={styles.fab} icon={() => <MaterialIcons name="group-add" size={24} color="#333" />} onPress={() => navigation.navigate('GroupCreation')} />
|
<FAB style={styles.fab} icon={() => <MaterialIcons name="group-add" size={24} color="#333" />} onPress={() => navigation.navigate('GroupCreation')} />
|
||||||
)}
|
)}
|
||||||
|
<CustomAlert
|
||||||
|
visible={alertInfo.visible}
|
||||||
|
title={alertInfo.title}
|
||||||
|
message={alertInfo.message}
|
||||||
|
buttons={alertInfo.buttons}
|
||||||
|
alertType={alertInfo.alertType}
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
</AppView>
|
</AppView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export default function LoginScreen() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(LOGIN_URL, { params: { mobile } });
|
const res = await axios.get(LOGIN_URL, { params: { mobile } });
|
||||||
|
console.log("login res -",res)
|
||||||
if (res.data.statusCode === 200) {
|
if (res.data.statusCode === 200) {
|
||||||
navigation.navigate('OtpVarify', { mobile });
|
navigation.navigate('OtpVarify', { mobile });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
UIManager,
|
UIManager,
|
||||||
Platform,
|
Platform,
|
||||||
Alert,
|
Alert,
|
||||||
|
ToastAndroid,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
||||||
import colors from "../../components/colors";
|
import colors from "../../components/colors";
|
||||||
|
|||||||
@@ -637,14 +637,31 @@ const Profile = () => {
|
|||||||
multiline
|
multiline
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CustomInput
|
<View style={styles.inputContainer}>
|
||||||
error=""
|
<Text style={styles.inputLabel}>Gender</Text>
|
||||||
label="Gender"
|
<View style={styles.genderRow}>
|
||||||
placeholder="Enter gender"
|
{["Male", "Female", "Other"].map((option) => (
|
||||||
value={user?.gender ?? ""}
|
<TouchableOpacity
|
||||||
onChangeText={(text) => setUser({ ...user, gender: text })}
|
key={option}
|
||||||
icon="male-female"
|
style={[
|
||||||
/>
|
styles.genderButton,
|
||||||
|
user?.gender === option && styles.genderButtonActive,
|
||||||
|
]}
|
||||||
|
onPress={() => setUser({ ...user, gender: option })}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.genderButtonText,
|
||||||
|
user?.gender === option && styles.genderButtonTextActive,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{option}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
|
||||||
<View style={styles.editButtons}>
|
<View style={styles.editButtons}>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@@ -869,6 +886,35 @@ const styles = StyleSheet.create({
|
|||||||
buttonTextOutline: {
|
buttonTextOutline: {
|
||||||
color: "#6366f1",
|
color: "#6366f1",
|
||||||
},
|
},
|
||||||
|
genderRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
marginTop: 8,
|
||||||
|
marginBottom: 16,
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
genderButton: {
|
||||||
|
flex: 1,
|
||||||
|
paddingVertical: 12,
|
||||||
|
backgroundColor: "#f1f5f9",
|
||||||
|
borderRadius: 12,
|
||||||
|
alignItems: "center",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#e2e8f0",
|
||||||
|
},
|
||||||
|
genderButtonActive: {
|
||||||
|
backgroundColor: colors.pastelBlue,
|
||||||
|
},
|
||||||
|
genderButtonText: {
|
||||||
|
color: "#64748b",
|
||||||
|
fontWeight: "500",
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
genderButtonTextActive: {
|
||||||
|
color: colors.black,
|
||||||
|
fontWeight: "700"
|
||||||
|
},
|
||||||
|
|
||||||
buttonIcon: {
|
buttonIcon: {
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user