運搬計画: グループ単位の回間移動・未割当戻し機能を追加

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Akira
2026-03-16 17:04:06 +09:00
parent bba04f24c2
commit 722ac4efd0

View File

@@ -547,6 +547,47 @@ export default function DeliveryEditPage({ planId }: Props) {
removeFieldFromTrip(fieldId, tripTempId); removeFieldFromTrip(fieldId, tripTempId);
}; };
// グループの全圃場を別のtripに移動
const moveGroupToTrip = (groupFieldIds: number[], fromTripTempId: string, toTripTempId: string) => {
setTrips(prev => {
const fromTrip = prev.find(t => t.tempId === fromTripTempId);
if (!fromTrip) return prev;
const fieldIdSet = new Set(groupFieldIds);
const movingItems = fromTrip.items.filter(item => fieldIdSet.has(item.fieldId));
if (movingItems.length === 0) return prev;
return prev.map(t => {
if (t.tempId === fromTripTempId) {
return { ...t, items: t.items.filter(item => !fieldIdSet.has(item.fieldId)) };
}
if (t.tempId === toTripTempId) {
const newItems = [...t.items];
for (const moving of movingItems) {
const existing = newItems.find(
item => item.fieldId === moving.fieldId && item.fertilizerId === moving.fertilizerId
);
if (existing) {
existing.bags += moving.bags;
} else {
newItems.push({ ...moving });
}
}
return { ...t, items: newItems };
}
return t;
});
});
};
// グループの全圃場をtripから未割り当てに戻す
const returnGroupToUnassigned = (groupFieldIds: number[], tripTempId: string) => {
const fieldIdSet = new Set(groupFieldIds);
setTrips(prev => prev.map(t => {
if (t.tempId !== tripTempId) return t;
return { ...t, items: t.items.filter(item => !fieldIdSet.has(item.fieldId)) };
}));
};
// tripのグループ小計 // tripのグループ小計
const getTripGroupFertTotal = useCallback((trip: LocalTrip, fieldIds: number[], fertilizerId: number): number => { const getTripGroupFertTotal = useCallback((trip: LocalTrip, fieldIds: number[], fertilizerId: number): number => {
return trip.items return trip.items
@@ -958,11 +999,34 @@ export default function DeliveryEditPage({ planId }: Props) {
{/* グループ小計行 */} {/* グループ小計行 */}
<div className="flex items-center gap-2 bg-green-50 px-2 py-1 rounded text-sm font-medium text-green-800"> <div className="flex items-center gap-2 bg-green-50 px-2 py-1 rounded text-sm font-medium text-green-800">
<span> {groupName}</span> <span> {groupName}</span>
<span className="text-xs text-green-600 ml-auto"> <span className="text-xs text-green-600 flex-1 text-right">
{groupFertTotals.map((f, i) => ( {groupFertTotals.map((f, i) => (
<span key={f.id}>{i > 0 ? ' ' : ''}{f.name}: {f.total.toFixed(2)}</span> <span key={f.id}>{i > 0 ? ' ' : ''}{f.name}: {f.total.toFixed(2)}</span>
))} ))}
</span> </span>
{fieldIds.length > 1 && (
<select
value=""
onChange={e => {
const val = e.target.value;
if (val === '__unassigned__') {
returnGroupToUnassigned(fieldIds, trip.tempId);
} else if (val) {
moveGroupToTrip(fieldIds, trip.tempId, val);
}
}}
className="border border-gray-300 rounded px-1.5 py-0.5 text-xs font-normal text-gray-600 focus:outline-none focus:ring-1 focus:ring-blue-500 flex-shrink-0"
>
<option value="">...</option>
{trips.filter(t => t.tempId !== trip.tempId).map(t => {
const displayIdx = trips.indexOf(t) + 1;
return (
<option key={t.tempId} value={t.tempId}> {displayIdx}{t.name ? ` (${t.name})` : ''}</option>
);
})}
<option value="__unassigned__"> </option>
</select>
)}
</div> </div>
{/* 圃場行 */} {/* 圃場行 */}
{fieldIds.map(fId => { {fieldIds.map(fId => {