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.
179 lines
5.5 KiB
179 lines
5.5 KiB
/**
|
|
* アプリケーションの共通レイアウトを提供するコンポーネント
|
|
* ヘッダー(AppBar)とメインコンテンツ領域を含む基本的なページ構造を定義
|
|
*/
|
|
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
AppBar,
|
|
Toolbar,
|
|
Typography,
|
|
Container,
|
|
Box,
|
|
Button,
|
|
Drawer,
|
|
List,
|
|
ListItemText,
|
|
ListItemIcon,
|
|
ListItemButton,
|
|
Divider,
|
|
IconButton,
|
|
AlertColor,
|
|
BottomNavigation,
|
|
BottomNavigationAction,
|
|
Paper
|
|
} from '@mui/material';
|
|
import {
|
|
Menu as MenuIcon,
|
|
ListAlt as ListAltIcon,
|
|
Inventory as InventoryIcon, // テストページ用のアイコン
|
|
Science as ScienceIcon, // 鈴木
|
|
SoupKitchen as SoupKitchenIcon,
|
|
ShoppingCart as ShoppingCartIcon
|
|
} from '@mui/icons-material';
|
|
import { useNavigate, Outlet, useLocation } from 'react-router-dom';
|
|
import { MessageContext } from './MessageContext';
|
|
import MessageAlert from './MessageAlert';
|
|
|
|
const Layout: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
|
|
|
|
const getTabIndex = (pathname: string) => {
|
|
pathname = pathname.split("/")[1];
|
|
console.log(pathname);
|
|
switch (pathname) {
|
|
case 'stock':
|
|
return 0;
|
|
case 'tasks':
|
|
return 1;
|
|
case 'recipeList':
|
|
return 2;
|
|
case 'addRecipe':
|
|
return 2;
|
|
default:
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const [bottomNavi, setBottomNavi] = useState(getTabIndex(location.pathname));
|
|
|
|
useEffect(() => {
|
|
setBottomNavi(getTabIndex(location.pathname));
|
|
}, [location.pathname]);
|
|
|
|
/**
|
|
* ログアウト処理を行うハンドラー関数
|
|
* ローカルストレージからトークンを削除し、ログインページにリダイレクト
|
|
*/
|
|
const handleLogout = () => {
|
|
localStorage.removeItem('token');
|
|
navigate('/login');
|
|
};
|
|
|
|
// メニューを開閉するハンドラー
|
|
const toggleDrawer = () => {
|
|
setDrawerOpen(!drawerOpen);
|
|
};
|
|
|
|
// メッセージ表示
|
|
|
|
// ページ遷移後もメッセージを維持
|
|
useEffect(() => {
|
|
const saved = sessionStorage.getItem('globalMessage');
|
|
if (saved) {
|
|
const { message, severity } = JSON.parse(saved);
|
|
showMessage(message, severity);
|
|
}
|
|
}, []);
|
|
|
|
const [msgOpen, setMsgOpen] = useState(false);
|
|
const [msgText, setMsgText] = useState('');
|
|
const [msgType, setMsgType] = useState<AlertColor>('info');
|
|
|
|
const showMessage = (msg: string, sev: AlertColor) => {
|
|
console.log('ShowMessage:', msg, sev);
|
|
setMsgText(msg);
|
|
setMsgType(sev);
|
|
setMsgOpen(true);
|
|
sessionStorage.setItem('globalMessage', JSON.stringify({ message: msg, severity: sev }));
|
|
};
|
|
|
|
const showErrorMessage = (message: string) => showMessage(message, 'error');
|
|
const showWarningMessage = (message: string) => showMessage(message, 'warning');
|
|
const showInfoMessage = (message: string) => showMessage(message, 'info');
|
|
const showSuccessMessage = (message: string) => showMessage(message, 'success');
|
|
|
|
const handleMsgClose = () => {
|
|
setMsgOpen(false);
|
|
// setMsgText(''); // ここで空にすると,メッセージが消えるアニメーションが始まる時点で文字が消えてしまう
|
|
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 (
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
|
|
{/* ヘッダー部分 - アプリ名とログアウトボタンを表示 */}
|
|
<AppBar position="static" elevation={0}>
|
|
<Toolbar>
|
|
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
|
shopchop
|
|
</Typography>
|
|
<Button color="inherit" onClick={handleLogout}>
|
|
ログアウト
|
|
</Button>
|
|
</Toolbar>
|
|
</AppBar>
|
|
|
|
<Paper sx={{ position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: (theme) => theme.zIndex.drawer + 1 }} elevation={3}>
|
|
<BottomNavigation
|
|
showLabels
|
|
value={bottomNavi}
|
|
onChange={onBottomNaviChange}
|
|
>
|
|
<BottomNavigationAction label="在庫" icon={<InventoryIcon />} />
|
|
<BottomNavigationAction label="買うもの" icon={<ShoppingCartIcon />} />
|
|
<BottomNavigationAction label="レシピ" icon={<SoupKitchenIcon />} />
|
|
</BottomNavigation>
|
|
</Paper>
|
|
|
|
{/* メインコンテンツ領域 - 子ルートのコンポーネントがここに表示される */}
|
|
<Box component="main" sx={{ flexGrow: 1, bgcolor: 'background.default', py: 3 }}>
|
|
<Container sx={{ padding: 0, mt: 0, mb: 0, mr: 'auto', ml: 'auto' }}>
|
|
<MessageContext.Provider value={{ showErrorMessage, showWarningMessage, showSuccessMessage, showInfoMessage }}>
|
|
<MessageAlert
|
|
open={msgOpen}
|
|
message={msgText}
|
|
severity={msgType}
|
|
onClose={handleMsgClose}
|
|
/>
|
|
<Outlet /> {/* React Router の Outlet - 子ルートのコンポーネントがここにレンダリングされる */}
|
|
</MessageContext.Provider>
|
|
</Container>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
|
|
export default Layout;
|
|
|