You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
joint_exc/frontend/src/pages/StockPage.tsx

360 lines
15 KiB

/**
*
*
*/
import React, { useState, useEffect } from 'react';
import { stockApi } from '../services/api';
import { Stock } from '../types/types';
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from "@mui/material";
import {
Container,
Typography,
Tooltip,
Fab,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
TextField,
Button,
Box,
} from '@mui/material';
import { STOCK_ERRORS } from '../constants/errorMessages';
const StockPage: React.FC = () => {
const [stocks, setStocks] = useState<Stock[]>([]);
// セル選択の表示状態
const [selectedRow, setSelectedRow] = useState<Stock | null>(null);
// 編集ダイアロボックスの表示状態
const [isEditOpen, setIsEditOpen] = useState(false);
// 削除メッセージダイアログの表示状態
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
// 在庫の編集状態
const [editStock, setEditStock] = useState<Stock | null>(null);
// コンポーネントマウント時にタスク一覧を取得
useEffect(() => {
fetchStocks();
}, []);
/**
* APIから在庫一覧を取得する関数
* state(tasks)
*/
const fetchStocks = async () => {
try {
const stocks = await stockApi.getStocks();
console.log('Stocks=', stocks)
setStocks(stocks);
} catch (error) {
console.error(`${STOCK_ERRORS.FETCH_FAILED}:`, error);
}
};
/**
*
*/
const handleUpdateStock = async (stockId: number, amount: number, price: number, buyDate: string, expDate: string) => {
try {
const today = new Date().toISOString().substring(0, 10);
await stockApi.updateStock({ stockId, amount, price, lastUpdate: today, buyDate, expDate });
fetchStocks(); // 削除後の買うもの一覧を再取得
} catch (error) {
console.error(`${STOCK_ERRORS.UPDATE_FAILED}:`, error);
}
};
/**
*
* IDのタスクをAPIを通じて削除
*/
const handleDeleteStock = async (stockId: number) => {
try {
await stockApi.deleteStock(stockId);
fetchStocks(); // 削除後の買うもの一覧を再取得
} catch (error) {
console.error(`${STOCK_ERRORS.DELETE_FAILED}:`, error);
}
};
/**
* (ISO 8601)yyyy/MM/ddに変換する関数
*/
const formatDate = (isoString: string): string => {
const date = new Date(isoString);
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0"); // 月は0始まりなので+1
const day = date.getDate().toString().padStart(2, "0");
return `${year}/${month}/${day}`;
};
/* Dateyyyy/MM/dd
const formatDate = (date: Date): string => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0"); // 月は0始まりなので+1
const day = date.getDate().toString().padStart(2, "0");
return `${year}/${month}/${day}`;
};
*/
/**
* .
*/
const handleRowClick = (stock: Stock) => {
setSelectedRow(prev => (prev?.stockId === stock.stockId ? null : stock));
};
/** 編集ボタンを押したときにダイアログを開く */
// ダイアログを開く際に `selectedRow` の値を `editStock` にセット
const handleOpenEdit = () => {
if (selectedRow) {
setEditStock({ ...selectedRow });
setIsEditOpen(true);
} else {
alert("編集する食材を選択してください。");
}
};
// 変更を適用
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);
}
}
setIsEditOpen(false); // ダイアログを閉じる
};
// ダイアログを開く際に `selectedRow` の値を `editStock` にコピー
useEffect(() => {
if (selectedRow) {
setEditStock({ ...selectedRow });
}
}, [selectedRow]); // `selectedRow` が変更されたら `editStock` に反映
// テキストフィールドの変更を検知
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (editStock) {
setEditStock({
...editStock,
[event.target.name]: event.target.value,
});
}
};
/** 編集ダイアログを閉じる */
const handleCloseEdit = () => {
setIsEditOpen(false);
};
/** 削除ボタンを押したときにダイアログを開く */
const handleOpenDelete = () => {
if (selectedRow) {
setIsDeleteOpen(true);
} else {
alert("削除する食材を選択してください。");
}
};
/** 削除ダイアログを閉じる */
const handleCloseDelete = () => {
setIsDeleteOpen(false);
};
/** テーブルを表示する関数 */
const StockTable = (stocks: Stock[], categories: string[]) => {
const filteredStocks = stocks.filter(stock => categories.includes(stock.category));
if (filteredStocks.length === 0) return null;
return (
<>
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredStocks.map(stock => {
const today = new Date();
const expDate = new Date(stock.expDate);
const timeDiff = expDate.getTime() - today.getTime();
const daysLeft = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
return (
<TableRow
key={stock.stockId}
onClick={() => handleRowClick(stock)}
style={{ backgroundColor: selectedRow?.stockId === stock.stockId ? "yellow" : "white", cursor: "pointer" }}
>
<TableCell>{stock.stuffName}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell
style={daysLeft <= 3 ? { color: "red", fontWeight: "bold" } : {}}
>
{formatDate(stock.expDate)}
</TableCell>
<TableCell>{formatDate(stock.buyDate)}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
{/* 編集ダイアログ */}
<Dialog open={isEditOpen} onClose={handleCloseEdit} fullWidth maxWidth="sm">
<DialogTitle></DialogTitle>
<DialogContent>
{editStock && (
<>
<Typography variant="h4">{editStock.stuffName}</Typography>
<TextField
label="数量"
fullWidth
margin="normal"
name="amount"
type="number"
value={editStock.amount}
onChange={handleChange}
/>
<TextField
label="購入価格"
fullWidth
margin="normal"
name="price"
type="number"
value={editStock.price}
onChange={handleChange}
/>
<TextField
label="賞味・消費期限 (yyyy-MM-dd)"
fullWidth
margin="normal"
name="expDate"
value={editStock.expDate}
onChange={handleChange}
/>
<TextField
label="購入日 (yyyy-MM-dd)"
fullWidth
margin="normal"
name="buyDate"
value={editStock.buyDate}
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>
</>
)}
</DialogContent>
</Dialog>
{/* 削除ダイアログ */}
<Dialog open={isDeleteOpen}
onClose={handleCloseDelete}
fullWidth
maxWidth="sm"
sx={{ overflow: "hidden" }}
>
<DialogTitle></DialogTitle>
<DialogContent>
{selectedRow && (
<>
<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>
</>
)}
</DialogContent>
</Dialog>
</>
);
};
return (
<Container>
<Typography variant="h4" component="h1" gutterBottom>
</Typography>
{/* タスク編集ボタン(全テーブル共通) */}
<Button variant="contained" color="success" onClick={handleOpenEdit} sx={{ mt: 3, mb: 2, left: '80%' }}>
</Button>
{/* タスク削除ボタン */}
<Button variant="contained" color="error" onClick={handleOpenDelete} sx={{ mt: 3, mb: 2, left: '82%' }}></Button>
{/* 在庫一覧リスト */}
{/* 乳製品 */}
<Typography variant="h4" component="h1" gutterBottom></Typography>
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{StockTable(stocks, ["乳製品"])}
</div>
{/* 肉・魚 */}
5 months ago
<Typography variant="h4" component="h1" gutterBottom></Typography>
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
5 months ago
{StockTable(stocks, ["魚・肉"])}
</div>
{/* 野菜 */}
<Typography variant="h4" component="h1" gutterBottom></Typography>
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{StockTable(stocks, ["野菜"])}
</div>
{/* 調味料 */}
<Typography variant="h4" component="h1" gutterBottom>調</Typography>
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{StockTable(stocks, ["調味料"])}
</div>
{/* その他 */}
<Typography variant="h4" component="h1" gutterBottom></Typography>
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{StockTable(stocks, ["その他"])}
</div>
</Container>
);
};
export default StockPage;