Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4d32515
refactor: clean legacy contents.
ZacZhangzhuo Aug 24, 2025
1568ebd
feat: import MUI and template.
ZacZhangzhuo Aug 24, 2025
5ae6b72
feat: compatibility.
ZacZhangzhuo Aug 24, 2025
51d64e1
feat: remove unused js.
ZacZhangzhuo Aug 24, 2025
f2274d4
feat: add resource.
ZacZhangzhuo Aug 24, 2025
e81c47b
feat: basic constructions.
ZacZhangzhuo Aug 24, 2025
7ac59e8
feat: colour theme.
ZacZhangzhuo Aug 24, 2025
584e309
feat: main page done.
ZacZhangzhuo Aug 24, 2025
367e336
feat: improvements.
ZacZhangzhuo Aug 24, 2025
72e47e9
feat: slide images in main page.
ZacZhangzhuo Aug 24, 2025
3902a37
feat: carousel improved.
ZacZhangzhuo Aug 31, 2025
21679c8
chore: small updates, button links, constant
Apr 5, 2026
ae55e38
feat: locale support
ZacZhangzhuo Apr 5, 2026
d31d1fc
feat: membership card button
ZacZhangzhuo Apr 5, 2026
b245b6a
feat: about us page
ZacZhangzhuo Apr 5, 2026
13c449c
feat: about us section
ZacZhangzhuo Apr 5, 2026
73686bf
feat: visuals, texts
ZacZhangzhuo Apr 5, 2026
d667546
feat: extract style
ZacZhangzhuo Apr 5, 2026
034558e
fix: mail
ZacZhangzhuo Apr 5, 2026
d59a14c
feat: ins icon
ZacZhangzhuo Apr 5, 2026
3543343
feat: table of people
ZacZhangzhuo Apr 5, 2026
d127d19
chore: text
ZacZhangzhuo Apr 5, 2026
4c2e6a0
chore: text
ZacZhangzhuo Apr 5, 2026
e94f0f5
feat: doc
ZacZhangzhuo Apr 5, 2026
1f653f4
chore: clean up
ZacZhangzhuo Apr 5, 2026
e2896da
chore: clean up
ZacZhangzhuo Apr 5, 2026
7b86732
chore: clean up
ZacZhangzhuo Apr 5, 2026
bd8558d
feat: action
ZacZhangzhuo Apr 5, 2026
bdd7a59
patch: text
ZacZhangzhuo Apr 17, 2026
9811b7b
feat: update hero images
ZacZhangzhuo Apr 17, 2026
b9438c1
feat: icon
ZacZhangzhuo Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
17 changes: 0 additions & 17 deletions .editorconfig

This file was deleted.

2 changes: 0 additions & 2 deletions .github/FUNDING.yml

This file was deleted.

26 changes: 12 additions & 14 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,35 @@ name: Deploy GitHub Pages
on:
push:
branches:
- main # Set a branch name to trigger deployment
- main
pull_request:

jobs:
deploy:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true # Fetch any Git submodules (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
fetch-depth: 0

- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
- uses: actions/setup-node@v4
with:
hugo-version: '0.97.3'
extended: true
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Build
run: hugo --minify
run: npm run build

