diff --git a/src/screens/add-card/AddCard.tsx b/src/screens/add-card/AddCard.tsx
index a86b2f5..5ee2ac0 100644
--- a/src/screens/add-card/AddCard.tsx
+++ b/src/screens/add-card/AddCard.tsx
@@ -113,6 +113,8 @@ const AddCard: React.FC = ({ navigation }: any) => {
limitType: 'with-absent',
defaultClassroom: '',
});
+ // Prevent duplicate submissions when user taps Save repeatedly
+ const [isSubmitting, setIsSubmitting] = useState(false);
const setSelectedColor = (color: string) => {
setCard(prev => ({
...prev,
@@ -296,17 +298,22 @@ const AddCard: React.FC = ({ navigation }: any) => {
setCard(newCard);
};
- const handleSubmit = () => {
+ const handleSubmit = async () => {
+ if (isSubmitting) return;
+ setIsSubmitting(true);
if (!card.title) {
Alert.alert('Error', 'Please Enter Course Title!');
+ setIsSubmitting(false);
return;
}
if (card.total < card.present) {
Alert.alert('Error', 'total should be >= present');
+ setIsSubmitting(false);
return;
}
if (card.target_percentage > 100 || card.target_percentage < 0) {
Alert.alert('Error', 'Target Percentage should be between 0 and 100');
+ setIsSubmitting(false);
return;
}
@@ -353,12 +360,15 @@ const AddCard: React.FC = ({ navigation }: any) => {
...finalCard,
markedAt: newMarkings,
};
- addCard(activeRegister, markedCard);
-
- navigation.goBack();
-
- if (Platform.OS === 'android') {
- ToastAndroid.show('New Course Added', ToastAndroid.SHORT);
+ try {
+ addCard(activeRegister, markedCard);
+ if (Platform.OS === 'android') {
+ ToastAndroid.show('New Course Added', ToastAndroid.SHORT);
+ }
+ navigation.goBack();
+ } finally {
+ // If the component unmounts after navigation.goBack, setting state is harmless.
+ setIsSubmitting(false);
}
};
@@ -381,11 +391,15 @@ const AddCard: React.FC = ({ navigation }: any) => {
: registerName}
-
+
Clear
-
- Save
+
+ {isSubmitting ? 'Saving...' : 'Save'}
@@ -676,6 +690,9 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
+ saveCardDisabled: {
+ opacity: 0.6,
+ },
container: {
flex: 1,
backgroundColor: '#18181B',
diff --git a/src/screens/edit-card/EditCard.tsx b/src/screens/edit-card/EditCard.tsx
index 8f8b2e1..89c18ad 100644
--- a/src/screens/edit-card/EditCard.tsx
+++ b/src/screens/edit-card/EditCard.tsx
@@ -115,101 +115,117 @@ const EditCard: React.FC = ({ navigation, route }: any) => {
limitType: 'with-absent',
defaultClassroom: '',
});
- const [presents, setPresents] = useState(0);
- const [totals, setTotals] = useState(0);
- useEffect(() => {
- const currCard = registers[card_register]?.cards?.find(
- curr => curr.id === card_id,
- );
- if (currCard) {
- setCard(currCard);
- setPresents(currCard.present);
- setTotals(currCard.total);
+ // Prevent duplicate submissions when user taps Save repeatedly
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const handleSubmit = () => {
+ if (isSubmitting) return;
+ setIsSubmitting(true);
+ if (!card.title) {
+ Alert.alert('Error', 'Please Enter Course Title!');
+ setIsSubmitting(false);
+ return;
+ }
+ if (card.total < card.present) {
+ Alert.alert('Error', 'total should be >= present');
+ setIsSubmitting(false);
+ return;
+ }
+ if (card.target_percentage > 100 || card.target_percentage < 0) {
+ Alert.alert('Error', 'Target Percentage should be between 0 and 100');
+ setIsSubmitting(false);
+ return;
}
- }, [card_register, card_id, registers]);
- const setSelectedColor = (color: string) => {
- setCard(prev => ({
- ...prev,
- tagColor: color,
- }));
- };
- const handleInputChange = (
- field: keyof CardInterface,
- value: string | number,
- ) => {
- setCard(prev => ({
- ...prev,
- [field]: value,
- }));
- };
+ // Create updated card with classroom info applied to all slots
+ const createUpdatedCardWithClassroom = (baseCard: CardInterface) => {
+ if (!currDayTime.classroom.trim()) return baseCard;
- const handleLimitToggle = (value: boolean) => {
- setCard(prev => ({
- ...prev,
- hasLimit: value,
- }));
- };
- const handleFreqUpdate = (value: number) => {
- setCard(prev => ({
- ...prev,
- limit: value,
- }));
- };
- const handleLimitType = (value: boolean) => {
- setCard(prev => ({
- ...prev,
- limitType: value === true ? 'with-absent' : 'without-absent',
- }));
- };
- const handleDayChange = (day: keyof Days) => {
- setCurrDayTime(prev => ({
- ...prev,
- day,
- }));
- };
+ const updatedDays = { ...baseCard.days };
+ let hasSlots = false;
- const isValidTime = (time: string) => {
- // check format HH:MM and not alphabets
- if (!/^\d{2}:\d{2}$/.test(time)) {
- return false;
- }
+ // Check if there are any slots and update them
+ Object.keys(updatedDays).forEach(day => {
+ if (updatedDays[day as keyof Days].length > 0) {
+ hasSlots = true;
+ updatedDays[day as keyof Days] = updatedDays[day as keyof Days].map(slot => ({
+ ...slot,
+ roomName: currDayTime.classroom.trim() || slot.roomName,
+ }));
+ }
+ });
- // check if hours and minutes are in valid range
- const [hours, minutes] = time.split(':').map(Number);
- if (hours < 1 || hours > 12) {
- return false;
- }
- if (minutes < 0 || minutes > 59) {
- return false;
- }
- return true;
- };
+ // If no slots exist, store as defaultClassroom
+ return {
+ ...baseCard,
+ days: updatedDays,
+ defaultClassroom: currDayTime.classroom.trim()
+ };
+ };
- const handleAddTime = () => {
- if (currDayTime.startTime === '00:00' && currDayTime.endTime === '00:00') {
- Alert.alert('Error', 'Please fill Correct Time!');
- return;
- }
- if (card.days[currDayTime.day]?.length >= 3) {
- Alert.alert('Error', 'Maximum 3 Slots Allowed on a single Day!');
- return;
- }
- const isNew = card.days[currDayTime.day].findIndex(
- dayTime =>
- dayTime.start ===
- convertTo24Hrs(currDayTime.startTime, currDayTime.isAM_start) &&
- dayTime.end ===
- convertTo24Hrs(currDayTime.endTime, currDayTime.isAM_end),
- );
- if (isNew !== -1) {
- Alert.alert('Error', 'Slot already exists!');
- return;
+ const finalCard = createUpdatedCardWithClassroom(card);
+
+ if (card.present !== presents || card.total !== totals) {
+ Alert.alert(
+ 'Save Changes',
+ 'Changing attendance markings will delete the current attendance dates. Are you sure you want to continue?',
+ [
+ {
+ text: 'Cancel',
+ style: 'cancel',
+ onPress: () => {
+ setIsSubmitting(false);
+ },
+ },
+ {
+ text: 'OK',
+ onPress: () => {
+ try {
+ let newMarkings: Markings[] = [];
+ for (let i = 0; i < card.present; i++) {
+ newMarkings.push({
+ id: i,
+ date: new Date().toString(),
+ isPresent: true,
+ });
+ }
+ for (let i = card.present; i < card.total; i++) {
+ newMarkings.push({
+ id: i + 1,
+ date: new Date().toString(),
+ isPresent: false,
+ });
+ }
+ const editedCard: CardInterface = {
+ ...finalCard,
+ markedAt: newMarkings,
+ };
+
+ editCard(card_register, editedCard, card_id);
+
+ if (Platform.OS === 'android') {
+ ToastAndroid.show('Changes Saved', ToastAndroid.SHORT);
+ }
+ navigation.goBack();
+ } finally {
+ setIsSubmitting(false);
+ }
+ },
+ },
+ ],
+ { cancelable: false },
+ );
+ } else {
+ try {
+ editCard(card_register, finalCard, card_id);
+
+ if (Platform.OS === 'android') {
+ ToastAndroid.show('Changes Saved', ToastAndroid.SHORT);
+ }
+ navigation.goBack();
+ } finally {
+ setIsSubmitting(false);
+ }
}
- if (
- !isValidTime(currDayTime.startTime) ||
- !isValidTime(currDayTime.endTime)
- ) {
Alert.alert('Error', 'Please Fill Correct Time in HH:MM Format!');
return;
}
@@ -436,11 +452,11 @@ const EditCard: React.FC = ({ navigation, route }: any) => {
: registerName}
-
+
Clear
-
- Save
+
+ {isSubmitting ? 'Saving...' : 'Save'}
@@ -653,6 +669,9 @@ const EditCard: React.FC = ({ navigation, route }: any) => {
alignItems: 'center',
justifyContent: 'center',
},
+ saveCardDisabled: {
+ opacity: 0.6,
+ },
functionButtons: {
marginLeft: 'auto',
flexDirection: 'row',