Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] 학생회 QA 대비 기능 유지보수 중간 배포 2차 (개발) #88

Merged
merged 6 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/@types/user.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ declare namespace User {
refreshToken: string;
}

export interface FindPasswordReqestDto {
name: string;
studentId: string;
email: string;
}

// ==

export interface FindPostsResponse {
Expand Down
11 changes: 11 additions & 0 deletions src/AuthRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { observer } from 'mobx-react-lite';
import { useEffect } from 'react';
import { Route } from 'react-router-dom';

import { useRootStore } from '@/stores/RootStore';

type Props = {
children?: React.ReactNode;
};

export const AuthRouter: React.FC<Props> = observer(({ children, ...rest }) => {
const {
auth: { fetch },
} = useRootStore();

useEffect(() => {
fetch();
}, []);

return <Route {...rest} render={() => children} />;
});
1 change: 1 addition & 0 deletions src/configs/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum PAGE_URL {
Auth = '/auth',
SignIn = '/auth/signin',
SignUp = '/auth/singup',
FindPassword = '/auth/findPassword',
Admission = '/auth/admission',

Home = '/home',
Expand Down
2 changes: 2 additions & 0 deletions src/pages/auth/AuthPageSwitch.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Route, Switch } from 'react-router-dom';

import { Admission } from './admission';
import { FindPassword } from './findPassword';
import { SignIn } from './signIn';
import { SignUp } from './signUp';

Expand All @@ -11,5 +12,6 @@ export const AuthPageSwitch: React.FC = () => (
<Route path={PAGE_URL.SignIn} component={SignIn} />
<Route path={PAGE_URL.Admission} component={Admission} />
<Route path={PAGE_URL.SignUp} component={SignUp} />
<Route path={PAGE_URL.FindPassword} component={FindPassword} />
</Switch>
);
88 changes: 88 additions & 0 deletions src/pages/auth/findPassword/FindPasswordPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { observer } from 'mobx-react-lite';
import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useHistory } from 'react-router-dom';

import { PageUiStoreImpl } from './FindPasswordPageUiStore';

import {
BodyScreen,
Header,
Input,
PageBody,
PageFooter,
PageStoreHOC,
NavButton,
SelectInput,
} from '@/components';
import { PAGE_URL } from '@/configs/path';
import { usePageUiStore } from '@/hooks';
import { useRootStore } from '@/stores/RootStore';

const FindPasswordPage: React.FC = observer(() => {
const { replace } = useHistory();
const { findPassword } = usePageUiStore<PageUiStore.FindPassword>();
const {
ui: { alert },
} = useRootStore();
const {
handleSubmit,
control,
formState: { errors },
} = useForm<User.FindPasswordReqestDto>();

if (
(errors.name && errors.name.type === 'required') ||
(errors.studentId && errors.studentId.type === 'required') ||
(errors.email && errors.email.type === 'required')
) {
alert({ message: '모든 항목을 다 입력해주세요.' });
}

const onSubmit = async (body: User.FindPasswordReqestDto) => {
const { success } = (await findPassword(body)) as unknown as StoreAPI;
if (success) {
replace(PAGE_URL.SignIn);
alert({ message: '이메일로 임시 비밀번호가 전송되었습니다. 로그인 이후 변경해주세요.' });
} else {
alert({ message: '잘못된 정보를 입력하였습니다.' });
}
};

return (
<>
<Header title="게시판 생성" withBack={PAGE_URL.Board} />
<PageBody>
<BodyScreen>
<Input
name="email"
label="이메일"
placeholder="이메일을 입력하세요"
required
control={control}
/>
<Input
name="name"
label="이름"
placeholder="이름을 입력하세요"
required
control={control}
/>
<Input
name="studentId"
label="학번"
placeholder="학번을 입력하세요 (ex. 20201234)"
required
control={control}
/>
</BodyScreen>
</PageBody>

<PageFooter>
<NavButton onClick={handleSubmit(onSubmit)}>임시 비밀번호 생성</NavButton>
</PageFooter>
</>
);
});

