Skip to content

Commit b8a6fcd

Browse files
feat: Add Teams page to showcase project members
This commit introduces a new "Teams" page to the application. The page displays a list of team members using a `TeamMemberCard` component, which shows each member's name, GitHub profile link, roles within the project, and an avatar (with a fallback). Roles are displayed as chips, allowing for multiple roles per member. A special "Maybe You!" card is also included as a call to action for potential contributors, linking to the project's contributing guidelines. Key changes include: - Creation of `TeamMember` interface. - Implementation of `TeamMemberCard` and `MaybeYouCard` components. - Static data definition for team members and CTA card content. - The main `TeamsPage` component to display the cards in a responsive grid. - Addition of necessary translations for English and Chinese. - Integration of the "Teams" link into the main navigation (desktop and mobile). - Routing setup for the `/teams` path. All new components and pages follow existing styling conventions and are responsive.
1 parent 0965a33 commit b8a6fcd

File tree

13 files changed

+403
-0
lines changed

13 files changed

+403
-0
lines changed

public/locales/en/components/navigationbar.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"documentation": "Documentation",
44
"plugins": "Plugins",
55
"themes": "Themes",
6+
"teams": "Teams",
67
"theme_builder": "Theme Builder",
78
"community": "Community",
89
"github": "GitHub",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"team_member_card": {
3+
"github_profile_link": "GitHub Profile"
4+
},
5+
"maybe_you_card": {
6+
"learn_more_button": "Learn More"
7+
}
8+
}

public/locales/en/pages/teams.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"teams_page": {
3+
"title": "Meet Our Team",
4+
"introduction": "The dedicated individuals making this project a success. We're passionate about creating great software."
5+
},
6+
"team_page": {
7+
"maybe_you_title": "Maybe You!",
8+
"maybe_you_text": "Interested in joining us? We welcome new contributors. Help shape the future of our project!"
9+
}
10+
}

public/locales/zh/components/navigationbar.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"documentation": "文档",
44
"plugins": "插件",
55
"themes": "主题",
6+
"teams": "团队",
67
"theme_builder": "主题构建器",
78
"community": "社区",
89
"github": "GitHub",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"team_member_card": {
3+
"github_profile_link": "GitHub 个人资料"
4+
},
5+
"maybe_you_card": {
6+
"learn_more_button": "了解更多"
7+
}
8+
}

public/locales/zh/pages/teams.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"teams_page": {
3+
"title": "认识我们的团队",
4+
"introduction": "致力于使这个项目成功的敬业人士。我们热衷于创造出色的软件。"
5+
},
6+
"team_page": {
7+
"maybe_you_title": "也许是你!",
8+
"maybe_you_text": "有兴趣加入我们吗?我们欢迎新的贡献者。帮助塑造我们项目的未来!"
9+
}
10+
}

