見直し前の最終
This commit is contained in:
@@ -39,7 +39,7 @@ export default function AllocationPage() {
|
||||
if (!background) setLoading(true);
|
||||
try {
|
||||
const [fieldsRes, cropsRes, plansRes] = await Promise.all([
|
||||
api.get('/fields/'),
|
||||
api.get('/fields/?ordering=group_name,display_order,id'),
|
||||
api.get('/plans/crops/'),
|
||||
api.get(`/plans/?year=${year}`),
|
||||
]);
|
||||
@@ -215,7 +215,6 @@ export default function AllocationPage() {
|
||||
};
|
||||
|
||||
const handleGroupChange = async (fieldId: number, groupName: string) => {
|
||||
// ローカル状態を先に更新(並び替え防止)
|
||||
setFields(prev => prev.map(f =>
|
||||
f.id === fieldId ? { ...f, group_name: groupName || null } : f
|
||||
));
|
||||
@@ -226,7 +225,6 @@ export default function AllocationPage() {
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to save group:', error);
|
||||
// エラー時は再取得
|
||||
await fetchData(true);
|
||||
}
|
||||
};
|
||||
@@ -448,155 +446,155 @@ export default function AllocationPage() {
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-md">
|
||||
<p className="text-sm text-blue-800">
|
||||
💡 グループ名を入力(または選択)して「グループ順」で並び替え、「↑」「↓」ボタンで順序を変更できます
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<>
|
||||
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-md">
|
||||
<p className="text-sm text-blue-800">
|
||||
💡 グループ名を入力して「グループ順」で並び替え、「↑」「↓」ボタンで順序を変更できます
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{sortedFields.length > 0 && (
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
{sortType === 'custom' && (
|
||||
<th className="px-2 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-20">
|
||||
順序
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
{sortType === 'custom' && (
|
||||
<th className="px-2 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-20">
|
||||
順序
|
||||
</th>
|
||||
)}
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
グループ
|
||||
</th>
|
||||
)}
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
グループ
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
圃場名
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
面積(反)
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
作物
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
品種
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
備考
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{sortedFields.map((field, index) => {
|
||||
const plan = getPlanForField(field.id);
|
||||
const selectedCropId = plan?.crop || 0;
|
||||
const selectedVarietyId = plan?.variety || 0;
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
圃場名
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
面積(反)
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
作物
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
品種
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
備考
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{sortedFields.map((field, index) => {
|
||||
const plan = getPlanForField(field.id);
|
||||
const selectedCropId = plan?.crop || 0;
|
||||
const selectedVarietyId = plan?.variety || 0;
|
||||
|
||||
return (
|
||||
<tr key={field.id} className="hover:bg-gray-50">
|
||||
{sortType === 'custom' && (
|
||||
<td className="px-2 py-4 whitespace-nowrap">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => moveUp(index)}
|
||||
disabled={index === 0 || saving === field.id}
|
||||
className="p-1 hover:bg-gray-100 rounded disabled:opacity-30"
|
||||
title="上へ移動"
|
||||
>
|
||||
<ArrowUp className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => moveDown(index)}
|
||||
disabled={index === sortedFields.length - 1 || saving === field.id}
|
||||
className="p-1 hover:bg-gray-100 rounded disabled:opacity-30"
|
||||
title="下へ移動"
|
||||
>
|
||||
<ArrowDown className="h-4 w-4" />
|
||||
</button>
|
||||
return (
|
||||
<tr key={field.id} className="hover:bg-gray-50">
|
||||
{sortType === 'custom' && (
|
||||
<td className="px-2 py-4 whitespace-nowrap">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => moveUp(index)}
|
||||
disabled={index === 0 || saving === field.id}
|
||||
className="p-1 hover:bg-gray-100 rounded disabled:opacity-30"
|
||||
title="上へ移動"
|
||||
>
|
||||
<ArrowUp className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => moveDown(index)}
|
||||
disabled={index === sortedFields.length - 1 || saving === field.id}
|
||||
className="p-1 hover:bg-gray-100 rounded disabled:opacity-30"
|
||||
title="下へ移動"
|
||||
>
|
||||
<ArrowDown className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
<td className="px-4 py-4 whitespace-nowrap">
|
||||
<input
|
||||
list="group-options"
|
||||
value={field.group_name || ''}
|
||||
onChange={(e) => handleGroupChange(field.id, e.target.value)}
|
||||
disabled={saving === field.id}
|
||||
placeholder="選択または入力"
|
||||
className="w-36 px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-green-500 disabled:opacity-50"
|
||||
/>
|
||||
<datalist id="group-options">
|
||||
{groupOptions.map((g) => (
|
||||
<option key={g} value={g} />
|
||||
))}
|
||||
</datalist>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{field.name}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
{field.address}
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
<td className="px-4 py-4 whitespace-nowrap">
|
||||
<input
|
||||
list="group-options"
|
||||
value={field.group_name || ''}
|
||||
onChange={(e) => handleGroupChange(field.id, e.target.value)}
|
||||
disabled={saving === field.id}
|
||||
placeholder="選択または入力"
|
||||
className="w-36 px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-green-500 disabled:opacity-50"
|
||||
/>
|
||||
<datalist id="group-options">
|
||||
{groupOptions.map((g) => (
|
||||
<option key={g} value={g} />
|
||||
))}
|
||||
</datalist>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{field.name}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
{field.address}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{field.area_tan}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<select
|
||||
value={selectedCropId || ''}
|
||||
onChange={(e) =>
|
||||
handleCropChange(field.id, e.target.value)
|
||||
}
|
||||
disabled={saving === field.id}
|
||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 text-sm disabled:opacity-50"
|
||||
>
|
||||
<option value="">選択してください</option>
|
||||
{crops.map((crop) => (
|
||||
<option key={crop.id} value={crop.id}>
|
||||
{crop.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<select
|
||||
value={selectedVarietyId || ''}
|
||||
onChange={(e) =>
|
||||
handleVarietyChange(field.id, e.target.value)
|
||||
}
|
||||
disabled={
|
||||
saving === field.id || !selectedCropId
|
||||
}
|
||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 text-sm disabled:opacity-50 disabled:bg-gray-100"
|
||||
>
|
||||
<option value="">選択してください</option>
|
||||
{getVarietiesForCrop(selectedCropId).map((variety) => (
|
||||
<option key={variety.id} value={variety.id}>
|
||||
{variety.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<input
|
||||
type="text"
|
||||
value={plan?.notes || ''}
|
||||
onChange={(e) =>
|
||||
handleNotesChange(field.id, e.target.value)
|
||||
}
|
||||
disabled={saving === field.id || !plan}
|
||||
placeholder="備考を入力"
|
||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 text-sm w-full disabled:opacity-50 disabled:bg-gray-100"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{field.area_tan}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<select
|
||||
value={selectedCropId || ''}
|
||||
onChange={(e) =>
|
||||
handleCropChange(field.id, e.target.value)
|
||||
}
|
||||
disabled={saving === field.id}
|
||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 text-sm disabled:opacity-50"
|
||||
>
|
||||
<option value="">選択してください</option>
|
||||
{crops.map((crop) => (
|
||||
<option key={crop.id} value={crop.id}>
|
||||
{crop.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<select
|
||||
value={selectedVarietyId || ''}
|
||||
onChange={(e) =>
|
||||
handleVarietyChange(field.id, e.target.value)
|
||||
}
|
||||
disabled={
|
||||
saving === field.id || !selectedCropId
|
||||
}
|
||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 text-sm disabled:opacity-50 disabled:bg-gray-100"
|
||||
>
|
||||
<option value="">選択してください</option>
|
||||
{getVarietiesForCrop(selectedCropId).map((variety) => (
|
||||
<option key={variety.id} value={variety.id}>
|
||||
{variety.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<input
|
||||
type="text"
|
||||
value={plan?.notes || ''}
|
||||
onChange={(e) =>
|
||||
handleNotesChange(field.id, e.target.value)
|
||||
}
|
||||
disabled={saving === field.id || !plan}
|
||||
placeholder="備考を入力"
|
||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 text-sm w-full disabled:opacity-50 disabled:bg-gray-100"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user