Merge remote-tracking branch 'origin/dev-frontend-stock' into dev-frontend-top

dev-frontend-stock
Haru.Kusano 5 months ago
commit 6270a14d4c
  1. 1
      frontend/src/App.tsx
  2. 3
      frontend/src/components/Layout.tsx
  3. 272
      frontend/src/pages/StockPage.tsx
  4. 54
      frontend/src/services/api.ts
  5. 16
      frontend/src/types/types.ts

@ -93,6 +93,7 @@ const App: React.FC = () => {
</PrivateRoute>
}
/>
</Route>
<Route path="/" element={<Layout />}>
{/* ルートパスへのアクセスはタスク一覧にリダイレクト */}

@ -22,6 +22,7 @@ import {
Menu as MenuIcon,
ListAlt as ListAltIcon,
Inventory as InventoryIcon, // テストページ用のアイコン
Science as ScienceIcon, // 鈴木
} from '@mui/icons-material';
import { useNavigate, Outlet, useLocation } from 'react-router-dom';
@ -100,6 +101,8 @@ const Layout: React.FC = () => {
<ListItemText primary="タスク一覧" />
</ListItemButton>
{/* テストページへのリンクを追加 */}
{/* 在庫リストへのリンクを追加 */}
<ListItemButton
onClick={() => handleNavigate('/stock')}
selected={isSelected('/stock')}

@ -2,15 +2,273 @@
*
*
*/
import React from 'react';
import { Box } from '@mui/material';
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,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
TextField,
Button,
Box,
} from '@mui/material';
import { TASK_ERRORS } from '../constants/errorMessages';
import CategoryDropDown from "../components/CategoryDropDown";
const StockPage: React.FC = () => {
return (
<Box>
{/* 白紙のページ - 何も表示しない */}
</Box>
);
const [stocks, setStocks] = useState<Stock[]>([]);
// 在庫編集ダイアログの表示状態
const [openDialog, setOpenDialog] = useState(false);
// コンポーネントマウント時にタスク一覧を取得
useEffect(() => {
fetchStocks();
}, []);
/**
* APIからタスク一覧を取得する関数
* state(tasks)
*/
const fetchStocks = async () => {
try {
const stocks = await stockApi.getStocks();
setStocks(stocks.stock_array);
} catch (error) {
console.error(`${TASK_ERRORS.FETCH_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}`;
};
*/
return (
<Container>
<Typography variant="h4" component="h1" gutterBottom>
</Typography>
{/* タスク編集ボタン */}
<Button color="success"
type="submit"
variant="contained"
sx={{ mt: 3, mb: 2, left: '80%' }}
>
</Button>
{/* タスク削除ボタン */}
<Button color="error"
type="submit"
variant="contained"
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" }}>
{/* 要素が無かったら表示しない */}
{stocks.filter(stock => stock.category === "乳製品").length === 0 ? null : (
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{stocks
.filter(stock => stock.category == "乳製品") // 乳製品だけ抽出
.map(stock => (
<TableRow key={stock.stock_id}
>
<TableCell>{stock.stuff_name}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</div>
<Typography variant="h4" component="h1" gutterBottom>
</Typography>
{/* 肉・魚一覧表示エリア - 青い背景のコンテナ */}
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{stocks.filter(stock => stock.category == "肉" || stock.category == "魚").length === 0 ? null : (
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{stocks
.filter(stock => stock.category == "肉" || stock.category == "魚") // 肉と魚だけ抽出
.map(stock => (
<TableRow key={stock.stock_id}>
<TableCell>{stock.stuff_name}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</div>
<Typography variant="h4" component="h1" gutterBottom>
</Typography>
{/* 野菜一覧表示エリア - 青い背景のコンテナ */}
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{stocks.filter(stock => stock.category === "野菜").length === 0 ? null : (
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{stocks
.filter(stock => stock.category == "野菜") // 野菜だけ抽出
.map(stock => (
<TableRow key={stock.stock_id}>
<TableCell>{stock.stuff_name}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</div>
<Typography variant="h4" component="h1" gutterBottom>
調
</Typography>
{/* 調味料一覧表示エリア - 青い背景のコンテナ */}
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{stocks.filter(stock => stock.category === "調味料").length === 0 ? null : (
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{stocks
.filter(stock => stock.category == "調味料") // 調味料だけ抽出
.map(stock => (
<TableRow key={stock.stock_id}>
<TableCell>{stock.stuff_name}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</div>
<Typography variant="h4" component="h1" gutterBottom>
</Typography>
{/* その他一覧表示エリア - 青い背景のコンテナ */}
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{stocks.filter(stock => stock.category === "その他").length === 0 ? null : (
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{stocks
.filter(stock => stock.category == "その他") // その他だけ抽出
.map(stock => (
<TableRow key={stock.stock_id}>
<TableCell>{stock.stuff_name}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</div>
</Container>
);
};
export default StockPage;

@ -3,7 +3,7 @@
* APIとの通信を担当するモジュール
*
*/
import { LoginCredentials, RegisterCredentials, AuthResponse, Task, ToBuy, Stuff } from '../types/types';
import { LoginCredentials, RegisterCredentials, AuthResponse, Task, ToBuy, Stuff, Stock } from '../types/types';
import { AUTH_ERRORS, TASK_ERRORS } from '../constants/errorMessages';
// APIのベースURL - 環境変数から取得するか、デフォルト値を使用
@ -89,8 +89,8 @@ export const authApi = {
*/
export const toBuyApi = {
/**
*
* @returns
*
* @returns
*/
getToBuys: async (): Promise<{ "tobuy_array": ToBuy[] }> => {
// const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
@ -214,6 +214,54 @@ export const stuffApi = {
}
export const stockApi = {
/**
*
* @returns
*/
getStocks: async (): Promise<{ "stock_array": Stock[] }> => {
// const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
// headers: getHeaders(), // 認証トークンを含むヘッダー
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.FETCH_FAILED);
// }
// return response.json();
return {
"stock_array": [
{
"stock_id": 1,
"stuff_id": 10,
"stuff_name": "豚肉",
"amount": 100,
"price": 200,
"buy_date": "2025-05-18T09:00:00.000Z",
"last_update": "2025-05-18T09:00:00.000Z",
"exp_date": "2025-05-19T10:15:00.000Z",
"category": "肉"
},
{
"stock_id": 2,
"stuff_id": 1,
"stuff_name": "トマト",
"amount": 10,
"price": 200,
"buy_date": "2025-05-18T09:00:00.000Z",
"last_update": "2025-05-18T09:00:00.000Z",
"exp_date": "2025-05-19T10:15:00.000Z",
"category": "野菜"
}
]
}
},
}
/**
* ()
* API機能を提供するオブジェクト

@ -35,6 +35,22 @@ export interface Stuff {
category: string,
}
/**
*
*
*/
export interface Stock {
stock_id: number,
stuff_id: number,
stuff_name: string,
amount: number,
price: number,
buy_date: string,
last_update: string,
exp_date: string,
category: string,
}
/**
*
*

Loading…
Cancel
Save