賞味期限をオプション化

feature-frontend-dateselect-fix
Masaharu.Kato 4 months ago
parent 4285d1ea24
commit 9c27f37cb5
  1. 2
      backend/src/main/java/com/example/todoapp/model/Stocks.java
  2. 8
      frontend/src/components/BuyExpDateSelect.tsx
  3. 16
      frontend/src/pages/StockPage.tsx
  4. 22
      frontend/src/pages/TaskListPage.tsx
  5. 33
      frontend/src/services/api.ts
  6. 6
      frontend/src/types/types.ts

@ -100,7 +100,7 @@ public class Stocks {
/**
* 賞味期限
*/
@Column(nullable = false)
@Column(nullable = true)
private LocalDate expDate;
}

@ -16,7 +16,7 @@ const validateBuyDate = ((buyDate: string) => {
})
// 日付文字列で早いほうを返す(空でないものは除く)
const validateExpDate = ((buyDate: string, expDate: string) => {
const validateExpDate = ((buyDate: string, expDate: string | null) => {
// console.log('validateExpDate:', buyDate, expDate)
if (!expDate) { // 賞味期限が未設定の場合そのまま未設定にする
return '';
@ -31,8 +31,8 @@ const BuyExpDateSelect = ({
newStock,
setNewStock,
}: {
newStock: {buyDate: string, expDate: string},
setNewStock: (tobuy: {buyDate: string, expDate: string}) => void,
newStock: {buyDate: string, expDate: string | null},
setNewStock: (tobuy: {buyDate: string, expDate: string | null}) => void,
}) => {
{/* 購入日・消費期限を横並びに */ }
@ -63,7 +63,7 @@ const BuyExpDateSelect = ({
fullWidth
value={validateExpDate(newStock.buyDate, newStock.expDate)}
onChange={(e) =>
setNewStock({ ...newStock, expDate: e.target.value })
setNewStock({ ...newStock, expDate: e.target.value ?? null })
}
InputLabelProps={{ shrink: true }}
InputProps={{

@ -108,7 +108,7 @@ const StockPage: React.FC = () => {
newStock.stuffId = null;
}
if (isNaN(newStock.amount)) return;
if (isNaN(newStock.price)) return;
if (newStock.price === null || isNaN(newStock.price)) return;
if (newStock.buyAmount !== null && isNaN(newStock.buyAmount)) {
newStock.buyAmount = null;
@ -118,6 +118,7 @@ const StockPage: React.FC = () => {
console.log(newStock)
// 購入日と消費・賞味期限の整合性チェック
if (newStock.expDate !== null) {
const buy = new Date(newStock.buyDate);
const exp = new Date(newStock.expDate);
// 時間をリセットして純粋な“日付のみ”のタイムスタンプを取得
@ -136,6 +137,7 @@ const StockPage: React.FC = () => {
showErrorMessage('購入日は消費・賞味期限より前の日付を設定してください.');
return;
}
}
const today = new Date().toISOString().substring(0, 10);
@ -232,6 +234,7 @@ const StockPage: React.FC = () => {
if (!editStock) return;
const { stockId, amount, buyAmount, price, shop, buyDate, expDate, lastUpdate } = editStock;
if (expDate !== null) {
// 購入日が消費・賞味期限より未来の場合はエラー表示
const buy = new Date(buyDate);
const exp = new Date(expDate);
@ -250,6 +253,7 @@ const StockPage: React.FC = () => {
showErrorMessage('購入日は消費・賞味期限より前の日付を設定してください.');
return;
}
}
try {
// Number型に変換した変数を用意
@ -368,6 +372,9 @@ const StockPage: React.FC = () => {
</TableHead>
<TableBody>
{filteredStocks.map(stock => {
let daysLeft = null;
if (stock.expDate !== null) {
const today = new Date();
const expDate = new Date(stock.expDate);
// 時間をリセットして純粋な“日付のみ”のタイムスタンプを取得
@ -376,13 +383,14 @@ const StockPage: React.FC = () => {
const expDateOnly = new Date(expDate);
expDateOnly.setHours(0, 0, 0, 0);
const timeDiff = expDateOnly.getTime() - todayDateOnly.getTime();
const daysLeft = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
daysLeft = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
// console.log("テーブルtoday:", today);
// console.log("テーブルexp:", expDate);
// console.log("テーブルtodayDateOnly:", todayDateOnly);
// console.log("テーブルexpDateOnly:", expDateOnly);
// console.log("日数差:", daysLeft);
}
return (
<TableRow
@ -393,9 +401,9 @@ const StockPage: React.FC = () => {
<TableCell align="center" sx={{ width: '40%', fontSize: '16px' }}>{stock.stuffName}</TableCell>
<TableCell align="center" sx={{ width: '20%', fontSize: '16px' }}>{stock.amount}</TableCell>
<TableCell align="center" sx={{ width: '40%', fontSize: '16px' }}
style={daysLeft <= 3 ? { color: "red", fontWeight: "bold" } : {}}
style={daysLeft !== null && daysLeft <= 3 ? { color: "red", fontWeight: "bold" } : {}}
>
{formatDate(stock.expDate)}
{stock.expDate && formatDate(stock.expDate)}
</TableCell>
</TableRow>
);

@ -115,6 +115,7 @@ const TaskListPage: React.FC = () => {
setToBuys(tobuys);
} catch (error) {
console.error(`${TOBUY_ERRORS.FETCH_FAILED}:`, error);
showErrorMessage(TOBUY_ERRORS.FETCH_FAILED);
}
};
@ -144,6 +145,7 @@ const TaskListPage: React.FC = () => {
fetchTasks(); // 削除後の買うもの一覧を再取得
} catch (error) {
console.error(`${TOBUY_ERRORS.DELETE_FAILED}:`, error);
showErrorMessage(TOBUY_ERRORS.DELETE_FAILED);
}
};
@ -175,6 +177,7 @@ const TaskListPage: React.FC = () => {
} catch (error) {
console.error(`${TOBUY_ERRORS.CREATE_FAILED}:`, error);
showErrorMessage(TOBUY_ERRORS.CREATE_FAILED);
}
};
@ -197,6 +200,7 @@ const TaskListPage: React.FC = () => {
fetchTasks(); // 作成後のタスク一覧を再取得
} catch (error) {
console.error(`${TOBUY_ERRORS.CREATE_FAILED}:`, error);
showErrorMessage(TOBUY_ERRORS.CREATE_FAILED);
}
};
@ -205,20 +209,32 @@ const TaskListPage: React.FC = () => {
*/
const handleBuy = async () => {
try {
// 今日の日付文字列(日付部分のみ)
const today = new Date().toISOString().substring(0, 10);
const parsedPrice = parseInt(newStock.price, 10);
console.log("newPrice:", newStock.price);
console.log("parsedPrice: ", parsedPrice);
// 購入価格のバリデーション
if (isNaN(parsedPrice)) {
showErrorMessage(GENERAL_ERRORS.INVALID_PRICE);
return;
//setNewStock({ ...newStock, price: parsedPrice });
}
// 購入数量のバリデーション
const amount = parseInt(newStock.amount, 10);
if (isNaN(amount)) {
showErrorMessage(TOBUY_ERRORS.INVALID_BUYAMOUNT);
return;
}
// 購入日のバリデーション(未設定の場合今日を設定)
if (!newStock.buyDate) {
newStock.buyDate = today;
}
await toBuyApi.buy({
tobuyId: selectedToBuyId,
...newStock,
@ -226,10 +242,14 @@ const TaskListPage: React.FC = () => {
price: parsedPrice,
lastUpdate: today
}); //データベースに送信
setOpenInfoDialog(false);
fetchTasks(); // 変更後後の買うもの一覧を再取得
} catch (error) {
console.error(`${TOBUY_ERRORS.BUY_FAILED}:`, error);
showErrorMessage(TOBUY_ERRORS.BUY_FAILED);
}
};

@ -188,12 +188,20 @@ export const toBuyApi = {
/**
*
*/
buy: async (req: { tobuyId: number, amount: number, price: number, shop: string, expDate: string, buyDate: string, lastUpdate: string }): Promise<{ result: boolean }> => {
buy: async (req: {
tobuyId: number,
amount: number,
price: number,
shop: string,
buyDate: string,
expDate: string | null,
lastUpdate: string
}): Promise<{ result: boolean }> => {
console.log('/api/tobuy/buy request: ', req)
req.buyDate = makeDateObject(req.buyDate)?.toISOString()?.substring(0, 10) || ''
req.expDate = makeDateObject(req.expDate)?.toISOString()?.substring(0, 10) || ''
req.buyDate = parseDate(req.buyDate) || getToday();
req.expDate = parseDate(req.expDate)
const response = await fetch(`${API_BASE_URL}/api/tobuy/buy`, {
method: 'POST',
@ -286,8 +294,8 @@ export const stockApi = {
console.log("送信するデータ:", stock); // 送信前のデータ確認
stock.buyDate = makeDateObject(stock.buyDate)?.toISOString()?.substring(0, 10) || ''
stock.expDate = makeDateObject(stock.expDate)?.toISOString()?.substring(0, 10) || ''
stock.buyDate = parseDate(stock.buyDate) || getToday();
stock.expDate = parseDate(stock.expDate)
console.log("変換後のデータ:", stock); // 日付変換後のデータ確認
@ -317,8 +325,8 @@ export const stockApi = {
updateStock: async (req: StockUpdateRequest): Promise<{ result: boolean; message: string }> => {
// console.log('req: ', req)
req.buyDate = makeDateObject(req.buyDate)?.toISOString()?.substring(0, 10) || ''
req.expDate = makeDateObject(req.expDate)?.toISOString()?.substring(0, 10) || ''
req.buyDate = parseDate(req.buyDate) || getToday();
req.expDate = parseDate(req.expDate)
console.log('req: ', req)
@ -485,15 +493,22 @@ export const recipeApi = {
};
function makeDateObject(dateStr: String) {
function parseDate(dateStr: string | null) {
// 例: '2025/06/15' または '2025-06-15' を '2025-06-15' に変換
if (dateStr) {
const parts = dateStr.split(/[-\/]/); // ハイフンかスラッシュで分割
if (parts.length === 3) {
return new Date(parts[0] + '-' + parts[1] + '-' + parts[2]);
const date = new Date(parts[0] + '-' + parts[1] + '-' + parts[2]);
return date.toISOString().substring(0, 10);
}
}
return null; // 無効な日付の場合
}
function getToday() {
return new Date().toISOString().substring(0, 10);
}
// /**
// * (サンプル,実際には不要)

@ -62,10 +62,10 @@ export interface StockUpdateRequest {
stockId: number,
amount: number,
buyAmount: number | null,
price: number,
price: number | null,
shop: string | null,
buyDate: string,
expDate: string,
expDate: string | null,
lastUpdate: string,
}
@ -97,7 +97,7 @@ export interface NewStock {
price: string,
shop: string,
buyDate: string,
expDate: string,
expDate: string | null,
}
/**

Loading…
Cancel
Save