src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import ErrorPage from './pages/Error';
1414
import HomePage from './pages/Home';
1515
import LoginProcessPage from './pages/LoginProcess';
1616
import PluginsPage from './pages/Plugins';
17+
import TeamsPage from './pages/Teams'; // Import the Teams page
1718
import ThemeBuilderPage from './pages/ThemeBuilder';
1819
import ThemesPage from './pages/Themes';
1920
import UserProfilePage from './pages/UserProfile';
@@ -77,6 +78,7 @@ const App: React.FC = () => {
7778
{ element: <PluginsPage />, path: '/plugins' },
7879
{ element: <ThemesPage />, path: '/themes' },
7980
{ element: <ThemeBuilderPage />, path: '/theme-builder' },
81+
{ element: <TeamsPage />, path: '/teams' }, // Add the Teams page route
8082
{
8183
element: <ProtectedRoute element={<UserProfilePage />} />,
8284
path: '/profile',

src/components/NavigationBar/NavigationBar.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import CodeIcon from '@mui/icons-material/Code';
44
import DescriptionIcon from '@mui/icons-material/Description';
55
import ExtensionIcon from '@mui/icons-material/Extension';
66
import GitHubIcon from '@mui/icons-material/GitHub';
7+
import GroupsIcon from '@mui/icons-material/Groups';
78
import LanguageIcon from '@mui/icons-material/Language';
89
import MenuIcon from '@mui/icons-material/Menu';
910
import NightlightIcon from '@mui/icons-material/Nightlight';
@@ -149,6 +150,9 @@ const NavigationBar: React.FC<{
149150
<Button component={Link} to="/themes" sx={generalNavLinkSx}>
150151
{t('navigation_bar.themes')}
151152
</Button>
153+
<Button component={Link} to="/teams" sx={generalNavLinkSx}>
154+
{t('navigation_bar.teams')}
155+
</Button>
152156
<Button component={Link} to="/theme-builder" sx={generalNavLinkSx}>
153157
{t('navigation_bar.theme_builder')}
154158
</Button>
@@ -352,6 +356,10 @@ const NavigationBar: React.FC<{
352356
<PaletteIcon sx={{ mr: 1 }} />
353357
<ListItemText primary={t('navigation_bar.themes')} />
354358
</ListItem>
359+
<ListItem component={Link} to="/teams" sx={generalNavLinkSx}>
360+
<GroupsIcon sx={{ mr: 1 }} />
361+
<ListItemText primary={t('navigation_bar.teams')} />
362+
</ListItem>
355363
<ListItem component={Link} to="/theme-builder" sx={generalNavLinkSx}>
356364
<CodeIcon sx={{ mr: 1 }} />
357365
<ListItemText primary={t('navigation_bar.theme_builder')} />

src/components/Team/MaybeYouCard.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { Box, Button, Card, CardContent, Typography } from '@mui/material';
2+
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; // Icon for CTA
3+
import React from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
6+
import useIsDesktop from '@/hooks/useIsDesktop';
7+
8+
/**
9+
* Props for the MaybeYouCard component.
10+
*/
11+
interface MaybeYouCardProps {
12+
/**
13+
* The URL for the call-to-action button.
14+
*/
15+
ctaUrl: string;
16+
/**
17+
* The title text for the card.
18+
*/
19+
title: string;
20+
/**
21+
* The descriptive text for the card.
22+
*/
23+
text: string;
24+
}
25+
26+
/**
27+
* Card component serving as a call to action for users to contribute.
28+
* It displays a title, descriptive text, and a button linking to a contribution URL.
29+
*/
30+
const MaybeYouCard: React.FC<MaybeYouCardProps> = ({ ctaUrl, title, text }) => {
31+
const { t } = useTranslation('components/team');
32+
const isDesktop = useIsDesktop();
33+
34+
const cardHeight = isDesktop ? 420 : 380; // Consistent with TeamMemberCard
35+
36+
return (
37+
<Card
38+
sx={{
39+
border: '2px dashed', // Distinct border style
40+
borderColor: 'primary.main', // Use primary color for the dashed border
41+
borderRadius: 5,
42+
display: 'flex',
43+
flexDirection: 'column',
44+
height: cardHeight,
45+
p: 2,
46+
width: isDesktop ? '100%' : '85vw', // Consistent with TeamMemberCard
47+
justifyContent: 'center', // Center content vertically
48+
alignItems: 'center', // Center content horizontally
49+
textAlign: 'center', // Center text
50+
backgroundColor: 'action.hover', // Slightly different background
51+
}}
52+
>
53+
<CardContent
54+
sx={{
55+
display: 'flex',
56+
flexDirection: 'column',
57+
alignItems: 'center',
58+
justifyContent: 'center',
59+
flexGrow: 1,
60+
}}
61+
>
62+
<AddCircleOutlineIcon sx={{ fontSize: 60, color: 'primary.main', mb: 2 }} />
63+
<Typography variant="h5" component="h3" sx={{ fontWeight: 'bold', mb: 1.5 }}>
64+
{t(title, title)}
65+
</Typography>
66+
<Typography variant="body1" color="text.secondary" sx={{ mb: 3 }}>
67+
{t(text, text)}
68+
</Typography>
69+
<Button
70+
variant="contained"
71+
color="primary"
72+
href={ctaUrl}
73+
target="_blank"
74+
rel="noopener noreferrer"
75+
sx={{
76+
borderRadius: '12px',
77+
textTransform: 'capitalize',
78+
fontWeight: 'bold',
79+
px: 4, // Add some padding to the button
80+
py: 1,
81+
}}
82+
>
83+
{t('maybe_you_card.learn_more_button', 'Learn More')}
84+
</Button>
85+
</CardContent>
86+
</Card>
87+
);
88+
};
89+
90+
export default MaybeYouCard;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { Box, Card, CardContent, CardMedia, Chip, Link, Typography } from '@mui/material';
2+
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
3+
import GitHubIcon from '@mui/icons-material/GitHub'; // Assuming GitHubIcon is available
4+
import React from 'react';
5+
import { useTranslation } from 'react-i18next';
6+
7+
import useIsDesktop from '@/hooks/useIsDesktop'; // Assuming this hook exists and is appropriate
8+
9+
import { TeamMember } from '../../interfaces/TeamMember';
10+
11+
/**
12+
* Props for the TeamMemberCard component.
13+
*/
14+
interface TeamMemberCardProps {
15+
/**
16+
* The team member data to display.
17+
*/
18+
member: TeamMember;
19+
}
20+
21+
/**
22+
* Card component to display information about a team member.
23+
* It shows the member's avatar, name, GitHub profile link, and roles.
24+
*/
25+
const TeamMemberCard: React.FC<TeamMemberCardProps> = ({ member }) => {
26+
const { t } = useTranslation('components/team');
27+
const isDesktop = useIsDesktop();
28+
29+
const cardHeight = isDesktop ? 420 : 380; // Adjusted height slightly from PluginCard
30+
const avatarSize = isDesktop ? 180 : 140; // Adjusted avatar size
31+
32+
return (
33+
<Card
34+
sx={{
35+
border: '2px solid',
36+
borderColor: 'divider',
37+
borderRadius: 5,
38+
display: 'flex',
39+
flexDirection: 'column',
40+
height: cardHeight,
41+
p: 2,
42+
width: isDesktop ? '100%' : '85vw', // Same width behavior as PluginCard
43+
justifyContent: 'space-between',
44+
}}
45+
>
46+
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: avatarSize, mb: 1 }}>
47+
{member.avatarUrl ? (
48+
<CardMedia
49+
component="img"
50+
image={member.avatarUrl}
51+
alt={member.name}
52+
sx={{
53+
borderRadius: '50%', // Circular avatar
54+
height: avatarSize,
55+
width: avatarSize,
56+
objectFit: 'cover',
57+
}}
58+
onError={(e: React.SyntheticEvent<HTMLImageElement, Event>) => {
59+
// Fallback to icon if image fails to load
60+
(e.target as HTMLImageElement).style.display = 'none';
61+
// Ideally, we'd render the icon here, but CardMedia doesn't easily allow a child fallback
62+
// For now, we'll rely on the AccountCircleIcon rendered below if avatarUrl is missing.
63+
// A more robust solution might involve a state variable to toggle between img and icon.
64+
}}
65+
/>
66+
) : (
67+
<AccountCircleIcon sx={{ fontSize: avatarSize, color: 'text.secondary' }} />
68+
)}
69+
</Box>
70+
<CardContent sx={{ textAlign: 'center', flexGrow: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', p: 1 }}>
71+
<Typography variant="h6" sx={{ fontWeight: 'bold', mb: 0.5 }}>
72+
{member.name}
73+
</Typography>
74+
<Link
75+
href={member.githubUrl}
76+
target="_blank"
77+
rel="noopener noreferrer"
78+
sx={{
79+
display: 'flex',
80+
alignItems: 'center',
81+
justifyContent: 'center',
82+
gap: 0.5,
83+
color: 'text.secondary',
84+
textDecoration: 'none',
85+
'&:hover': {
86+
textDecoration: 'underline',
87+
color: 'primary.main',
88+
},
89+
mb: 1,
90+
}}
91+
>
92+
<GitHubIcon sx={{ fontSize: 20 }} />
93+
<Typography variant="body2">
94+
{t('team_member_card.github_profile', 'GitHub')}
95+
</Typography>
96+
</Link>
97+
<Box
98+
sx={{
99+
display: 'flex',
100+
flexWrap: 'wrap',
101+
justifyContent: 'center',
102+
gap: 0.5,
103+
mt: 1,
104+
maxHeight: isDesktop ? 80 : 60, // Max height for roles container
105+
overflowY: 'auto', // Scroll if many roles
106+
}}
107+
>
108+
{member.roles.map((role) => (
109+
<Chip label={role} key={role} size="small" />
110+
))}
111+
</Box>
112+
</CardContent>
113+
</Card>
114+
);
115+
};
116+
117+
export default TeamMemberCard;

src/constants/TeamData.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { TeamMember } from '@/interfaces/TeamMember';
2+
import { Endpoints } from '@/constants/Endpoints';
3+
4+
/**
5+
* Array of team members for the project.
6+
*/
7+
export const teamMembers: TeamMember[] = [
8+
{
9+
id: 'member-1',
10+
name: 'Alex Doe',
11+
githubUrl: 'https://github.com/octocat',
12+
roles: ['Core Maintainer', 'Lead Developer'],
13+
avatarUrl: 'https://i.pravatar.cc/150?u=member-1',
14+
},
15+
{
16+
id: 'member-2',
17+
name: 'Jamie Lan',
18+
githubUrl: 'https://github.com/torvalds', // Linus Torvalds' GitHub for variety
19+
roles: ['Documentation Lead', 'Community Support'],
20+
// avatarUrl is intentionally left undefined to test fallback
21+
},
22+
{
23+
id: 'member-3',
24+
name: 'Sam Ray',
25+
githubUrl: 'https://github.com/gaearon', // Dan Abramov's GitHub for variety
26+
roles: ['UI/UX Designer', 'Frontend Specialist'],
27+
avatarUrl: 'https://i.pravatar.cc/150?u=member-3',
28+
},
29+
];
30+
31+
/**
32+
* Data for the "Maybe You!" call-to-action card on the team page.
33+
*/
34+
export const maybeYouCardData = {
35+
/**
36+
* Translation key for the title of the card.
37+
*/
38+
titleKey: 'team_page.maybe_you_title',
39+
/**
40+
* Translation key for the descriptive text of the card.
41+
*/
42+
textKey: 'team_page.maybe_you_text',
43+
/**
44+
* URL for the call-to-action button, typically linking to contribution guidelines.
45+
*/
46+
ctaUrl: `${Endpoints.projectCoreRepoUrl}/blob/main/CONTRIBUTING.md`,
47+
};

src/interfaces/TeamMember.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Represents a team member.
3+
*/
4+
export interface TeamMember {
5+
/**
6+
* The unique identifier for the team member.
7+
*/
8+
id: string;
9+
10+
/**
11+
* The name of the team member.
12+
*/
13+
name: string;
14+
15+
/**
16+
* The GitHub profile URL of the team member.
17+
*/
18+
githubUrl: string;
19+
20+
/**
21+
* An array of roles the team member has.
22+
*/
23+
roles: string[];
24+
25+
/**
26+
* The URL of the team member's avatar image (optional).
27+
*/
28+
avatarUrl?: string;
29+
}

0 commit comments

Comments
 (0)