diff --git a/package-lock.json b/package-lock.json
index d4efc6e..a0b731d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.0.1",
"dependencies": {
"@react-native-async-storage/async-storage": "^2.2.0",
+ "@react-native-community/slider": "^5.0.1",
"@react-native-picker/picker": "^2.11.2",
"@react-native-vector-icons/ionicons": "^12.3.0",
"@react-native-vector-icons/material-icons": "^12.3.0",
@@ -18,11 +19,12 @@
"@react-navigation/native": "^7.1.17",
"@react-navigation/native-stack": "^7.3.26",
"@react-navigation/stack": "^7.4.8",
- "axios": "^1.11.0",
+ "axios": "^1.12.2",
"install": "^0.13.0",
"npm": "^11.6.0",
"react": "19.1.0",
"react-native": "0.81.1",
+ "react-native-calendars": "^1.1313.0",
"react-native-elements": "^3.4.3",
"react-native-image-picker": "^8.2.1",
"react-native-keychain": "^10.0.0",
@@ -2980,6 +2982,12 @@
"node": ">=10"
}
},
+ "node_modules/@react-native-community/slider": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@react-native-community/slider/-/slider-5.0.1.tgz",
+ "integrity": "sha512-K3JRWkIW4wQ79YJ6+BPZzp1SamoikxfPRw7Yw4B4PElEQmqZFrmH9M5LxvIo460/3QSrZF/wCgi3qizJt7g/iw==",
+ "license": "MIT"
+ },
"node_modules/@react-native-picker/picker": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.2.tgz",
@@ -4520,9 +4528,9 @@
}
},
"node_modules/axios": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
- "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
+ "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -9007,7 +9015,6 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
- "devOptional": true,
"license": "MIT"
},
"node_modules/lodash.isequal": {
@@ -9797,6 +9804,16 @@
"node": ">=10"
}
},
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -13196,6 +13213,38 @@
}
}
},
+ "node_modules/react-native-calendars": {
+ "version": "1.1313.0",
+ "resolved": "https://registry.npmjs.org/react-native-calendars/-/react-native-calendars-1.1313.0.tgz",
+ "integrity": "sha512-YQ7Vg57rBRVymolamYDTxZ0lPOELTDHQbTukTWdxR47aRBYJwKI6ocRbwcY5gYgyDwNgJS4uLGu5AvmYS74LYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "hoist-non-react-statics": "^3.3.1",
+ "lodash": "^4.17.15",
+ "memoize-one": "^5.2.1",
+ "prop-types": "^15.5.10",
+ "react-native-safe-area-context": "4.5.0",
+ "react-native-swipe-gestures": "^1.0.5",
+ "recyclerlistview": "^4.0.0",
+ "xdate": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "moment": "^2.29.4"
+ }
+ },
+ "node_modules/react-native-calendars/node_modules/react-native-safe-area-context": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.5.0.tgz",
+ "integrity": "sha512-0WORnk9SkREGUg2V7jHZbuN5x4vcxj/1B0QOcXJjdYWrzZHgLcUzYWWIUecUPJh747Mwjt/42RZDOaFn3L8kPQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native-elements": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-3.4.3.tgz",
@@ -13343,6 +13392,12 @@
"react-native": "*"
}
},
+ "node_modules/react-native-swipe-gestures": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz",
+ "integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw==",
+ "license": "MIT"
+ },
"node_modules/react-native-vector-icons": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.3.0.tgz",
@@ -13481,6 +13536,21 @@
"node": ">= 6"
}
},
+ "node_modules/recyclerlistview": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.3.tgz",
+ "integrity": "sha512-STR/wj/FyT8EMsBzzhZ1l2goYirMkIgfV3gYEPxI3Kf3lOnu6f7Dryhyw7/IkQrgX5xtTcDrZMqytvteH9rL3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "lodash.debounce": "4.0.8",
+ "prop-types": "15.8.1",
+ "ts-object-utils": "0.0.5"
+ },
+ "peerDependencies": {
+ "react": ">= 15.2.1",
+ "react-native": ">= 0.30.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -14666,6 +14736,12 @@
"typescript": ">=4.2.0"
}
},
+ "node_modules/ts-object-utils": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz",
+ "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==",
+ "license": "ISC"
+ },
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@@ -15233,6 +15309,12 @@
"async-limiter": "~1.0.0"
}
},
+ "node_modules/xdate": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/xdate/-/xdate-0.8.3.tgz",
+ "integrity": "sha512-1NhJWPJwN+VjbkACT9XHbQK4o6exeSVtS2CxhMPwUE7xQakoEFTlwra9YcqV/uHQVyeEUYoYC46VGDJ+etnIiw==",
+ "license": "(MIT OR GPL-2.0)"
+ },
"node_modules/xmlbuilder": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
diff --git a/package.json b/package.json
index e30f84f..dbdff86 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"@react-native-async-storage/async-storage": "^2.2.0",
+ "@react-native-community/slider": "^5.0.1",
"@react-native-picker/picker": "^2.11.2",
"@react-native-vector-icons/ionicons": "^12.3.0",
"@react-native-vector-icons/material-icons": "^12.3.0",
@@ -20,11 +21,12 @@
"@react-navigation/native": "^7.1.17",
"@react-navigation/native-stack": "^7.3.26",
"@react-navigation/stack": "^7.4.8",
- "axios": "^1.11.0",
+ "axios": "^1.12.2",
"install": "^0.13.0",
"npm": "^11.6.0",
"react": "19.1.0",
"react-native": "0.81.1",
+ "react-native-calendars": "^1.1313.0",
"react-native-elements": "^3.4.3",
"react-native-image-picker": "^8.2.1",
"react-native-keychain": "^10.0.0",
diff --git a/src/assets/colors/colors.js b/src/assets/colors/colors.js
new file mode 100644
index 0000000..c2380e2
--- /dev/null
+++ b/src/assets/colors/colors.js
@@ -0,0 +1,15 @@
+const colours = {
+ lightblue: '#98e2f4',
+ colorOne: '#83f5cc',
+ primary: '#F4721E',
+ secondary: '#FF9A62',
+ textPrimary: '#333333',
+ textSecondary: '#666666',
+ background: '#FFFFFF',
+ success: '#008e02',
+ error: '#FF3B30',
+ warning: '#FF9500',
+ info: '#007AFF',
+};
+
+export default colours;
diff --git a/src/components/CustomButton.tsx b/src/components/CustomButton.tsx
index 185fb67..63b7b7f 100644
--- a/src/components/CustomButton.tsx
+++ b/src/components/CustomButton.tsx
@@ -13,7 +13,7 @@ type CustomButtonProps = {
export default function CustomButton({
title,
onPress,
- backgroundColor = "#6C63FF",
+ backgroundColor = "#000000ff",
textColor = "#fff",
loading = false,
style,
diff --git a/src/components/colors.ts b/src/components/colors.ts
index 6da0717..0206238 100644
--- a/src/components/colors.ts
+++ b/src/components/colors.ts
@@ -1,10 +1,15 @@
// colors.ts
const colors = {
- pastelPink: "#F8BBD0", // darker pink
- pastelBlue: "#90CAF9", // darker blue
- pastelGreen: "#A5D6A7", // darker green
- pastelOrange: "#FFCC80", // darker orange
+ pastelPink: "#decbfcff", // darker pink
+ pastelBlue: "#D7E9F7", // darker blue
+ pastelGreen: "#DFF6DD", // darker green
+ pastelOrange: "#FFF4D2", // darker orange
+ // darker accent colors
+ Home: "#995dfaff", // Rose red for contrast
+ Profile: "#2D9CDB", // Deep sky blue
+ Group: "#27AE60", // Green for freshness
+ Settings: "#F39C12", // Amber for warmth
// common text + UI colors
textDark: "#212121",
textLight: "#555555",
diff --git a/src/navigation/BottomTabs.tsx b/src/navigation/BottomTabs.tsx
index d66a3e8..2f276c0 100644
--- a/src/navigation/BottomTabs.tsx
+++ b/src/navigation/BottomTabs.tsx
@@ -1,73 +1,91 @@
import React from "react";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { View, Text, StyleSheet, Platform } from "react-native";
-import { Ionicons } from '@react-native-vector-icons/ionicons';
+import { Ionicons, IoniconsIconName } from "@react-native-vector-icons/ionicons";
import GroupScreen from "../screens/GroupScreen";
import Profile from "../screens/profile/Profile";
import Home from "../screens/home/Home";
+import colors from "../components/colors";
+import Setting from "../screens/setting/Setting";
const Tab = createBottomTabNavigator();
const TAB_CONFIG = {
- activeColor: "#eb87a3ff", // coffee-like orange
- inactiveColor: "#000000ff",
- backgroundColor: "#e3e9efff",
- height: 80,
- iconSize: 30,
- labelSize: 12,
- labelWeight: "600",
+ activeColor: "#000",
+ inactiveColor: "#8e8e93",
+ backgroundColor: "#fff",
+ height: 70,
+ iconSize: 26,
+};
+
+// Background (tab highlight) colors
+const activeTabColors: { [key: string]: string } = {
+ Home: colors.pastelPink, // Soft pink
+ Profile: colors.pastelBlue, // Light sky blue
+ Group: colors.pastelGreen, // Gentle mint green
+ Settings: colors.pastelOrange, // Warm pastel yellow
+};
+
+// Icon/Text colors on active tabs
+const activeIconColors: { [key: string]: string } = {
+ Home: colors.Home, // Rose red for contrast
+ Profile: colors.Profile, // Deep sky blue
+ Group: colors.Group, // Green for freshness
+ Settings: colors.Settings, // Amber for warmth
};
-function SettingsScreen() {
- return (
-
- ⚙️ Settings
-
- );
-}
-
export default function BottomTabs() {
return (
({
headerShown: false,
tabBarStyle: styles.tabBar,
- tabBarActiveTintColor: TAB_CONFIG.activeColor,
- tabBarInactiveTintColor: TAB_CONFIG.inactiveColor,
- tabBarLabelStyle: {
- fontSize: TAB_CONFIG.labelSize,
- fontWeight: TAB_CONFIG.labelWeight as any,
- marginBottom: Platform.OS === "ios" ? 0 : 5,
- },
- tabBarIcon: ({ color, focused }) => {
- let iconName:
- | "home"
- | "home-outline"
- | "person"
- | "person-outline"
- | "settings"
- | "settings-outline"
- | "people"
- | "people-outline" = "home-outline";
+ tabBarLabel: ({ focused, color }) =>
+ !focused ? (
+ {route.name}
+ ) : null,
+
+ tabBarIcon: ({ focused }) => {
+ let iconName: IoniconsIconName = "home-outline";
+
+ // Determine icon based on route
if (route.name === "Home") iconName = focused ? "home" : "home-outline";
- else if (route.name === "Profile")
- iconName = focused ? "person" : "person-outline";
- else if (route.name === "Settings")
- iconName = focused ? "settings" : "settings-outline";
- else if (route.name === "Group")
- iconName = focused ? "people" : "people-outline";
+ else if (route.name === "Profile") iconName = focused ? "person" : "person-outline";
+ else if (route.name === "Settings") iconName = focused ? "settings" : "settings-outline";
+ else if (route.name === "Group") iconName = focused ? "people" : "people-outline";
- return ;
+ // 3. Get the active background color and icon color from the maps
+ const activeBackgroundColor = activeTabColors[route.name];
+ const activeIconColor = activeIconColors[route.name];
+
+ return (
+
+
+
+ );
},
})}
>
-
+
);
}
@@ -77,7 +95,7 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: "center",
alignItems: "center",
- backgroundColor: "#dae4ed",
+ backgroundColor: "#f5f7fa",
},
text: {
fontSize: 20,
@@ -86,19 +104,38 @@ const styles = StyleSheet.create({
},
tabBar: {
position: "absolute",
- bottom: 0,
- left: 0,
- right: 0,
+ bottom: 20,
+ left: 20,
+ right: 20,
height: TAB_CONFIG.height,
backgroundColor: TAB_CONFIG.backgroundColor,
- borderTopLeftRadius: 30,
- borderTopRightRadius: 30,
+ borderRadius: 20,
shadowColor: "#000",
- shadowOpacity: 0.10,
- shadowOffset: { width: 0, height: -2 },
+ shadowOpacity: 0.1,
+ shadowOffset: { width: 0, height: 5 },
shadowRadius: 8,
- elevation: 20,
+ elevation: 10,
+ paddingBottom: Platform.OS === "ios" ? 25 : 10,
paddingTop: 10,
- borderTopWidth: 0,
+ marginHorizontal: 10,
+ },
+ label: {
+ fontSize: 12,
+ marginBottom: 5,
+ },
+ iconContainer: {
+ justifyContent: "center",
+ alignItems: "center",
+ width: 50, // Reduced size for a sleeker look
+ height: 50, // Reduced size
+ marginTop: 10,
+ borderRadius: 15,
+ },
+ activeIconContainer: {
+ // The default active style, background color is now applied dynamically
+ elevation: 2,
+ shadowColor: '#000',
+ shadowOpacity: 0.1,
+ shadowRadius: 5,
},
});
diff --git a/src/screens/GroupCreation.tsx b/src/screens/GroupCreation.tsx
index 30b79cd..0425d37 100644
--- a/src/screens/GroupCreation.tsx
+++ b/src/screens/GroupCreation.tsx
@@ -1,36 +1,104 @@
-import { Picker } from "@react-native-picker/picker";
import React, { useState } from "react";
import {
View,
Text,
- TextInput,
- TouchableOpacity,
StyleSheet,
ScrollView,
KeyboardAvoidingView,
Platform,
+ TouchableOpacity,
+ Modal,
+ Alert,
} from "react-native";
+import Slider from "@react-native-community/slider";
import { useNavigation } from "@react-navigation/native";
+import { CustomInput } from "../components/common/Custom";
import CustomButton from "../components/CustomButton";
+import { Calendar } from "react-native-calendars";
+import { Ionicons } from "@react-native-vector-icons/ionicons";
+
+const calendarTheme = {
+ backgroundColor: '#ffffff',
+ calendarBackground: '#ffffff',
+ textSectionTitleColor: '#62b59a',
+ selectedDayTextColor: '#333',
+ todayTextColor: '#62b59a',
+ dayTextColor: '#333',
+ textDisabledColor: '#d9e1e8',
+ arrowColor: '#62b59a',
+ monthTextColor: '#333',
+ textDayFontWeight: '500',
+ textMonthFontWeight: 'bold',
+ textDayHeaderFontWeight: '600',
+ textDayFontSize: 16,
+ textMonthFontSize: 18,
+ textDayHeaderFontSize: 14,
+};
export default function GroupCreationForm() {
const navigation = useNavigation();
-
const [form, setForm] = useState({
- contributionAmount: "",
+ contributionAmount: "4000",
customCycleDays: "",
- cycleType: "CUSTOM",
+ cycleType: "MONTHLY",
description: "",
- endDate: "",
groupName: "",
maxParticipants: "",
minParticipants: "",
startDate: "",
visibility: "PRIVATE",
+ fundDistribution: "SEQUENTIAL",
});
+ const [errors, setErrors] = useState({}); // State for validation errors
+ const [isCalendarVisible, setCalendarVisible] = useState(false);
+
const handleChange = (key: string, value: string) => {
setForm({ ...form, [key]: value });
+ // Clear error for the field when user starts typing
+ if (errors[key]) {
+ setErrors({ ...errors, [key]: null });
+ }
+ };
+
+ const handleAmountChange = (value: number) => {
+ setForm({ ...form, contributionAmount: Math.round(value).toString() });
+ };
+
+ const handleDayPress = (day: any) => {
+ setForm({ ...form, startDate: day.dateString });
+ setCalendarVisible(false);
+ setErrors({ ...errors, startDate: null });
+ };
+
+ const presetAmounts = [2000, 4000, 8000];
+ const cycleTypes = ["CUSTOM", "MONTHLY", "QUARTERLY", "YEARLY"];
+ const distributionTypes = ["SEQUENTIAL", "RANDOM"];
+
+ const handlePresetAmount = (amount: number) => {
+ setForm({ ...form, contributionAmount: amount.toString() });
+ };
+
+ const validateForm = () => {
+ const newErrors: any = {};
+ if (!form.groupName.trim()) newErrors.groupName = "Group name is required.";
+ if (!form.description.trim()) newErrors.description = "Description is required.";
+ if (!form.minParticipants || Number(form.minParticipants) <= 0) newErrors.minParticipants = "Min participants must be greater than 0.";
+ if (!form.maxParticipants || Number(form.maxParticipants) < Number(form.minParticipants)) newErrors.maxParticipants = "Max must be greater than or equal to min.";
+ 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.startDate) newErrors.startDate = "Start date is required.";
+
+ setErrors(newErrors);
+ return Object.keys(newErrors).length === 0;
+ };
+
+ const handleReview = () => {
+ if (validateForm()) {
+ navigation.navigate("GroupReview", { form });
+ } else {
+ Alert.alert("Incomplete Form", "Please fill out all required fields correctly.");
+ }
};
return (
@@ -39,166 +107,98 @@ export default function GroupCreationForm() {
behavior={Platform.OS === "ios" ? "padding" : undefined}
>
- Group Creation
-
- {/* Group Info */}
- Group Name
- handleChange("groupName", val)}
- />
-
- Description
- handleChange("description", val)}
- />
-
- Visibility
-
- handleChange("visibility", val)}
- style={styles.picker}
- >
-
-
-
-
-
- {/* Participants */}
- Minimum Participants
- handleChange("minParticipants", val)}
- />
-
- Maximum Participants
- handleChange("maxParticipants", val)}
- />
-
- {/* Contribution */}
- Contribution Amount (₹)
- handleChange("contributionAmount", val)}
- />
-
- Cycle Type
-
- handleChange("cycleType", val)}
- style={styles.picker}
- >
-
-
-
-
-
+ Group Creation
-
+ handleChange("groupName", val)} icon="people" error={errors.groupName} />
+ handleChange("description", val)} multiline icon="document-text" error={errors.description} />
+
+ Visibility
+
+ handleChange("visibility", "PRIVATE")}>Private
+ handleChange("visibility", "PUBLIC")}>Public
- {form.cycleType === "CUSTOM" && (
- <>
- Custom Cycle Days
- handleChange("customCycleDays", val)}
- />
- >
- )}
+ Fund Distribution
+
+ {distributionTypes.map((type) => ( handleChange("fundDistribution", type)} style={[styles.toggleButton, form.fundDistribution === type && styles.selectedButton]}>{type.charAt(0) + type.slice(1).toLowerCase()}))}
+
+
+
+ handleChange("minParticipants", val)} keyboardType="numeric" icon="person-add" error={errors.minParticipants} />
+ handleChange("maxParticipants", val)} keyboardType="numeric" icon="people-circle" error={errors.maxParticipants} />
+
+
+ Contribution Amount (INR)
+
+ {presetAmounts.map((amount) => ( handlePresetAmount(amount)} style={[styles.presetButton, form.contributionAmount === amount.toString() && styles.presetSelected]}>{amount}))}
+
+
+
+ handleChange("contributionAmount", val)} keyboardType="numeric" icon="cash" error={errors.contributionAmount} />
+
+
+ Cycle Type
+
+ {cycleTypes.map((type) => ( handleChange("cycleType", type)} style={[styles.cycleTypeButton, form.cycleType === type && styles.cycleTypeSelected]}>{type.charAt(0) + type.slice(1).toLowerCase()}))}
+
+ {form.cycleType === "CUSTOM" && ( handleChange("customCycleDays", val)} keyboardType="numeric" icon="calendar" error={errors.customCycleDays} />)}
- {/* Timeline */}
Start Date
- handleChange("startDate", val)}
- />
+ setCalendarVisible(true)}>
+
+ {form.startDate || "Select a start date"}
+
+ {errors.startDate && {errors.startDate}}
- End Date
- handleChange("endDate", val)}
- />
-
- {/* Next Button
- navigation.navigate("GroupReview", { form })}
- style={styles.nextBtn}
- >
- ➡ Review
- */}
- navigation.navigate("GroupReview", { form })}
- />
+
+
+ setCalendarVisible(false)}>
+ setCalendarVisible(false)}>
+
+
+
);
}
const styles = StyleSheet.create({
- container: { flexGrow: 1, padding: 20 },
- title: { fontSize: 22, fontWeight: "bold", marginBottom: 15, color: "#333" },
- label: { fontSize: 14, fontWeight: "600", marginBottom: 5, color: "#444" },
- input: {
- borderWidth: 1,
- borderColor: "#ddd",
- borderRadius: 12,
- padding: 12,
- marginBottom: 15,
- fontSize: 16,
- backgroundColor: "#fff",
- elevation: 1,
+ container: { flexGrow: 1, padding: 20, backgroundColor: "#f5f6fa" },
+ titleContainer: { backgroundColor: "#83f5cc", paddingHorizontal: 18, paddingVertical: 2, borderRadius: 15, alignSelf: "flex-start", marginBottom: 30 },
+ title: { fontSize: 22, fontWeight: "bold", color: "#333" },
+ label: { fontSize: 14, fontWeight: "600", marginBottom: 10, color: "#444" },
+ toggleContainer: { flexDirection: "row", gap: 10, marginBottom: 15 },
+ toggleButton: { flex: 1, paddingVertical: 12, borderWidth: 1, borderColor: "#ddd", borderRadius: 12, alignItems: "center", backgroundColor: "#fff" },
+ selectedButton: { backgroundColor: "#83f5cc", borderColor: "#62b59a" },
+ toggleButtonText: { color: "#444", fontWeight: "600" },
+ selectedButtonText: { color: "#333" },
+ participantsRow: { flexDirection: "row", justifyContent: "space-between", alignItems: 'flex-start', marginBottom: 10 },
+ participantInput: { flex: 0.48 },
+ presetContainer: { flexDirection: 'row', justifyContent: 'flex-start', gap: 10, marginBottom: 10 },
+ presetButton: { paddingVertical: 8, paddingHorizontal: 15, backgroundColor: '#ddd', borderRadius: 20 },
+ presetSelected: { backgroundColor: '#83f5cc' },
+ presetButtonText: { color: '#555', fontWeight: '600' },
+ presetSelectedText: { color: '#333' },
+ amountContainer: { flexDirection: 'row', alignItems: 'center', marginBottom: 15, },
+ slider: { flex: 1 },
+ amountInput: { width: 120, marginLeft: 10 },
+ cycleTypeContainer: { flexDirection: 'row', flexWrap: 'wrap', gap: 10, marginBottom: 20 },
+ cycleTypeButton: { paddingVertical: 10, paddingHorizontal: 15, backgroundColor: '#fff', borderWidth: 1, borderColor: '#ddd', borderRadius: 20, elevation: 1 },
+ cycleTypeSelected: { backgroundColor: '#83f5cc', borderColor: '#62b59a' },
+ cycleTypeButtonText: { color: '#444', fontWeight: '600', textTransform: 'capitalize' },
+ cycleTypeSelectedText: { color: '#333' },
+ datePickerButton: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#fff', borderWidth: 1, borderColor: '#ddd', borderRadius: 12, paddingVertical: 15, paddingHorizontal: 15, marginBottom: 5, },
+ datePickerText: { marginLeft: 10, fontSize: 16, color: '#333' },
+ modalBackdrop: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0, 0, 0, 0.5)' },
+ calendarWrapper: { width: '90%', backgroundColor: 'white', borderRadius: 16, paddingVertical: 10, elevation: 5, shadowColor: '#000', shadowOpacity: 0.2, shadowRadius: 10, },
+ // --- ERROR STYLES ---
+ errorBorder: {
+ borderColor: '#ff4d4d', // Red border for fields with errors
},
- pickerWrapper: {
- borderWidth: 1,
- borderColor: "#ddd",
- borderRadius: 12,
- marginBottom: 15,
- backgroundColor: "#fff",
- elevation: 1,
+ errorText: {
+ color: '#ff4d4d',
+ fontSize: 12,
+ marginBottom: 10,
+ marginLeft: 5,
},
- picker: { height: 60, width: "100%" },
- nextBtn: {
- backgroundColor: "#eb87a3ff",
- paddingVertical: 16,
- borderRadius: 12,
- alignItems: "center",
- marginTop: 10,
- },
- btnText: { color: "#fff", fontWeight: "bold", fontSize: 16 },
});
diff --git a/src/screens/GroupReview.tsx b/src/screens/GroupReview.tsx
index 40f3673..242ac46 100644
--- a/src/screens/GroupReview.tsx
+++ b/src/screens/GroupReview.tsx
@@ -1,10 +1,10 @@
import React from "react";
-import { View, Text, StyleSheet, TouchableOpacity, Alert, ScrollView } from "react-native";
-import axios from "axios";
-import { GROUP_CREATION_URL } from "../utils/ApiUrls";
-import App from "../../App";
+import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from "react-native";
+import {Ionicons} from "@react-native-vector-icons/ionicons";
import AppView from "../components/AppView";
import { apiCall } from "../utils/ApiHelper";
+import { GROUP_CREATION_URL } from "../utils/ApiUrls";
+import CustomButton from "../components/CustomButton";
export default function GroupReview({ route, navigation }: any) {
const { form } = route.params;
@@ -12,83 +12,180 @@ export default function GroupReview({ route, navigation }: any) {
const handleSubmit = async () => {
const payload = {
...form,
- contributionAmount: Number(form.contributionAmount),
- customCycleDays: Number(form.customCycleDays),
- maxParticipants: Number(form.maxParticipants),
- minParticipants: Number(form.minParticipants),
+ contributionAmount: Number(form.contributionAmount) || 0,
+ customCycleDays: Number(form.customCycleDays) || 0,
+ maxParticipants: Number(form.maxParticipants) || 0,
+ minParticipants: Number(form.minParticipants) || 0,
};
await apiCall(GROUP_CREATION_URL, "POST", payload);
};
- const renderRow = (label: string, value: string | number) => (
-
- {label}
- {value}
+ const TimelineSection = ({ icon, title, children }: { icon: any; title: string; children: React.ReactNode }) => (
+
+
+
+
+
+
+
+
+ {title}
+
+ {children}
+
+
);
+ const renderRow = (label: string, value: string | number) => {
+ if (!value && typeof value !== 'number') return null;
+ return (
+
+ {label}
+ {value}
+
+ );
+ };
+
return (
-
- Review Your Group
-
- {renderRow("Group Name", form.groupName)}
- {renderRow("Description", form.description)}
- {renderRow("Min Participants", form.minParticipants)}
- {renderRow("Max Participants", form.maxParticipants)}
- {renderRow("Contribution", `₹${form.contributionAmount}`)}
- {renderRow("Cycle Type", form.cycleType)}
- {form.cycleType === "CUSTOM" && renderRow("Custom Cycle", `${form.customCycleDays} days`)}
- {renderRow("Start Date", form.startDate)}
- {renderRow("End Date", form.endDate)}
- {renderRow("Visibility", form.visibility)}
-
+
+
+ {form.groupName || "Group Review"}
+ Confirm the details to launch your group.
+
-
- Submit
-
-
+
+ {renderRow("Name", form.groupName)}
+ {form.description ? (
+
+ Description
+ {form.description}
+
+ ) : null}
+
+
+
+ {renderRow("Visibility", form.visibility)}
+ {renderRow("Distribution", form.fundDistribution)}
+ {renderRow("Min Members", form.minParticipants)}
+ {renderRow("Max Members", form.maxParticipants)}
+
+
+
+ {renderRow("Contribution", `${form.contributionAmount} INR`)}
+ {renderRow("Cycle", form.cycleType)}
+ {form.cycleType === "CUSTOM" && renderRow("Cycle Days", `${form.customCycleDays} days`)}
+ {renderRow("Start Date", form.startDate)}
+
+
+
);
}
const styles = StyleSheet.create({
- container: { flexGrow: 1, padding: 20, backgroundColor: "#f5f5f5" },
- title: {
- fontSize: 24,
- fontWeight: "bold",
- marginBottom: 20,
- color: "#222",
- textAlign: "center",
+ container: {
+ padding: 20,
+ backgroundColor: "#f5f6fa",
},
- reviewBox: {
- padding: 18,
- borderWidth: 1,
- borderColor: "#ddd",
- borderRadius: 16,
- backgroundColor: "#fff",
- marginBottom: 25,
+ header: {
+ marginBottom: 30,
+ alignItems: 'flex-start',
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: "bold",
+ color: "#333",
+ },
+ subtitle: {
+ fontSize: 16,
+ color: "#666",
+ marginTop: 2,
+ },
+ section: {
+ flexDirection: 'row',
+ marginBottom: 10,
+ },
+ timeline: {
+ alignItems: 'center',
+ width: 40,
+ },
+ timelineIcon: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ backgroundColor: '#83f5cc',
+ justifyContent: 'center',
+ alignItems: 'center',
elevation: 3,
- shadowColor: "#000",
- shadowOpacity: 0.1,
- shadowOffset: { width: 0, height: 2 },
- shadowRadius: 5,
+ },
+ timelineLine: {
+ flex: 1,
+ width: 2,
+ backgroundColor: '#e0e0e0',
+ },
+ content: {
+ flex: 1,
+ paddingLeft: 10,
+ paddingBottom: 20,
+ },
+ sectionTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: '#444',
+ marginBottom: 10,
+ },
+ card: {
+ backgroundColor: "#fff",
+ borderRadius: 12,
+ padding: 15,
+ elevation: 2,
},
row: {
flexDirection: "row",
justifyContent: "space-between",
paddingVertical: 10,
borderBottomWidth: 1,
- borderBottomColor: "#eee",
+ borderBottomColor: '#f5f5f5',
+ },
+ label: {
+ fontSize: 15,
+ color: "#666",
+ fontWeight: "500",
+ },
+ value: {
+ fontSize: 15,
+ color: "#333",
+ fontWeight: "600",
+ textAlign: 'right',
+ textTransform: 'capitalize',
+ },
+ descriptionContainer: {
+ paddingVertical: 10,
+ },
+ descriptionText: {
+ fontSize: 15,
+ color: '#333',
+ marginTop: 4,
+ lineHeight: 22,
},
- label: { fontSize: 16, fontWeight: "600", color: "#444" },
- value: { fontSize: 16, fontWeight: "500", color: "#111" },
submitBtn: {
- backgroundColor: "#008e02",
- paddingVertical: 18,
- borderRadius: 14,
+ backgroundColor: "#83f5cc",
+ flexDirection: 'row',
+ justifyContent: 'center',
alignItems: "center",
- elevation: 2,
+ paddingVertical: 16,
+ borderRadius: 12,
+ elevation: 3,
+ marginTop: 20,
+ borderWidth: 1.5,
+ borderColor: '#62b59a',
+ },
+ btnText: {
+ color: "#333",
+ fontWeight: "bold",
+ fontSize: 18,
+ marginLeft: 10,
},
- btnText: { color: "#fff", fontWeight: "bold", fontSize: 18 },
});
diff --git a/src/screens/GroupScreen.tsx b/src/screens/GroupScreen.tsx
index d9b370e..20be00f 100644
--- a/src/screens/GroupScreen.tsx
+++ b/src/screens/GroupScreen.tsx
@@ -1,46 +1,146 @@
import React, { useState } from "react";
-import { View, Text, StyleSheet } from "react-native";
-import { FAB, Portal, Provider } from "react-native-paper";
-import Tooltip from "react-native-walkthrough-tooltip";
+import { View, Text, StyleSheet, FlatList, TouchableOpacity } from "react-native";
+import { FAB } from "react-native-paper";
import { MaterialIcons } from '@react-native-vector-icons/material-icons';
import { useNavigation } from "@react-navigation/native";
import { StackNavigationProp } from "@react-navigation/stack";
import { RootStackParamList } from "../navigation/AppNavigator";
+import AppView from "../components/AppView";
+import colors from "../components/colors";
+
+// --- Placeholder Data ---
+const myGroupsData = [
+ { id: '1', name: 'Family Savings', members: 5 },
+ { id: '2', name: 'Office Lunch Club', members: 12 },
+];
+const joinedGroupsData = [
+ { id: '3', name: 'Apartment Committee', members: 8 },
+];
+const publicGroupsData = [
+ { id: '4', name: 'City Runners', members: 50 },
+ { id: '5', name: 'Book Readers', members: 25 },
+];
+
+// --- Placeholder Group List Component ---
+const GroupList = ({ data }: { data: any[] }) => {
+ if (data.length === 0) {
+ return No groups found.;
+ }
+
+ return (
+ item.id}
+ renderItem={({ item }) => (
+
+ {item.name}
+ {item.members} members
+
+ )}
+ />
+ );
+};
export default function GroupScreen() {
- const [tooltipVisible, setTooltipVisible] = useState(false);
- const navigation = useNavigation>();
+ const [activeTab, setActiveTab] = useState("My Groups");
+ const navigation = useNavigation>();
+
+ const renderContent = () => {
+ switch (activeTab) {
+ case "My Groups":
+ return ;
+ case "Joined":
+ return ;
+ case "Public":
+ return ;
+ default:
+ return null;
+ }
+ };
+
return (
-
+
- Groups
-
-
- }
- onPress={() => {
- navigation.navigate('GroupCreation')
- }}
- />
-
-
+ {/* --- Header and Segmented Control --- */}
+ Your Groups
+
+ {["My Groups", "Joined", "Public"].map((tab) => (
+ setActiveTab(tab)}
+ >
+
+ {tab}
+
+
+ ))}
+
+
+ {/* --- Dynamic Content Area --- */}
+
+ {renderContent()}
+
+
+ }
+ onPress={() => navigation.navigate('GroupCreation')}
+ />
-
+
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: "#dae4ed",
- padding: 16,
- paddingTop: 40,
+ // padding: 16,
+ // paddingTop: 40,
+ // backgroundColor: '#f5f6fa',
},
title: {
- fontSize: 22,
+ fontSize: 26,
fontWeight: "bold",
color: "#333",
+ marginBottom: 20,
+ },
+ segmentedControl: {
+ flexDirection: 'row',
+ backgroundColor: '#e0e0e0',
+ borderRadius: 10,
+ padding: 4,
+ marginBottom: 20,
+ },
+ segmentButton: {
+ flex: 1,
+ paddingVertical: 8,
+ borderRadius: 8,
+ alignItems: 'center',
+ },
+ segmentButtonActive: {
+ backgroundColor: '#fff',
+ elevation: 2,
+ shadowColor: '#000',
+ shadowOpacity: 0.1,
+ shadowRadius: 5,
+ },
+ segmentText: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#555',
+ },
+ segmentTextActive: {
+ color: '#333',
+ },
+ contentArea: {
+ flex: 1,
},
fab: {
position: "absolute",
@@ -50,12 +150,32 @@ const styles = StyleSheet.create({
height: 70,
justifyContent: "center",
alignItems: "center",
- backgroundColor: "#dae4ed",
- borderRadius: 60,
+ backgroundColor: "#83f5cc",
+ borderRadius: 35,
+ elevation: 4,
},
- tooltipText: {
- color: "#fff",
+ // --- Group List Styles ---
+ groupCard: {
+ backgroundColor: '#fff',
+ padding: 16,
+ borderRadius: 12,
+ marginBottom: 12,
+ elevation: 1,
+ },
+ groupName: {
+ fontSize: 16,
+ fontWeight: 'bold',
+ color: '#333',
+ },
+ groupMembers: {
fontSize: 14,
- fontWeight: "500",
+ color: '#777',
+ marginTop: 4,
+ },
+ emptyText: {
+ textAlign: 'center',
+ marginTop: 50,
+ fontSize: 16,
+ color: '#888',
},
});
diff --git a/src/screens/OtpVarificationScreen.tsx b/src/screens/OtpVarificationScreen.tsx
index 968dfdf..f3e2ebc 100644
--- a/src/screens/OtpVarificationScreen.tsx
+++ b/src/screens/OtpVarificationScreen.tsx
@@ -18,7 +18,7 @@ import {
} from "react-native";
import { RootStackParamList } from "../navigation/AppNavigator";
import * as Keychain from "react-native-keychain";
-import { base_URL, LOGIN_URL } from "../utils/ApiUrls";
+import { LOGIN_URL, VARIFY_OTP_URL } from "../utils/ApiUrls";
import { StackNavigationProp } from "@react-navigation/stack";
const theme = {
@@ -77,7 +77,7 @@ export default function OtpVerificationScreen() {
try {
const payload = { mobile, otp };
const res = await axios.post(
- `${base_URL}user/otp/verify`,
+ VARIFY_OTP_URL,
payload,
{ headers: { "Content-Type": "application/json", Accept: "*/*" } }
);
diff --git a/src/screens/home/Home.tsx b/src/screens/home/Home.tsx
index fc94e21..83bdb99 100644
--- a/src/screens/home/Home.tsx
+++ b/src/screens/home/Home.tsx
@@ -2,12 +2,13 @@ import { StyleSheet, Text, View } from 'react-native'
import React from 'react'
import AppView from '../../components/AppView'
import UserProfileStatus from './UserProfileStatus'
+import colors from '../../components/colors'
const Home = () => {
return (
-
-
-
+
+
+
)
}
diff --git a/src/screens/home/UserProfileStatus.tsx b/src/screens/home/UserProfileStatus.tsx
index 334030b..2e00de8 100644
--- a/src/screens/home/UserProfileStatus.tsx
+++ b/src/screens/home/UserProfileStatus.tsx
@@ -9,8 +9,8 @@ import {
Alert,
} from "react-native";
import * as Keychain from "react-native-keychain";
-import { base_URL } from "../../utils/ApiUrls";
import { useFocusEffect, useNavigation } from "@react-navigation/native";
+import { BASE_URL } from "../../utils/ApiUrls";
export type ApiResponse = {
message: string;
@@ -43,7 +43,7 @@ const UserProfileStatus: React.FC = () => {
setLoading(true);
setError(null);
- const response = await fetch(`${base_URL}user/profile/status`, {
+ const response = await fetch(`${BASE_URL}user/profile/status`, {
method: "GET",
headers: {
Accept: "*/*",
@@ -154,8 +154,8 @@ export default UserProfileStatus;
const styles = StyleSheet.create({
container: {
flex: 1,
- padding: 16,
- backgroundColor: "#f5f7fb",
+ // padding: 16,
+ // backgroundColor: "#f5f7fb",
},
centered: {
flex: 1,
diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx
index 5805f9e..6399bd9 100644
--- a/src/screens/profile/Profile.tsx
+++ b/src/screens/profile/Profile.tsx
@@ -16,12 +16,12 @@ import {
import * as Keychain from "react-native-keychain";
import { Ionicons } from "@react-native-vector-icons/ionicons";
import { launchImageLibrary } from "react-native-image-picker";
-import { base_URL } from "../../utils/ApiUrls";
import { CustomButton, CustomInput } from "../../components/common/Custom";
import colors from "../../components/colors";
import SwitchNavigator from "./SwitchNavigator";
import { useFocusEffect } from "@react-navigation/native";
import { ApiResponse } from "../home/UserProfileStatus";
+import { BASE_URL } from "../../utils/ApiUrls";
@@ -64,7 +64,7 @@ const Profile = () => {
setFetching(true);
const response = await fetch(
- `${base_URL}user/profile/view`,
+ `${BASE_URL}user/profile/view`,
{
method: "GET",
headers: {
@@ -98,7 +98,7 @@ const Profile = () => {
const token = credentials.password;
const response = await fetch(
- `${base_URL}user/profile/update`,
+ `${BASE_URL}user/profile/update`,
{
method: "PUT",
headers: {
@@ -169,7 +169,7 @@ const Profile = () => {
} as any);
const response = await fetch(
- `${base_URL}user/update/userImg`,
+ `${BASE_URL}user/update/userImg`,
{
method: "PUT",
headers: {
@@ -216,7 +216,7 @@ const Profile = () => {
setLoading(true);
setError(null);
- const response = await fetch(`${base_URL}user/profile/status`, {
+ const response = await fetch(`${BASE_URL}user/profile/status`, {
method: "GET",
headers: {
Accept: "*/*",
@@ -275,7 +275,7 @@ const Profile = () => {
return (
-
+
{/* Header */}
@@ -298,7 +298,7 @@ const Profile = () => {
{/* Profile Image Section */}
{
statusData?.role && statusData?.organizerStatus ?
- Your Role: {statusData.role} | Organizer Status: {statusData.organizerStatus}
+ Your Role: {statusData.role} | Organizer Status: {statusData.organizerStatus}
:
}
@@ -481,7 +481,7 @@ const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.pastelBlue,
- marginBottom: '30%',
+ paddingBottom: '30%',
},
loadingContainer: {
flex: 1,
@@ -500,19 +500,19 @@ const styles = StyleSheet.create({
alignItems: "center",
padding: 16,
paddingTop: 50,
- backgroundColor: "#6366f1",
+ // backgroundColor: "#6366f1",
borderBottomLeftRadius: 20,
borderBottomRightRadius: 20,
- elevation: 4,
- shadowColor: "#000",
- shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.2,
- shadowRadius: 4,
+ // elevation: 4,
+ // shadowColor: "#000",
+ // shadowOffset: { width: 0, height: 2 },
+ // shadowOpacity: 0.2,
+ // shadowRadius: 4,
},
headerTitle: {
fontSize: 22,
fontWeight: "700",
- color: "#fff"
+ color: "#000"
},
refreshButton: {
padding: 4,
diff --git a/src/screens/setting/Setting.tsx b/src/screens/setting/Setting.tsx
new file mode 100644
index 0000000..397133b
--- /dev/null
+++ b/src/screens/setting/Setting.tsx
@@ -0,0 +1,16 @@
+import { StyleSheet, Text, View } from 'react-native'
+import React from 'react'
+import AppView from '../../components/AppView'
+import colors from '../../components/colors'
+
+const Setting = () => {
+ return (
+
+ Setting
+
+ )
+}
+
+export default Setting
+
+const styles = StyleSheet.create({})
\ No newline at end of file
diff --git a/src/utils/ApiUrls.tsx b/src/utils/ApiUrls.tsx
index 6ff1acf..2de0e95 100644
--- a/src/utils/ApiUrls.tsx
+++ b/src/utils/ApiUrls.tsx
@@ -1,3 +1,6 @@
-export const base_URL = "https://api.godemo.in/FundCollect/api/"
-export const LOGIN_URL = `${base_URL}user/generate/otp?mobile=`
-export const VARIFY_OTP_URL = `${base_URL}user/otp/verify`
+export const BASE_URL = "https://api.godemo.in/FundCollect/api/"
+export const LOGIN_URL = `${BASE_URL}user/generate/otp?mobile=`
+export const VARIFY_OTP_URL = `${BASE_URL}user/otp/verify`
+export const ORGRAGISTRATION = `${BASE_URL}organizer/registration/apply`
+export const GROUP_CREATION_URL = `${BASE_URL}group/create`
+export const PROFILE_URL =`${BASE_URL}user/profile/view`