feat: add history long-press delete and quick item status actions
Some checks failed
Luggage List Build / build-web (push) Successful in 29s
Luggage List Build / release (push) Has been cancelled
Luggage List Build / build-android (push) Has been cancelled

This commit is contained in:
2026-04-18 14:34:05 +02:00
parent 2e45261354
commit d40bd6a41c
6 changed files with 121 additions and 8 deletions

View File

@@ -358,6 +358,44 @@ export default function AppRoot() {
});
}
function quickSetItemStatus(itemId, status) {
if (!selectedTripId) return;
setData((prev) => {
const items = prev.itemsByTrip[selectedTripId] || [];
return {
...prev,
itemsByTrip: {
...prev.itemsByTrip,
[selectedTripId]: items.map((item) =>
item.id === itemId
? {
...item,
status,
lentTo: status === 'lent-to' ? item.lentTo : '',
updatedAt: Date.now(),
}
: item
),
},
};
});
}
function deleteCheckup(checkupId) {
if (!selectedTripId) return;
setData((prev) => {
const existing = prev.checkupsByTrip[selectedTripId] || [];
return {
...prev,
checkupsByTrip: {
...prev.checkupsByTrip,
[selectedTripId]: existing.filter((checkup) => checkup.id !== checkupId),
},
};
});
setSelectedCheckupId((prev) => (prev === checkupId ? null : prev));
}
function createFreshCheckupSession() {
if (!selectedTripItems.length) {
setCheckupSession([]);
@@ -564,6 +602,7 @@ export default function AppRoot() {
openAddItemModal={openAddItemModal}
openEditItemModal={openEditItemModal}
deleteItem={deleteItem}
quickSetItemStatus={quickSetItemStatus}
/>
)}
@@ -584,6 +623,7 @@ export default function AppRoot() {
selectedTripCheckups={selectedTripCheckups}
selectedCheckupId={selectedCheckupId}
setSelectedCheckupId={setSelectedCheckupId}
onDeleteCheckup={deleteCheckup}
/>
)}
</ScrollView>

View File

