作付け計画画面 (/allocation):
年度をlocalStorageに保存・復元(ブラウザを閉じても維持、明示的に変えるまで固定)
過去年度を表示中は琥珀色のバナー「{year}年度のデータを参照中(過去年度)」+ 「今年度に戻る」ボタン
テーブル枠も過去年度では薄いセピア調に変化
デフォルトは今年度(2026)
帳票出力画面 (/reports):
デフォルトを 2025 固定 → new Date().getFullYear() に変更
セレクタも動的5年分に変更
ダッシュボード (/dashboard):
既に今年度デフォルト(変更不要)
記憶:
CLAUDE.md「重要な設計判断」に年度管理方針を追記済み
MEMORY.md に Phase 2 のグローバル作業年度導入方針を記録済み
This commit is contained in:
@@ -146,6 +146,13 @@ Variety (品種マスタ)
|
|||||||
- `display_order` (リスト表示時の順序)
|
- `display_order` (リスト表示時の順序)
|
||||||
- マイグレーション0004で追加
|
- マイグレーション0004で追加
|
||||||
|
|
||||||
|
5. **年度管理の設計方針**(⚠️ Phase 2 で必ず参照):
|
||||||
|
- **作付け計画**: 年度セレクタで独立して来年度も選べる。選んだ年度はlocalStorageに保存して維持
|
||||||
|
- **過去年度**: 「参照モード」として視覚的に区別(背景色・バナー)
|
||||||
|
- **Phase 2 の栽培管理・販売管理**: グローバル作業年度を導入し、基本は今年度に従う
|
||||||
|
- **栽培記録・作業日誌**: 日付中心設計、年度は日付から自動算出
|
||||||
|
- 参考: ソリマチ農業簿記の年度管理方式(明示的に年度を選択、変更するまで固定)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔑 重要な制約・ルール
|
## 🔑 重要な制約・ルール
|
||||||
|
|||||||
@@ -23,7 +23,13 @@ export default function AllocationPage() {
|
|||||||
const [fields, setFields] = useState<Field[]>([]);
|
const [fields, setFields] = useState<Field[]>([]);
|
||||||
const [crops, setCrops] = useState<Crop[]>([]);
|
const [crops, setCrops] = useState<Crop[]>([]);
|
||||||
const [plans, setPlans] = useState<Plan[]>([]);
|
const [plans, setPlans] = useState<Plan[]>([]);
|
||||||
const [year, setYear] = useState<number>(2025);
|
const [year, setYear] = useState<number>(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const saved = localStorage.getItem('allocationYear');
|
||||||
|
if (saved) return parseInt(saved);
|
||||||
|
}
|
||||||
|
return new Date().getFullYear();
|
||||||
|
});
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [saving, setSaving] = useState<number | null>(null);
|
const [saving, setSaving] = useState<number | null>(null);
|
||||||
const [showSidebar, setShowSidebar] = useState(true);
|
const [showSidebar, setShowSidebar] = useState(true);
|
||||||
@@ -44,9 +50,13 @@ export default function AllocationPage() {
|
|||||||
const [filterUnassigned, setFilterUnassigned] = useState(false);
|
const [filterUnassigned, setFilterUnassigned] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
localStorage.setItem('allocationYear', String(year));
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [year]);
|
}, [year]);
|
||||||
|
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
const isPastYear = year < currentYear;
|
||||||
|
|
||||||
const fetchData = async (background = false) => {
|
const fetchData = async (background = false) => {
|
||||||
if (!background) setLoading(true);
|
if (!background) setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -540,7 +550,9 @@ export default function AllocationPage() {
|
|||||||
<div className="flex-1 min-w-0 p-4 lg:p-0">
|
<div className="flex-1 min-w-0 p-4 lg:p-0">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<div className="mb-6 flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
<div className="mb-6 flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||||
<h1 className="text-2xl font-bold text-gray-900">作付け計画</h1>
|
<h1 className="text-2xl font-bold text-gray-900">
|
||||||
|
作付け計画 <span className="text-green-700">{year}年度</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
{/* スマホ用集計ボタン */}
|
{/* スマホ用集計ボタン */}
|
||||||
<button
|
<button
|
||||||
@@ -569,11 +581,11 @@ export default function AllocationPage() {
|
|||||||
id="year"
|
id="year"
|
||||||
value={year}
|
value={year}
|
||||||
onChange={(e) => setYear(parseInt(e.target.value))}
|
onChange={(e) => setYear(parseInt(e.target.value))}
|
||||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 font-semibold"
|
||||||
>
|
>
|
||||||
<option value={2025}>2025年</option>
|
{Array.from({ length: 5 }, (_, i) => new Date().getFullYear() - 2 + i).map((y) => (
|
||||||
<option value={2026}>2026年</option>
|
<option key={y} value={y}>{y}年</option>
|
||||||
<option value={2027}>2027年</option>
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -649,6 +661,20 @@ export default function AllocationPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
{isPastYear && (
|
||||||
|
<div className="mb-4 p-3 bg-amber-50 border border-amber-300 rounded-md flex items-center justify-between">
|
||||||
|
<p className="text-sm text-amber-800 font-medium">
|
||||||
|
{year}年度のデータを参照中(過去年度)
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => setYear(currentYear)}
|
||||||
|
className="text-sm text-amber-700 underline hover:text-amber-900"
|
||||||
|
>
|
||||||
|
{currentYear}年度に戻る
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-md">
|
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-md">
|
||||||
<p className="text-sm text-blue-800">
|
<p className="text-sm text-blue-800">
|
||||||
💡 グループ名を入力して「グループ順」で並び替え、「↑」「↓」ボタンで順序を変更できます
|
💡 グループ名を入力して「グループ順」で並び替え、「↑」「↓」ボタンで順序を変更できます
|
||||||
@@ -699,7 +725,7 @@ export default function AllocationPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
<div className={`rounded-lg shadow overflow-hidden ${isPastYear ? 'bg-amber-50/50 ring-1 ring-amber-200' : 'bg-white'}`}>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const previewPdf = async (url: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function ReportsPage() {
|
export default function ReportsPage() {
|
||||||
const [year, setYear] = useState<number>(2025);
|
const [year, setYear] = useState<number>(new Date().getFullYear());
|
||||||
const [busy, setBusy] = useState<string | null>(null);
|
const [busy, setBusy] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleAction = async (action: 'download' | 'preview', type: 'kyosai' | 'chusankan') => {
|
const handleAction = async (action: 'download' | 'preview', type: 'kyosai' | 'chusankan') => {
|
||||||
@@ -67,9 +67,9 @@ export default function ReportsPage() {
|
|||||||
onChange={(e) => setYear(parseInt(e.target.value))}
|
onChange={(e) => setYear(parseInt(e.target.value))}
|
||||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 w-full max-w-xs"
|
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 w-full max-w-xs"
|
||||||
>
|
>
|
||||||
<option value={2025}>2025年</option>
|
{Array.from({ length: 5 }, (_, i) => new Date().getFullYear() - 2 + i).map((y) => (
|
||||||
<option value={2026}>2026年</option>
|
<option key={y} value={y}>{y}年</option>
|
||||||
<option value={2027}>2027年</option>
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user