import React, { useEffect, useRef, useState } from 'react';
import { Image, KeyboardAvoidingView, Modal, Platform, Pressable, ScrollView, Text, TextInput, View } from 'react-native';
import Ionicons from '@expo/vector-icons/Ionicons';
import { ITEM_PLACEMENTS, ITEM_STATUSES } from '../constants';
import Field from '../components/Field';
import { styles } from '../styles';
import { cn } from '../utils/cn';
const CATEGORY_OPTIONS = ['toiletries', 'electronics', 'documents', 'outfits', 'accessories', 'other'];
const PRESET_CATEGORIES = CATEGORY_OPTIONS.filter((option) => option !== 'other');
const IMAGE_QUALITY_OPTIONS = ['low', 'balanced', 'high'];
function normalizeValue(value) {
return (value || '').trim().toLowerCase();
}
function optionLabel(value) {
return (value || '').replace(/-/g, ' ');
}
function SelectField({ value, placeholder, onPress }) {
return (
{value ? optionLabel(value) : placeholder}
);
}
function SelectPopupModal({ visible, title, options, value, onSelect, onClose }) {
if (!visible) return null;
return (
{title}
Close
{options.map((option, index) => {
const active = value === option;
const isLast = index === options.length - 1;
return (
onSelect(option)}
>
{optionLabel(option)}
);
})}
);
}
function qualityValue(level) {
if (level === 'high') return 0.95;
if (level === 'low') return 0.45;
return 0.75;
}
export default function ItemModal({
visible,
itemForm,
previousCustomPlacements,
setItemModalVisible,
updateItemForm,
pickItemImage,
takeItemImage,
saveItemFromModal,
}) {
const [categoryCustomMode, setCategoryCustomMode] = useState(false);
const [placementCustomMode, setPlacementCustomMode] = useState(false);
const [openPicker, setOpenPicker] = useState(null);
const nameInputRef = useRef(null);
const quantityInputRef = useRef(null);
const descriptionInputRef = useRef(null);
const lentToInputRef = useRef(null);
useEffect(() => {
if (!visible) {
setOpenPicker(null);
return;
}
const normalizedCategory = normalizeValue(itemForm.category);
const customCategory = !!normalizedCategory && !PRESET_CATEGORIES.includes(normalizedCategory);
setCategoryCustomMode(customCategory);
setPlacementCustomMode(itemForm.placement === 'other');
setOpenPicker(null);
}, [visible, itemForm.id]);
useEffect(() => {
if (!visible || itemForm.id) return undefined;
const timeout = setTimeout(() => {
nameInputRef.current?.focus?.();
}, 80);
return () => clearTimeout(timeout);
}, [visible, itemForm.id]);
const mediaOptions = {
quality: qualityValue(itemForm.imageQuality),
allowCrop: !!itemForm.imageAllowCrop,
};
const normalizedCategory = normalizeValue(itemForm.category);
const categorySelectValue = PRESET_CATEGORIES.includes(normalizedCategory) ? normalizedCategory : '';
const savedPlacementOptions = (previousCustomPlacements || []).slice(0, 6);
const savedPlacementValue = savedPlacementOptions.includes(itemForm.placementCustom) ? itemForm.placementCustom : '';
const pickerConfig = {
category: {
title: 'Select category',
options: CATEGORY_OPTIONS,
value: categorySelectValue,
onSelect: chooseCategory,
},
status: {
title: 'Select status',
options: ITEM_STATUSES,
value: itemForm.status,
onSelect: chooseStatus,
},
placement: {
title: 'Select placement',
options: ITEM_PLACEMENTS,
value: itemForm.placement,
onSelect: choosePlacement,
},
savedPlacement: {
title: 'Saved custom locations',
options: savedPlacementOptions,
value: savedPlacementValue,
onSelect: chooseSavedPlacement,
},
imageQuality: {
title: 'Image quality',
options: IMAGE_QUALITY_OPTIONS,
value: itemForm.imageQuality,
onSelect: chooseImageQuality,
},
};
const activePicker = openPicker ? pickerConfig[openPicker] : null;
function chooseCategory(value) {
if (value === 'other') {
setCategoryCustomMode(true);
if (PRESET_CATEGORIES.includes(normalizedCategory)) {
updateItemForm('category', '');
}
setOpenPicker(null);
return;
}
setCategoryCustomMode(false);
updateItemForm('category', value);
setOpenPicker(null);
}
function chooseStatus(value) {
updateItemForm('status', value);
setOpenPicker(null);
}
function choosePlacement(value) {
if (value === 'other') {
setPlacementCustomMode(true);
updateItemForm('placement', 'other');
setOpenPicker(null);
return;
}
setPlacementCustomMode(false);
updateItemForm('placement', value);
setOpenPicker(null);
}
function chooseSavedPlacement(value) {
updateItemForm('placementCustom', value);
setOpenPicker(null);
}
function chooseImageQuality(value) {
updateItemForm('imageQuality', value);
setOpenPicker(null);
}
function resetCategorySelector() {
setCategoryCustomMode(false);
updateItemForm('category', '');
setOpenPicker(null);
}
function resetPlacementSelector() {
setPlacementCustomMode(false);
updateItemForm('placement', ITEM_PLACEMENTS[0] || 'suitcase');
setOpenPicker(null);
}
return (
{itemForm.id ? 'Update Item' : 'Add Item'}
setItemModalVisible(false)}>
Close
updateItemForm('name', v)}
placeholder="Toothbrush"
placeholderTextColor="#71717a"
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => quantityInputRef.current?.focus?.()}
/>
{
const numeric = (v || '').replace(/[^0-9]/g, '');
updateItemForm('quantity', numeric ? Math.max(1, Number.parseInt(numeric, 10)) : 1);
}}
placeholder="1"
placeholderTextColor="#71717a"
keyboardType="number-pad"
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => descriptionInputRef.current?.focus?.()}
/>
updateItemForm('description', v)}
placeholder="Optional"
placeholderTextColor="#71717a"
returnKeyType={itemForm.status === 'lent-to' ? 'next' : 'done'}
onSubmitEditing={() => {
if (itemForm.status === 'lent-to') {
lentToInputRef.current?.focus?.();
}
}}
/>
{categoryCustomMode ? (
updateItemForm('category', v)}
placeholder="Custom category"
placeholderTextColor="#71717a"
/>
) : (
setOpenPicker('category')}
/>
)}
setOpenPicker('status')}
/>
{placementCustomMode ? (
updateItemForm('placementCustom', v)}
placeholder="bath-kit"
placeholderTextColor="#71717a"
/>
Custom location
{savedPlacementOptions.length ? (
setOpenPicker('savedPlacement')}
/>
) : null}
) : (
setOpenPicker('placement')}
/>
)}
{itemForm.status === 'lent-to' ? (
updateItemForm('lentTo', v)}
placeholder="Person name"
placeholderTextColor="#71717a"
returnKeyType="done"
/>
) : null}
takeItemImage(mediaOptions)}>
Take photo
pickItemImage(mediaOptions)}>
{itemForm.imageUri ? 'From gallery (change)' : 'From gallery'}
{!!itemForm.imageUri ? (
<>
setOpenPicker('imageQuality')}
/>
updateItemForm('imageAllowCrop', !itemForm.imageAllowCrop)}>
Enable optional crop before save
>
) : null}
{itemForm.id ? 'Save Changes' : 'Add Item'}
activePicker?.onSelect?.(value)}
onClose={() => setOpenPicker(null)}
/>
);
}