341 lines
11 KiB
JavaScript
341 lines
11 KiB
JavaScript
import React, { useState, useEffect, useCallback } from 'react';
|
|
import { View, Text, FlatList, StyleSheet, Modal, TouchableOpacity, TextInput, Alert } from 'react-native';
|
|
import * as SQLite from 'expo-sqlite';
|
|
import { useFocusEffect } from '@react-navigation/native';
|
|
import { Picker } from '@react-native-picker/picker';
|
|
import { format } from 'date-fns';
|
|
import { it } from 'date-fns/locale';
|
|
import DateTimePicker from '@react-native-community/datetimepicker';
|
|
|
|
export default function TransactionItem() {
|
|
const [transactions, setTransactions] = useState([]);
|
|
const [modalVisible, setModalVisible] = useState(false);
|
|
const [selectedTransaction, setSelectedTransaction] = useState(null);
|
|
const [editDescription, setEditDescription] = useState('');
|
|
const [editAmount, setEditAmount] = useState('');
|
|
const [editCategory, setEditCategory] = useState('');
|
|
const [editDate, setEditDate] = useState(new Date());
|
|
const [editType, setEditType] = useState('expense');
|
|
const [showDatePicker, setShowDatePicker] = useState(false);
|
|
|
|
const loadTransactions = useCallback(async () => {
|
|
try {
|
|
const db = await SQLite.openDatabaseAsync('moneyAppDB');
|
|
const result = await db.getAllAsync('SELECT * FROM transactions ORDER BY date DESC, id DESC');
|
|
setTransactions(result);
|
|
} catch (error) {
|
|
console.error('Error fetching transactions:', error);
|
|
}
|
|
}, []);
|
|
|
|
const sortedTransactions = transactions.sort((a, b) => {
|
|
const dateA = new Date(a.date.split('-').reverse().join('-'));
|
|
const dateB = new Date(b.date.split('-').reverse().join('-'));
|
|
return dateB - dateA;
|
|
});
|
|
|
|
const groupedTransactions = sortedTransactions.reduce((acc, transaction) => {
|
|
const date = new Date(transaction.date.split('-').reverse().join('-'));
|
|
const formattedDate = format(date, 'dd MMMM yyyy', { locale: it });
|
|
if (!acc[formattedDate]) {
|
|
acc[formattedDate] = [];
|
|
}
|
|
acc[formattedDate].push(transaction);
|
|
return acc;
|
|
}, {});
|
|
|
|
|
|
useEffect(() => {
|
|
loadTransactions();
|
|
}, [loadTransactions]);
|
|
|
|
useFocusEffect(
|
|
useCallback(() => {
|
|
loadTransactions();
|
|
}, [loadTransactions])
|
|
);
|
|
|
|
const openModal = (transaction) => {
|
|
setSelectedTransaction(transaction);
|
|
setEditDescription(transaction.description);
|
|
setEditAmount(transaction.amount.toString());
|
|
setEditCategory(transaction.category);
|
|
setEditDate(new Date(transaction.date.split('-').reverse().join('-')));
|
|
setEditType(transaction.type);
|
|
setModalVisible(true);
|
|
};
|
|
|
|
const updateTransaction = async () => {
|
|
try {
|
|
const db = await SQLite.openDatabaseAsync('moneyAppDB');
|
|
await db.runAsync(
|
|
'UPDATE transactions SET description = ?, amount = ?, category = ?, date = ?, type = ? WHERE id = ?',
|
|
[editDescription, parseFloat(editAmount), editCategory, format(editDate, 'dd-MM-yyyy'), editType, selectedTransaction.id]
|
|
);
|
|
setModalVisible(false);
|
|
loadTransactions();
|
|
Alert.alert('Successo', 'Transazione aggiornata con successo');
|
|
} catch (error) {
|
|
console.error('Error updating transaction:', error);
|
|
Alert.alert('Errore', 'Impossibile aggiornare la transazione');
|
|
}
|
|
};
|
|
|
|
const deleteTransaction = async () => {
|
|
Alert.alert(
|
|
'Conferma eliminazione',
|
|
'Sei sicuro di voler eliminare questa transazione?',
|
|
[
|
|
{ text: 'Annulla', style: 'cancel' },
|
|
{
|
|
text: 'Elimina',
|
|
style: 'destructive',
|
|
onPress: async () => {
|
|
try {
|
|
const db = await SQLite.openDatabaseAsync('moneyAppDB');
|
|
await db.runAsync('DELETE FROM transactions WHERE id = ?', [selectedTransaction.id]);
|
|
setModalVisible(false);
|
|
loadTransactions();
|
|
Alert.alert('Successo', 'Transazione eliminata con successo');
|
|
} catch (error) {
|
|
console.error('Error deleting transaction:', error);
|
|
Alert.alert('Errore', 'Impossibile eliminare la transazione');
|
|
}
|
|
}
|
|
}
|
|
]
|
|
);
|
|
};
|
|
|
|
const formatDate = (dateString) => {
|
|
const [day, month, year] = dateString.split('-');
|
|
const date = new Date(`${year}-${month}-${day}`);
|
|
return format(date, 'dd MMMM yyyy', { locale: it });
|
|
};
|
|
|
|
const sortedGroupedTransactions = Object.keys(groupedTransactions)
|
|
.sort((a, b) => new Date(b.split(' ').reverse().join(' ')) - new Date(a.split(' ').reverse().join(' ')))
|
|
.reduce((acc, key) => {
|
|
acc[key] = groupedTransactions[key];
|
|
return acc;
|
|
}, {});
|
|
|
|
const renderItem = ({ item }) => (
|
|
<TouchableOpacity onPress={() => openModal(item)} style={styles.item}>
|
|
<View>
|
|
<Text style={styles.description}>{item.description}</Text>
|
|
<Text style={styles.date}>{item.date}</Text>
|
|
</View>
|
|
<Text style={[styles.amount, { color: item.type === 'expense' ? 'red' : 'green' }]}>
|
|
{item.type === 'expense' ? '-' : '+'}{Math.abs(item.amount).toFixed(2)} €
|
|
</Text>
|
|
</TouchableOpacity>
|
|
);
|
|
|
|
return (
|
|
<><FlatList
|
|
data={Object.keys(groupedTransactions)}
|
|
renderItem={({ item: date }) => (
|
|
<View key={date}>
|
|
<Text style={styles.dateHeader}>{date}</Text>
|
|
<FlatList
|
|
data={groupedTransactions[date]}
|
|
renderItem={renderItem}
|
|
keyExtractor={item => item.id.toString()} />
|
|
</View>
|
|
)}
|
|
keyExtractor={item => item} /><Modal
|
|
animationType="slide"
|
|
transparent={true}
|
|
visible={modalVisible}
|
|
onRequestClose={() => setModalVisible(false)}
|
|
>
|
|
<View style={styles.centeredView}>
|
|
<View style={styles.modalView}>
|
|
<Text style={styles.modalTitle}>Dettagli Transazione</Text>
|
|
|
|
<TextInput
|
|
style={styles.input}
|
|
value={editDescription}
|
|
onChangeText={setEditDescription}
|
|
placeholder="Descrizione" />
|
|
<TextInput
|
|
style={styles.input}
|
|
value={editAmount}
|
|
onChangeText={setEditAmount}
|
|
placeholder="Importo"
|
|
placeholderTextColor="#fff"
|
|
keyboardType="decimal-pad" />
|
|
<View style={{ borderColor: 'gray', borderWidth: 1, marginBottom: 10 }}>
|
|
<Picker
|
|
selectedValue={editCategory}
|
|
style={styles.picker}
|
|
onValueChange={(itemValue) => setEditCategory(itemValue)}
|
|
>
|
|
<Picker.Item label="Spesa" value="spesa" />
|
|
<Picker.Item label="Svago" value="svago" />
|
|
<Picker.Item label="Trasporti" value="trasporti" />
|
|
<Picker.Item label="Cibo" value="cibo" />
|
|
<Picker.Item label="Varie" value="varie" />
|
|
</Picker>
|
|
</View>
|
|
<View style={{ borderColor: 'gray', borderWidth: 1, marginBottom: 10 }}>
|
|
<Picker
|
|
selectedValue={editType}
|
|
style={styles.picker}
|
|
onValueChange={(itemValue) => setEditType(itemValue)}
|
|
>
|
|
<Picker.Item label="Entrata" value="income" />
|
|
<Picker.Item label="Uscita" value="expense" />
|
|
</Picker>
|
|
</View>
|
|
<TouchableOpacity onPress={() => setShowDatePicker(true)}>
|
|
<Text style={styles.input}>{format(editDate, 'dd-MM-yyyy')}</Text>
|
|
</TouchableOpacity>
|
|
{showDatePicker && (
|
|
<DateTimePicker
|
|
value={editDate}
|
|
mode="date"
|
|
display="default"
|
|
onChange={(event, selectedDate) => {
|
|
const currentDate = selectedDate || editDate;
|
|
setShowDatePicker(false);
|
|
setEditDate(currentDate);
|
|
} } />
|
|
)}
|
|
|
|
<View style={styles.buttonContainer}>
|
|
<TouchableOpacity style={styles.button} onPress={updateTransaction}>
|
|
<Text style={styles.buttonText}>Aggiorna</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.button} onPress={deleteTransaction}>
|
|
<Text style={styles.buttonText}>Elimina</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.button} onPress={() => setModalVisible(false)}>
|
|
<Text style={styles.buttonText}>Chiudi</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</Modal></>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
paddingHorizontal: 10,
|
|
marginBottom: 20,
|
|
},
|
|
title: {
|
|
fontSize: 20,
|
|
fontWeight: 'bold',
|
|
marginBottom: 10,
|
|
color: '',
|
|
},
|
|
item: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
padding: 10,
|
|
marginVertical: 5,
|
|
borderRadius: 10,
|
|
backgroundColor: '#1a1a1a',
|
|
},
|
|
description: {
|
|
color: '#fff',
|
|
fontSize: 18,
|
|
fontWeight: 'bold',
|
|
},
|
|
amount: {
|
|
color: 'red',
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
},
|
|
date: {
|
|
color: '#fff',
|
|
fontSize: 14,
|
|
},
|
|
centeredView: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
},
|
|
modalView: {
|
|
margin: 20,
|
|
backgroundColor: '#000',
|
|
borderRadius: 20,
|
|
padding: 35,
|
|
alignItems: 'center',
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 4,
|
|
elevation: 5,
|
|
},
|
|
modalTitle: {
|
|
color: '#fff',
|
|
fontSize: 18,
|
|
fontWeight: 'bold',
|
|
marginBottom: 15,
|
|
},
|
|
input: {
|
|
height: 60,
|
|
width: 302,
|
|
borderColor: 'gray',
|
|
borderWidth: 1,
|
|
marginBottom: 10,
|
|
paddingHorizontal: 15,
|
|
paddingVertical: 15,
|
|
color: '#fff',
|
|
fontSize: 16,
|
|
backgroundColor: '#1a1a1a'
|
|
},
|
|
picker: {
|
|
height: 50,
|
|
width: 300,
|
|
color: '#fff',
|
|
borderColor: 'gray',
|
|
borderWidth: 1,
|
|
backgroundColor: '#1a1a1a'
|
|
},
|
|
buttonContainer: {
|
|
paddingTop: 20,
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
width: '100%',
|
|
},
|
|
button: {
|
|
backgroundColor: '#2196F3',
|
|
borderRadius: 20,
|
|
padding: 10,
|
|
elevation: 2,
|
|
marginTop: 10,
|
|
minWidth: 100,
|
|
},
|
|
deleteButton: {
|
|
backgroundColor: '#FF0000',
|
|
},
|
|
closeButton: {
|
|
marginTop: 20,
|
|
},
|
|
buttonText: {
|
|
color: '#fff',
|
|
fontWeight: 'bold',
|
|
textAlign: 'center',
|
|
},
|
|
date: {
|
|
color: '#8c8c8c',
|
|
fontSize: 14,
|
|
},
|
|
dateHeader: {
|
|
fontSize: 16,
|
|
color: '#fff',
|
|
marginTop: 20,
|
|
marginBottom: 10,
|
|
marginLeft: 5,
|
|
},
|
|
});
|