feat: add trip edit/archive flow and item bulk filters
This commit is contained in:
114
src/AppRoot.js
114
src/AppRoot.js
@@ -83,7 +83,9 @@ export default function AppRoot() {
|
||||
|
||||
const topInset = Platform.OS === 'android' ? (RNStatusBar.currentHeight || 0) + 10 : 0;
|
||||
|
||||
const selectedTrip = useMemo(() => data.trips.find((trip) => trip.id === selectedTripId) || null, [data.trips, selectedTripId]);
|
||||
const visibleTrips = useMemo(() => data.trips.filter((trip) => !trip.archived), [data.trips]);
|
||||
|
||||
const selectedTrip = useMemo(() => visibleTrips.find((trip) => trip.id === selectedTripId) || null, [visibleTrips, selectedTripId]);
|
||||
|
||||
const selectedTripItems = useMemo(() => {
|
||||
if (!selectedTripId) return [];
|
||||
@@ -139,18 +141,18 @@ export default function AppRoot() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!loaded) return;
|
||||
if (!data.trips.length) {
|
||||
if (!visibleTrips.length) {
|
||||
setSelectedTripId(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedTripId && data.trips.some((trip) => trip.id === selectedTripId)) {
|
||||
if (selectedTripId && visibleTrips.some((trip) => trip.id === selectedTripId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bestTripId = findBestTripId(data.trips);
|
||||
setSelectedTripId(bestTripId || data.trips[0].id);
|
||||
}, [data.trips, selectedTripId, loaded]);
|
||||
const bestTripId = findBestTripId(visibleTrips);
|
||||
setSelectedTripId(bestTripId || visibleTrips[0].id);
|
||||
}, [visibleTrips, selectedTripId, loaded]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tab !== 'checkup') return;
|
||||
@@ -244,6 +246,7 @@ export default function AppRoot() {
|
||||
startDate: tripForm.startDate,
|
||||
endDate: tripForm.endDate,
|
||||
imageUri: tripForm.imageUri,
|
||||
archived: false,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
@@ -278,6 +281,62 @@ export default function AppRoot() {
|
||||
setData((prev) => ({ ...prev, defaultTemplateTripId: tripId }));
|
||||
}
|
||||
|
||||
function saveTripEdits(tripId, patch) {
|
||||
if (!patch.name.trim()) {
|
||||
Alert.alert('Missing name', 'Trip name is required.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const start = parseYMD(patch.startDate);
|
||||
const end = parseYMD(patch.endDate);
|
||||
|
||||
if (!start || !end) {
|
||||
Alert.alert('Invalid dates', 'Please select valid trip dates.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (start > end) {
|
||||
Alert.alert('Invalid dates', 'Start date cannot be after end date.');
|
||||
return false;
|
||||
}
|
||||
|
||||
setData((prev) => ({
|
||||
...prev,
|
||||
trips: prev.trips.map((trip) =>
|
||||
trip.id === tripId
|
||||
? {
|
||||
...trip,
|
||||
name: patch.name.trim(),
|
||||
location: patch.location.trim(),
|
||||
startDate: patch.startDate,
|
||||
endDate: patch.endDate,
|
||||
imageUri: patch.imageUri,
|
||||
updatedAt: Date.now(),
|
||||
}
|
||||
: trip
|
||||
),
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function setTripArchived(tripId, archived) {
|
||||
setData((prev) => ({
|
||||
...prev,
|
||||
trips: prev.trips.map((trip) =>
|
||||
trip.id === tripId
|
||||
? {
|
||||
...trip,
|
||||
archived,
|
||||
archivedAt: archived ? Date.now() : null,
|
||||
updatedAt: Date.now(),
|
||||
}
|
||||
: trip
|
||||
),
|
||||
defaultTemplateTripId: archived && prev.defaultTemplateTripId === tripId ? null : prev.defaultTemplateTripId,
|
||||
}));
|
||||
}
|
||||
|
||||
function deleteTrip(tripId) {
|
||||
Alert.alert('Delete trip?', 'Trip items and check-up history will also be deleted.', [
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
@@ -406,6 +465,30 @@ export default function AppRoot() {
|
||||
});
|
||||
}
|
||||
|
||||
function bulkSetItemStatus(itemIds, status) {
|
||||
if (!selectedTripId || !itemIds.length) return;
|
||||
const idSet = new Set(itemIds);
|
||||
setData((prev) => {
|
||||
const items = prev.itemsByTrip[selectedTripId] || [];
|
||||
return {
|
||||
...prev,
|
||||
itemsByTrip: {
|
||||
...prev.itemsByTrip,
|
||||
[selectedTripId]: items.map((item) =>
|
||||
idSet.has(item.id)
|
||||
? {
|
||||
...item,
|
||||
status,
|
||||
lentTo: status === 'lent-to' ? item.lentTo : '',
|
||||
updatedAt: Date.now(),
|
||||
}
|
||||
: item
|
||||
),
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function deleteCheckup(checkupId) {
|
||||
if (!selectedTripId) return;
|
||||
setData((prev) => {
|
||||
@@ -624,20 +707,32 @@ export default function AppRoot() {
|
||||
keyboardShouldPersistTaps="handled"
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<TripPicker trips={data.trips} selectedTripId={selectedTripId} onChooseTrip={setSelectedTripId} />
|
||||
<TripPicker trips={visibleTrips} selectedTripId={selectedTripId} onChooseTrip={setSelectedTripId} />
|
||||
|
||||
{tab === 'trips' && (
|
||||
<TripsTab
|
||||
tripForm={tripForm}
|
||||
updateTripForm={updateTripForm}
|
||||
pickTripImage={() => pickImage((uri) => updateTripForm('imageUri', uri))}
|
||||
takeTripImage={() => takeImage((uri) => updateTripForm('imageUri', uri))}
|
||||
pickTripImage={(onPicked) =>
|
||||
pickImage((uri) => {
|
||||
if (typeof onPicked === 'function') onPicked(uri);
|
||||
else updateTripForm('imageUri', uri);
|
||||
})
|
||||
}
|
||||
takeTripImage={(onPicked) =>
|
||||
takeImage((uri) => {
|
||||
if (typeof onPicked === 'function') onPicked(uri);
|
||||
else updateTripForm('imageUri', uri);
|
||||
})
|
||||
}
|
||||
templateTrip={templateTrip}
|
||||
createTrip={createTrip}
|
||||
trips={data.trips}
|
||||
selectedTripId={selectedTripId}
|
||||
chooseTrip={setSelectedTripId}
|
||||
setTripAsTemplate={setTripAsTemplate}
|
||||
saveTripEdits={saveTripEdits}
|
||||
setTripArchived={setTripArchived}
|
||||
deleteTrip={deleteTrip}
|
||||
onInputFocus={onInputFocus}
|
||||
defaultTemplateTripId={data.defaultTemplateTripId}
|
||||
@@ -655,6 +750,7 @@ export default function AppRoot() {
|
||||
openEditItemModal={openEditItemModal}
|
||||
deleteItem={deleteItem}
|
||||
quickSetItemStatus={quickSetItemStatus}
|
||||
bulkSetItemStatus={bulkSetItemStatus}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user