Skip to content

Commit

Permalink
Add: Add version swapper
Browse files Browse the repository at this point in the history
  • Loading branch information
whats2000 committed Jul 3, 2024
1 parent 918515a commit 614d9b3
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 29 deletions.
16 changes: 14 additions & 2 deletions nsysu_selector_helper/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@ module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:prettier/recommended',
'prettier',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
plugins: ['react-refresh', 'prettier', 'react'],
rules: {
'prettier/prettier': 'warn',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'linebreak-style': 'off',
'react/react-in-jsx-scope': 'off',
'react/self-closing-comp': ['error', { component: true, html: true }],
},
}
settings: {
react: {
version: 'detect',
},
},
};
57 changes: 54 additions & 3 deletions nsysu_selector_helper/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,65 @@
import { FC } from 'react';
import { ConfigProvider } from 'antd';
import { FC, useEffect, useState } from 'react';
import { ConfigProvider, Spin } from 'antd';

import type { AcademicYear, Course } from '@/types';
import { useThemeConfig } from '@/hooks/useThemeConfig';
import { NSYSUCourseAPI } from '@/api/NSYSUCourseAPI.ts';
import SectionHeader from '#/SectionHeader.tsx';

const App: FC = () => {
const [themeConfig, _setThemeConfig] = useThemeConfig();

const [isLoading, setIsLoading] = useState(true);

const [selectedKeys, setSelectedKeys] = useState(['all-courses']);
const [availableSemesters, setAvailableSemesters] = useState<AcademicYear>({
latest: '',
history: {},
});
const [selectedSemester, setSelectedSemester] = useState('');
const [courses, setCourses] = useState<Course[]>([]);

useEffect(() => {
NSYSUCourseAPI.getAvailableSemesters().then((availableSemesters) => {
setAvailableSemesters(availableSemesters);
setSelectedSemester(availableSemesters.latest);
});
}, []);

useEffect(() => {
if (selectedSemester === '') {
return;
}
setIsLoading(true);
NSYSUCourseAPI.getSemesterUpdates(selectedSemester)
.then((updates) => {
NSYSUCourseAPI.getCourses(selectedSemester, updates.latest).then(
(courses) => {
setCourses(courses);
setTimeout(() => {
setIsLoading(false);
}, 250);
},
);
})
.catch((error) => {
console.error(error);
setTimeout(() => {
setIsLoading(false);
}, 250);
});
}, [selectedSemester]);

return (
<ConfigProvider theme={themeConfig}>
<SectionHeader />
{isLoading && <Spin spinning={true} fullscreen />}
<SectionHeader
selectedKeys={selectedKeys}
setSelectedKeys={setSelectedKeys}
availableSemesters={availableSemesters}
selectedSemester={selectedSemester}
setSelectedSemester={setSelectedSemester}
/>
</ConfigProvider>
);
};
Expand Down
154 changes: 130 additions & 24 deletions nsysu_selector_helper/src/components/SectionHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,56 @@
import { FC } from 'react';
import { Flex, Menu, MenuProps, theme } from 'antd';
import { FC, useState } from 'react';
import {
Flex,
Menu,
MenuProps,
Drawer,
Button,
theme,
Select,
SelectProps,
} from 'antd';
import {
BookFilled,
BookOutlined,
FileDoneOutlined,
FileSearchOutlined,
NotificationOutlined,
MenuOutlined,
} from '@ant-design/icons';
import banner from '@/assets/banner.svg';
import styled from 'styled-components';

type HeaderProps = {};
import { AcademicYear } from '@/types';

const HeaderContainer = styled(Flex)<{
$primaryColor: string;
$textColor: string;
}>`
padding: 0 20px;
background-color: ${(props) => props.$primaryColor};
color: ${(props) => props.$textColor};
min-height: 52px;
`;

const StyledSelect = styled(Select)`
@media (max-width: 890px) {
display: none;
}
`;

