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() {
|
function createFreshCheckupSession() {
|
||||||
if (!selectedTripItems.length) {
|
if (!selectedTripItems.length) {
|
||||||
setCheckupSession([]);
|
setCheckupSession([]);
|
||||||
@@ -564,6 +602,7 @@ export default function AppRoot() {
|
|||||||
openAddItemModal={openAddItemModal}
|
openAddItemModal={openAddItemModal}
|
||||||
openEditItemModal={openEditItemModal}
|
openEditItemModal={openEditItemModal}
|
||||||
deleteItem={deleteItem}
|
deleteItem={deleteItem}
|
||||||
|
quickSetItemStatus={quickSetItemStatus}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -584,6 +623,7 @@ export default function AppRoot() {
|
|||||||
selectedTripCheckups={selectedTripCheckups}
|
selectedTripCheckups={selectedTripCheckups}
|
||||||
selectedCheckupId={selectedCheckupId}
|
selectedCheckupId={selectedCheckupId}
|
||||||
setSelectedCheckupId={setSelectedCheckupId}
|
setSelectedCheckupId={setSelectedCheckupId}
|
||||||
|
onDeleteCheckup={deleteCheckup}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ function statusAccent(status) {
|
|||||||
return STATUS_COLORS[status] || '#64748b';
|
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 (
|
return (
|
||||||
<View style={styles.itemCard}>
|
<View style={styles.itemCard}>
|
||||||
<View style={[styles.itemAccent, { backgroundColor: statusAccent(item.status) }]} />
|
<View style={[styles.itemAccent, { backgroundColor: statusAccent(item.status) }]} />
|
||||||
@@ -29,6 +32,16 @@ export default function ItemCard({ item, onEdit, onDelete }) {
|
|||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
</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}
|
{item.imageUri ? <Image source={{ uri: item.imageUri }} style={styles.previewImageSmall} /> : null}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -271,6 +271,31 @@ export const styles = StyleSheet.create({
|
|||||||
marginTop: 2,
|
marginTop: 2,
|
||||||
fontSize: 13,
|
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: {
|
answerRow: {
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function CheckupTab({
|
|||||||
<View style={styles.sectionRow}>
|
<View style={styles.sectionRow}>
|
||||||
<Text style={styles.sectionTitle}>Check-Up</Text>
|
<Text style={styles.sectionTitle}>Check-Up</Text>
|
||||||
<Pressable style={styles.secondaryBtnTight} onPress={createFreshCheckupSession}>
|
<Pressable style={styles.secondaryBtnTight} onPress={createFreshCheckupSession}>
|
||||||
<Text style={styles.secondaryBtnText}>Restart</Text>
|
<Text style={styles.secondaryBtnText}>Check-up</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Pressable, Text, View } from 'react-native';
|
import { Alert, Pressable, Text, View } from 'react-native';
|
||||||
import { styles } from '../styles';
|
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 (
|
return (
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<Text style={styles.sectionTitle}>History</Text>
|
<Text style={styles.sectionTitle}>History</Text>
|
||||||
@@ -13,13 +30,17 @@ export default function HistoryTab({ selectedTrip, selectedTripCheckups, selecte
|
|||||||
|
|
||||||
{selectedTripCheckups.map((checkup) => (
|
{selectedTripCheckups.map((checkup) => (
|
||||||
<View key={checkup.id} style={styles.cardSoft}>
|
<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.cardTitle}>{new Date(checkup.createdAt).toLocaleString()}</Text>
|
||||||
<Text style={styles.cardMeta}>
|
<Text style={styles.cardMeta}>
|
||||||
{checkup.snapshot.length} items · correct: {checkup.stats?.correct ?? checkup.snapshot.filter((x) => x.result === 'correct').length} · bad:{' '}
|
{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}
|
{checkup.stats?.bad ?? checkup.snapshot.filter((x) => x.result === 'bad').length}
|
||||||
</Text>
|
</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>
|
</Pressable>
|
||||||
|
|
||||||
{selectedCheckupId === checkup.id ? (
|
{selectedCheckupId === checkup.id ? (
|
||||||
|
|||||||
@@ -3,7 +3,14 @@ import { Pressable, Text, View } from 'react-native';
|
|||||||
import ItemCard from '../components/ItemCard';
|
import ItemCard from '../components/ItemCard';
|
||||||
import { styles } from '../styles';
|
import { styles } from '../styles';
|
||||||
|
|
||||||
export default function ItemsTab({ selectedTrip, selectedTripItems, openAddItemModal, openEditItemModal, deleteItem }) {
|
export default function ItemsTab({
|
||||||
|
selectedTrip,
|
||||||
|
selectedTripItems,
|
||||||
|
openAddItemModal,
|
||||||
|
openEditItemModal,
|
||||||
|
deleteItem,
|
||||||
|
quickSetItemStatus,
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<View style={styles.sectionRow}>
|
<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.length === 0 && selectedTrip ? <Text style={styles.muted}>No items yet.</Text> : null}
|
||||||
|
|
||||||
{selectedTripItems.map((item) => (
|
{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>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user