Compare commits

...

3 Commits

  1. 4
      frontend/src/App.css
  2. 22
      frontend/src/components/AddByRecipeDialog.tsx
  3. 5
      frontend/src/components/AddStuffAmountDialog.tsx
  4. 38
      frontend/src/components/DeleteStuffDialog.tsx
  5. 43
      frontend/src/components/Layout.tsx
  6. 33
      frontend/src/components/StuffHistoryDialog.tsx
  7. 2
      frontend/src/index.css
  8. 14
      frontend/src/pages/RecipeList.tsx
  9. 24
      frontend/src/pages/StockPage.tsx
  10. 4
      frontend/src/pages/TaskListPage.tsx

@ -23,8 +23,8 @@ body {
/* Material UIのコンテナコンポーネントのカスタマイズ */ /* Material UIのコンテナコンポーネントのカスタマイズ */
.MuiContainer-root { .MuiContainer-root {
padding-top: 24px; /* padding-top: 24px;
padding-bottom: 24px; padding-bottom: 24px; */
} }
/* Material UIのペーパーコンポーネントのカスタマイズ */ /* Material UIのペーパーコンポーネントのカスタマイズ */

@ -76,10 +76,10 @@ const AddByRecipeDialog = ({
/> />
</DialogTitle> </DialogTitle>
<DialogContent dividers sx={{ padding: 2 }}> <DialogContent dividers sx={{ padding: 2 }}>
<Typography variant="h3"> <Typography sx={{fontSize: '1.6rem'}}>
{recipe.recipeName} {recipe.recipeName}
</Typography> </Typography>
<Typography> <Typography sx={{mt: 2, mb: 2}}>
({recipe.maxServings}) ({recipe.maxServings})
</Typography> </Typography>
<div> <div>
@ -112,11 +112,17 @@ const AddByRecipeDialog = ({
defaultValue={1} defaultValue={1}
value={numOfPeople} value={numOfPeople}
onChange={(e) => { onChange={(e) => {
// const num = parseInt(e.target.value, 10); const value = e.target.value;
// setNumOfPeaple(!isNaN(num) ? num : ''); const parsedValue = parseInt(value, 10); // 数値に変換
setNumOfPeaple(e.target.value); if (/*!isNaN(parsedValue) && */ isNaN(parsedValue) || parsedValue >= 1) { //負数除外
setNumOfPeaple(value);
}
}}
onKeyDown={(e) => {
if (e.key === '-' || e.key === 'e' || e.key === 'E') {
e.preventDefault();
}
}} }}
sx={{ minWidth: "8px", width: "100%" }}
inputProps={{ inputMode: "numeric", min: 1, pattern: "[0-9]*" }} // ここで整数のみ許可 inputProps={{ inputMode: "numeric", min: 1, pattern: "[0-9]*" }} // ここで整数のみ許可
/> />
@ -133,14 +139,14 @@ const AddByRecipeDialog = ({
color="primary" color="primary"
onClick={async () => { onClick={async () => {
const num = parseInt(numOfPeople, 10); const num = parseInt(numOfPeople, 10);
if (!num || isNaN(num)) { if (!num || isNaN(num) || num <= 0) {
showErrorMessage('人数が正しく入力されていません。'); showErrorMessage('人数が正しく入力されていません。');
return; return;
} }
const finalAddResult = await toBuyApi.addByRecipe(recipe.recipeId, num, checked); const finalAddResult = await toBuyApi.addByRecipe(recipe.recipeId, num, checked);
setOpenDialog(false); setOpenDialog(false);
if (finalAddResult.data.length === 0) { if (finalAddResult.data.length === 0) {
showInfoMessage('必要な食材が在庫にあったのでリストには追加されませんでした'); showInfoMessage('必要な食材が在庫にあったのでリストには追加されませんでした');
} else { } else {
showSuccessMessage('レシピが保存されて買うものリストに追加されました!'); showSuccessMessage('レシピが保存されて買うものリストに追加されました!');
} }

@ -136,6 +136,11 @@ const AddStuffAmountDialog = ({
setNewItem({ ...newItem, amount: parsedValue }); // number型で保存 setNewItem({ ...newItem, amount: parsedValue }); // number型で保存
} }
}} }}
onKeyDown={(e) => {
if (e.key === '-' || e.key === 'e' || e.key === 'E') {
e.preventDefault();
}
}}
className="numberField" className="numberField"
type="number" type="number"
inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可 inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可

@ -4,6 +4,7 @@ import {
DialogContent, DialogContent,
Button, Button,
Typography, Typography,
DialogActions,
} from '@mui/material'; } from '@mui/material';
const DeleteStuffDialog = ({ const DeleteStuffDialog = ({
@ -18,24 +19,25 @@ const DeleteStuffDialog = ({
onSubmit: () => void, onSubmit: () => void,
}) => { }) => {
{/* 削除ダイアログ */} {/* 削除ダイアログ */ }
return <Dialog open={openDialog} onClose={() => setOpenDialog(false)} disableScrollLock={true} return <Dialog open={openDialog} onClose={() => setOpenDialog(false)} disableScrollLock={true}
fullWidth fullWidth
maxWidth="sm" maxWidth="sm"
sx={{ overflow: "hidden" }} sx={{ overflow: "hidden" }}
> >
<DialogTitle></DialogTitle> <DialogTitle></DialogTitle>
<DialogContent> <DialogContent>
<Typography variant="h4">{stuffName}</Typography> <Typography sx={{fontSize: '1.6rem'}}>{stuffName} </Typography>
<Typography variant="body1" color="error"> 注意: 削除すると復元できません</Typography> <Typography variant="body1" color="error"> 注意: 削除すると復元できません</Typography>
<Button onClick={() => setOpenDialog(false)} sx={{ mt: 3, mb: 2, left: '70%' }}></Button> </DialogContent>
<Button variant="contained" color="error" onClick={() => { <DialogActions>
onSubmit(); <Button onClick={() => setOpenDialog(false)} ></Button>
setOpenDialog(false); // 削除処理後にダイアログを閉じる <Button variant="contained" color="error" onClick={() => {
}} onSubmit();
style={{ marginTop: "10px" }} sx={{ mt: 3, mb: 2, left: '72%' }}></Button> setOpenDialog(false); // 削除処理後にダイアログを閉じる
</DialogContent> }}></Button>
</Dialog> </DialogActions>
</Dialog>
} }

@ -76,10 +76,6 @@ const Layout: React.FC = () => {
navigate('/login'); navigate('/login');
}; };
// メニューを開閉するハンドラー // メニューを開閉するハンドラー
const toggleDrawer = () => { const toggleDrawer = () => {
setDrawerOpen(!drawerOpen); setDrawerOpen(!drawerOpen);
@ -119,7 +115,21 @@ const Layout: React.FC = () => {
sessionStorage.removeItem('globalMessage'); sessionStorage.removeItem('globalMessage');
}; };
const onBottomNaviChange = (event: React.SyntheticEvent, value: any) => {
setBottomNavi(value);
switch (value) {
case 0:
navigate('stock');
break;
case 1:
navigate('tasks');
break;
case 2:
navigate('recipeList');
break;
}
// ここでルーティング処理などを行う
}
return ( return (
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}> <Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
@ -135,29 +145,11 @@ const Layout: React.FC = () => {
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<Paper sx={{ position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: (theme) => theme.zIndex.drawer + 1 }} elevation={3}> <Paper sx={{ position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: (theme) => theme.zIndex.drawer + 1 }} elevation={3}>
<BottomNavigation <BottomNavigation
showLabels showLabels
value={bottomNavi} value={bottomNavi}
onChange={(event, newValue) => { onChange={onBottomNaviChange}
setBottomNavi(newValue);
switch (newValue) {
case 0:
navigate('stock');
break;
case 1:
navigate('tasks');
break;
case 2:
navigate('recipeList');
break;
}
// ここでルーティング処理などを行う
}}
> >
<BottomNavigationAction label="在庫" icon={<InventoryIcon />} /> <BottomNavigationAction label="在庫" icon={<InventoryIcon />} />
<BottomNavigationAction label="買うもの" icon={<ShoppingCartIcon />} /> <BottomNavigationAction label="買うもの" icon={<ShoppingCartIcon />} />
@ -165,10 +157,9 @@ const Layout: React.FC = () => {
</BottomNavigation> </BottomNavigation>
</Paper> </Paper>
{/* メインコンテンツ領域 - 子ルートのコンポーネントがここに表示される */} {/* メインコンテンツ領域 - 子ルートのコンポーネントがここに表示される */}
<Box component="main" sx={{ flexGrow: 1, bgcolor: 'background.default', py: 3 }}> <Box component="main" sx={{ flexGrow: 1, bgcolor: 'background.default', py: 3 }}>
<Container sx={{ padding: 0, margin: 0 }}> <Container sx={{ padding: 0, mt: 0, mb: 0, mr: 'auto', ml: 'auto' }}>
<MessageContext.Provider value={{ showErrorMessage, showWarningMessage, showSuccessMessage, showInfoMessage }}> <MessageContext.Provider value={{ showErrorMessage, showWarningMessage, showSuccessMessage, showInfoMessage }}>
<MessageAlert <MessageAlert
open={msgOpen} open={msgOpen}

@ -14,7 +14,8 @@ import {
TableHead, // 追加 TableHead, // 追加
TableRow, // 追加 TableRow, // 追加
Paper, Paper,
Typography, // 追加: TableContainerの背景用 Typography,
styled, // 追加: TableContainerの背景用
} from '@mui/material'; } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close'; // 追加: 閉じるアイコンをインポート import CloseIcon from '@mui/icons-material/Close'; // 追加: 閉じるアイコンをインポート
import { StockHistory } from '../types/types'; import { StockHistory } from '../types/types';
@ -31,6 +32,14 @@ const StuffHistoryDialog = ({
stockHistories: StockHistory[], stockHistories: StockHistory[],
}) => { }) => {
const StyledTableCell = styled(TableCell)({
paddingTop: '8px',
paddingBottom: '8px',
paddingLeft: '8px',
paddingRight: '8px',
whiteSpace: 'nowrap',
})
return ( return (
<Dialog open={openDialog} onClose={() => setOpenDialog(false)} disableScrollLock={true} PaperProps={{ sx: { minWidth: '90vw', maxHeight: '80vh' } }}> <Dialog open={openDialog} onClose={() => setOpenDialog(false)} disableScrollLock={true} PaperProps={{ sx: { minWidth: '90vw', maxHeight: '80vh' } }}>
<DialogTitle sx={{ m: 0, p: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <DialogTitle sx={{ m: 0, p: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@ -62,15 +71,15 @@ const StuffHistoryDialog = ({
overflowX: 'auto', overflowX: 'auto',
}} }}
> >
<Table sx={{ minWidth: 400 }} aria-label="purchase history table"> <Table aria-label="purchase history table">
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}> <TableHead sx={{ backgroundColor: "#ebcba2", color: "#333" }}>
<TableRow> <TableRow>
{/* 各ヘッダーセルに white-space: nowrap; を適用 */} {/* 各ヘッダーセルに white-space: nowrap; を適用 */}
<TableCell sx={{ whiteSpace: 'nowrap' }}></TableCell> <StyledTableCell></StyledTableCell>
{/* 「購入店舗」ヘッダーも nowrap にし、minWidth でスクロールを考慮 */} {/* 「購入店舗」ヘッダーも nowrap にし、minWidth でスクロールを考慮 */}
<TableCell sx={{ whiteSpace: 'nowrap', minWidth: '120px' }}></TableCell> <StyledTableCell></StyledTableCell>
<TableCell align="right" sx={{ whiteSpace: 'nowrap' }}></TableCell> <StyledTableCell align="right"></StyledTableCell>
<TableCell align="right" sx={{ whiteSpace: 'nowrap' }}></TableCell> <StyledTableCell align="right"></StyledTableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
@ -80,13 +89,13 @@ const StuffHistoryDialog = ({
sx={{ '&:last-child td, &:last-child th': { border: 0 } }} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
> >
{/* 各ボディセルに white-space: nowrap; を適用 */} {/* 各ボディセルに white-space: nowrap; を適用 */}
<TableCell component="th" scope="row" sx={{ whiteSpace: 'nowrap' }}> <StyledTableCell component="th" scope="row" sx={{ whiteSpace: 'nowrap' }}>
{history.buyDate} {history.buyDate}
</TableCell> </StyledTableCell>
{/* 「購入店舗」セルに nowrap と minWidth を適用 */} {/* 「購入店舗」セルに nowrap と minWidth を適用 */}
<TableCell sx={{ whiteSpace: 'nowrap', minWidth: '150px' }}>{history.shop || ''}</TableCell> <StyledTableCell sx={{whiteSpace: 'wrap'}}>{history.shop || ''}</StyledTableCell>
<TableCell align="right" sx={{ whiteSpace: 'nowrap' }}>{history.buyAmount}</TableCell> <StyledTableCell align="right" sx={{pr: '16px'}}>{history.buyAmount}</StyledTableCell>
<TableCell align="right" sx={{ whiteSpace: 'nowrap' }}>{history.price}</TableCell> <StyledTableCell align="right" sx={{pr: '16px'}}>{history.price}</StyledTableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>

@ -18,7 +18,7 @@ html, body {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
/* スクロールバーのレイアウトシフトを防止 */ /* スクロールバーのレイアウトシフトを防止 */
scrollbar-gutter: stable both-edges; scrollbar-gutter: stable both-edges;
overflow: auto overflow: auto;
} }
/* ルート要素のスタイル - Reactアプリケーションのマウントポイント */ /* ルート要素のスタイル - Reactアプリケーションのマウントポイント */

@ -114,26 +114,26 @@ const RecipeList: React.FC = () => {
<ListItemSecondaryAction> <ListItemSecondaryAction>
{recipe.maxServings ? {recipe.maxServings ?
<Typography variant="body1" component="span" sx={{ marginRight: '1em', color: "mediumaquamarine" }}> <Typography variant="body1" component="span" sx={{ mr: 0, color: "mediumaquamarine" }}>
{recipe.maxServings} {recipe.maxServings}
</Typography> </Typography>
: <></>} : <></>}
{recipe.maxServings === 0 ? {recipe.maxServings === 0 ?
<Tooltip title="" sx={{ color: "tomato" }}> <Tooltip title="">
<IconButton edge="end" aria-label=""> <IconButton sx={{ mr: 0, ml: 0, color: "tomato" }} edge="end" aria-label="">
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
</Tooltip> : </Tooltip> :
<Tooltip title="" sx={{ color: "mediumaquamarine" }}> <Tooltip title="">
<IconButton edge="end" aria-label=""> <IconButton sx={{ mr: 0, ml: 0, color: "mediumaquamarine" }} edge="end" aria-label="">
<TaskAltIcon /> <TaskAltIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
} }
<Tooltip title="編集"> <Tooltip title="編集" sx={{}}>
<IconButton edge="end" aria-label="編集" <IconButton sx={{ mr: 0, ml: 0 }} edge="end" aria-label="編集"
onClick={() => { onClick={() => {
navigate('/addRecipe/' + recipe.recipeId); navigate('/addRecipe/' + recipe.recipeId);
}} }}

@ -453,6 +453,7 @@ const StockPage: React.FC = () => {
margin="normal" margin="normal"
name="amount" name="amount"
type="number" type="number"
fullWidth
className="numberField" className="numberField"
value={editStock.amount} value={editStock.amount}
onChange={handleChange} onChange={handleChange}
@ -470,7 +471,7 @@ const StockPage: React.FC = () => {
margin="normal" margin="normal"
name="buyAmount" name="buyAmount"
type="number" type="number"
className="numberField" fullWidth
value={editStock.buyAmount} value={editStock.buyAmount}
onChange={handleChange} onChange={handleChange}
inputProps={{ min: 0 }} inputProps={{ min: 0 }}
@ -484,10 +485,10 @@ const StockPage: React.FC = () => {
{/* 購入価格フィールド */} {/* 購入価格フィールド */}
<TextField <TextField
label="購入価格" label="購入価格"
fullWidth
margin="normal" margin="normal"
name="price" name="price"
type="number" type="number"
fullWidth
value={editStock.price} value={editStock.price}
onChange={handleChange} onChange={handleChange}
inputProps={{ min: 0 }} inputProps={{ min: 0 }}
@ -511,19 +512,18 @@ const StockPage: React.FC = () => {
{/* 購入日・賞味期限入力 */} {/* 購入日・賞味期限入力 */}
<BuyExpDateSelect newStock={editStock} setNewStock={({ buyDate, expDate }) => setEditStock({ ...editStock, buyDate, expDate })} /> <BuyExpDateSelect newStock={editStock} setNewStock={({ buyDate, expDate }) => setEditStock({ ...editStock, buyDate, expDate })} />
</>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 2, mt: 3, mb: 2 }}> )}
</DialogContent>
<DialogActions>
<Button onClick={() => { setOpenEditDialog(false); setSelectedRow(null); }}> <Button onClick={() => { setOpenEditDialog(false); setSelectedRow(null); }}>
</Button> </Button>
<Button variant="contained" color="success" onClick={handleApplyChanges} sx={{ mt: 3, mb: 2 }}> <Button variant="contained" color="success" onClick={handleApplyChanges}>
</Button> </Button>
<Button variant="contained" color="error" onClick={() => setOpenDeleteDialog(true)} sx={{ mt: 3, mb: 2 }}></Button> <Button variant="contained" color="error" onClick={() => setOpenDeleteDialog(true)}></Button>
</Box> </DialogActions>
</>
)}
</DialogContent>
</Dialog> </Dialog>
{/* 削除ダイアログ */} {/* 削除ダイアログ */}
@ -613,10 +613,10 @@ const StockPage: React.FC = () => {
{!openCategory[category] ? <ArrowDownIcon color="primary" /> : <ArrowUpIcon color="primary" />} {!openCategory[category] ? <ArrowDownIcon color="primary" /> : <ArrowUpIcon color="primary" />}
{category} {category}
</Typography> </Typography>
{!stocks {openCategory[category] && (!stocks
? <Typography>...</Typography> ? <Typography>...</Typography>
: StockTable(stocks, [category]) : StockTable(stocks, [category])
} )}
</Box> </Box>
) )
})} })}

@ -290,7 +290,7 @@ const TaskListPage: React.FC = () => {
</Typography> </Typography>
{/* 買い物リスト:数量変更ボタン */} {/* 買い物リスト:数量変更ボタン */}
<Tooltip title="数量変更"> <Tooltip title="数量変更">
<IconButton sx={{ marginRight: 0, marginLeft: 0 }} edge="end" aria-label="数量変更" <IconButton sx={{ mr: 0, ml: 0 }} edge="end" aria-label="数量変更"
onClick={() => { onClick={() => {
setOpenAmountDialog(true) setOpenAmountDialog(true)
setEditingItem(tobuy) setEditingItem(tobuy)
@ -301,7 +301,7 @@ const TaskListPage: React.FC = () => {
</Tooltip> </Tooltip>
{/* 買い物リスト:食材情報記入ボタン */} {/* 買い物リスト:食材情報記入ボタン */}
<Tooltip title="購入情報を記入"> <Tooltip title="購入情報を記入">
<IconButton color="primary" sx={{ marginRight: 0, marginLeft: 0 }} edge="end" aria-label="購入情報を記入" <IconButton color="primary" sx={{ mr: 0, ml: 0 }} edge="end" aria-label="購入情報を記入"
onClick={() => { onClick={() => {
setOpenInfoDialog(true) setOpenInfoDialog(true)
setEditingItem(tobuy) setEditingItem(tobuy)

Loading…
Cancel
Save