const StyledMenu = styled(Menu)<{
$textColor: string;
}>`
padding-bottom: 5px;
background: transparent;
width: 100%;
justify-content: flex-end;
@media (max-width: 890px) {
display: none;
}
li.ant-menu-item {
li.ant-menu-item,
div.ant-menu-submenu-title {
background-color: transparent;
color: ${(props) => props.$textColor} !important;
}
Expand All @@ -45,17 +67,42 @@ const StyledMenu = styled(Menu)<{
const Brand = styled.img`
height: 36px;
margin-right: 20px;
padding: 8px;
@media (max-width: 290px) {
display: none;
}
`;

const SectionHeader: FC<HeaderProps> = () => {
const MobileMenu = styled(Flex)`
display: none;
@media (max-width: 890px) {
display: flex;
}
`;

type HeaderProps = {
selectedKeys: string[];
setSelectedKeys: (keys: string[]) => void;
availableSemesters: AcademicYear;
selectedSemester: string;
setSelectedSemester: (semester: string) => void;
};

const SectionHeader: FC<HeaderProps> = ({
selectedKeys,
setSelectedKeys,
availableSemesters,
selectedSemester,
setSelectedSemester,
}) => {
const { token } = theme.useToken();
const primaryColor = token.colorPrimary;
const textColor = '#ffffff';

const [drawerOpen, setDrawerOpen] = useState(false);

const navTabs: MenuProps['items'] = [
{ key: 'all-courses', label: '所有課程', icon: <BookOutlined /> },
{ key: 'semester-compulsory', label: '學期必修', icon: <BookFilled /> },
Expand All @@ -68,25 +115,84 @@ const SectionHeader: FC<HeaderProps> = () => {
{ key: 'announcements', label: '公告', icon: <NotificationOutlined /> },
];

const semesterCodeMap = {
1: '上',
2: '下',
3: '暑',
};

const semesterOptions: SelectProps['options'] = Object.keys(
availableSemesters.history,
)
.sort((a, b) => b.localeCompare(a))
.map((year) => ({
key: year,
label: `${year.slice(0, -1)} ${semesterCodeMap[parseInt(year.slice(-1))]}`,
value: year,
}));

const handleMenuClick = (e: any) => {
setSelectedKeys([e.key]);
setDrawerOpen(false);
};

return (
<HeaderContainer
$primaryColor={primaryColor}
$textColor={textColor}
justify={'space-between'}
>
<Flex align={'center'} justify={'center'} style={{ padding: '5px' }}>
<Brand src={banner as string} alt='NSYSU Selector Helper' />
</Flex>
<StyledMenu
<>
<HeaderContainer
$primaryColor={primaryColor}
$textColor={textColor}
mode='horizontal'
items={navTabs}
style={{
marginLeft: 'auto',
}}
selectedKeys={['all-courses']}
/>
</HeaderContainer>
justify={'space-between'}
>
<Flex align={'center'} justify={'center'}>
<Brand src={banner as string} alt='NSYSU Selector Helper' />
</Flex>
<Flex align={'center'} justify={'center'}>
<StyledSelect
value={selectedSemester === '' ? '載入中...' : selectedSemester}
onChange={(value) => setSelectedSemester(value)}
options={semesterOptions}
loading={!semesterOptions.length}
style={{ width: 120 }}
/>
</Flex>
<StyledMenu
$textColor={textColor}
mode='horizontal'
items={navTabs}
selectedKeys={selectedKeys}
onClick={handleMenuClick}
/>
<MobileMenu align={'center'} justify={'center'}>
<Button
type='primary'
icon={<MenuOutlined />}
onClick={() => setDrawerOpen(true)}
/>
</MobileMenu>
</HeaderContainer>
<Drawer
title='中山大學選課小幫手'
placement='left'
onClose={() => setDrawerOpen(false)}
open={drawerOpen}
>
<Flex vertical={true} gap={20}>
<Select
value={selectedSemester === '' ? '載入中...' : selectedSemester}
onChange={(value) => setSelectedSemester(value)}
options={semesterOptions}
loading={!semesterOptions.length}
style={{ width: '100%' }}
/>
<Menu
mode='vertical'
items={navTabs}
selectedKeys={selectedKeys}
onClick={handleMenuClick}
/>
</Flex>
</Drawer>
</>
);
};

Expand Down

0 comments on commit 614d9b3

Please sign in to comment.