From e3d04ad6545b0b52ad5d8b021e1e7c2041de9be9 Mon Sep 17 00:00:00 2001 From: Diksha Date: Tue, 7 Oct 2025 22:02:45 +0530 Subject: [PATCH] fix: prevent duplicate subject creation by disabling Save while submitting --- src/screens/add-card/AddCard.tsx | 37 ++++-- src/screens/edit-card/EditCard.tsx | 201 ++++++++++++++++------------- 2 files changed, 137 insertions(+), 101 deletions(-) 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',