完璧に動作しています。
テスト 結果 確定取消 API ✅ is_confirmed: false, confirmed_at: null USE トランザクション削除 ✅ current_stock が 27.5→32 に復帰 引当再作成 ✅ reserved_stock = 5.000 に復帰 追加した変更: stock_service.py:81-93 — unconfirm_spreading(): USE削除→確定フラグリセット→引当再作成 fertilizer/views.py — unconfirm アクション(POST /api/fertilizer/plans/{id}/unconfirm/) fertilizer/page.tsx — 一覧に「確定取消」ボタン(確定済み計画のみ表示) FertilizerEditPage.tsx — 編集画面ヘッダーに「確定取消」ボタン + 在庫情報再取得
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { ChevronLeft, Plus, X, Calculator, Save, FileDown } from 'lucide-react';
|
||||
import { ChevronLeft, Plus, X, Calculator, Save, FileDown, Undo2 } from 'lucide-react';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import { api } from '@/lib/api';
|
||||
import { Fertilizer, FertilizationPlan, Crop, Field } from '@/types';
|
||||
import { Crop, FertilizationPlan, Fertilizer, Field, StockSummary } from '@/types';
|
||||
|
||||
type CalcMethod = 'per_tan' | 'even' | 'nitrogen';
|
||||
|
||||
@@ -63,6 +63,10 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
const [calcMatrix, setCalcMatrix] = useState<Matrix>({});
|
||||
const [adjusted, setAdjusted] = useState<Matrix>({});
|
||||
const [roundedColumns, setRoundedColumns] = useState<Set<number>>(new Set());
|
||||
const [stockByMaterialId, setStockByMaterialId] = useState<Record<number, StockSummary>>({});
|
||||
const [initialPlanTotals, setInitialPlanTotals] = useState<Record<number, number>>({});
|
||||
const [isConfirmed, setIsConfirmed] = useState(false);
|
||||
const [confirmedAt, setConfirmedAt] = useState<string | null>(null);
|
||||
|
||||
const [loading, setLoading] = useState(!isNew);
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -71,15 +75,26 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
// ─── 初期データ取得
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
setSaveError(null);
|
||||
try {
|
||||
const [cropsRes, fertsRes, fieldsRes] = await Promise.all([
|
||||
const [cropsRes, fertsRes, fieldsRes, stockRes] = await Promise.all([
|
||||
api.get('/plans/crops/'),
|
||||
api.get('/fertilizer/fertilizers/'),
|
||||
api.get('/fields/?ordering=display_order,id'),
|
||||
api.get('/materials/fertilizer-stock/'),
|
||||
]);
|
||||
setCrops(cropsRes.data);
|
||||
setAllFertilizers(fertsRes.data);
|
||||
setAllFields(fieldsRes.data);
|
||||
setStockByMaterialId(
|
||||
stockRes.data.reduce(
|
||||
(acc: Record<number, StockSummary>, summary: StockSummary) => {
|
||||
acc[summary.material_id] = summary;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
)
|
||||
);
|
||||
|
||||
if (!isNew && planId) {
|
||||
const planRes = await api.get(`/fertilizer/plans/${planId}/`);
|
||||
@@ -87,6 +102,8 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
setName(plan.name);
|
||||
setYear(plan.year);
|
||||
setVarietyId(plan.variety);
|
||||
setIsConfirmed(plan.is_confirmed);
|
||||
setConfirmedAt(plan.confirmed_at);
|
||||
|
||||
const fertIds = Array.from(new Set(plan.entries.map((e) => e.fertilizer)));
|
||||
const ferts = fertsRes.data.filter((f: Fertilizer) => fertIds.includes(f.id));
|
||||
@@ -110,6 +127,12 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
newAdjusted[e.field][e.fertilizer] = String(e.bags);
|
||||
});
|
||||
setAdjusted(newAdjusted);
|
||||
setInitialPlanTotals(
|
||||
plan.entries.reduce((acc: Record<number, number>, entry) => {
|
||||
acc[entry.fertilizer] = (acc[entry.fertilizer] ?? 0) + Number(entry.bags);
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
// 保存済み calc_settings でページ開時に自動計算してラベル用 calcMatrix を生成
|
||||
const validSettings = plan.calc_settings?.filter((s) => s.param) ?? [];
|
||||
@@ -142,7 +165,9 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
} catch (e: unknown) {
|
||||
const err = e as { response?: { status?: number; data?: unknown } };
|
||||
console.error('初期データ取得エラー:', err);
|
||||
alert(`データの読み込みに失敗しました (${err.response?.status ?? 'network error'})\n${JSON.stringify(err.response?.data ?? '')}`);
|
||||
setSaveError(
|
||||
`データの読み込みに失敗しました (${err.response?.status ?? 'network error'}): ${JSON.stringify(err.response?.data ?? '')}`
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -170,6 +195,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
|
||||
// ─── 肥料追加・削除
|
||||
const addFertilizer = (fert: Fertilizer) => {
|
||||
if (isConfirmed) return;
|
||||
if (planFertilizers.find((f) => f.id === fert.id)) return;
|
||||
setPlanFertilizers((prev) => [...prev, fert]);
|
||||
setCalcSettings((prev) => [...prev, { fertilizer_id: fert.id, method: 'per_tan', param: '' }]);
|
||||
@@ -177,6 +203,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
};
|
||||
|
||||
const removeFertilizer = (id: number) => {
|
||||
if (isConfirmed) return;
|
||||
setPlanFertilizers((prev) => prev.filter((f) => f.id !== id));
|
||||
setCalcSettings((prev) => prev.filter((s) => s.fertilizer_id !== id));
|
||||
const dropCol = (m: Matrix): Matrix => {
|
||||
@@ -195,12 +222,14 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
|
||||
// ─── 圃場追加・削除
|
||||
const addField = (field: Field) => {
|
||||
if (isConfirmed) return;
|
||||
if (selectedFields.find((f) => f.id === field.id)) return;
|
||||
setSelectedFields((prev) => [...prev, field]);
|
||||
setShowFieldPicker(false);
|
||||
};
|
||||
|
||||
const removeField = (id: number) => {
|
||||
if (isConfirmed) return;
|
||||
setSelectedFields((prev) => prev.filter((f) => f.id !== id));
|
||||
setCalcMatrix((prev) => { const next = { ...prev }; delete next[id]; return next; });
|
||||
setAdjusted((prev) => { const next = { ...prev }; delete next[id]; return next; });
|
||||
@@ -210,8 +239,16 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
|
||||
// ─── 自動計算
|
||||
const runCalc = async (setting: CalcSetting) => {
|
||||
if (!setting.param) return alert('パラメータを入力してください');
|
||||
if (selectedFields.length === 0) return alert('対象圃場を選択してください');
|
||||
if (isConfirmed) return;
|
||||
if (!setting.param) {
|
||||
setSaveError('パラメータを入力してください');
|
||||
return;
|
||||
}
|
||||
if (selectedFields.length === 0) {
|
||||
setSaveError('対象圃場を選択してください');
|
||||
return;
|
||||
}
|
||||
setSaveError(null);
|
||||
|
||||
const targetFields = calcNewOnly
|
||||
? selectedFields.filter((f) => {
|
||||
@@ -222,7 +259,10 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
})
|
||||
: selectedFields;
|
||||
|
||||
if (targetFields.length === 0) return alert('未入力の圃場がありません。「全圃場」で実行してください。');
|
||||
if (targetFields.length === 0) {
|
||||
setSaveError('未入力の圃場がありません。「全圃場」で実行してください。');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await api.post('/fertilizer/calculate/', {
|
||||
@@ -246,7 +286,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
// adjusted は保持する(テキストボックスにDB/確定値を維持し、ラベルに計算結果を表示)
|
||||
} catch (e: unknown) {
|
||||
const err = e as { response?: { data?: { error?: string } } };
|
||||
alert(err.response?.data?.error ?? '計算に失敗しました');
|
||||
setSaveError(err.response?.data?.error ?? '計算に失敗しました');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -258,6 +298,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
|
||||
// ─── セル更新(adjusted を更新)
|
||||
const updateCell = (fieldId: number, fertId: number, value: string) => {
|
||||
if (isConfirmed) return;
|
||||
setAdjusted((prev) => {
|
||||
const next = { ...prev };
|
||||
if (!next[fieldId]) next[fieldId] = {};
|
||||
@@ -268,6 +309,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
|
||||
// ─── 列単位で四捨五入 / 元に戻す(トグル)
|
||||
const roundColumn = (fertId: number) => {
|
||||
if (isConfirmed) return;
|
||||
if (roundedColumns.has(fertId)) {
|
||||
// 元に戻す: adjusted からこの列を削除 → calc値が再び表示される
|
||||
setAdjusted((prev) => {
|
||||
@@ -318,10 +360,30 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
selectedFields.reduce((sum, f) => sum + effectiveValue(f.id, fertId), 0);
|
||||
|
||||
const grandTotal = planFertilizers.reduce((sum, f) => sum + colTotal(f.id), 0);
|
||||
const getNumericValue = (value: string | null | undefined) => {
|
||||
const parsed = parseFloat(value ?? '0');
|
||||
return isNaN(parsed) ? 0 : parsed;
|
||||
};
|
||||
const getStockInfo = (fertilizer: Fertilizer) =>
|
||||
fertilizer.material_id ? stockByMaterialId[fertilizer.material_id] ?? null : null;
|
||||
const getPlanAvailableStock = (fertilizer: Fertilizer) => {
|
||||
const stock = getStockInfo(fertilizer);
|
||||
if (!stock) return null;
|
||||
return getNumericValue(stock.available_stock) + (initialPlanTotals[fertilizer.id] ?? 0);
|
||||
};
|
||||
const getPlanShortage = (fertilizer: Fertilizer) => {
|
||||
const available = getPlanAvailableStock(fertilizer);
|
||||
if (available === null) return 0;
|
||||
return Math.max(colTotal(fertilizer.id) - available, 0);
|
||||
};
|
||||
|
||||
// ─── 保存(adjusted 優先、なければ calc 値を使用)
|
||||
const handleSave = async () => {
|
||||
setSaveError(null);
|
||||
if (isConfirmed) {
|
||||
setSaveError('確定済みの施肥計画は編集できません。');
|
||||
return;
|
||||
}
|
||||
if (!name.trim()) { setSaveError('計画名を入力してください'); return; }
|
||||
if (!varietyId) { setSaveError('品種を選択してください'); return; }
|
||||
if (selectedFields.length === 0) { setSaveError('圃場を1つ以上選択してください'); return; }
|
||||
@@ -362,9 +424,35 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
}
|
||||
};
|
||||
|
||||
// ─── 確定取消
|
||||
const handleUnconfirm = async () => {
|
||||
if (!planId) return;
|
||||
setSaveError(null);
|
||||
try {
|
||||
await api.post(`/fertilizer/plans/${planId}/unconfirm/`);
|
||||
setIsConfirmed(false);
|
||||
setConfirmedAt(null);
|
||||
// 引当が再作成されるので在庫情報を再取得
|
||||
const stockRes = await api.get('/materials/fertilizer-stock/');
|
||||
setStockByMaterialId(
|
||||
stockRes.data.reduce(
|
||||
(acc: Record<number, StockSummary>, summary: StockSummary) => {
|
||||
acc[summary.material_id] = summary;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setSaveError('確定取消に失敗しました');
|
||||
}
|
||||
};
|
||||
|
||||
// ─── PDF出力
|
||||
const handlePdf = async () => {
|
||||
if (!planId) return;
|
||||
setSaveError(null);
|
||||
try {
|
||||
const res = await api.get(`/fertilizer/plans/${planId}/pdf/`, { responseType: 'blob' });
|
||||
const url = URL.createObjectURL(new Blob([res.data], { type: 'application/pdf' }));
|
||||
@@ -374,7 +462,8 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (e) {
|
||||
alert('PDF出力に失敗しました');
|
||||
console.error(e);
|
||||
setSaveError('PDF出力に失敗しました');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -406,6 +495,15 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{!isNew && isConfirmed && (
|
||||
<button
|
||||
onClick={handleUnconfirm}
|
||||
className="flex items-center gap-2 px-4 py-2 border border-amber-300 rounded-lg text-sm text-amber-700 hover:bg-amber-50"
|
||||
>
|
||||
<Undo2 className="h-4 w-4" />
|
||||
確定取消
|
||||
</button>
|
||||
)}
|
||||
{!isNew && (
|
||||
<button
|
||||
onClick={handlePdf}
|
||||
@@ -417,11 +515,11 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
)}
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50"
|
||||
disabled={saving || isConfirmed}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Save className="h-4 w-4" />
|
||||
{saving ? '保存中...' : '保存'}
|
||||
{isConfirmed ? '確定済み' : saving ? '保存中...' : '保存'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -435,6 +533,16 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isConfirmed && (
|
||||
<div className="mb-4 flex items-start gap-2 bg-sky-50 border border-sky-300 text-sky-800 rounded-lg px-4 py-3 text-sm">
|
||||
<span className="font-bold shrink-0">i</span>
|
||||
<span>
|
||||
この施肥計画は散布確定済みです。
|
||||
{confirmedAt ? ` 確定日時: ${new Date(confirmedAt).toLocaleString('ja-JP')}` : ''}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 基本情報 */}
|
||||
<div className="bg-white rounded-lg shadow p-4 mb-4 flex flex-wrap gap-4 items-end">
|
||||
<div className="flex-1 min-w-48">
|
||||
@@ -444,6 +552,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="例: 2025年度 コシヒカリ 元肥"
|
||||
disabled={isConfirmed}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -452,6 +561,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
className="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500"
|
||||
value={year}
|
||||
onChange={(e) => setYear(parseInt(e.target.value))}
|
||||
disabled={isConfirmed}
|
||||
>
|
||||
{years.map((y) => <option key={y} value={y}>{y}年度</option>)}
|
||||
</select>
|
||||
@@ -462,6 +572,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500"
|
||||
value={varietyId}
|
||||
onChange={(e) => setVarietyId(e.target.value ? parseInt(e.target.value) : '')}
|
||||
disabled={isConfirmed}
|
||||
>
|
||||
<option value="">品種を選択</option>
|
||||
{crops.map((crop) => (
|
||||
@@ -487,7 +598,8 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setShowFieldPicker(true)}
|
||||
className="flex items-center gap-1 text-xs text-green-600 hover:text-green-800 border border-green-300 rounded px-2 py-1"
|
||||
disabled={isConfirmed}
|
||||
className="flex items-center gap-1 text-xs text-green-600 hover:text-green-800 border border-green-300 rounded px-2 py-1 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Plus className="h-3 w-3" />圃場を追加
|
||||
</button>
|
||||
@@ -504,7 +616,11 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
className="flex items-center gap-1 bg-green-50 border border-green-200 rounded-full px-3 py-1 text-xs text-green-800"
|
||||
>
|
||||
{f.name}({f.area_tan}反)
|
||||
<button onClick={() => removeField(f.id)} className="text-green-400 hover:text-red-500">
|
||||
<button
|
||||
onClick={() => removeField(f.id)}
|
||||
disabled={isConfirmed}
|
||||
className="text-green-400 hover:text-red-500 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</span>
|
||||
@@ -522,13 +638,15 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
type="checkbox"
|
||||
checked={calcNewOnly}
|
||||
onChange={(e) => setCalcNewOnly(e.target.checked)}
|
||||
disabled={isConfirmed}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
未入力圃場のみ
|
||||
</label>
|
||||
<button
|
||||
onClick={() => setShowFertPicker(true)}
|
||||
className="flex items-center gap-1 text-xs text-green-600 hover:text-green-800 border border-green-300 rounded px-2 py-1"
|
||||
disabled={isConfirmed}
|
||||
className="flex items-center gap-1 text-xs text-green-600 hover:text-green-800 border border-green-300 rounded px-2 py-1 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Plus className="h-3 w-3" />肥料を追加
|
||||
</button>
|
||||
@@ -550,6 +668,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
className="border border-gray-300 rounded px-2 py-1 text-xs"
|
||||
value={setting.method}
|
||||
onChange={(e) => updateCalcSetting(fert.id, 'method', e.target.value)}
|
||||
disabled={isConfirmed}
|
||||
>
|
||||
{Object.entries(METHOD_LABELS).map(([k, v]) => (
|
||||
<option key={k} value={k}>{v}</option>
|
||||
@@ -562,17 +681,20 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
value={setting.param}
|
||||
onChange={(e) => updateCalcSetting(fert.id, 'param', e.target.value)}
|
||||
placeholder="値"
|
||||
disabled={isConfirmed}
|
||||
/>
|
||||
<span className="text-xs text-gray-500 w-24">{METHOD_UNIT[setting.method]}</span>
|
||||
<button
|
||||
onClick={() => runCalc(setting)}
|
||||
className="flex items-center gap-1 text-xs bg-blue-50 border border-blue-300 text-blue-700 rounded px-3 py-1 hover:bg-blue-100"
|
||||
disabled={isConfirmed}
|
||||
className="flex items-center gap-1 text-xs bg-blue-50 border border-blue-300 text-blue-700 rounded px-3 py-1 hover:bg-blue-100 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Calculator className="h-3 w-3" />計算
|
||||
</button>
|
||||
<button
|
||||
onClick={() => removeFertilizer(fert.id)}
|
||||
className="ml-auto text-gray-300 hover:text-red-500"
|
||||
disabled={isConfirmed}
|
||||
className="ml-auto text-gray-300 hover:text-red-500 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -593,18 +715,44 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
<th className="text-right px-3 py-3 border border-gray-200 font-medium text-gray-700 whitespace-nowrap">面積(反)</th>
|
||||
{planFertilizers.map((f) => {
|
||||
const isRounded = roundedColumns.has(f.id);
|
||||
const stock = getStockInfo(f);
|
||||
const planAvailable = getPlanAvailableStock(f);
|
||||
const shortage = getPlanShortage(f);
|
||||
return (
|
||||
<th key={f.id} className="text-center px-3 py-2 border border-gray-200 font-medium text-gray-700 whitespace-nowrap">
|
||||
{f.name}
|
||||
<div>{f.name}</div>
|
||||
{stock ? (
|
||||
<div className="mt-1 space-y-0.5 text-[11px] font-normal leading-4">
|
||||
<div className="text-gray-500">
|
||||
在庫 {stock.current_stock}{stock.stock_unit_display}
|
||||
{planAvailable !== null && (
|
||||
<span className="ml-1">
|
||||
/ 利用可能 {planAvailable.toFixed(2)}{stock.stock_unit_display}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={shortage > 0 ? 'text-red-600' : 'text-gray-500'}>
|
||||
計画計 {colTotal(f.id).toFixed(2)}{stock.stock_unit_display}
|
||||
{shortage > 0 && (
|
||||
<span className="ml-1">/ 不足 {shortage.toFixed(2)}{stock.stock_unit_display}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-1 text-[11px] font-normal text-amber-600">
|
||||
在庫情報なし
|
||||
</div>
|
||||
)}
|
||||
<span className="flex items-center justify-center gap-1.5 text-xs font-normal text-gray-400 mt-0.5">
|
||||
(袋)
|
||||
<button
|
||||
onClick={() => roundColumn(f.id)}
|
||||
disabled={isConfirmed}
|
||||
className={`inline-flex items-center justify-center w-5 h-5 rounded font-bold leading-none ${
|
||||
isRounded
|
||||
? 'bg-amber-100 text-amber-600 hover:bg-amber-200'
|
||||
: 'bg-blue-100 text-blue-500 hover:bg-blue-200'
|
||||
}`}
|
||||
} disabled:opacity-40 disabled:cursor-not-allowed`}
|
||||
title={isRounded ? '元の計算値に戻す' : '四捨五入して整数に丸める'}
|
||||
>
|
||||
{isRounded ? '↩' : '≈'}
|
||||
@@ -641,6 +789,7 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
value={inputValue}
|
||||
onChange={(e) => updateCell(field.id, fert.id, e.target.value)}
|
||||
placeholder="-"
|
||||
disabled={isConfirmed}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
@@ -689,7 +838,8 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
<button
|
||||
key={f.id}
|
||||
onClick={() => addField(f)}
|
||||
className="w-full text-left px-3 py-2 hover:bg-green-50 rounded text-sm flex justify-between"
|
||||
disabled={isConfirmed}
|
||||
className="w-full text-left px-3 py-2 hover:bg-green-50 rounded text-sm flex justify-between disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<span>{f.name}</span>
|
||||
<span className="text-gray-400">{f.area_tan}反</span>
|
||||
@@ -703,7 +853,8 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
<button
|
||||
key={f.id}
|
||||
onClick={() => addField(f)}
|
||||
className="w-full text-left px-3 py-2 hover:bg-gray-50 rounded text-sm flex justify-between"
|
||||
disabled={isConfirmed}
|
||||
className="w-full text-left px-3 py-2 hover:bg-gray-50 rounded text-sm flex justify-between disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<span>{f.name}</span>
|
||||
<span className="text-gray-400">{f.area_tan}反</span>
|
||||
@@ -730,7 +881,8 @@ export default function FertilizerEditPage({ planId }: { planId?: number }) {
|
||||
<button
|
||||
key={f.id}
|
||||
onClick={() => addFertilizer(f)}
|
||||
className="w-full text-left px-3 py-2 hover:bg-green-50 rounded text-sm"
|
||||
disabled={isConfirmed}
|
||||
className="w-full text-left px-3 py-2 hover:bg-green-50 rounded text-sm disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<span className="font-medium">{f.name}</span>
|
||||
{f.maker && <span className="ml-2 text-gray-400 text-xs">{f.maker}</span>}
|
||||
|
||||
Reference in New Issue
Block a user