import { BottomSheet, BottomSheetTextInput } from '@/aesthetics/BottomSheet'; import { Text } from '@/aesthetics/Text'; import { buttonHeight, font, radius, spacing, useColors } from '@/aesthetics/styles'; import { useHaptics } from '@/aesthetics/useHaptics'; import { useStrings } from '@/common/hooks/useStrings'; import { useState, useCallback } from 'react'; import { Pressable, StyleSheet, View } from 'react-native'; import { X } from 'lucide-react-native'; import { TYPE_OPTION_KEYS, TYPE_ICONS, NUMBER_UNITS } from './constants'; import type { InputType, MemoLogger, MemoLoggerOption } from './types'; type Props = { visible: boolean; onClose: () => void; onCreate: (logger: Omit) => void; onUpdate?: (id: string, updates: Partial>) => void; onOpenIconPicker: (index: number, onSelect: (icon: string) => void) => void; logger?: MemoLogger; }; export function MemoLoggerEditor({ visible, onClose, onCreate, onUpdate, onOpenIconPicker, logger, }: Props) { const c = useColors(); const strings = useStrings(); const { selection } = useHaptics(); const typeLabels: Record = { singleselect: strings.userCustomLogs.types.select, multiselect: strings.userCustomLogs.types.multi, text: strings.userCustomLogs.types.text, number: strings.userCustomLogs.types.number, check: strings.userCustomLogs.types.check, }; const createInitialOptions = (): MemoLoggerOption[] => [ { label: strings.userCustomLogs.optionPlaceholder(1), icon: '1️⃣' }, { label: strings.userCustomLogs.optionPlaceholder(2), icon: '2️⃣' }, { label: strings.userCustomLogs.optionPlaceholder(3), icon: '3️⃣' }, ]; const [name, setName] = useState(''); const [icon, setIcon] = useState(); const [type, setType] = useState('text'); const [options, setOptions] = useState(createInitialOptions); const [unit, setUnit] = useState(''); const [unitType, setUnitType] = useState('plain'); const isEditing = !!logger; const [prevLogger, setPrevLogger] = useState(logger); if (logger !== prevLogger) { setPrevLogger(logger); if (logger) { setName(logger.name); setIcon(logger.icon); setType(logger.type); setOptions(logger.options?.length ? logger.options : createInitialOptions()); setUnit(logger.unit ?? ''); setUnitType(logger.unit ? 'custom' : 'plain'); } } const reset = () => { setName(''); setIcon(undefined); setType('text'); setOptions(createInitialOptions()); setUnit(''); setUnitType('plain'); }; const handleClose = () => { reset(); onClose(); }; const handleSave = () => { if (!name.trim()) return; const isSelect = type === 'singleselect' || type === 'multiselect'; const data = { name: name.trim(), icon, type, unit: type === 'number' ? unit || undefined : undefined, options: isSelect ? options : undefined, }; if (isEditing && onUpdate) { onUpdate(logger.id, data); } else { onCreate(data); } reset(); }; const handleLoggerIconPress = useCallback(() => { onOpenIconPicker(-1, setIcon); }, [onOpenIconPicker]); const handleIconPress = useCallback( (index: number) => { onOpenIconPicker(index, (icon: string) => { setOptions(prev => { const updated = [...prev]; updated[index] = { ...updated[index], icon }; return updated; }); }); }, [onOpenIconPicker] ); const updateOptionLabel = (index: number, label: string) => { setOptions(prev => { const updated = [...prev]; updated[index] = { ...updated[index], label }; return updated; }); }; const addOption = () => { setOptions(prev => [ ...prev, { label: strings.userCustomLogs.optionPlaceholder(prev.length + 1), icon: '⭐' }, ]); }; const removeOption = (index: number) => { setOptions(prev => prev.filter((_, i) => i !== index)); }; const selectUnit = (id: string, unitValue: string) => { setUnitType(id); setUnit(unitValue); }; const isSelect = type === 'singleselect' || type === 'multiselect'; return ( {strings.userCustomLogs.loggerName} {icon || '📝'} {strings.userCustomLogs.type} {TYPE_OPTION_KEYS.map(typeKey => { const Icon = TYPE_ICONS[typeKey]; const selected = type === typeKey; return ( { selection(); setType(typeKey); }} style={[styles.typeOption, { backgroundColor: selected ? c.accent : c.tagBg }]} > {typeLabels[typeKey]} ); })} {isSelect && ( {options.map((opt, i) => ( handleIconPress(i)} style={[styles.iconBtn, { backgroundColor: c.tagBg }]} > {opt.icon} updateOptionLabel(i, text)} placeholder={strings.userCustomLogs.optionPlaceholder(i + 1)} placeholderTextColor={c.tertiaryText} /> {options.length > 2 && ( removeOption(i)} style={styles.removeBtn}> )} ))} {options.length < 10 && ( {strings.userCustomLogs.addOption} )} )} {type === 'number' && ( <> {strings.userCustomLogs.unit} {NUMBER_UNITS.map(u => ( { selection(); selectUnit(u.id, u.unit); }} style={[ styles.unitChip, { backgroundColor: unitType === u.id ? c.accent : c.tagBg }, ]} > {u.label} ))} )} {isEditing ? strings.userCustomLogs.saveLogger : strings.userCustomLogs.createLogger} ); } const styles = StyleSheet.create({ container: { gap: spacing.lg }, label: { fontSize: font.md, fontWeight: '600', marginBottom: -spacing.sm }, nameRow: { flexDirection: 'row', gap: spacing.sm }, loggerIconBtn: { width: 48, height: 48, borderRadius: radius.md, alignItems: 'center', justifyContent: 'center', }, loggerIconText: { fontSize: 24 }, input: { paddingVertical: spacing.md, paddingHorizontal: spacing.lg, borderRadius: radius.md, fontSize: font.md, }, nameInput: { flex: 1 }, typeRow: { flexDirection: 'row', gap: spacing.xs }, typeOption: { flex: 1, minHeight: buttonHeight.lg, paddingVertical: spacing.sm, borderRadius: radius.md, alignItems: 'center', justifyContent: 'center', gap: 4, }, typeOptionLabel: { fontSize: font.xs, fontWeight: '500' }, optionsEditor: { gap: spacing.sm }, optionRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm }, iconBtn: { width: 40, height: 40, borderRadius: radius.md, alignItems: 'center', justifyContent: 'center', }, iconBtnText: { fontSize: 20 }, optionInput: { flex: 1, paddingVertical: spacing.sm, paddingHorizontal: spacing.md, borderRadius: radius.md, fontSize: font.sm, }, removeBtn: { padding: spacing.sm }, addOptionBtn: { paddingVertical: spacing.sm, paddingHorizontal: spacing.md, borderRadius: radius.md, alignItems: 'center', }, unitRow: { flexDirection: 'row', gap: spacing.sm }, unitChip: { flex: 1, minHeight: buttonHeight.sm, paddingVertical: spacing.sm, borderRadius: radius.md, alignItems: 'center', justifyContent: 'center', }, unitLabel: { fontSize: font.sm, fontWeight: '600' }, createBtn: { minHeight: buttonHeight.md, paddingVertical: spacing.md, borderRadius: radius.md, alignItems: 'center', justifyContent: 'center', }, createLabel: { fontSize: font.md, fontWeight: '600' }, disabled: { opacity: 0.4 }, });