@@ -7,7 +7,10 @@ function statusAccent(status) {
return STATUS_COLORS[status] || '#64748b';
}
export default function ItemCard({ item, onEdit, onDelete }) {
export default function ItemCard({ item, onEdit, onDelete, onQuickPack, onQuickUnpack }) {
const isPacked = item.status === 'packed';
const isUnpacked = item.status === 'unpacked';
return (
<View style={styles.itemCard}>
<View style={[styles.itemAccent, { backgroundColor: statusAccent(item.status) }]} />
@@ -29,6 +32,16 @@ export default function ItemCard({ item, onEdit, onDelete }) {
</Pressable>
</View>
</View>
<View style={styles.quickStatusRow}>
<Pressable style={[styles.quickStatusBtn, isPacked && styles.quickStatusBtnActive]} onPress={onQuickPack}>
<Text style={[styles.quickStatusBtnText, isPacked && styles.quickStatusBtnTextActive]}>Pack</Text>
</Pressable>
<Pressable style={[styles.quickStatusBtn, isUnpacked && styles.quickStatusBtnActive]} onPress={onQuickUnpack}>
<Text style={[styles.quickStatusBtnText, isUnpacked && styles.quickStatusBtnTextActive]}>Unpack</Text>
</Pressable>
</View>
{item.imageUri ? <Image source={{ uri: item.imageUri }} style={styles.previewImageSmall} /> : null}
</View>
</View>

View File

@@ -271,6 +271,31 @@ export const styles = StyleSheet.create({
marginTop: 2,
fontSize: 13,
},
quickStatusRow: {
flexDirection: 'row',
gap: 8,
marginTop: 2,
},
quickStatusBtn: {
paddingVertical: 6,
paddingHorizontal: 12,
borderRadius: 999,
borderWidth: 1,
borderColor: '#334155',
backgroundColor: '#0b1220',
},
quickStatusBtnActive: {
borderColor: '#60a5fa',
backgroundColor: '#1d2a3a',
},
quickStatusBtnText: {
color: '#cbd5e1',
fontWeight: '700',
fontSize: 12,
},
quickStatusBtnTextActive: {
color: '#dbeafe',
},
answerRow: {
marginTop: 8,

View File

@@ -15,7 +15,7 @@ export default function CheckupTab({
<View style={styles.sectionRow}>
<Text style={styles.sectionTitle}>Check-Up</Text>
<Pressable style={styles.secondaryBtnTight} onPress={createFreshCheckupSession}>
<Text style={styles.secondaryBtnText}>Restart</Text>
<Text style={styles.secondaryBtnText}>Check-up</Text>
</Pressable>
</View>

View File

@@ -1,8 +1,25 @@
import React from 'react';
import { Pressable, Text, View } from 'react-native';
import { Alert, Pressable, Text, View } from 'react-native';
import { styles } from '../styles';
export default function HistoryTab({ selectedTrip, selectedTripCheckups, selectedCheckupId, setSelectedCheckupId }) {
export default function HistoryTab({
selectedTrip,
selectedTripCheckups,
selectedCheckupId,
setSelectedCheckupId,
onDeleteCheckup,
}) {
function askDelete(checkup) {
Alert.alert('Delete check-up?', 'This snapshot will be removed from history.', [
{ text: 'Cancel', style: 'cancel' },
{
text: 'Delete',
style: 'destructive',
onPress: () => onDeleteCheckup(checkup.id),
},
]);
}
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>History</Text>
@@ -13,13 +30,17 @@ export default function HistoryTab({ selectedTrip, selectedTripCheckups, selecte
{selectedTripCheckups.map((checkup) => (
<View key={checkup.id} style={styles.cardSoft}>
<Pressable onPress={() => setSelectedCheckupId((prev) => (prev === checkup.id ? null : checkup.id))}>
<Pressable
onPress={() => setSelectedCheckupId((prev) => (prev === checkup.id ? null : checkup.id))}
onLongPress={() => askDelete(checkup)}
delayLongPress={280}
>
<Text style={styles.cardTitle}>{new Date(checkup.createdAt).toLocaleString()}</Text>
<Text style={styles.cardMeta}>
{checkup.snapshot.length} items · correct: {checkup.stats?.correct ?? checkup.snapshot.filter((x) => x.result === 'correct').length} · bad:{' '}
{checkup.stats?.bad ?? checkup.snapshot.filter((x) => x.result === 'bad').length}
</Text>
<Text style={styles.cardMeta}>{selectedCheckupId === checkup.id ? 'Tap to collapse' : 'Tap to open'}</Text>
<Text style={styles.cardMeta}>{selectedCheckupId === checkup.id ? 'Tap to collapse' : 'Tap to open'} · long hold to delete</Text>
</Pressable>
{selectedCheckupId === checkup.id ? (

View File

@@ -3,7 +3,14 @@ import { Pressable, Text, View } from 'react-native';
import ItemCard from '../components/ItemCard';
import { styles } from '../styles';
export default function ItemsTab({ selectedTrip, selectedTripItems, openAddItemModal, openEditItemModal, deleteItem }) {
export default function ItemsTab({
selectedTrip,
selectedTripItems,
openAddItemModal,
openEditItemModal,
deleteItem,
quickSetItemStatus,
}) {
return (
<View style={styles.section}>
<View style={styles.sectionRow}>
@@ -17,7 +24,14 @@ export default function ItemsTab({ selectedTrip, selectedTripItems, openAddItemM
{selectedTripItems.length === 0 && selectedTrip ? <Text style={styles.muted}>No items yet.</Text> : null}
{selectedTripItems.map((item) => (
<ItemCard key={item.id} item={item} onEdit={openEditItemModal} onDelete={deleteItem} />
<ItemCard
key={item.id}
item={item}
onEdit={openEditItemModal}
onDelete={deleteItem}
onQuickPack={() => quickSetItemStatus(item.id, 'packed')}
onQuickUnpack={() => quickSetItemStatus(item.id, 'unpacked')}
/>
))}
</View>
);