feat: add history long-press delete and quick item status actions
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user