|
|
|
@ -29,6 +29,7 @@ import { |
|
|
|
|
import { STOCK_ERRORS } from '../constants/errorMessages'; |
|
|
|
|
import DatePicker, { registerLocale } from 'react-datepicker'; |
|
|
|
|
import { ja } from 'date-fns/locale/ja'; // date-fnsの日本語ロケールをインポート
|
|
|
|
|
import { useMessage } from '../components/MessageContext'; |
|
|
|
|
|
|
|
|
|
// 日付をyyyy-MM-dd形式で返す関数
|
|
|
|
|
const formatDateLocal = (date: Date) => { |
|
|
|
@ -72,6 +73,8 @@ const StockPage: React.FC = () => { |
|
|
|
|
// 在庫の編集状態
|
|
|
|
|
const [editStock, setEditStock] = useState<Stock | null>(null); |
|
|
|
|
|
|
|
|
|
const { showWarningMessage } = useMessage(); |
|
|
|
|
|
|
|
|
|
// コンポーネントマウント時にタスク一覧を取得
|
|
|
|
|
useEffect(() => { |
|
|
|
|
fetchStocks(); |
|
|
|
@ -146,7 +149,7 @@ const StockPage: React.FC = () => { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* カテゴリー選択?? |
|
|
|
|
* カテゴリー選択 |
|
|
|
|
*/ |
|
|
|
|
const onChangeCategory = async (category: string) => { |
|
|
|
|
setNewStock({ ...newStock, category }) |
|
|
|
@ -177,18 +180,23 @@ const StockPage: React.FC = () => { |
|
|
|
|
const handleOpenAdd = () => { |
|
|
|
|
setIsAddOpen(true); |
|
|
|
|
}; |
|
|
|
|
/** 削除ダイアログを閉じる */ |
|
|
|
|
/** 追加ダイアログを閉じる */ |
|
|
|
|
const handleCloseAdd = () => { |
|
|
|
|
setIsAddOpen(false); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* セルを選択する関数. 再度クリックで選択解除 |
|
|
|
|
* セルを選択する関数. 再度クリックで選択解除 → 行選択ではなくチェックボックスにしたため不要 |
|
|
|
|
*/ |
|
|
|
|
const handleRowClick = (stock: Stock) => { |
|
|
|
|
// const handleRowClick = (stock: Stock) => {
|
|
|
|
|
// setSelectedRow(prev => (prev?.stockId === stock.stockId ? null : stock));
|
|
|
|
|
// };
|
|
|
|
|
// チェックボックス切り替え
|
|
|
|
|
const handleCheckboxChange = (stock: Stock) => { |
|
|
|
|
setSelectedRow(prev => (prev?.stockId === stock.stockId ? null : stock)); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 編集ボタンを押したときにダイアログを開く */ |
|
|
|
|
// ダイアログを開く際に `selectedRow` の値を `editStock` にセット
|
|
|
|
|
const handleOpenEdit = () => { |
|
|
|
@ -196,29 +204,38 @@ const StockPage: React.FC = () => { |
|
|
|
|
setEditStock({ ...selectedRow }); |
|
|
|
|
setIsEditOpen(true); |
|
|
|
|
} else { |
|
|
|
|
alert("編集する食材を選択してください。"); |
|
|
|
|
showWarningMessage("編集する食材を選択してください。"); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
// 変更を適用
|
|
|
|
|
// 変更を適用. 数量に0を入力したとき、削除ダイアログに飛ぶ機能を追加
|
|
|
|
|
const handleApplyChanges = async () => { |
|
|
|
|
if (editStock) { |
|
|
|
|
try { |
|
|
|
|
await handleUpdateStock( |
|
|
|
|
editStock.stockId, |
|
|
|
|
Number(editStock.amount), |
|
|
|
|
Number(editStock.price), |
|
|
|
|
editStock.buyDate, |
|
|
|
|
editStock.expDate |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
setSelectedRow(editStock); // `selectedRow` を更新して変更を即時反映
|
|
|
|
|
fetchStocks(); // 最新データを取得してテーブルに反映
|
|
|
|
|
setSelectedRow(null); // セルの選択を解除
|
|
|
|
|
} catch (error) { |
|
|
|
|
console.error(`${STOCK_ERRORS.UPDATE_FAILED}:`, error); |
|
|
|
|
if (!editStock) return; |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
if (Number(editStock.amount) === 0) { |
|
|
|
|
// 数量が 0 の場合は削除処理へ誘導
|
|
|
|
|
setIsEditOpen(false); // 編集ダイアログを閉じる
|
|
|
|
|
setSelectedRow(editStock); // 削除対象をセット
|
|
|
|
|
setIsDeleteOpen(true); // 削除ダイアログを開く
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
await handleUpdateStock( |
|
|
|
|
editStock.stockId, |
|
|
|
|
Number(editStock.amount), |
|
|
|
|
Number(editStock.price), |
|
|
|
|
editStock.buyDate, |
|
|
|
|
editStock.expDate |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
setSelectedRow(editStock); // 更新後に選択行を反映
|
|
|
|
|
fetchStocks(); // 最新データを取得
|
|
|
|
|
setSelectedRow(null); // 選択解除
|
|
|
|
|
} catch (error) { |
|
|
|
|
console.error(`${STOCK_ERRORS.UPDATE_FAILED}:`, error); |
|
|
|
|
} |
|
|
|
|
setIsEditOpen(false); // ダイアログを閉じる
|
|
|
|
|
|
|
|
|
|
setIsEditOpen(false); // 編集ダイアログを閉じる
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// ダイアログを開く際に `selectedRow` の値を `editStock` にコピー
|
|
|
|
@ -229,15 +246,25 @@ const StockPage: React.FC = () => { |
|
|
|
|
}, [selectedRow]); // `selectedRow` が変更されたら `editStock` に反映
|
|
|
|
|
|
|
|
|
|
// テキストフィールドの変更を検知
|
|
|
|
|
// 負の値を入力できないように書き換え
|
|
|
|
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { |
|
|
|
|
const { name, value } = event.target; |
|
|
|
|
|
|
|
|
|
// 数値項目に対して負の値をブロック
|
|
|
|
|
const numericFields = ['amount', 'price']; |
|
|
|
|
if (numericFields.includes(name) && Number(value) < 0) { |
|
|
|
|
return; // 無視して更新しない
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (editStock) { |
|
|
|
|
setEditStock({ |
|
|
|
|
...editStock, |
|
|
|
|
[event.target.name]: event.target.value, |
|
|
|
|
[name]: value, |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 編集ダイアログを閉じる */ |
|
|
|
|
const handleCloseEdit = () => { |
|
|
|
|
setIsEditOpen(false); |
|
|
|
@ -248,7 +275,7 @@ const StockPage: React.FC = () => { |
|
|
|
|
if (selectedRow) { |
|
|
|
|
setIsDeleteOpen(true); |
|
|
|
|
} else { |
|
|
|
|
alert("削除する食材を選択してください。"); |
|
|
|
|
showWarningMessage("削除する食材を選択してください。"); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
/** 削除ダイアログを閉じる */ |
|
|
|
@ -268,15 +295,15 @@ const StockPage: React.FC = () => { |
|
|
|
|
<Table> |
|
|
|
|
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}> |
|
|
|
|
<TableRow> |
|
|
|
|
<TableCell padding="checkbox" /> |
|
|
|
|
<TableCell>食材名</TableCell> |
|
|
|
|
<TableCell>数量</TableCell> |
|
|
|
|
<TableCell>購入価格</TableCell> |
|
|
|
|
<TableCell>購入日</TableCell> |
|
|
|
|
<TableCell>消費・賞味期限</TableCell> |
|
|
|
|
</TableRow> |
|
|
|
|
</TableHead> |
|
|
|
|
<TableBody> |
|
|
|
|
{filteredStocks.map(stock => { |
|
|
|
|
const isSelected = selectedRow?.stockId === stock.stockId; |
|
|
|
|
const today = new Date(); |
|
|
|
|
const expDate = new Date(stock.expDate); |
|
|
|
|
const timeDiff = expDate.getTime() - today.getTime(); |
|
|
|
@ -285,13 +312,19 @@ const StockPage: React.FC = () => { |
|
|
|
|
return ( |
|
|
|
|
<TableRow |
|
|
|
|
key={stock.stockId} |
|
|
|
|
onClick={() => handleRowClick(stock)} |
|
|
|
|
style={{ backgroundColor: selectedRow?.stockId === stock.stockId ? "yellow" : "white", cursor: "pointer" }} |
|
|
|
|
sx={{ |
|
|
|
|
backgroundColor: isSelected ? 'yellow' : 'white', |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
> |
|
|
|
|
<TableCell padding="checkbox"> |
|
|
|
|
<Checkbox |
|
|
|
|
checked={isSelected} |
|
|
|
|
onChange={() => handleCheckboxChange(stock)} |
|
|
|
|
/> |
|
|
|
|
</TableCell> |
|
|
|
|
<TableCell>{stock.stuffName}</TableCell> |
|
|
|
|
<TableCell>{stock.amount}</TableCell> |
|
|
|
|
<TableCell>{stock.price}</TableCell> |
|
|
|
|
<TableCell>{formatDate(stock.buyDate)}</TableCell> |
|
|
|
|
<TableCell |
|
|
|
|
style={daysLeft <= 3 ? { color: "red", fontWeight: "bold" } : {}} |
|
|
|
|
> |
|
|
|
@ -320,6 +353,13 @@ const StockPage: React.FC = () => { |
|
|
|
|
type="number" |
|
|
|
|
value={editStock.amount} |
|
|
|
|
onChange={handleChange} |
|
|
|
|
inputProps={{ min: 0 }} |
|
|
|
|
onKeyDown={(e) => { |
|
|
|
|
if (e.key === '-' || e.key === 'e' || e.key === 'E') { |
|
|
|
|
e.preventDefault(); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
/> |
|
|
|
|
<TextField |
|
|
|
|
label="購入価格" |
|
|
|
@ -329,34 +369,69 @@ const StockPage: React.FC = () => { |
|
|
|
|
type="number" |
|
|
|
|
value={editStock.price} |
|
|
|
|
onChange={handleChange} |
|
|
|
|
/> |
|
|
|
|
<TextField |
|
|
|
|
label="購入日 (yyyy-MM-dd)" |
|
|
|
|
fullWidth |
|
|
|
|
margin="normal" |
|
|
|
|
name="buyDate" |
|
|
|
|
value={editStock.buyDate} |
|
|
|
|
onChange={handleChange} |
|
|
|
|
/> |
|
|
|
|
<TextField |
|
|
|
|
label="消費・賞味期限 (yyyy-MM-dd)" |
|
|
|
|
fullWidth |
|
|
|
|
margin="normal" |
|
|
|
|
name="expDate" |
|
|
|
|
value={editStock.expDate} |
|
|
|
|
onChange={handleChange} |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<Button onClick={() => { setIsEditOpen(false); setSelectedRow(null); }} sx={{ mt: 3, mb: 2, left: '68%' }}>キャンセル</Button> |
|
|
|
|
<Button |
|
|
|
|
variant="contained" |
|
|
|
|
color="success" |
|
|
|
|
onClick={handleApplyChanges} |
|
|
|
|
sx={{ mt: 3, mb: 2, left: "68%" }} |
|
|
|
|
> |
|
|
|
|
変更を適用 |
|
|
|
|
</Button> |
|
|
|
|
inputProps={{ min: 0 }} |
|
|
|
|
onKeyDown={(e) => { |
|
|
|
|
if (e.key === '-' || e.key === 'e' || e.key === 'E') { |
|
|
|
|
e.preventDefault(); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
/> |
|
|
|
|
{/* 購入日・消費期限を横並びに */} |
|
|
|
|
<Box sx={{ display: 'flex', gap: 2, mb: 2 }}> |
|
|
|
|
{/* 購入日 */} |
|
|
|
|
<DatePicker |
|
|
|
|
selected={editStock.buyDate ? new Date(editStock.buyDate) : null} |
|
|
|
|
onChange={(date) => { |
|
|
|
|
if (editStock) { |
|
|
|
|
setEditStock({ |
|
|
|
|
...editStock, |
|
|
|
|
buyDate: date ? formatDateLocal(date) : '', |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
dateFormat="yyyy/MM/dd" |
|
|
|
|
customInput={ |
|
|
|
|
<TextField |
|
|
|
|
margin="normal" |
|
|
|
|
label="購入日 (yyyy/MM/dd)" |
|
|
|
|
fullWidth |
|
|
|
|
name="buyDate" |
|
|
|
|
/> |
|
|
|
|
} |
|
|
|
|
isClearable |
|
|
|
|
/> |
|
|
|
|
{/* 消費・賞味期限 */} |
|
|
|
|
<DatePicker |
|
|
|
|
selected={editStock.expDate ? new Date(editStock.expDate) : null} |
|
|
|
|
onChange={(date) => { |
|
|
|
|
if (editStock) { |
|
|
|
|
setEditStock({ |
|
|
|
|
...editStock, |
|
|
|
|
expDate: date ? formatDateLocal(date) : '', |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
dateFormat="yyyy/MM/dd" |
|
|
|
|
customInput={ |
|
|
|
|
<TextField |
|
|
|
|
margin="normal" |
|
|
|
|
label="消費・賞味期限 (yyyy/MM/dd)" |
|
|
|
|
fullWidth |
|
|
|
|
name="expDate" |
|
|
|
|
/> |
|
|
|
|
} |
|
|
|
|
isClearable |
|
|
|
|
/> |
|
|
|
|
</Box> |
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 2, mt: 3, mb: 2 }}> |
|
|
|
|
<Button onClick={() => { setIsEditOpen(false); setSelectedRow(null); }}> |
|
|
|
|
キャンセル |
|
|
|
|
</Button> |
|
|
|
|
<Button variant="contained" color="success" onClick={handleApplyChanges}> |
|
|
|
|
変更を適用 |
|
|
|
|
</Button> |
|
|
|
|
</Box> |
|
|
|
|
</> |
|
|
|
|
)} |
|
|
|
|
</DialogContent> |
|
|
|
@ -375,13 +450,26 @@ const StockPage: React.FC = () => { |
|
|
|
|
<> |
|
|
|
|
<Typography variant="h4">【{selectedRow.stuffName}】を削除します。</Typography> |
|
|
|
|
<Typography variant="body1" color="error">⚠️ 注意: 削除すると復元できません。</Typography> |
|
|
|
|
<Button onClick={() => { setIsDeleteOpen(false); setSelectedRow(null); }} sx={{ mt: 3, mb: 2, left: '70%' }}>キャンセル</Button> |
|
|
|
|
<Button variant="contained" color="error" onClick={() => { |
|
|
|
|
handleDeleteStock(selectedRow.stockId); |
|
|
|
|
setIsDeleteOpen(false); // 削除処理後にダイアログを閉じる
|
|
|
|
|
setSelectedRow(null); // セルの選択を解除
|
|
|
|
|
}} |
|
|
|
|
style={{ marginTop: "10px" }} sx={{ mt: 3, mb: 2, left: '72%' }}>削除</Button> |
|
|
|
|
|
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 2, mt: 3, mb: 2 }}> |
|
|
|
|
<Button onClick={() => { |
|
|
|
|
setIsDeleteOpen(false); |
|
|
|
|
setSelectedRow(null); |
|
|
|
|
}}> |
|
|
|
|
キャンセル |
|
|
|
|
</Button> |
|
|
|
|
<Button |
|
|
|
|
variant="contained" |
|
|
|
|
color="error" |
|
|
|
|
onClick={() => { |
|
|
|
|
handleDeleteStock(selectedRow.stockId); |
|
|
|
|
setIsDeleteOpen(false); |
|
|
|
|
setSelectedRow(null); |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
削除 |
|
|
|
|
</Button> |
|
|
|
|
</Box> |
|
|
|
|
</> |
|
|
|
|
)} |
|
|
|
|
</DialogContent> |
|
|
|
@ -397,14 +485,29 @@ const StockPage: React.FC = () => { |
|
|
|
|
在庫一覧 |
|
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
<Box sx={{ textAlign: 'right' }}> |
|
|
|
|
{/* <Box sx={{ textAlign: 'right' }}> */} |
|
|
|
|
<Box |
|
|
|
|
sx={{ |
|
|
|
|
position: 'sticky', |
|
|
|
|
top: 0, |
|
|
|
|
zIndex: 1000, |
|
|
|
|
backgroundColor: '#f5f5f5', |
|
|
|
|
padding: 2, |
|
|
|
|
display: 'flex', |
|
|
|
|
gap: 0.5, |
|
|
|
|
justifyContent: 'flex-end', // ← 右寄せ
|
|
|
|
|
borderBottom: 'none', // ← これで線を消す
|
|
|
|
|
boxShadow: 'none', // ← 影も消す
|
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
|
|
|
|
|
{/* 在庫の食材追加ボタン */} |
|
|
|
|
<Button variant="contained" color="primary" onClick={handleOpenAdd} sx={{ mt: 3, mb: 2, mr: 1 }}> |
|
|
|
|
追加 |
|
|
|
|
</Button> |
|
|
|
|
|
|
|
|
|
{/* 新規タスク作成ダイアログ */} |
|
|
|
|
<Dialog open={isAddOpen} onClose={() => setIsAddOpen(false)} disableScrollLock={true}> |
|
|
|
|
<Dialog open={isAddOpen} onClose={handleCloseAdd} disableScrollLock={true}> |
|
|
|
|
<Box display="flex" alignItems="center" > |
|
|
|
|
<DialogTitle sx={{ flexGrow: 1 }}>在庫に食材を追加</DialogTitle> |
|
|
|
|
<FormGroup row> |
|
|
|
@ -515,15 +618,6 @@ const StockPage: React.FC = () => { |
|
|
|
|
isClearable |
|
|
|
|
//withPortal // ← 他の文字との重なり対策
|
|
|
|
|
/> |
|
|
|
|
{/* |
|
|
|
|
<TextField |
|
|
|
|
margin="dense" |
|
|
|
|
label="購入日(yyyy/MM/dd)" |
|
|
|
|
fullWidth |
|
|
|
|
value={newStock.buyDate} |
|
|
|
|
onChange={(e) => setNewStock({ ...newStock, buyDate: e.target.value })} |
|
|
|
|
/> |
|
|
|
|
*/} |
|
|
|
|
{/* 消費・賞味期限入力フィールド */} |
|
|
|
|
<DatePicker |
|
|
|
|
popperClassName="custom-datepicker-popper" |
|
|
|
@ -554,8 +648,10 @@ const StockPage: React.FC = () => { |
|
|
|
|
</Dialog> |
|
|
|
|
|
|
|
|
|
{/* 在庫の食材編集ボタン(全テーブル共通) */} |
|
|
|
|
<Button variant="contained" color="success" onClick={handleOpenEdit} sx={{ mt: 3, mb: 2, mr: 1 }}> |
|
|
|
|
編集 |
|
|
|
|
<Button variant="contained" color="success" onClick={handleOpenEdit} sx={{ |
|
|
|
|
mt: 3, mb: 2, mr: 1 |
|
|
|
|
}}> |
|
|
|
|
詳細・編集 |
|
|
|
|
</Button> |
|
|
|
|
|
|
|
|
|
{/* 在庫の食材削除ボタン (全テーブル共通) */} |
|
|
|
|