- name: Deploy
uses: peaceiris/actions-gh-pages@v3
# If you're changing the branch from main,
# also change the `main` in `refs/heads/main`
# below accordingly.
if: ${{ github.ref == 'refs/heads/main' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
publish_dir: ./build/client
14 changes: 5 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# IDEs
.idea/
.DS_Store
/node_modules/

# Hugo
/resources/
public/
assets/jsconfig.json

# Mac
**/.DS_Store
# React Router
/.react-router/
/build/
Empty file removed .hugo_build.lock
Empty file.
21 changes: 0 additions & 21 deletions LICENSE.md

This file was deleted.

55 changes: 41 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
# 苏黎世学联静态网站
[https://acssz.github.io](https://acssz.github.io)
# ACSSZ Website

Based on the [Research Group Theme](https://github.com/wowchemy/starter-hugo-research-group)
Static website for the **Association of Chinese Students and Scholars in Zurich (ACSSZ / 苏黎世中国学生学者联合会)**.

The deployment workflow is set that
- Editors change contents in the `main` branch either locally or directly in the remote repository, then push their commits
- Any push to the `main` branch will trigger a new deployment in the `gh-pages` branch
- Website is updated
## Tech Stack

## References
| Layer | Technology |
|---|---|
| Framework | [React Router v7](https://reactrouter.com/) (SSR/SSG mode) |
| UI Library | [MUI v7](https://mui.com/) (Material UI) + Emotion CSS-in-JS |
| Language | TypeScript |
| Bundler | Vite |
| Carousel | [Embla Carousel](https://www.embla-carousel.com/) |

- [Wowchemy research group theme](https://github.com/wowchemy/starter-hugo-research-group)
- [Site Customization](https://wowchemy.com/docs/getting-started/customization/)
- [Wowchemy -- Create site locally](https://wowchemy.com/docs/getting-started/install-hugo-extended/)
- [Wowchemy -- Deploy with Github Pages](https://wowchemy.com/docs/hugo-tutorials/deployment/#github-pages)
- [First deployment notice](https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-first-deployment-with-github_token)
- [Github pages setup](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site)
## Getting Started

```bash
npm install
npm run dev # development server
npm run build # production build
npm run typecheck # type check
```

## Page Structure

The main page is composed of sections stacked vertically in this order:

| Section | Anchor | Description |
|---|---|---|
| AppBar | — | Sticky nav with language & theme toggles |
| Hero | — | Auto-scrolling carousel |
| Events | `#events` | Event cards |
| Our Cooperation Partners | `#sponsors` | Partner logo pills |
| Departments | `#departments` | Department description cards |
| Useful Information | `#information` | Freshmen handbook (external iframe) |
| FAQ | `#faq` | Accordion Q&A |
| About Us | `#about` | Text, team table, and photo |
| Contact Us | `#footer` | Email, WeChat, social links |
| Copyright | — | CC BY-NC-ND 4.0 |

![diagram](docs/page-structure.svg)

## License

Content is licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/).
72 changes: 72 additions & 0 deletions app/components/AboutUs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Box from "@mui/material/Box";
import Container from "@mui/material/Container";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableRow from "@mui/material/TableRow";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import { useLocale } from "../context/LocaleContext.tsx";
import { sectionContainerSx, sectionTitleSx, sectionBodySx } from "../styles/section.ts";

export default function AboutUs() {
const { locale } = useLocale();
const { title, paragraphs, team } = locale.about;

return (
<Container id="about" sx={sectionContainerSx}>
<Typography component="h2" variant="h4" sx={{ ...sectionTitleSx, width: "100%", textAlign: { sm: "left", md: "center" } }}>
{title}
</Typography>

<Box sx={{ width: "100%", display: "flex", flexDirection: "column", gap: 2 }}>
{paragraphs.map((paragraph, index) => (
<Typography key={index} variant="body1" sx={sectionBodySx}>
{paragraph}
</Typography>
))}
</Box>

<Box
component="img"
src="media/ACSSZ.jpg"
alt={title}
sx={{ width: "100%", borderRadius: 2, objectFit: "cover" }}
/>

<TableContainer component={Paper} variant="outlined" sx={{ width: "100%" }}>
<Table size="small">
<TableBody>
{team.map((dept, deptIndex) =>
dept.members.map((member, memberIndex) => {
const isFirstRowOfDept = memberIndex === 0;
const dividerSx = isFirstRowOfDept && deptIndex > 0
? { borderTop: "2px solid", borderColor: "divider" }
: {};
return (
<TableRow key={`${dept.name}-${memberIndex}`}>
{isFirstRowOfDept && (
<TableCell
rowSpan={dept.members.length}
sx={{ fontWeight: "bold", verticalAlign: "middle", color: "text.primary", whiteSpace: "nowrap", ...dividerSx }}
>
{dept.name}
</TableCell>
)}
<TableCell sx={{ color: "text.secondary", whiteSpace: "nowrap", ...dividerSx }}>
{member.role}
</TableCell>
<TableCell sx={{ color: "text.primary", ...dividerSx }}>
{member.names.join(" ")}
</TableCell>
</TableRow>
);
})
)}
</TableBody>
</Table>
</TableContainer>
</Container>
);
}
146 changes: 146 additions & 0 deletions app/components/AppBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import * as React from "react";
import { styled, alpha } from "@mui/material/styles";
import Box from "@mui/material/Box";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import Container from "@mui/material/Container";
import Divider from "@mui/material/Divider";
import MenuItem from "@mui/material/MenuItem";
import Drawer from "@mui/material/Drawer";
import MenuIcon from "@mui/icons-material/Menu";
import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
import TranslateIcon from "@mui/icons-material/Translate";
import ColorModeIconDropdown from "../shared-theme/ColorModeIconDropdown.tsx";
import Icon from "./Icon.tsx";
import ExternalLinkButton from "./ExternalLinkButton.tsx";
import { ADMIN_LOGIN_URL, MEMBERSHIP_CARD_URL } from "../config.ts";
import { useLocale } from "../context/LocaleContext.tsx";
import type { LocaleKey } from "../locales/index.ts";

const StyledToolbar = styled(Toolbar)(({ theme }) => ({
display: "flex",
alignItems: "center",
justifyContent: "space-between",
flexShrink: 0,
borderRadius: `calc(${theme.shape.borderRadius}px + 8px)`,
backdropFilter: "blur(24px)",
border: "1px solid",
borderColor: (theme.vars || theme).palette.divider,
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.background.defaultChannel} / 0.4)`
: alpha(theme.palette.background.default, 0.4),
boxShadow: (theme.vars || theme).shadows[1],
padding: "8px 12px",
}));

const LANGUAGE_LABELS: Record<LocaleKey, string> = { en: "EN", zh: "中文" };
const LANGUAGE_TOGGLE: Record<LocaleKey, LocaleKey> = { en: "zh", zh: "en" };

export default function AppAppBar() {
const [open, setOpen] = React.useState(false);
const { locale, localeKey, setLocaleKey } = useLocale();
const nav = locale.nav;

const toggleDrawer = (newOpen: boolean) => () => {
setOpen(newOpen);
};

const toggleLanguage = () => {
setLocaleKey(LANGUAGE_TOGGLE[localeKey]);
};

return (
<AppBar
position="fixed"
enableColorOnDark
sx={{
boxShadow: 0,
bgcolor: "transparent",
backgroundImage: "none",
mt: "calc(var(--template-frame-height, 0px) + 28px)",
}}
>
<Container maxWidth="lg">
<StyledToolbar variant="dense" disableGutters>
<Box sx={{ flexGrow: 1, display: "flex", alignItems: "center", px: 0 }}>
<Icon />
<Box sx={{ display: { xs: "none", md: "flex" } }}>
<Button component="a" href="#information" variant="text" color="info" size="small" sx={{ textDecoration: "none" }}>
{nav.usefulInfo}
</Button>
<Button component="a" href="#events" variant="text" color="info" size="small" sx={{ textDecoration: "none" }}>
{nav.eventNotification}
</Button>
<Button component="a" href="#sponsors" variant="text" color="info" size="small" sx={{ textDecoration: "none" }}>
{nav.businessCooperation}
</Button>
<ExternalLinkButton href={MEMBERSHIP_CARD_URL} variant="text" color="info" size="small" sx={{ minWidth: 0 }}>
{nav.membershipCard}
</ExternalLinkButton>
<Button component="a" href="#departments" variant="text" color="info" size="small" sx={{ minWidth: 0, textDecoration: "none" }}>
{nav.departments}
</Button>
<Button component="a" href="#about" variant="text" color="info" size="small" sx={{ minWidth: 0, textDecoration: "none" }}>
{nav.aboutUs}
</Button>
</Box>
</Box>
<Box sx={{ display: { xs: "none", md: "flex" }, gap: 1, alignItems: "center" }}>
<ExternalLinkButton href={ADMIN_LOGIN_URL} color="primary" variant="text" size="small">
{nav.adminLogin}
</ExternalLinkButton>
<IconButton onClick={toggleLanguage} disableRipple size="small" aria-label="Toggle language">
<TranslateIcon fontSize="small" />
</IconButton>
<ColorModeIconDropdown />
</Box>
<Box sx={{ display: { xs: "flex", md: "none" }, gap: 1 }}>
<ColorModeIconDropdown size="medium" />
<IconButton aria-label="Menu button" onClick={toggleDrawer(true)}>
<MenuIcon />
</IconButton>
<Drawer
anchor="top"
open={open}
onClose={toggleDrawer(false)}
slotProps={{
paper: {
sx: { top: "var(--template-frame-height, 0px)" },
},
}}
>
<Box sx={{ p: 2, backgroundColor: "background.default" }}>
<Box sx={{ display: "flex", justifyContent: "flex-end" }}>
<IconButton onClick={toggleDrawer(false)}>
<CloseRoundedIcon />
</IconButton>
</Box>

<MenuItem component="a" href="#information" sx={{ textDecoration: "none", color: "inherit" }}>{nav.usefulInfo}</MenuItem>
<MenuItem>{nav.qAndA}</MenuItem>
<MenuItem component="a" href="#events" sx={{ textDecoration: "none", color: "inherit" }}>{nav.eventNotification}</MenuItem>
<MenuItem>{nav.eventReview}</MenuItem>
<MenuItem component="a" href="#sponsors" sx={{ textDecoration: "none", color: "inherit" }}>{nav.businessCooperation}</MenuItem>
<MenuItem component="a" href={MEMBERSHIP_CARD_URL} target="_blank" rel="noopener noreferrer" sx={{ textDecoration: "none", color: "inherit" }}>{nav.membershipCard}</MenuItem>
<MenuItem component="a" href="#about" sx={{ textDecoration: "none", color: "inherit" }}>{nav.aboutUs}</MenuItem>
<Divider sx={{ my: 3 }} />
<MenuItem>
<Button onClick={toggleLanguage} color="info" variant="outlined" fullWidth>
{LANGUAGE_LABELS[LANGUAGE_TOGGLE[localeKey]]}
</Button>
</MenuItem>
<MenuItem>
<ExternalLinkButton href={ADMIN_LOGIN_URL} color="primary" variant="contained" fullWidth>
{nav.adminLogin}
</ExternalLinkButton>
</MenuItem>
</Box>
</Drawer>
</Box>
</StyledToolbar>
</Container>
</AppBar>
);
}
Loading
Loading