import React, { useState, createContext, useContext, useCallback } from 'react';
import {
AppBar,
Toolbar,
InputBase,
Card,
CardContent,
Typography,
Button,
Grid,
createTheme,
ThemeProvider,
Box,
List,
ListItem,
ListItemText,
Popper,
Paper,
ClickAwayListener,
Grow,
Container,
IconButton,
Select,
MenuItem,
FormControl,
InputLabel,
Tabs,
Tab,
Tooltip
} from '@mui/material';
import {
Search as SearchIcon,
Add as AddIcon,
ArrowForward as ArrowForwardIcon,
Home as HomeIcon,
Api as ApiIcon,
Description as DescriptionIcon,
AttachMoney as AttachMoneyIcon,
Help as HelpIcon,
AccountCircle as AccountCircleIcon,
Dashboard as DashboardIcon,
BarChart as BarChartIcon,
Settings as SettingsIcon
} from '@mui/icons-material';
import { styled, alpha } from '@mui/material/styles';
import { IntlProvider, FormattedMessage, useIntl } from 'react-intl';
// Language context
const LanguageContext = createContext();
// Language provider component
const LanguageProvider = ({ children, value }) => {
return (
<LanguageContext.Provider value={value}>
{children}
</LanguageContext.Provider>
);
};
// Hook to use language
const useLanguage = () => {
const context = useContext(LanguageContext);
if (context === undefined) {
throw new Error('useLanguage must be used within a LanguageProvider');
}
return context;
};
// Translation messages
const messages = {
zh: {
'app.title': 'API查询平台',
'app.subtitle': '轻松查找、比较和集成各种API',
'search.placeholder': '请输入API名称',
'search.button': '搜索',
'api.categories': 'API 类别',
'api.type': 'API类型',
'api.type.all': '全部',
'api.type.hbim': 'HBIM',
'api.type.project': '项目',
'api.add': '新增 API',
'api.count': '{count} 个 API',
'api.type.label': '类型: {type}',
'tab.home': '首页',
'tab.apis': 'API',
'tab.docs': '文档',
'tab.pricing': '价格',
'tab.support': '支持',
'tab.account': '账户',
'tab.dashboard': '仪表板',
'tab.analytics': '分析',
'tab.settings': '设置',
'language.switch': '切换到英文',
'category.auth': '认证与授权',
'category.data': '数据处理',
'category.payment': '支付集成',
'category.social': '社交媒体',
'category.map': '地图服务',
'category.notification': '消息推送',
},
en: {
'app.title': 'API Query Platform',
'app.subtitle': 'Easily find, compare and integrate various APIs',
'search.placeholder': 'Enter API name',
'search.button': 'Search',
'api.categories': 'API Categories',
'api.type': 'API Type',
'api.type.all': 'All',
'api.type.hbim': 'HBIM',
'api.type.project': 'Project',
'api.add': 'Add API',
'api.count': '{count} APIs',
'api.type.label': 'Type: {type}',
'tab.home': 'Home',
'tab.apis': 'APIs',
'tab.docs': 'Documentation',
'tab.pricing': 'Pricing',
'tab.support': 'Support',
'tab.account': 'Account',
'tab.dashboard': 'Dashboard',
'tab.analytics': 'Analytics',
'tab.settings': 'Settings',
'language.switch': 'Switch to Chinese',
'category.auth': 'Authentication & Authorization',
'category.data': 'Data Processing',
'category.payment': 'Payment Integration',
'category.social': 'Social Media',
'category.map': 'Map Services',
'category.notification': 'Notifications',
},
};
// API categories data
const apiCategories = [
{ id: 1, nameId: 'category.auth', count: 15, type: 'hbim' },
{ id: 2, nameId: 'category.data', count: 23, type: 'hbim' },
{ id: 3, nameId: 'category.payment', count: 8, type: 'project' },
{ id: 4, nameId: 'category.social', count: 12, type: 'project' },
{ id: 5, nameId: 'category.map', count: 7, type: 'hbim' },
{ id: 6, nameId: 'category.notification', count: 9, type: 'project' },
];
// Styled components (unchanged)
const Search = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.white, 1),
'&:hover': {
backgroundColor: alpha(theme.palette.common.white, 0.95),
},
marginRight: theme.spacing(2),
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
width: 'auto',
},
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: theme.palette.text.primary,
width: '100%',
'& .MuiInputBase-input': {
padding: theme.spacing(1.5, 1, 1.5, 0),
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('md')]: {
width: '50ch',
},
},
}));
const StyledPopper = styled(Popper)(({ theme }) => ({
zIndex: theme.zIndex.modal,
width: '100%',
[theme.breakpoints.up('md')]: {
width: '50ch',
},
}));
const ResultItem = styled(ListItem)(({ theme }) => ({
'&:hover': {
backgroundColor: alpha(theme.palette.primary.main, 0.08),
},
}));
const StyledTab = styled(Tab)(({ theme }) => ({
minWidth: 100,
maxWidth: 100,
width: 100,
padding: theme.spacing(1),
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}));
const theme = createTheme({
palette: {
primary: {
main: '#2196f3',
},
secondary: {
main: '#f50057',
},
background: {
default: '#f5f5f5',
},
},
typography: {
fontFamily: [
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
},
});
function ApiCatalogPage() {
const [searchTerm, setSearchTerm] = useState('');
const [anchorEl, setAnchorEl] = useState(null);
const [apiType, setApiType] = useState('all');
const [tabValue, setTabValue] = useState(0);
const { locale, setLocale } = useLanguage();
const intl = useIntl();
const handleSearchChange = (event) => {
setSearchTerm(event.target.value);
setAnchorEl(event.currentTarget);
};
const handleClickAway = () => {
setAnchorEl(null);
};
const handleApiTypeChange = (event) => {
setApiType(event.target.value);
};
const handleTabChange = (event, newValue) => {
setTabValue(newValue);
};
const toggleLanguage = useCallback(() => {
setLocale(prevLocale => prevLocale === 'zh' ? 'en' : 'zh');
}, [setLocale]);
const filteredCategories = apiCategories.filter(category =>
apiType === 'all' || category.type === apiType
);
const open = Boolean(anchorEl) && searchTerm.length > 0;
const tabItems = [
{ id: 'tab.home', label: intl.formatMessage({ id: 'tab.home' }), icon: <HomeIcon /> },
{ id: 'tab.apis', label: intl.formatMessage({ id: 'tab.apis' }), icon: <ApiIcon /> },
{ id: 'tab.docs', label: intl.formatMessage({ id: 'tab.docs' }), icon: <DescriptionIcon /> },
{ id: 'tab.pricing', label: intl.formatMessage({ id: 'tab.pricing' }), icon: <AttachMoneyIcon /> },
{ id: 'tab.support', label: intl.formatMessage({ id: 'tab.support' }), icon: <HelpIcon /> },
{ id: 'tab.account', label: intl.formatMessage({ id: 'tab.account' }), icon: <AccountCircleIcon /> },
{ id: 'tab.dashboard', label: intl.formatMessage({ id: 'tab.dashboard' }), icon: <DashboardIcon /> },
{ id: 'tab.analytics', label: intl.formatMessage({ id: 'tab.analytics' }), icon: <BarChartIcon /> },
{ id: 'tab.settings', label: intl.formatMessage({ id: 'tab.settings' }), icon: <SettingsIcon /> },
];
return (
<ThemeProvider theme={theme}>
<div className="min-h-screen bg-gray-100">
<AppBar position="static" color="default" elevation={0}>
<Toolbar>
<Tabs
value={tabValue}
onChange={handleTabChange}
aria-label="navigation tabs"
variant="scrollable"
scrollButtons="auto"
sx={{ flexGrow: 1 }}
>
{tabItems.map((tab, index) => (
<Tooltip title={tab.label} key={tab.id}>
<StyledTab
icon={tab.icon}
label={tab.label}
id={`tab-${index}`}
aria-controls={`tabpanel-${index}`}
/>
</Tooltip>
))}
</Tabs>
<Button onClick={toggleLanguage}>
<FormattedMessage id="language.switch" />
</Button>
</Toolbar>
</AppBar>
<Box sx={{ bgcolor: 'primary.main', color: 'white', py: 8, textAlign: 'center' }}>
<Container maxWidth="lg">
<Typography variant="h3" component="h1" gutterBottom>
<FormattedMessage id="app.title" />
</Typography>
<Typography variant="h6" component="p" gutterBottom sx={{ mb: 4 }}>
<FormattedMessage id="app.subtitle" />
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'center', position: 'relative' }}>
<Search>
<SearchIconWrapper>
<SearchIcon />
</SearchIconWrapper>
<StyledInputBase
placeholder={intl.formatMessage({ id: 'search.placeholder' })}
inputProps={{ 'aria-label': 'search' }}
value={searchTerm}
onChange={handleSearchChange}
/>
<Button
variant="contained"
color="secondary"
sx={{ position: 'absolute', right: 0, top: 0, bottom: 0 }}
>
<FormattedMessage id="search.button" />
</Button>
</Search>
<StyledPopper open={open} anchorEl={anchorEl} placement="bottom-start" transition>
{({ TransitionProps }) => (
<Grow {...TransitionProps} style={{ transformOrigin: 'top left' }}>
<Paper elevation={3} sx={{ mt: 1, borderRadius: 2, overflow: 'hidden' }}>
<ClickAwayListener onClickAway={handleClickAway}>
<List sx={{ py: 0 }}>
{/* We would need to implement a proper search functionality here */}
<ResultItem button>
<ListItemText
primary={intl.formatMessage({ id: 'category.auth' })}
primaryTypographyProps={{
sx: { fontWeight: 'medium' }
}}
/>
</ResultItem>
</List>
</ClickAwayListener>
</Paper>
</Grow>
)}
</StyledPopper>
</Box>
</Container>
</Box>
<Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h5" component="h2">
<FormattedMessage id="api.categories" />
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<FormControl variant="outlined" size="small">
<InputLabel id="api-type-select-label">
<FormattedMessage id="api.type" />
</InputLabel>
<Select
labelId="api-type-select-label"
id="api-type-select"
value={apiType}
onChange={handleApiTypeChange}
label={intl.formatMessage({ id: 'api.type' })}
>
<MenuItem value="all"><FormattedMessage id="api.type.all" /></MenuItem>
<MenuItem value="hbim"><FormattedMessage id="api.type.hbim" /></MenuItem>
<MenuItem value="project"><FormattedMessage id="api.type.project" /></MenuItem>
</Select>
</FormControl>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
sx={{ fontWeight: 'bold' }}
>
<FormattedMessage id="api.add" />
</Button>
</Box>
</Box>
<Grid container spacing={3}>
{filteredCategories.map((category) => (
<Grid item xs={12} sm={6} md={4} key={category.id}>
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column', transition: '0.3s', '&:hover': { transform: 'translateY(-5px)', boxShadow: 3 } }}>
<CardContent sx={{ flexGrow: 1 }}>
<Typography variant="h6" component="h3" gutterBottom>
<FormattedMessage id={category.nameId} />
</Typography>
<Typography variant="body2" color="text.secondary">
<FormattedMessage id="api.count" values={{ count: category.count }} />
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
<FormattedMessage id="api.type.label" values={{ type: intl.formatMessage({ id: `api.type.${category.type}` }) }} />
</Typography>
</CardContent>
<Box sx={{ p: 2, display: 'flex', justifyContent: 'flex-end' }}>
<IconButton size="small" color="primary">
<ArrowForwardIcon />
</IconButton>
</Box>
</Card>
</Grid>
))}
</Grid>
</Container>
</div>
</ThemeProvider>
);
}
function App() {
const [locale, setLocale] = useState('zh');
const toggleLanguage = useCallback(() => {
setLocale(prevLocale => prevLocale === 'zh' ? 'en' : 'zh');
}, []);
return (
<LanguageProvider value={{ locale, setLocale: toggleLanguage }}>
<IntlProvider messages={messages[locale]} locale={locale}>
<ApiCatalogPage />
</IntlProvider>
</LanguageProvider>
);
}
export default function Component() {
return <App />;
}