export default PageStoreHOC(<FindPasswordPage />, { store: PageUiStoreImpl });
21 changes: 21 additions & 0 deletions src/pages/auth/findPassword/FindPasswordPageUiStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { makeAutoObservable } from 'mobx';

import { AuthRepoImpl as Repo } from '@/stores/repositories/AuthRepo';

export class FindPasswordPageUiStore {
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}

// TODO: 게시판 관리 화면 구현 시 추가 검증 필요
*findPassword(body: User.FindPasswordReqestDto): Generator {
try {
yield Repo.findPassword(body);
return { success: true };
} catch (error) {
return error;
}
}
}

export const PageUiStoreImpl = new FindPasswordPageUiStore();
1 change: 1 addition & 0 deletions src/pages/auth/findPassword/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as FindPassword } from './FindPasswordPage';
24 changes: 19 additions & 5 deletions src/pages/auth/signIn/SignInPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import styled from '@emotion/styled';
import PermIdentityOutlinedIcon from '@mui/icons-material/PermIdentityOutlined';
import { Checkbox, FormControlLabel, typographyClasses, checkboxClasses } from '@mui/material';
import { observer } from 'mobx-react-lite';
import { useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
Expand Down Expand Up @@ -51,7 +53,7 @@ const SignInPage: React.FC = observer(() => {
render={({ field }) => (
<Input
type="email"
placeholder="아이디"
placeholder="이메일"
InputProps={{
startAdornment: <PermIdentityOutlinedIcon sx={{ fontSize: 16 }} />,
}}
Expand All @@ -77,13 +79,25 @@ const SignInPage: React.FC = observer(() => {

<SubLink>
<Link to={PAGE_URL.SignUp}>회원가입</Link>
{/* TODO: 개발
<Link to={PAGE_URL.SignUp}>아이디 찾기</Link> */}
{/* TODO: 개발
<Link to={PAGE_URL.SignUp}>비밀번호 찾기</Link> */}
<Link to={PAGE_URL.FindPassword}>비밀번호를 잃어버리셨나요?</Link>
</SubLink>
</PageWrapper>
);
});

const CheckboxLabel = styled(FormControlLabel)`
margin-top: 9px;
margin-left: 6px;

.${typographyClasses.root} {
margin-top: 3px;
font-size: 12px;
line-height: 14px;
}

.${checkboxClasses.root} {
padding: 7px;
}
`;

export default PageStoreHOC(<SignInPage />, { store: PageUiStoreImpl });
8 changes: 4 additions & 4 deletions src/pages/auth/signUp/SignUpPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ const SignUpPage: React.FC = observer(() => {
<br />
<Input
name="email"
label="아이디"
placeholder="아이디를 입력하세요"
label="이메일"
placeholder="이메일를 입력하세요"
required
control={control}
rules={{
required: '아이디를 입력해주세요.',
required: '이메일를 입력해주세요.',
validate: value => {
// 이메일 유효성 검사는 진행했고,
if (isDuplicatedEmail === true) {
// 검사했던 이메일과 지금 입력된 이메일이 같은 경우 에러
if (chekedEmail === value) return '중복된 아이디입니다.';
if (chekedEmail === value) return '중복된 이메일입니다.';
else return true;
}
return true;
Expand Down
4 changes: 0 additions & 4 deletions src/pages/board/boardCreate/BoardCreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ const BoardCreatePage: React.FC = observer(() => {
},
});

const handleSelectChange = (name: string, selectedValue: string) => {
setValue(name as 'category' | 'circleName', selectedValue);
};

if (
(errors.name && errors.name.type === 'required') ||
(errors.description && errors.description.type === 'required')
Expand Down
16 changes: 3 additions & 13 deletions src/pages/board/boardList/BoardListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,19 @@ import { PageUiStoreImpl } from './BoardListPageUiStore';
import { Boards, BoardCreateButton } from './components';
import { DeleteBoardModal } from './components/DeleteBoardModal';

import { UniformLogo } from '@/assets';
import { BodyScreen, GNB, Header, PageBody, PageStoreHOC } from '@/components';
import { usePageUiStore } from '@/hooks';
import { useRootStore } from '@/stores/RootStore';

const BoardListPage: React.FC = () => {
const { fetchBoards } = usePageUiStore<PageUiStore.BoardList>();
const {
auth: { fetch, me },
} = useRootStore();
const { fetch } = usePageUiStore<PageUiStore.BoardList>();

useEffect(() => {
fetch();
fetchBoards();
}, [fetch]);
}, []);

return (
<>
{me?.isAdmin || me?.isCircleLeader || me?.isPresident ? (
<Header title="게시판 목록" RightComponent={<BoardCreateButton />} />
) : (
<Header title="게시판 목록" RightComponent={<UniformLogo />} />
)}
<Header title="게시판 목록" RightComponent={<BoardCreateButton />} />

<PageBody>
<BodyScreen>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/board/boardList/BoardListPageUiStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class BoardListPageUiStore {
}

// 생성한 게시판의 카테고리가 이미 있으면 그 카테고리 안에 넣고, 없으면 새로 생성
*fetchBoards(): Generator {
*fetch(): Generator {
const boards = (yield Repo.fetch()) as Model.Board[];
this.boards = new Map();

Expand Down
27 changes: 19 additions & 8 deletions src/pages/board/boardList/components/BoardCreateButton.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
import styled from '@emotion/styled';
import AddBoxIcon from '@mui/icons-material/AddBox';
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
import { observer } from 'mobx-react-lite';
import { generatePath, useParams } from 'react-router-dom';

import { UniformLogo } from '@/assets';
import { ClearLink, RightButtonWrapper } from '@/components';
import { PAGE_URL, PostParams } from '@/configs/path';
import { usePageUiStore } from '@/hooks';
import { useRootStore } from '@/stores/RootStore';

export const BoardCreateButton: React.FC = observer(() => {
const {
auth: { me },
} = useRootStore();

return (
<Wrapper to={generatePath(PAGE_URL.BoardCreate)}>
<Icon fontSize="large" />
</Wrapper>
<>
{me && (me.isAdmin || me.isCircleLeader || me.isPresident) ? (
<Wrapper to={generatePath(PAGE_URL.BoardCreate)}>
<Icon fontSize="large" />
</Wrapper>
) : (
<UniformLogo />
)}
</>
);
});

const Wrapper = styled(ClearLink)`
${RightButtonWrapper}
`;

const Icon = styled(AddBoxIcon)`
const Icon = styled(PlaylistAddIcon)`
position: absolute;
top: 11px;
right: 20px;
top: 9px;
right: 25px;
`;
4 changes: 4 additions & 0 deletions src/stores/repositories/AuthRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class AuthRepo {
updatePassword = async (body: User.PasswordUpdateRequestDto) => {
return API.put(`${this.URI}/password`, body);
};

findPassword = async (body: User.FindPasswordReqestDto) => {
return API.put(`${this.URI}/password/find`, body);
};
}

export const AuthRepoImpl = new AuthRepo();
2 changes: 2 additions & 0 deletions src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="vite/client" />

import type { AdmissionPageUiStore } from './pages/auth/admission/AdmissionPageUiStore';
import type { FindPasswordPageUiStore } from './pages/auth/findPassword/FindPasswordPageUiStore';
import type { SignInPageUiStore } from './pages/auth/signIn/SignInPageUiStore';
import type { SignUpPageUiStore } from './pages/auth/signUp/SignUpPageUiStore';
import type { BoardCreatePageUiStore } from './pages/board/boardCreate/BoardCreatePageUiStore';
Expand Down Expand Up @@ -73,6 +74,7 @@ declare global {
type SignIn = SignInPageUiStore;
type SignUp = SignUpPageUiStore;
type Admission = AdmissionPageUiStore;
type FindPassword = FindPasswordPageUiStore;
type Home = HomePageUiStore;
type CircleHome = CircleHomePageUiStore;
type CircleJoin = CircleJoinPageUiStore;
Expand Down