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.
313 lines
11 KiB
313 lines
11 KiB
/**
|
|
* テストページコンポーネント
|
|
* 白紙の状態で表示されるテスト用のページ
|
|
*/
|
|
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
Container,
|
|
Typography,
|
|
Tooltip,
|
|
List,
|
|
ListItem,
|
|
ListItemText,
|
|
ListItemSecondaryAction,
|
|
IconButton,
|
|
Checkbox,
|
|
Fab,
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions,
|
|
TextField,
|
|
Button,
|
|
Box,
|
|
FormGroup,
|
|
MenuItem,
|
|
Select,
|
|
FormControl,
|
|
InputLabel,
|
|
ListItemIcon
|
|
} from '@mui/material';
|
|
import {
|
|
Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon,
|
|
SoupKitchen as SoupKitchenIcon, Edit as EditIcon, Save as SaveIcon, ListAlt as ListAltIcon
|
|
} from '@mui/icons-material';
|
|
import AddStuffAmountDialog from '../components/AddStuffAmountDialog';
|
|
import { StuffAndCategoryAndAmount } from '../types/types';
|
|
import EditAmountDialog from '../components/EditAmountDialog';
|
|
import { recipeApi, toBuyApi } from '../services/api';
|
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
|
|
const AddRecipe: React.FC = () => {
|
|
const { recipeId: recipeIdStr } = useParams();
|
|
const recipeId = recipeIdStr ? parseInt(recipeIdStr) : null
|
|
|
|
const navigate = useNavigate();
|
|
|
|
// 何人分かを格納する
|
|
const [numOfPeaple, setNumOfPeaple] = useState<number>(1);
|
|
const [openNumOfPeapleDialog, setOpenNumOfPeapleDialog] = useState(false);
|
|
// 料理名,説明
|
|
const [recipeName, setRecipeName] = useState<string>('');
|
|
const [recipeSummary, setRecipeSummary] = useState<string>('');
|
|
// 材料リスト
|
|
const [items, setItems] = useState<StuffAndCategoryAndAmount[]>([]);
|
|
// 材料追加作成ダイアログの表示状態
|
|
const [openAddDialog, setOpenAddDialog] = useState(false);
|
|
// 材料追加作成ダイアログの表示状態
|
|
const [openAmountDialog, setOpenAmountDialog] = useState(false);
|
|
// 新規アイテムの入力内容
|
|
const emptyItem: StuffAndCategoryAndAmount = { stuffId: null, stuffName: '', category: '', amount: 1 }
|
|
const [newItem, setNewItem] = useState<StuffAndCategoryAndAmount>(emptyItem);
|
|
// 編集しているアイテム
|
|
const [editingItem, setEditingItem] = useState<StuffAndCategoryAndAmount>(emptyItem);
|
|
const [editingItemIdx, setEditingItemIdx] = useState(0);
|
|
|
|
const loadRecipe = async () => {
|
|
if (recipeId && !recipeName) {
|
|
const recipe = await recipeApi.getById(recipeId);
|
|
console.log('loaded recipe=', recipe)
|
|
setRecipeName(recipe.recipeName)
|
|
setRecipeSummary(recipe.summary)
|
|
setItems(recipe.stuffAndAmountArray)
|
|
}
|
|
}
|
|
|
|
const handleSaveRecipe = async () => {
|
|
|
|
// if (!recipeName) {
|
|
// alert('レシピ名が入力されていません!')
|
|
// console.log("yes1");
|
|
// return false;
|
|
// }
|
|
|
|
// if (!items.length) {
|
|
// alert('材料が追加されていません!')
|
|
// console.log("yes2");
|
|
// return false;
|
|
// }
|
|
|
|
if (!recipeId) {
|
|
console.log("yes3");
|
|
// 新規追加
|
|
const response = await recipeApi.addRecipe({
|
|
recipeName,
|
|
summary: recipeSummary,
|
|
stuffAndAmountArray: items,
|
|
})
|
|
return response.recipeId;
|
|
}
|
|
|
|
const response = await recipeApi.updateRecipe({
|
|
recipeId,
|
|
recipeName,
|
|
summary: recipeSummary,
|
|
stuffAndAmountArray: items,
|
|
})
|
|
console.log("yes4");
|
|
return recipeId;
|
|
}
|
|
|
|
const checkRecipeAndItems = async () => {
|
|
|
|
if (!recipeName) {
|
|
alert('レシピ名が入力されていません!')
|
|
console.log("yes1");
|
|
return false;
|
|
}
|
|
|
|
if (!items.length) {
|
|
alert('材料が追加されていません!')
|
|
console.log("yes2");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const handleSubmit = async () => {
|
|
const recipeId = await handleSaveRecipe();
|
|
// alert('レシピが保存されました!');
|
|
if (!recipeId) return;
|
|
navigate('/recipeList');
|
|
}
|
|
|
|
const handleSubmitAndAddToBuy = async () => {
|
|
console.log("too");
|
|
console.log(recipeName);
|
|
const recipeId = await handleSaveRecipe();
|
|
console.log("before");
|
|
if (!recipeId) return false;
|
|
console.log("ds");
|
|
await toBuyApi.addByRecipe(recipeId);
|
|
// alert('レシピが保存されて買うものリストに追加されました!');
|
|
navigate('/tasks');
|
|
}
|
|
|
|
const openNumOfPeopleDialog = async () => {
|
|
const check = await checkRecipeAndItems();
|
|
if (!check) return false;
|
|
setOpenNumOfPeapleDialog(true);
|
|
}
|
|
|
|
const cancelNumOfPeopleDialog = async () => {
|
|
const recipeId = await handleSaveRecipe();
|
|
if (!recipeId) return false;
|
|
setOpenNumOfPeapleDialog(false);
|
|
}
|
|
|
|
// コンポーネントマウント時にタスク一覧を取得
|
|
useEffect(() => {
|
|
loadRecipe();
|
|
}, []);
|
|
|
|
return (
|
|
(recipeId && !recipeName)
|
|
? <p>読み込み中...</p>
|
|
:
|
|
<Box>
|
|
<div>
|
|
<h1>
|
|
<SoupKitchenIcon sx={{ marginRight: "0.5em" }} />
|
|
{!recipeId ? '料理の追加' : '料理の編集'}
|
|
</h1>
|
|
<TextField autoFocus margin="dense" label="料理名" fullWidth
|
|
value={recipeName} onChange={(e) => setRecipeName(e.target.value)}
|
|
/>
|
|
<TextField margin="dense" label="説明" fullWidth
|
|
value={recipeSummary} onChange={(e) => setRecipeSummary(e.target.value)}
|
|
/>
|
|
</div>
|
|
<h2 style={{ marginTop: "0.5em" }}>
|
|
材料リスト
|
|
</h2>
|
|
{/* すべての材料情報を表示 */}
|
|
{(!items || !items.length)
|
|
? (<p>+ボタンで材料を追加してください</p>)
|
|
: (<List>{items.map((item, index) => (
|
|
|
|
<ListItem
|
|
key={index}
|
|
sx={{
|
|
bgcolor: 'background.paper',
|
|
mb: 1,
|
|
borderRadius: 1,
|
|
boxShadow: 1,
|
|
}}
|
|
>
|
|
<ListItemText primary={item.stuffName} />
|
|
{/* 買い物リスト:食材情報記入ボタン */}
|
|
<ListItemSecondaryAction>
|
|
<Typography variant="body1" component="span" sx={{ marginRight: '1em' }}>
|
|
{`× ${item.amount}`}
|
|
</Typography>
|
|
{/* 買い物リスト:数量変更ボタン */}
|
|
<Tooltip title="数量変更">
|
|
<IconButton sx={{ marginRight: 0, marginLeft: 0 }} edge="end" aria-label="数量変更"
|
|
onClick={() => {
|
|
setOpenAmountDialog(true)
|
|
setEditingItemIdx(index)
|
|
setEditingItem(item)
|
|
}}
|
|
>
|
|
<EditIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
{/* 買い物リスト:食材削除ボタン */}
|
|
<Tooltip title="項目を削除"
|
|
componentsProps={{
|
|
tooltip: { sx: { backgroundColor: "white", color: "red", fontSize: "0.8rem", padding: "6px", borderRadius: "6px" } },
|
|
}}>
|
|
<IconButton edge="end" sx={{ marginRight: 0, marginLeft: 0 }} aria-label="delete"
|
|
onClick={() => setItems([...items.slice(0, index), ...items.slice(index + 1)])}>
|
|
<DeleteIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
|
|
</ListItemSecondaryAction>
|
|
|
|
</ListItem>
|
|
|
|
))}</List>)}
|
|
|
|
|
|
|
|
<div style={{ position: "fixed", left: "80%", transform: 'translateX(-50%)', bottom: "10%" }}>
|
|
<Box sx={{ textAlign: 'center' }}>
|
|
<Typography variant="caption">材料を追加</Typography>
|
|
</Box>
|
|
<Fab color="primary" onClick={() => setOpenAddDialog(true)}>
|
|
<AddIcon sx={{ fontSize: "1.5rem" }} />
|
|
</Fab>
|
|
</div>
|
|
|
|
<div style={{ position: "fixed", left: "50%", transform: 'translateX(-50%)', bottom: "2%", whiteSpace: 'nowrap' }}>
|
|
<Button variant='contained' color="primary" onClick={handleSubmit} sx={{ marginRight: "1rem" }}>
|
|
<SaveIcon sx={{ fontSize: "1.5rem", marginRight: "0.5rem" }} />
|
|
レシピを保存
|
|
</Button>
|
|
<Button variant='contained' color="primary" onClick={openNumOfPeopleDialog}>
|
|
<ListAltIcon sx={{ fontSize: "1.5rem", marginRight: "0.5rem" }} />
|
|
保存して買うものリストへ追加
|
|
</Button>
|
|
</div>
|
|
|
|
{/* 新規材料追加ダイアログ */}
|
|
<AddStuffAmountDialog openDialog={openAddDialog} setOpenDialog={setOpenAddDialog} newItem={newItem} setNewItem={setNewItem}
|
|
onSubmit={() => {
|
|
console.log('newItem:', newItem);
|
|
setItems([...items, newItem]);
|
|
setOpenAddDialog(false);
|
|
}} />
|
|
|
|
{/* 数量変更ダイアログ */}
|
|
<EditAmountDialog openDialog={openAmountDialog} setOpenDialog={setOpenAmountDialog}
|
|
editingItem={editingItem}
|
|
setEditingItem={(v) => setEditingItem({ ...editingItem, ...v })}
|
|
onSubmit={() => {
|
|
setItems([...items.slice(0, editingItemIdx), editingItem, ...items.slice(editingItemIdx + 1)])
|
|
setOpenAmountDialog(false);
|
|
}} />
|
|
|
|
<Dialog open={openNumOfPeapleDialog} onClose={() => setOpenNumOfPeapleDialog(false)} disableScrollLock={true}
|
|
style={{ width : '100%', position : 'fixed', left: '50%', transform: 'translateX(-50%)' }}
|
|
>
|
|
<Box display="flex" alignItems="center"
|
|
>
|
|
<DialogTitle sx={{ flexGrow: 1 }}>何人前かを入力</DialogTitle>
|
|
</Box>
|
|
<DialogContent>
|
|
<div>
|
|
{/* 人数入力フィールド */}
|
|
<TextField
|
|
margin="dense"
|
|
label="何人前"
|
|
fullWidth
|
|
value={numOfPeaple}
|
|
onChange={(e) => {
|
|
const value = e.target.value;
|
|
const parsedValue = parseInt(value, 10); // 数値に変換
|
|
if (/*!isNaN(parsedValue) && */ isNaN(parsedValue) || parsedValue >= 1) { //負数除外
|
|
setNumOfPeaple(parsedValue); // number型で保存
|
|
}
|
|
}}
|
|
sx={{ minWidth: "8px", width: "100%" }}
|
|
type="number"
|
|
inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可
|
|
|
|
/>
|
|
</div>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={() => setOpenNumOfPeapleDialog(false)}>前の画面に戻る</Button>
|
|
<Button onClick={() => handleSubmitAndAddToBuy} variant="contained"
|
|
style={{ width: '40%', fontSize: '1vw' }}
|
|
>
|
|
人数分の食材を買い物リストへ登録
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default AddRecipe; |