diff --git a/app/(tabs)/beneficiaries/[id]/index.tsx b/app/(tabs)/beneficiaries/[id]/index.tsx index 3296d25..c6a12c5 100644 --- a/app/(tabs)/beneficiaries/[id]/index.tsx +++ b/app/(tabs)/beneficiaries/[id]/index.tsx @@ -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() { {/* Avatar */} - + {editForm.avatar ? ( ) : ( @@ -483,9 +505,17 @@ export default function BeneficiaryDetailScreen() { )} - - - + {isUploadingAvatar && ( + + + Uploading... + + )} + {!isUploadingAvatar && ( + + + + )} {/* Name */} @@ -517,13 +547,22 @@ export default function BeneficiaryDetailScreen() { setIsEditModalVisible(false)} + disabled={isSavingEdit} > Cancel - - Save + + {isSavingEdit ? ( + + ) : ( + Save + )} @@ -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, + }, }); diff --git a/components/ui/BeneficiaryMenu.tsx b/components/ui/BeneficiaryMenu.tsx index d9451bb..fff36cc 100644 --- a/components/ui/BeneficiaryMenu.tsx +++ b/components/ui/BeneficiaryMenu.tsx @@ -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':