Fix Edit navigation from menu + add avatar upload indicator
- BeneficiaryMenu: Navigate with ?edit=true param to open edit modal - Beneficiary index: Auto-open edit modal when edit=true in URL - Add loading indicator on Save button during edit save - Add "Uploading..." overlay on avatar during image upload
This commit is contained in:
parent
7105bb72f7
commit
429a18d1eb
@ -54,7 +54,7 @@ const getDashboardUrl = (deploymentId?: number) => {
|
||||
const FERDINAND_DEPLOYMENT_ID = 21;
|
||||
|
||||
export default function BeneficiaryDetailScreen() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const { id, edit } = useLocalSearchParams<{ id: string; edit?: string }>();
|
||||
const { setCurrentBeneficiary } = useBeneficiary();
|
||||
const toast = useToast();
|
||||
|
||||
@ -74,6 +74,8 @@ export default function BeneficiaryDetailScreen() {
|
||||
// Edit modal state
|
||||
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
|
||||
const [editForm, setEditForm] = useState({ name: '', address: '', avatar: undefined as string | undefined });
|
||||
const [isSavingEdit, setIsSavingEdit] = useState(false);
|
||||
const [isUploadingAvatar, setIsUploadingAvatar] = useState(false);
|
||||
|
||||
// Avatar lightbox state
|
||||
const [lightboxVisible, setLightboxVisible] = useState(false);
|
||||
@ -193,6 +195,15 @@ export default function BeneficiaryDetailScreen() {
|
||||
loadBeneficiary();
|
||||
}, [loadBeneficiary]);
|
||||
|
||||
// Auto-open edit modal if navigated with ?edit=true parameter
|
||||
useEffect(() => {
|
||||
if (edit === 'true' && beneficiary && !isLoading && !isEditModalVisible) {
|
||||
handleEditPress();
|
||||
// Clear the edit param to prevent re-opening on future navigations
|
||||
router.setParams({ edit: undefined });
|
||||
}
|
||||
}, [edit, beneficiary, isLoading, isEditModalVisible]);
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
setIsRefreshing(true);
|
||||
loadBeneficiary(false);
|
||||
@ -235,6 +246,7 @@ export default function BeneficiaryDetailScreen() {
|
||||
}
|
||||
|
||||
const beneficiaryId = parseInt(id, 10);
|
||||
setIsSavingEdit(true);
|
||||
|
||||
try {
|
||||
// Update basic info
|
||||
@ -245,12 +257,15 @@ export default function BeneficiaryDetailScreen() {
|
||||
|
||||
if (!response.ok) {
|
||||
toast.error('Error', response.error?.message || 'Failed to save changes.');
|
||||
setIsSavingEdit(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload avatar if changed (new local file URI)
|
||||
if (editForm.avatar && editForm.avatar.startsWith('file://')) {
|
||||
setIsUploadingAvatar(true);
|
||||
const avatarResult = await api.updateBeneficiaryAvatar(beneficiaryId, editForm.avatar);
|
||||
setIsUploadingAvatar(false);
|
||||
if (!avatarResult.ok) {
|
||||
console.warn('[BeneficiaryDetail] Failed to upload avatar:', avatarResult.error?.message);
|
||||
// Show info but don't fail the whole operation
|
||||
@ -263,6 +278,9 @@ export default function BeneficiaryDetailScreen() {
|
||||
loadBeneficiary(false);
|
||||
} catch (err) {
|
||||
toast.error('Error', 'Failed to save changes.');
|
||||
} finally {
|
||||
setIsSavingEdit(false);
|
||||
setIsUploadingAvatar(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -475,7 +493,11 @@ export default function BeneficiaryDetailScreen() {
|
||||
|
||||
<ScrollView style={styles.modalContent}>
|
||||
{/* Avatar */}
|
||||
<TouchableOpacity style={styles.avatarPicker} onPress={handlePickAvatar}>
|
||||
<TouchableOpacity
|
||||
style={styles.avatarPicker}
|
||||
onPress={handlePickAvatar}
|
||||
disabled={isSavingEdit}
|
||||
>
|
||||
{editForm.avatar ? (
|
||||
<Image source={{ uri: editForm.avatar }} style={styles.avatarPickerImage} />
|
||||
) : (
|
||||
@ -483,9 +505,17 @@ export default function BeneficiaryDetailScreen() {
|
||||
<Ionicons name="camera" size={32} color={AppColors.textMuted} />
|
||||
</View>
|
||||
)}
|
||||
{isUploadingAvatar && (
|
||||
<View style={styles.avatarUploadOverlay}>
|
||||
<ActivityIndicator size="large" color={AppColors.white} />
|
||||
<Text style={styles.avatarUploadText}>Uploading...</Text>
|
||||
</View>
|
||||
)}
|
||||
{!isUploadingAvatar && (
|
||||
<View style={styles.avatarPickerBadge}>
|
||||
<Ionicons name="pencil" size={12} color={AppColors.white} />
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* Name */}
|
||||
@ -517,13 +547,22 @@ export default function BeneficiaryDetailScreen() {
|
||||
|
||||
<View style={styles.modalFooter}>
|
||||
<TouchableOpacity
|
||||
style={styles.cancelButton}
|
||||
style={[styles.cancelButton, isSavingEdit && styles.buttonDisabled]}
|
||||
onPress={() => setIsEditModalVisible(false)}
|
||||
disabled={isSavingEdit}
|
||||
>
|
||||
<Text style={styles.cancelButtonText}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.saveButton} onPress={handleSaveEdit}>
|
||||
<TouchableOpacity
|
||||
style={[styles.saveButton, isSavingEdit && styles.buttonDisabled]}
|
||||
onPress={handleSaveEdit}
|
||||
disabled={isSavingEdit}
|
||||
>
|
||||
{isSavingEdit ? (
|
||||
<ActivityIndicator size="small" color={AppColors.white} />
|
||||
) : (
|
||||
<Text style={styles.saveButtonText}>Save</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
@ -714,6 +753,18 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 2,
|
||||
borderColor: AppColors.surface,
|
||||
},
|
||||
avatarUploadOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||
borderRadius: AvatarSizes.lg / 2,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
avatarUploadText: {
|
||||
color: AppColors.white,
|
||||
fontSize: FontSizes.sm,
|
||||
marginTop: Spacing.xs,
|
||||
},
|
||||
inputGroup: {
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
@ -765,4 +816,7 @@ const styles = StyleSheet.create({
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.white,
|
||||
},
|
||||
buttonDisabled: {
|
||||
opacity: 0.6,
|
||||
},
|
||||
});
|
||||
|
||||
@ -68,8 +68,8 @@ export function BeneficiaryMenu({
|
||||
if (onEdit) {
|
||||
onEdit();
|
||||
} else {
|
||||
// Navigate to main page with edit intent
|
||||
router.push(`/(tabs)/beneficiaries/${beneficiaryId}`);
|
||||
// Navigate to main page with edit=true param to open edit modal
|
||||
router.push(`/(tabs)/beneficiaries/${beneficiaryId}?edit=true`);
|
||||
}
|
||||
break;
|
||||
case 'access':
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user