박권수

feat. bottle view -> medicine Modal, User Feedback, User Register and Header button, etc...

...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
13 "axios": "^0.21.1", 13 "axios": "^0.21.1",
14 "highcharts": "^9.2.0", 14 "highcharts": "^9.2.0",
15 "highcharts-react-official": "^3.0.0", 15 "highcharts-react-official": "^3.0.0",
16 + "moment": "^2.29.1",
16 "react": "^17.0.2", 17 "react": "^17.0.2",
17 "react-dom": "^17.0.2", 18 "react-dom": "^17.0.2",
18 "react-router-dom": "^5.2.0", 19 "react-router-dom": "^5.2.0",
...@@ -20,6 +21,7 @@ ...@@ -20,6 +21,7 @@
20 "recoil": "^0.4.0", 21 "recoil": "^0.4.0",
21 "recoil-persist": "^3.0.0", 22 "recoil-persist": "^3.0.0",
22 "styled-components": "^5.3.0", 23 "styled-components": "^5.3.0",
24 + "sweetalert2": "^11.1.3",
23 "typescript": "^4.1.2", 25 "typescript": "^4.1.2",
24 "web-vitals": "^1.0.1" 26 "web-vitals": "^1.0.1"
25 }, 27 },
......
...@@ -16,4 +16,11 @@ export default { ...@@ -16,4 +16,11 @@ export default {
16 logout : () => { 16 logout : () => {
17 return client.post('/auth/logout'); 17 return client.post('/auth/logout');
18 }, 18 },
19 + verifyToken : (token : any) => {
20 + return client.get('/auth/verifytoken', {
21 + headers : {
22 + Authorization : token,
23 + },
24 + });
25 + },
19 }; 26 };
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -7,18 +7,4 @@ export const client : AxiosInstance = axios.create({ ...@@ -7,18 +7,4 @@ export const client : AxiosInstance = axios.create({
7 'Content-Type': 'application/json', 7 'Content-Type': 'application/json',
8 'Access-Control-Allow-Origin': '*', 8 'Access-Control-Allow-Origin': '*',
9 }, 9 },
10 -}); 10 +});
11 -
12 -client.interceptors.request.use(
13 -
14 -);
15 -
16 -client.interceptors.response.use(
17 - function (response) {
18 - return response;
19 - },
20 - function (error) {
21 - console.log(error.message);
22 - return error;
23 - }
24 -);
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -14,7 +14,33 @@ export const Container = styled.div ` ...@@ -14,7 +14,33 @@ export const Container = styled.div `
14 align-items : center; 14 align-items : center;
15 `; 15 `;
16 16
17 -export const HeaderWrapper = styled.div ` 17 +export const HeaderLeftWrapper = styled.div `
18 + flex : 1;
19 + display : flex;
20 + flex-direction : row;
21 + justify-content : flex-start;
22 + align-items : center;
23 +
24 + border : none;
25 +
26 + margin : 0 30px;
27 +`;
28 +
29 +export const HeaderRightWrapper = styled.div `
30 + flex : 1;
31 + display : flex;
32 + flex-direction : row;
33 + justify-content : flex-end;
34 + align-items : center;
35 +
36 + border : none;
37 +
38 + margin : 0 30px;
39 +
40 +`;
41 +
42 +export const HeaderCenterWrapper = styled.div `
43 + flex : 3;
18 display : flex; 44 display : flex;
19 flex-direction : row; 45 flex-direction : row;
20 justify-content : center; 46 justify-content : center;
...@@ -31,8 +57,69 @@ export const TitleImg = styled.img ` ...@@ -31,8 +57,69 @@ export const TitleImg = styled.img `
31 `; 57 `;
32 58
33 export const Title = styled.div ` 59 export const Title = styled.div `
34 - flex : 1;
35 text-align : center; 60 text-align : center;
36 font-size : 17px; 61 font-size : 17px;
37 font-weight : 800; 62 font-weight : 800;
63 +`;
64 +
65 +export const Backbutton = styled.button `
66 + border : 1px solid #337DFF;
67 + border-radius : 5px;
68 + background-color : transparent;
69 + color : #337DFF;
70 + padding : 4px 17px;
71 +
72 + transition : .25s all;
73 +
74 + display : flex;
75 + flex-direction : row;
76 + justify-content : center;
77 +
78 + cursor : pointer;
79 +
80 + &:hover {
81 + opacity : .5;
82 + }
83 +`;
84 +
85 +export const BackbuttonImg = styled.img `
86 + height : 14px;
87 + width : 14px;
88 + margin : 0 6px 0 0;
89 +`;
90 +
91 +export const BackbuttonText = styled.div `
92 + font-size : 15px;
93 + font-weight : 700;
94 + letter-spacing : 1px;
95 +`;
96 +
97 +export const LogoutButton = styled.button `
98 + display : flex;
99 + justify-content : center;
100 + // align-items : center;
101 +
102 + background-color : transparent;
103 + border : none;
104 + border-bottom : 1px solid #a0a0a0;
105 + padding : 0 4px 4px 4px;
106 +
107 + cursor : pointer;
108 + transition : .25s all;
109 +
110 + &:hover {
111 + opacity : .5;
112 + }
113 +`;
114 +
115 +export const LogoutButtonImg = styled.img `
116 + height : 15px;
117 + width : 15px;
118 + margin : 0 4px 0 0;
119 +`;
120 +
121 +export const LogoutButtonText = styled.div `
122 + color : #a0a0a0;
123 + font-size : 13px;
124 + letter-spacing : 1px;
38 `; 125 `;
...\ No newline at end of file ...\ No newline at end of file
......
1 -import React from 'react'; 1 +import React, { useEffect } from 'react';
2 +import { RouteComponentProps } from 'react-router';
2 3
4 +import { useRecoilState } from 'recoil';
5 +import * as recoilUtil from '../../util/recoilUtil';
6 +
7 +import * as Alert from '../../util/alertMessage';
3 import * as styled from './HeaderStyled'; 8 import * as styled from './HeaderStyled';
4 9
10 +import { authApi } from '../../api';
11 +
5 const headerImg = '/static/img/pharmacy.png'; 12 const headerImg = '/static/img/pharmacy.png';
13 +const backButtonWhite = '/static/img/backButtonWhite.png';
14 +const backButtonBlue = '/static/img/backButtonBlue.png';
15 +const logout = '/static/img/logout.png';
16 +
17 +
18 +// eslint-disable-next-line @typescript-eslint/no-empty-interface
19 +interface HeaderProps extends RouteComponentProps {}
20 +
21 +const Header = (props : HeaderProps) => {
22 +
23 + const [token, setToken] = useRecoilState(recoilUtil.token);
24 +
25 +
26 + const onLogout = () => {
27 + const logout = async () => {
28 + try {
29 + await authApi.logout();
30 + setToken(null);
31 + Alert.onSuccess('로그인 페이지로 이동합니다.', () => props.history.push('/login'));
32 + } catch(e) {
33 + Alert.onError('알 수 없는 에러가 발생했습니다.', () => props.history.push('/'));
34 + }
35 + };
36 +
37 + Alert.onCheck('정말 로그아웃 하시겠습니까?', logout, () => null);
38 + };
39 +
40 + const verifyToken = async () => {
41 + try {
42 + const result = await authApi.verifyToken(token);
43 + if(result.statusText !== 'OK') {
44 + setToken(null);
45 + Alert.onError('세션이 만료되었습니다. 다시 로그인하세요.', () => props.history.push('/login'));
46 + }
47 + } catch(e) {
48 + console.log(e);
49 + setToken(null);
50 + Alert.onError('세션이 만료되었습니다. 다시 로그인하세요.', () => props.history.push('/login'));
51 + }
52 + };
53 +
54 + const onGoBack = () => {
55 + props.history.goBack();
56 + };
6 57
58 + useEffect(() => {
59 + verifyToken();
60 + }, []);
7 61
8 -const Header = () => {
9 return ( 62 return (
10 <styled.Container> 63 <styled.Container>
11 - <styled.HeaderWrapper> 64 + <styled.HeaderLeftWrapper>
65 + {
66 + (token && token.length && props.location.pathname !== '/') || props.location.pathname === '/register' ?
67 + <styled.Backbutton
68 + onClick = {onGoBack}
69 + >
70 + <styled.BackbuttonImg src = {backButtonBlue}/>
71 + <styled.BackbuttonText>뒤로 가기</styled.BackbuttonText>
72 + </styled.Backbutton> : null
73 + }
74 + </styled.HeaderLeftWrapper>
75 + <styled.HeaderCenterWrapper>
12 <styled.TitleImg src = {headerImg} /> 76 <styled.TitleImg src = {headerImg} />
13 <styled.Title>내 손 안의 주치의</styled.Title> 77 <styled.Title>내 손 안의 주치의</styled.Title>
14 - </styled.HeaderWrapper> 78 + </styled.HeaderCenterWrapper>
79 + <styled.HeaderRightWrapper>
80 + {
81 + token && token.length ?
82 + <styled.LogoutButton
83 + onClick = {onLogout}
84 + >
85 + <styled.LogoutButtonImg src = {logout}/>
86 + <styled.LogoutButtonText>로그아웃</styled.LogoutButtonText>
87 + </styled.LogoutButton> : null
88 + }
89 + </styled.HeaderRightWrapper>
15 </styled.Container> 90 </styled.Container>
16 ) 91 )
17 }; 92 };
......
1 import React, { useEffect } from 'react'; 1 import React, { useEffect } from 'react';
2 -import * as recoilUtil from '../../util/recoilUtil';
3 -import { useRecoilState } from 'recoil';
4 2
5 import * as styled from './ErrorStyled'; 3 import * as styled from './ErrorStyled';
6 4
7 5
8 const ErrorContainer = () => { 6 const ErrorContainer = () => {
9 -
10 - const [error, setError] = useRecoilState(recoilUtil.error);
11 -
12 - useEffect(() => {
13 - console.log(error);
14 - }, [error]);
15 -
16 return ( 7 return (
17 - <> 8 + <styled.Container>
18 - { 9 + </styled.Container>
19 - error ?
20 - <styled.Container>
21 - {error}
22 - </styled.Container> : null
23 - }
24 - </>
25 ); 10 );
26 }; 11 };
27 12
28 -export default ErrorContainer; 13 +export default ErrorContainer;
...\ No newline at end of file ...\ No newline at end of file
......
1 +import Swal from "sweetalert2";
2 +
3 +export const onError = (text : string, confirmAction : () => void) => {
4 + Swal.fire({
5 + title : '오류 발생',
6 + icon : 'error',
7 + text,
8 + confirmButtonText : '확인',
9 + confirmButtonColor : '#337DFF'
10 + }).then((res) => {
11 + if(res.isConfirmed) {
12 + confirmAction();
13 + }
14 + });
15 +};
16 +
17 +export const onSuccess = (text : string, successAction : () => void) => {
18 + Swal.fire({
19 + title : '성공',
20 + icon : 'success',
21 + text,
22 + showConfirmButton : false,
23 + timer : 1500,
24 + }).then(res => {
25 + successAction();
26 + });
27 +};
28 +
29 +export const onCheck = (text : string, confirmAction : () => void, denyAction : () => void) => {
30 + Swal.fire({
31 + title : '확인',
32 + icon : 'question',
33 + text,
34 + confirmButtonText : '확인',
35 + confirmButtonColor : '#337DFF',
36 + showDenyButton : true,
37 + denyButtonText : '취소',
38 + denyButtonColor : '#343434',
39 + }).then(res => {
40 + if(res.isConfirmed) {
41 + confirmAction();
42 + } else if(res.isDenied) {
43 + denyAction();
44 + }
45 + });
46 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import moment from 'moment';
2 +
3 +export const make = (chartData : any[], numberOfRow : number) => {
4 + const now = new Date();
5 + const result : any = {};
6 + new Array(numberOfRow).fill(null).forEach((item : any, index : number) => {
7 + const key = moment(now).format('MM/DD');
8 + result[key] = 0;
9 + now.setDate(now.getDate() - 1);
10 + })
11 +
12 + chartData.forEach((data : any) => {
13 + const key : string = moment(data.takeDate).format('MM/DD');
14 + result[key] = result[key] + 1;
15 + });
16 +
17 + const categories : any = [];
18 + const data : any = [];
19 +
20 + Object.entries(result).forEach((item : any) => {
21 + categories.push(item[0]);
22 + data.push(item[1]);
23 + });
24 +
25 + categories.reverse();
26 + data.reverse();
27 +
28 + return {
29 + categories,
30 + data,
31 + }
32 +};
...\ No newline at end of file ...\ No newline at end of file
...@@ -13,10 +13,4 @@ export const userTypeCd = atom({ ...@@ -13,10 +13,4 @@ export const userTypeCd = atom({
13 key : 'userTypeCd', 13 key : 'userTypeCd',
14 default : 'NORMAL', 14 default : 'NORMAL',
15 effects_UNSTABLE : [persistAtom], 15 effects_UNSTABLE : [persistAtom],
16 -});
17 -
18 -export const error = atom({
19 - key : 'error',
20 - default : null,
21 - effects_UNSTABLE : [persistAtom],
22 -})
...\ No newline at end of file ...\ No newline at end of file
16 +});
...\ No newline at end of file ...\ No newline at end of file
......
1 import React from "react"; 1 import React from "react";
2 -import { BrowserRouter, Route, Switch } from 'react-router-dom'; 2 +import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
3 3
4 import Error from '../components/error'; 4 import Error from '../components/error';
5 -import Header from '../components/Header';
6 import { LoginContainer } from "./login"; 5 import { LoginContainer } from "./login";
7 import { RegisterContainer } from './register'; 6 import { RegisterContainer } from './register';
8 import { MainContainer } from "./main"; 7 import { MainContainer } from "./main";
...@@ -13,12 +12,12 @@ const Router = () => { ...@@ -13,12 +12,12 @@ const Router = () => {
13 return ( 12 return (
14 <BrowserRouter> 13 <BrowserRouter>
15 <Error /> 14 <Error />
16 - <Header />
17 <Switch> 15 <Switch>
18 <Route exact path = '/' component = {MainContainer}/> 16 <Route exact path = '/' component = {MainContainer}/>
19 <Route exact path = '/login' component = {LoginContainer}/> 17 <Route exact path = '/login' component = {LoginContainer}/>
20 <Route exact path = '/register' component = {RegisterContainer}/> 18 <Route exact path = '/register' component = {RegisterContainer}/>
21 - <Route exact path = '/bottle' component = {BottleInfoContainer}/> 19 + <Route exact path = '/bottle/:bottleId' component = {BottleInfoContainer}/>
20 + <Redirect path = '*' to = '/'/>
22 </Switch> 21 </Switch>
23 </BrowserRouter> 22 </BrowserRouter>
24 ) 23 )
......
1 import React, { useState, useEffect } from 'react'; 1 import React, { useState, useEffect } from 'react';
2 import { RouteComponentProps } from 'react-router-dom'; 2 import { RouteComponentProps } from 'react-router-dom';
3 import { useRecoilValue } from 'recoil'; 3 import { useRecoilValue } from 'recoil';
4 +
4 import * as recoilUtil from '../../util/recoilUtil'; 5 import * as recoilUtil from '../../util/recoilUtil';
6 +import * as makeChart from '../../util/makeChart';
7 +import * as Alert from '../../util/alertMessage';
5 8
6 -import HighCharts from 'highcharts'; 9 +import moment from 'moment';
7 -import HighchartsReact from 'highcharts-react-official';
8 10
11 +import Header from '../../components/Header';
9 import BottleInfoPresenter from './BottleInfoPresenter'; 12 import BottleInfoPresenter from './BottleInfoPresenter';
10 13
11 import { doctorApi } from '../../api'; 14 import { doctorApi } from '../../api';
12 15
13 16
14 -type BottleInfoProps = RouteComponentProps 17 +
18 +interface RouteParmas {
19 + bottleId : string;
20 +}
21 +
22 +type BottleInfoProps = RouteComponentProps<RouteParmas>
15 23
16 const BottleInfoContainer = (props : BottleInfoProps) => { 24 const BottleInfoContainer = (props : BottleInfoProps) => {
17 25
26 + const { bottleId } = props.match.params;
27 +
18 const token = useRecoilValue(recoilUtil.token); 28 const token = useRecoilValue(recoilUtil.token);
19 const userTypeCd = useRecoilValue(recoilUtil.userTypeCd); 29 const userTypeCd = useRecoilValue(recoilUtil.userTypeCd);
30 +
31 + const [bottleInfo, setBottleInfo] = useState<any>({
32 + feedbackList : [],
33 + medicine : {},
34 + bottleId : 0,
35 + takeMedicineHist : [],
36 + });
37 +
38 + const numberOfChartItem = 7;
39 + const [chartOption, setChartOption] = useState<any>({
40 + chart : {
41 + type : 'line',
42 + height : 300,
43 + width : 450,
44 + },
45 + title : {
46 + text : '',
47 + style : { 'color' : '#343434', 'fontSize' : '15px', 'fontWeight' : '700' },
48 + margin : 10,
49 + },
50 + xAxis : {
51 + categories : [],
52 + },
53 + series : [{
54 + name : '약 복용 횟수',
55 + color : '#337DFF',
56 + data : [],
57 + }],
58 + });
59 +
60 + const [feedback, setFeedback] = useState<string>('');
61 + const [fdbType, setFdbType] = useState<string>('RECOMMEND');
62 +
63 + const [medicineInfoModal, setMedicineInfoModal] = useState<boolean>(false);
64 +
20 65
66 + const fetchData = async () => {
67 + setFeedback('');
68 + setFdbType('RECOMMEND');
69 + setMedicineInfoModal(false);
21 70
71 + try {
72 + const result = await doctorApi.getPatientBottleDetail(token, bottleId);
73 + if (result.statusText === 'OK') {
74 + const { categories, data } = makeChart.make(result.data.takeMedicineHist, numberOfChartItem);
75 + setBottleInfo({
76 + ...result.data,
77 + feedbackList : result.data.feedbackList.map((feedback : any) => {
78 + return {
79 + ...feedback,
80 + fdbDtm : moment(feedback.fdbDtm).format('YYYY-MM-DD hh:mm'),
81 + }
82 + }),
83 + });
84 + setChartOption({
85 + ...chartOption,
86 + title : {
87 + text : result.data.medicine.name,
88 + },
89 + xAxis : {
90 + categories,
91 + },
92 + series : [{
93 + data,
94 + }]
95 + });
96 + } else {
97 + Alert.onError('접근 권한이 없습니다.', () => props.history.push('/'));
98 + }
99 + } catch(e) {
100 + Alert.onError(e.response.data.error, () => props.history.push('/'));
101 +
102 + console.log(e);
103 + }
104 + };
105 +
106 + const onSetFeedback = (e : React.ChangeEvent<HTMLTextAreaElement>) => {
107 + setFeedback(e.target.value);
108 + };
109 +
110 + const onSubmitFeedback = () => {
111 + const register = async () => {
112 + if(feedback.length) {
113 + try {
114 + const res = await doctorApi.writeBottleFeedback(token, {
115 + bottleId,
116 + fdbType,
117 + feedback
118 + });
119 + if(res.statusText === 'OK') {
120 + Alert.onSuccess('피드백이 등록되었습니다.', () => fetchData());
121 + } else {
122 + console.log(res);
123 + Alert.onError('피드백 등록에 실패했습니다.', () => null);
124 + }
125 + } catch(e) {
126 + Alert.onError(e.response.data.error, () => fetchData());
127 + }
128 + } else {
129 + Alert.onError('피드백 내용을 입력하세요.', () => null);
130 + }
131 + }
132 +
133 + Alert.onCheck('피드백을 등록하시겠습니까?', register, () => null);
134 +
135 + };
136 +
137 +
22 useEffect(() => { 138 useEffect(() => {
23 if(userTypeCd !== 'DOCTOR') { 139 if(userTypeCd !== 'DOCTOR') {
24 - props.history.push('/'); 140 + Alert.onError('접근 권한이 없습니다.', () => props.history.push('/'));
25 } 141 }
142 + fetchData();
26 }, [userTypeCd]); 143 }, [userTypeCd]);
27 144
28 return ( 145 return (
146 + <>
147 + <Header {...props} />
29 <BottleInfoPresenter 148 <BottleInfoPresenter
149 + bottleInfo = {bottleInfo}
150 + chartOption = {chartOption}
151 +
152 + medicineInfoModal = {medicineInfoModal}
153 + setMedicineInfoModal = {setMedicineInfoModal}
30 154
155 + feedback = {feedback}
156 + onSetFeedback = {onSetFeedback}
157 + fdbType = {fdbType}
158 + setFdbType = {setFdbType}
159 + onSubmitFeedback = {onSubmitFeedback}
31 /> 160 />
161 + </>
32 ); 162 );
33 }; 163 };
34 164
......
1 import React from 'react'; 1 import React from 'react';
2 +import HighCharts from 'highcharts';
3 +import HighchartsReact from 'highcharts-react-official';
2 4
3 import * as styled from './BottleInfoStyled'; 5 import * as styled from './BottleInfoStyled';
4 6
7 +const plus = '/static/img/plus.png';
8 +const closeButton = '/static/img/close.png';
5 9
6 -const BottleInfoPresenter = () => { 10 +
11 +interface BottleInfoProps {
12 + bottleInfo : {
13 + feedbackList : any[];
14 + medicine : any;
15 + bottleId : number;
16 + takeMedicineHist : any[];
17 + };
18 + chartOption : any;
19 +
20 + medicineInfoModal : boolean;
21 + setMedicineInfoModal : (arg0 : boolean) => void;
22 +
23 + feedback : string;
24 + onSetFeedback : React.ChangeEventHandler<HTMLTextAreaElement>;
25 + fdbType : string;
26 + setFdbType : (arg0 : string) => void;
27 + onSubmitFeedback : () => void;
28 +}
29 +
30 +const BottleInfoPresenter = (props : BottleInfoProps) => {
7 return ( 31 return (
8 <styled.Container> 32 <styled.Container>
9 - 33 + {
34 + props.medicineInfoModal ?
35 + <styled.ModalContainer>
36 + <styled.ModalClsButtonWrapper>
37 + <styled.ModalClsButton
38 + onClick = {() => props.setMedicineInfoModal(false)}
39 + >
40 + <styled.ModalClsButtonImg src = {closeButton}/>
41 + <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText>
42 + </styled.ModalClsButton>
43 + </styled.ModalClsButtonWrapper>
44 + <styled.ModalContentWrapper>
45 + <styled.ModalContent>
46 + <styled.MedicineNameWrapper>
47 + <styled.MedicineName>{props.bottleInfo.medicine.name}</styled.MedicineName>
48 + <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{props.bottleInfo.medicine.company}</styled.MedicineName>
49 + </styled.MedicineNameWrapper>
50 + <styled.MedicineInfoWrapper>
51 + <styled.MedicineEachInfoWrapper>
52 + <styled.MedicineEachInfoTitle>효능</styled.MedicineEachInfoTitle>
53 + <styled.MedicineEachInfo>{props.bottleInfo.medicine.target}</styled.MedicineEachInfo>
54 + </styled.MedicineEachInfoWrapper>
55 + <styled.MedicineEachInfoWrapper>
56 + <styled.MedicineEachInfoTitle>복용 정보</styled.MedicineEachInfoTitle>
57 + <styled.MedicineEachInfo>{props.bottleInfo.medicine.dosage}</styled.MedicineEachInfo>
58 + </styled.MedicineEachInfoWrapper>
59 + <styled.MedicineEachInfoWrapper>
60 + <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>주의 사항</styled.MedicineEachInfoTitle>
61 + <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.warn}</styled.MedicineEachInfo>
62 + </styled.MedicineEachInfoWrapper>
63 + <styled.MedicineEachInfoWrapper>
64 + <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>부작용</styled.MedicineEachInfoTitle>
65 + <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.antiEffect}</styled.MedicineEachInfo>
66 + </styled.MedicineEachInfoWrapper>
67 + </styled.MedicineInfoWrapper>
68 + </styled.ModalContent>
69 + </styled.ModalContentWrapper>
70 + </styled.ModalContainer> : null
71 + }
72 + <styled.ChartAndFeedbackWrapper>
73 + <styled.ChartWrapper>
74 + <styled.MedicineDetailViewButtonWrapper>
75 + <styled.MedicineDetailViewButton
76 + onClick = {() => props.setMedicineInfoModal(true)}
77 + >
78 + <styled.MedicineDetailViewButtonImg src = {plus}/>
79 + </styled.MedicineDetailViewButton>
80 + </styled.MedicineDetailViewButtonWrapper>
81 + <HighchartsReact
82 + highCharts = {HighCharts}
83 + options = {props.chartOption}
84 + />
85 + </styled.ChartWrapper>
86 + <styled.FeedbackWrapper>
87 + <styled.FeedbackInfoTitle>피드백 내역</styled.FeedbackInfoTitle>
88 + {
89 + props.bottleInfo.feedbackList.map((feedback : any) => {
90 + return (
91 + <styled.FeedbackEachItemWrapper
92 + key = {feedback._id}
93 + >
94 + <styled.FeedbackType fdbType = {feedback.fdbType}/>
95 + <styled.FeedbackTitle>{feedback.feedback}</styled.FeedbackTitle>
96 + <styled.FeedbackDtm>등록일<br/>{feedback.fdbDtm}</styled.FeedbackDtm>
97 + </styled.FeedbackEachItemWrapper>
98 + )
99 + })
100 + }
101 + </styled.FeedbackWrapper>
102 + </styled.ChartAndFeedbackWrapper>
103 + <styled.NewFeedbackRegWrapper>
104 + <styled.NewFeedbackRegInput
105 + value = {props.feedback}
106 + onChange = {props.onSetFeedback}
107 + />
108 + <styled.NewFeedbackButtonWrapper>
109 + <styled.NewFeedbackTypeButtonWrapper>
110 + <styled.NewFeedbackTypeButtonEachWrapper>
111 + <styled.NewFeedbackTypeButton
112 + valueType = 'RECOMMEND'
113 + selected = {props.fdbType === 'RECOMMEND'}
114 + onClick = {() => props.setFdbType('RECOMMEND')}
115 + />
116 + <styled.NewFeedbackTypeButtonText>권고</styled.NewFeedbackTypeButtonText>
117 + </styled.NewFeedbackTypeButtonEachWrapper>
118 + <styled.NewFeedbackTypeButtonEachWrapper>
119 + <styled.NewFeedbackTypeButton
120 + valueType = 'CAUTION'
121 + selected = {props.fdbType === 'CAUTION'}
122 + onClick = {() => props.setFdbType('CAUTION')}
123 + />
124 + <styled.NewFeedbackTypeButtonText>주의</styled.NewFeedbackTypeButtonText>
125 + </styled.NewFeedbackTypeButtonEachWrapper>
126 + <styled.NewFeedbackTypeButtonEachWrapper>
127 + <styled.NewFeedbackTypeButton
128 + valueType = 'WARN'
129 + selected = {props.fdbType === 'WARN'}
130 + onClick = {() => props.setFdbType('WARN')}
131 + />
132 + <styled.NewFeedbackTypeButtonText>경고</styled.NewFeedbackTypeButtonText>
133 + </styled.NewFeedbackTypeButtonEachWrapper>
134 + <styled.NewFeedbackTypeButtonEachWrapper>
135 + <styled.NewFeedbackTypeButton
136 + valueType = 'CRITICAL'
137 + selected = {props.fdbType === 'CRITICAL'}
138 + onClick = {() => props.setFdbType('CRITICAL')}
139 + />
140 + <styled.NewFeedbackTypeButtonText>치명</styled.NewFeedbackTypeButtonText>
141 + </styled.NewFeedbackTypeButtonEachWrapper>
142 + </styled.NewFeedbackTypeButtonWrapper>
143 + <styled.NewFeedbackRegButton
144 + onClick = {props.onSubmitFeedback}
145 + >
146 + {/* <styled.NewFeedbackRegButtonImg /> */}
147 + <styled.NewFeedbackRegButtonText>피드백<br/>등록</styled.NewFeedbackRegButtonText>
148 + </styled.NewFeedbackRegButton>
149 + </styled.NewFeedbackButtonWrapper>
150 + </styled.NewFeedbackRegWrapper>
10 </styled.Container> 151 </styled.Container>
11 ); 152 );
12 }; 153 };
......
1 -import styled from 'styled-components'; 1 +import styled, { keyframes } from 'styled-components';
2 +
3 +
4 +const ModalOn = keyframes `
5 + 0% {
6 + background-color : rgba(52, 52, 52, .0);
7 + }
8 + 20% {
9 + background-color : rgba(52, 52, 52, .2);
10 + }
11 + 40% {
12 + background-color : rgba(52, 52, 52, .4);
13 + }
14 + 60% {
15 + background-color : rgba(52, 52, 52, .5);
16 + }
17 + 80% {
18 + background-color : rgba(52, 52, 52, .6);
19 + }
20 + 100% {
21 + background-color : rgba(52, 52, 52, .7);
22 + }
23 +
24 +`;
25 +
2 26
3 export const Container = styled.div ` 27 export const Container = styled.div `
28 + height : 100vh;
29 + width : 100%;
30 + display : flex;
31 + flex-direction : column;
32 + justify-content : center;
33 +`;
34 +
35 +export const ModalContainer = styled.div `
36 + height : 100%;
37 + width : 100%;
38 + z-index : 99;
39 + position : absolute;
40 +
41 + display : flex;
42 + flex-direction : column;
43 +
44 + animation : ${ModalOn} .5s;
45 +
46 + background-color : rgba(52, 52, 52, .7);
47 +
48 +`;
49 +
50 +export const ModalClsButtonWrapper = styled.div `
51 + flex : 1;
52 +
53 + display : flex;
54 +
55 + justify-content : flex-end;
56 + align-items : center;
57 + padding : 0 20px;
58 +
59 + border : none;
60 + background-color : transprent;
61 +`;
62 +
63 +export const ModalClsButton = styled.button `
64 + border : none;
65 + background-color : transparent;
66 +
67 + cursor : pointer;
68 +
69 + color : #fff;
70 +
71 + display : flex;
72 + flex-direction : row;
73 +
74 + justify-content : center;
75 + align-items : center;
76 +
77 + transition : .25s all;
78 + &:hover {
79 + opacity : .5;
80 + }
81 +`;
82 +
83 +export const ModalClsButtonImg = styled.img `
84 + height : 20px;
85 + width : 20px;
86 +
87 + margin : 0 10px 0 0;
88 +`;
89 +
90 +export const ModalClsButtonText = styled.div `
91 + font-size : 18px;
92 + font-weight : 700;
93 +`;
94 +
95 +export const ModalContentWrapper = styled.div `
96 + flex : 8;
97 +
98 + display : flex;
99 + flex-direction : column;
100 +
101 + justify-content : center;
102 + align-items : center;
103 +
104 + border : none;
105 +`;
106 +
107 +export const ModalContent = styled.div `
108 + width : 700px;
109 + height : 500px;
110 +
111 + background-color : #fff;
112 + border : 1.2px solid #337DFF;
113 + border-radius : 5px;
114 +
115 + display : flex;
116 + flex-direction : column;
117 +
118 + // justify-content : center;
119 + align-items : center;
120 +`;
121 +
122 +export const MedicineNameWrapper = styled.div `
123 + flex : 1
124 + border : none;
125 + border-bottom : 1px solid #ddd;
126 + width : 100%;
127 +
128 + padding : 4% 0;
129 +
130 + display : flex;
131 + flex-direction : column;
132 + justify-content : center;
133 + align-items : center;
134 +`;
135 +
136 +export const MedicineName = styled.div `
137 + font-size : 20px;
138 + font-weight : 700;
139 + letter-spacing : 1px;
140 +
141 + color : #337DFF;
142 +`;
143 +
144 +export const MedicineInfoWrapper = styled.div `
145 + flex : 9;
146 + width : 100%;
147 +
148 + overflow : scroll;
149 + border : 1px solid;
150 +
151 + display : flex;
152 + flex-direction : column;
153 + align-items : center;
154 +
155 + padding : 0 0 0 3px;
156 +
157 + &::-webkit-scrollbar {
158 + width : 3px;
159 + background-color : transparent;
160 + height : 0px;
161 + }
162 +
163 + &::-webkit-scrollbar-thumb {
164 + background-color : #337DFF;
165 + }
166 +
167 +`;
168 +
169 +export const MedicineEachInfoWrapper = styled.div `
170 + display : flex;
171 + flex-direction : column;
172 +
173 + width : 80%;
174 + padding : 20px 10%;
175 + border : none;
176 + border-bottom : 1px solid #ddd;
177 +`;
178 +
179 +export const MedicineEachInfoTitle = styled.div `
180 + font-size : 14px;
181 + font-weight : 500;
182 + color : #337DFF;
183 + margin : 0 0 5px 0;
184 +`;
185 +
186 +export const MedicineEachInfo = styled.div `
187 + font-size : 16px;
188 + letter-spacing : 1px;
189 +`;
190 +
191 +export const ChartAndFeedbackWrapper = styled.div `
192 + flex : 3;
193 + display : flex;
194 + flex-direction : row;
195 +
196 + justify-content : space-around;
197 +
198 +`;
199 +
200 +export const ChartWrapper = styled.div `
201 + border : 1px solid transparent;
202 + padding : 10px;
203 +
204 + border-radius : 4px;
205 +
206 + display : flex;
207 + flex-direction : column;
208 + justify-content : center;
209 + align-items : center;
210 +
211 + box-shadow: 0px 0px 10px #a0a0a0;
212 +
213 +`;
214 +
215 +export const MedicineDetailViewButtonWrapper = styled.div `
216 + border : none;
217 + width : 100%;
218 +
219 + display : flex;
220 + flex-direction : row;
221 + justify-content : flex-end;
222 + align-items : center;
223 +`;
224 +
225 +export const MedicineDetailViewButton = styled.button `
226 + height : 25px;
227 + width : 25px;
228 +
229 + display : flex;
230 + justify-content : center;
231 + align-items : center;
232 +
233 + background-color : transparent;
234 + border : 1px solid #ddd;
235 + border-radius : 3px;
236 +
237 + cursor : pointer;
238 +
239 + box-shadow: 0px 0px 3px 0 #a0a0a0;
240 +
241 + opacity : .7;
242 +
243 + &:hover {
244 + opacity : 1;
245 + }
246 +`;
247 +
248 +export const MedicineDetailViewButtonImg = styled.img `
249 + height : 15px;
250 + width : 15px;
251 +`;
252 +
253 +export const FeedbackWrapper = styled.div `
254 + overflow : scroll;
255 + border : 1px solid transparent;
256 + width : 500px;
257 +
258 + max-height : 420px;
259 +
260 + border-radius : 4px;
261 +
262 + box-shadow: 0px 0px 10px #a0a0a0;
263 +
264 + display : flex;
265 + flex-direction : column;
266 +
267 + align-items : center;
268 +
269 + padding : 0 0 0 3px;
270 +
271 + &::-webkit-scrollbar {
272 + width : 3px;
273 + background-color : transparent;
274 + height : 0px;
275 + }
276 +
277 + &::-webkit-scrollbar-thumb {
278 + background-color : #337DFF;
279 + }
280 +`;
281 +
282 +export const FeedbackInfoTitle = styled.div `
283 + width : 100%;
284 + text-align : center;
285 +
286 + border : none;
287 + border-bottom : 2px solid #ddd;
288 +
289 + padding : 10px 0;
290 +
291 + font-size : 17px;
292 + font-weight : 600;
293 +
294 + background-color : transparent;
295 +`;
296 +
297 +export const FeedbackEachItemWrapper = styled.div `
298 + width : 100%;
299 + padding : 10px 0;
300 +
301 + border : none;
302 + border-bottom : 1px solid #ddd;
303 +
304 + background-color : transparent;
305 +
306 + display : flex;
307 + flex-direction : row;
308 + align-items : center;
309 +`;
310 +
311 +export const FeedbackType = styled.div<{fdbType : string}> `
312 + margin : 0 0 0 10px;
313 +
314 + border : none;
315 + border-radius : 100px;
316 +
317 + height : 12px;
318 + width : 12px;
319 +
320 +
321 + background-color : ${props =>
322 + props.fdbType === 'RECOMMEND' ? '#337DFF'
323 + : props.fdbType === 'CAUTION' ? '#FFE77B'
324 + : props.fdbType === 'WARN' ? '#FF8941'
325 + : props.fdbType === 'CRITICAL' ? '#E40000' : 'transparent'
326 + };
327 +`;
328 +
329 +export const FeedbackTitle = styled.div `
330 + flex : 7;
331 +
332 + height : 100%;
333 +
334 + padding : 0 10px;
335 +
336 + display : flex;
337 + flex-direction : row;
338 +
339 + align-items : center;
340 +
341 + font-size : 12px;
342 + font-weight : 500;
343 +`;
344 +
345 +export const FeedbackDtm = styled.div `
346 + flex : 2;
347 +
348 + height : 100%;
349 +
350 + padding : 0 10px;
351 +
352 + display : flex;
353 + flex-direction : row;
354 +
355 + align-items : center;
356 +
357 + border : none;
358 + border-left : 1px solid #ddd;
359 +
360 + font-size : 10px;
361 + font-weight : 400;
362 +`;
363 +
364 +
365 +export const NewFeedbackRegWrapper = styled.div `
366 + margin : 20px 10px;
367 +
368 + flex : 2;
369 + display : flex;
370 + flex-direction : row;
371 + justify-content : center;
372 +
373 + padding : 20px;
374 +
375 + border : 1px solid transparent;
376 + border-radius : 4px;
377 +
378 + box-shadow: 0px 0px 10px #a0a0a0;
379 +
380 +`;
381 +
382 +export const NewFeedbackRegInput = styled.textarea `
383 + resize : none;
384 + flex : 10;
385 +
386 + padding : 30px;
387 +
388 + font-size : 14px;
389 +
390 + border : 1px solid #a0a0a0;
391 + border-radius : 4px;
392 +`;
393 +
394 +export const NewFeedbackButtonWrapper = styled.div `
395 + flex : 2;
396 + border : none;
397 +
398 + display : flex;
399 + flex-direction : column;
400 + justify-content : center;
401 + align-items : center;
402 +
403 + gap : 2%;
404 +
405 +`;
406 +
407 +export const NewFeedbackTypeButtonWrapper = styled.div `
408 + flex : 5;
409 + display : flex;
410 + flex-direction : column;
411 +
412 + border : none;
413 + width : 70%;
414 +`;
415 +
416 +export const NewFeedbackTypeButtonEachWrapper = styled.div `
417 + flex : 1;
418 + display : flex;
419 + flex-direction : row;
420 + justify-content : flex-start;
421 + align-items : center;
422 +
423 + width : 100%;
424 + padding : 10% 0;
425 +
426 + border : none;
427 +`;
428 +
429 +export const NewFeedbackTypeButton = styled.button<{valueType : string, selected : boolean}> `
430 + height : 15px;
431 + width : 15px;
432 +
433 + border : 1px solid #ddd;
434 + border-radius : 3px;
435 + background-color : ${props =>
436 + props.selected && props.valueType === 'RECOMMEND' ? '#337DFF'
437 + : props.selected && props.valueType === 'CAUTION' ? '#FFE77B'
438 + : props.selected && props.valueType === 'WARN' ? '#FF8941'
439 + : props.selected && props.valueType === 'CRITICAL' ? '#E40000' : 'transparent'
440 + };
441 +
442 + display : flex;
443 + justify-content : center;
444 + align-items : center;
445 +
446 + cursor : pointer;
447 +
448 + margin : 0 5% 0 0;
449 +
450 + transition : .25s all;
451 +
452 + &:hover {
453 + background-color : ${props =>
454 + props.valueType === 'RECOMMEND' ? '#337DFF'
455 + : props.valueType === 'CAUTION' ? '#FFE77B'
456 + : props.valueType === 'WARN' ? '#FF8941'
457 + : props.valueType === 'CRITICAL' ? '#E40000' : 'transparent'
458 + };
459 + }
460 +`
461 +
462 +export const NewFeedbackTypeButtonText = styled.div `
463 + font-size : 12px;
464 + font-weight : 500;
465 + letter-spacing : 1px;
466 +`;
467 +
468 +export const NewFeedbackRegButton = styled.button `
469 + flex : 2;
470 + width : 70%;
471 +
472 + cursor : pointer;
473 +
474 + border : 1px solid transparent;
475 + border-radius : 4px;
476 + background-color : #343434;
477 + color : #fff;
478 +
479 + transition : .25s all;
480 +
481 + &:hover {
482 + background-color : transparent;
483 + border : 1px solid #343434;
484 + color : #343434;
485 + }
486 +`;
487 +
488 +export const NewFeedbackRegButtonText = styled.div `
489 + font-size : 15px;
490 + letter-spacing : 1px;
491 + font-weight : 600;
492 +
493 + font-color : #fff
494 +
495 +
496 +`;
497 +
498 +export const NewFeedbackRegButtonImg = styled.img `
4 499
5 `; 500 `;
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -4,12 +4,16 @@ import { RouteComponentProps } from 'react-router-dom'; ...@@ -4,12 +4,16 @@ import { RouteComponentProps } from 'react-router-dom';
4 import { useRecoilState } from 'recoil'; 4 import { useRecoilState } from 'recoil';
5 import * as recoilUtil from '../../util/recoilUtil'; 5 import * as recoilUtil from '../../util/recoilUtil';
6 6
7 +import * as Alert from '../../util/alertMessage';
8 +
9 +import Header from '../../components/Header';
7 import LoginPresenter from './LoginPresenter'; 10 import LoginPresenter from './LoginPresenter';
8 11
9 import { authApi } from '../../api'; 12 import { authApi } from '../../api';
10 13
11 14
12 -type LoginProps = RouteComponentProps 15 +// eslint-disable-next-line @typescript-eslint/no-empty-interface
16 +interface LoginProps extends RouteComponentProps {}
13 17
14 const LoginContainer = (props : LoginProps) => { 18 const LoginContainer = (props : LoginProps) => {
15 19
...@@ -17,9 +21,19 @@ const LoginContainer = (props : LoginProps) => { ...@@ -17,9 +21,19 @@ const LoginContainer = (props : LoginProps) => {
17 userId : '', 21 userId : '',
18 password : '', 22 password : '',
19 }); 23 });
24 +
20 const [token, setToken] = useRecoilState(recoilUtil.token); 25 const [token, setToken] = useRecoilState(recoilUtil.token);
21 const [userTypeCd, setUserTypeCd] = useRecoilState(recoilUtil.userTypeCd); 26 const [userTypeCd, setUserTypeCd] = useRecoilState(recoilUtil.userTypeCd);
22 - const [error, setError] = useRecoilState(recoilUtil.error); 27 +
28 +
29 + const fetchData = async() => {
30 + if(token && token.length) {
31 + const result = await authApi.verifyToken(token);
32 + if (result.statusText === 'OK') {
33 + props.history.push('/');
34 + }
35 + }
36 + };
23 37
24 const onSetUserId = (e : React.ChangeEvent<HTMLInputElement>) => { 38 const onSetUserId = (e : React.ChangeEvent<HTMLInputElement>) => {
25 setLoginForm({ 39 setLoginForm({
...@@ -42,23 +56,26 @@ const LoginContainer = (props : LoginProps) => { ...@@ -42,23 +56,26 @@ const LoginContainer = (props : LoginProps) => {
42 const onLogin = async () => { 56 const onLogin = async () => {
43 try { 57 try {
44 const result : any = await authApi.login(loginForm); 58 const result : any = await authApi.login(loginForm);
45 - if(result.statusText === 'OK') { 59 + if(result.statusText === 'OK' && result.data.userTypeCd !== 'NORMAL') {
46 setToken(result.data.token); 60 setToken(result.data.token);
47 setUserTypeCd(result.data.userTypeCd); 61 setUserTypeCd(result.data.userTypeCd);
48 - props.history.push('/'); 62 + Alert.onSuccess('로그인 성공, 메인 화면으로 이동합니다.', () => props.history.push('/'));
63 + } else if(result.data.userTypeCd === 'NORMAL') {
64 + Alert.onError('권한이 없는 유저입니다.', () => props.history.push('/'));
49 } 65 }
50 } catch(e) { 66 } catch(e) {
51 - setError('로그인에 실패했습니다.'); 67 + Alert.onError(e.response.data, () => null);
52 - console.log(e);
53 } 68 }
54 69
55 }; 70 };
56 71
57 useEffect(() => { 72 useEffect(() => {
58 - console.log('loginPage'); 73 + fetchData();
59 }, []); 74 }, []);
60 75
61 return ( 76 return (
77 + <>
78 + <Header {...props} />
62 <LoginPresenter 79 <LoginPresenter
63 loginForm = {loginForm} 80 loginForm = {loginForm}
64 onSetUserId = {onSetUserId} 81 onSetUserId = {onSetUserId}
...@@ -66,6 +83,7 @@ const LoginContainer = (props : LoginProps) => { ...@@ -66,6 +83,7 @@ const LoginContainer = (props : LoginProps) => {
66 onGoRegister = {onGoRegister} 83 onGoRegister = {onGoRegister}
67 onLogin = {onLogin} 84 onLogin = {onLogin}
68 /> 85 />
86 + </>
69 ) 87 )
70 }; 88 };
71 89
......
...@@ -17,7 +17,7 @@ const LoginPresenter = (props : LoginProps) => { ...@@ -17,7 +17,7 @@ const LoginPresenter = (props : LoginProps) => {
17 return ( 17 return (
18 <styled.Container> 18 <styled.Container>
19 <styled.LoginWrapper> 19 <styled.LoginWrapper>
20 - <styled.LoginTitle>로그인</styled.LoginTitle> 20 + <styled.LoginTitle>🩺로그인</styled.LoginTitle>
21 <styled.LoginInputWrapper> 21 <styled.LoginInputWrapper>
22 <styled.LoginEachInputWrapper> 22 <styled.LoginEachInputWrapper>
23 <styled.LoginInputText> 23 <styled.LoginInputText>
......
1 -import styled from 'styled-components'; 1 +import styled, { keyframes} from 'styled-components';
2 +
3 +
4 +const twinkle = keyframes `
5 + 0% {
6 + opacity : 1;
7 + background-color : #337DFF;
8 + }
9 + 20% {
10 + opacity : .75;
11 + }
12 + 40% {
13 + opacity : .5;
14 + }
15 + 60% {
16 + opacity : .5;
17 + }
18 + 80% {
19 + opacity : .75;
20 + }
21 + 100% {
22 + opacity : 1;
23 + background-color : #337DFF;
24 + }
25 +`
26 +
27 +const twinkleText = keyframes `
28 + 0% {
29 + opacity : 1;
30 + }
31 + 20% {
32 + opacity : .75;
33 + }
34 + 40% {
35 + opacity : .5;
36 + }
37 + 60% {
38 + opacity : .5;
39 + }
40 + 80% {
41 + opacity : .75;
42 + }
43 + 100% {
44 + opacity : 1;
45 + }
46 +`;
2 47
3 export const Container = styled.div ` 48 export const Container = styled.div `
4 width : 100%; 49 width : 100%;
...@@ -73,10 +118,8 @@ export const RegisterButton = styled.button ` ...@@ -73,10 +118,8 @@ export const RegisterButton = styled.button `
73 118
74 font-size : 11px; 119 font-size : 11px;
75 120
76 - transition : .25s all;
77 -
78 &:hover { 121 &:hover {
79 - opacity : .5; 122 + animation : ${twinkleText} 2s infinite;
80 } 123 }
81 124
82 `; 125 `;
...@@ -97,10 +140,10 @@ export const LoginButton = styled.button<{isLoginButton : boolean}> ` ...@@ -97,10 +140,10 @@ export const LoginButton = styled.button<{isLoginButton : boolean}> `
97 border-radius : 5px; 140 border-radius : 5px;
98 padding : 10px 30px; 141 padding : 10px 30px;
99 142
143 + width : 80%;
144 +
100 cursor : pointer; 145 cursor : pointer;
101 146
102 - transition : .25s all;
103 -
104 color : #343434; 147 color : #343434;
105 font-weight : 600; 148 font-weight : 600;
106 149
...@@ -108,6 +151,7 @@ export const LoginButton = styled.button<{isLoginButton : boolean}> ` ...@@ -108,6 +151,7 @@ export const LoginButton = styled.button<{isLoginButton : boolean}> `
108 background-color : #343434; 151 background-color : #343434;
109 color : #fff; 152 color : #fff;
110 border : 1.2px solid transparent; 153 border : 1.2px solid transparent;
154 + animation : ${twinkle} 1.5s infinite linear;
111 } 155 }
112 156
113 margin : 0 15px; 157 margin : 0 15px;
......
...@@ -4,6 +4,8 @@ import { RouteComponentProps} from 'react-router-dom'; ...@@ -4,6 +4,8 @@ import { RouteComponentProps} from 'react-router-dom';
4 import { useRecoilValue } from 'recoil'; 4 import { useRecoilValue } from 'recoil';
5 import * as recoilUtil from '../../util/recoilUtil'; 5 import * as recoilUtil from '../../util/recoilUtil';
6 6
7 +
8 +import Header from '../../components/Header';
7 import DoctorMenuContainer from './doctor'; 9 import DoctorMenuContainer from './doctor';
8 import ManagerMenuContainer from './manager'; 10 import ManagerMenuContainer from './manager';
9 11
...@@ -22,9 +24,15 @@ const MainContainer = (props : MainProps) => { ...@@ -22,9 +24,15 @@ const MainContainer = (props : MainProps) => {
22 }, []); 24 }, []);
23 25
24 return ( 26 return (
25 - userTypeCd === 'DOCTOR' ? 27 + <>
26 - <DoctorMenuContainer {...props}/> : 28 + <Header {...props}/>
27 - <ManagerMenuContainer {...props}/> 29 + {
30 + userTypeCd === 'DOCTOR' ?
31 + <DoctorMenuContainer {...props}/> :
32 + userTypeCd === 'MANAGER' ?
33 + <ManagerMenuContainer {...props}/> : null
34 + }
35 + </>
28 ); 36 );
29 }; 37 };
30 38
......
...@@ -6,6 +6,8 @@ import DoctorMenuPresenter from './DoctorMenuPresenter'; ...@@ -6,6 +6,8 @@ import DoctorMenuPresenter from './DoctorMenuPresenter';
6 import { useRecoilState, useRecoilValue } from 'recoil'; 6 import { useRecoilState, useRecoilValue } from 'recoil';
7 import * as recoilUtil from '../../../util/recoilUtil'; 7 import * as recoilUtil from '../../../util/recoilUtil';
8 8
9 +import * as Alert from '../../../util/alertMessage';
10 +
9 import { doctorApi, authApi } from '../../../api'; 11 import { doctorApi, authApi } from '../../../api';
10 12
11 13
...@@ -36,13 +38,15 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { ...@@ -36,13 +38,15 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => {
36 38
37 const [searchPatientKeyword, setSearchPatientKeyword] = useState<string>(''); 39 const [searchPatientKeyword, setSearchPatientKeyword] = useState<string>('');
38 const [filteringPatientList, setFilteringPatientList] = useState<any>([]); 40 const [filteringPatientList, setFilteringPatientList] = useState<any>([]);
39 - 41 +
40 const [patientDetail, setPatientDetail] = useState<any>(); 42 const [patientDetail, setPatientDetail] = useState<any>();
41 43
42 const [editModal, setEditModal] = useState<boolean>(false); 44 const [editModal, setEditModal] = useState<boolean>(false);
45 + const [editPatientInfo, setEditPatientInfo] = useState<string>('');
46 +
43 const [newPatientRegisterModal, setNewPatientRegisterModal] = useState<boolean>(false); 47 const [newPatientRegisterModal, setNewPatientRegisterModal] = useState<boolean>(false);
44 const [newPatientSearchId, setNewPatientSearchId] = useState<string>(''); 48 const [newPatientSearchId, setNewPatientSearchId] = useState<string>('');
45 - 49 + const [newPatientSearchResult, setNewPatientSearchResult] = useState<any | null>(null);
46 50
47 51
48 const fetchData = async() => { 52 const fetchData = async() => {
...@@ -92,7 +96,8 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { ...@@ -92,7 +96,8 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => {
92 } 96 }
93 }; 97 };
94 98
95 - const onInitialize = () => { 99 + const onInitialize = async () => {
100 + await fetchData();
96 setInfo({ 101 setInfo({
97 infoType : 'DOCTOR', 102 infoType : 'DOCTOR',
98 userNm : doctorInfo.doctorNm, 103 userNm : doctorInfo.doctorNm,
...@@ -103,9 +108,46 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { ...@@ -103,9 +108,46 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => {
103 }); 108 });
104 setFilteringPatientList([]); 109 setFilteringPatientList([]);
105 setSearchPatientKeyword(''); 110 setSearchPatientKeyword('');
111 + setEditModal(false);
112 + setEditPatientInfo('');
113 + setNewPatientRegisterModal(false);
114 + setNewPatientSearchId('');
115 + setNewPatientSearchResult(null);
106 setPatientDetail(null); 116 setPatientDetail(null);
107 }; 117 };
108 118
119 + const onEditPatientInfo = (e : React.ChangeEvent<HTMLTextAreaElement>) => {
120 + setEditPatientInfo(e.target.value);
121 + };
122 +
123 + const onSubmitPatientInfo = () => {
124 + if(editPatientInfo.length && patientDetail) {
125 + const onSubmit = async () => {
126 + try {
127 + const result = await doctorApi.writePatientInfo(token, {
128 + patientId : patientDetail.profile.userId,
129 + info : editPatientInfo,
130 + });
131 + if(result.statusText === 'OK') {
132 + Alert.onSuccess('환자의 특이사항을 업데이트했습니다.', () => onInitialize());
133 + } else {
134 + Alert.onError('특이사항을 기록하는데 실패했습니다.', () => null);
135 + }
136 +
137 + } catch(e) {
138 + Alert.onError(e.response.data.error, () => null);
139 + }
140 + };
141 +
142 + Alert.onCheck('환자의 특이사항을 업데이트하시겠습니까?', onSubmit, () => null);
143 +
144 + } else {
145 + Alert.onError('환자의 특이사항을 기록하세요.', () => null);
146 + }
147 +
148 +
149 + };
150 +
109 151
110 const onSetNewPatientSearchId = (e : React.ChangeEvent<HTMLInputElement>) => { 152 const onSetNewPatientSearchId = (e : React.ChangeEvent<HTMLInputElement>) => {
111 setNewPatientSearchId(e.target.value); 153 setNewPatientSearchId(e.target.value);
...@@ -114,16 +156,51 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { ...@@ -114,16 +156,51 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => {
114 const onSearchNewPatientByEmail = async () => { 156 const onSearchNewPatientByEmail = async () => {
115 try { 157 try {
116 await doctorApi.searchPatientById(token, newPatientSearchId).then(res => { 158 await doctorApi.searchPatientById(token, newPatientSearchId).then(res => {
117 - console.log(res.data); 159 + setNewPatientSearchResult(res.data);
118 - }).catch(err => console.log(err)); 160 + }).catch(err => {
161 + console.log(err);
162 + Alert.onError('검색 결과가 없습니다.', () => null);
163 + setNewPatientSearchResult(null);
164 + });
119 } catch(e) { 165 } catch(e) {
120 - console.log(e); 166 + Alert.onError(e.response.data.error, () => null);
167 + }
168 + };
169 +
170 + const onRegisterNewPatient = () => {
171 + if(newPatientSearchResult) {
172 + const { patientId, patientNm } = newPatientSearchResult;
173 + const onRegisterReq = async () => {
174 + try {
175 + const result = await doctorApi.registerPatient(token, {
176 + patientId,
177 + });
178 + if(result.statusText === 'OK') {
179 + Alert.onSuccess('환자에게 담당의 등록 요청을 전송했습니다.', () => null);
180 + } else {
181 + Alert.onError('환자에게 담당의 등록 요청을 실패했습니다.', () => null);
182 + }
183 + } catch(e) {
184 + Alert.onError(e.response.data.error, () => null);
185 + }
186 + };
187 +
188 + Alert.onCheck(`${patientNm} 환자에게 담당의 등록 요청을 전송하시겠습니까?`, onRegisterReq, () => null);
189 + } else {
190 + Alert.onError('환자를 먼저 검색해주세요.', () => null);
121 } 191 }
122 }; 192 };
123 193
194 + const onCloseModal = async () => {
195 + setNewPatientRegisterModal(false);
196 + setNewPatientSearchId('');
197 + setNewPatientSearchResult(null);
198 + setEditModal(false);
199 + setEditPatientInfo('');
200 + };
124 201
125 const onGoBottleDetail = (bottleId : number) => { 202 const onGoBottleDetail = (bottleId : number) => {
126 - console.log(bottleId); 203 + props.history.push(`/bottle/${bottleId}`);
127 }; 204 };
128 205
129 206
...@@ -158,12 +235,19 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { ...@@ -158,12 +235,19 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => {
158 235
159 editModal = {editModal} 236 editModal = {editModal}
160 setEditModal = {setEditModal} 237 setEditModal = {setEditModal}
238 + editPatientInfo = {editPatientInfo}
239 + onEditPatientInfo = {onEditPatientInfo}
240 + onSubmitPatientInfo = {onSubmitPatientInfo}
161 241
162 newPatientRegisterModal = {newPatientRegisterModal} 242 newPatientRegisterModal = {newPatientRegisterModal}
163 setNewPatientRegisterModal = {setNewPatientRegisterModal} 243 setNewPatientRegisterModal = {setNewPatientRegisterModal}
164 newPatientSearchId = {newPatientSearchId} 244 newPatientSearchId = {newPatientSearchId}
165 onSetNewPatientSearchId = {onSetNewPatientSearchId} 245 onSetNewPatientSearchId = {onSetNewPatientSearchId}
166 onSearchNewPatientByEmail = {onSearchNewPatientByEmail} 246 onSearchNewPatientByEmail = {onSearchNewPatientByEmail}
247 + onRegisterNewPatient = {onRegisterNewPatient}
248 + onCloseModal = {onCloseModal}
249 +
250 + newPatientSearchResult = {newPatientSearchResult}
167 /> 251 />
168 ); 252 );
169 }; 253 };
......
...@@ -5,6 +5,8 @@ import * as styled from './DoctorMenuStyled'; ...@@ -5,6 +5,8 @@ import * as styled from './DoctorMenuStyled';
5 const medicineImg = '/static/img/medicine.png'; 5 const medicineImg = '/static/img/medicine.png';
6 const lensImg = '/static/img/lens.png'; 6 const lensImg = '/static/img/lens.png';
7 const closeButton = '/static/img/close.png'; 7 const closeButton = '/static/img/close.png';
8 +const edit = '/static/img/edit.png';
9 +const refreshing = '/static/img/refreshing.png';
8 10
9 11
10 interface DoctorMenuProps { 12 interface DoctorMenuProps {
...@@ -27,23 +29,30 @@ interface DoctorMenuProps { ...@@ -27,23 +29,30 @@ interface DoctorMenuProps {
27 29
28 editModal : boolean; 30 editModal : boolean;
29 setEditModal : any; 31 setEditModal : any;
32 + editPatientInfo : string;
33 + onEditPatientInfo : React.ChangeEventHandler<HTMLTextAreaElement>;
34 + onSubmitPatientInfo : () => void;
30 35
31 newPatientRegisterModal : boolean; 36 newPatientRegisterModal : boolean;
32 setNewPatientRegisterModal : any; 37 setNewPatientRegisterModal : any;
33 newPatientSearchId: string; 38 newPatientSearchId: string;
34 onSetNewPatientSearchId : React.ChangeEventHandler<HTMLInputElement>; 39 onSetNewPatientSearchId : React.ChangeEventHandler<HTMLInputElement>;
35 onSearchNewPatientByEmail : () => void; 40 onSearchNewPatientByEmail : () => void;
41 + onRegisterNewPatient : () => void;
42 + onCloseModal : () => void;
43 +
44 + newPatientSearchResult : any;
36 } 45 }
37 46
38 const DoctorMenuPresenter = (props : DoctorMenuProps) => { 47 const DoctorMenuPresenter = (props : DoctorMenuProps) => {
39 return ( 48 return (
40 <styled.Container> 49 <styled.Container>
41 { 50 {
42 - props.editModal ? 51 + props.newPatientRegisterModal ?
43 <styled.ModalContainer> 52 <styled.ModalContainer>
44 <styled.ModalClsButtonWrapper> 53 <styled.ModalClsButtonWrapper>
45 <styled.ModalClsButton 54 <styled.ModalClsButton
46 - onClick = {() => props.setEditModal(false)} 55 + onClick = {() => props.setNewPatientRegisterModal(false)}
47 > 56 >
48 <styled.ModalClsButtonImg src = {closeButton}/> 57 <styled.ModalClsButtonImg src = {closeButton}/>
49 <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> 58 <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText>
...@@ -65,13 +74,28 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { ...@@ -65,13 +74,28 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => {
65 </styled.NewPatientSearchButton> 74 </styled.NewPatientSearchButton>
66 </styled.NewPatientSearchWrapper> 75 </styled.NewPatientSearchWrapper>
67 <styled.NewPatientSearchResultWrapper> 76 <styled.NewPatientSearchResultWrapper>
68 - 🤔검색 결과가 없습니다. 77 + {
78 + props.newPatientSearchResult ?
79 + <styled.NewPatientSearchResult>
80 + <styled.NewPatientSearchResultInfoWrapper>
81 + <styled.NewPatientSearchResultInfo>이름 : </styled.NewPatientSearchResultInfo>
82 + <styled.NewPatientSearchResultInfoText>
83 + {props.newPatientSearchResult.patientNm}
84 + </styled.NewPatientSearchResultInfoText>
85 + </styled.NewPatientSearchResultInfoWrapper>
86 + </styled.NewPatientSearchResult> :
87 + '🤔검색 결과가 없습니다.'
88 + }
69 </styled.NewPatientSearchResultWrapper> 89 </styled.NewPatientSearchResultWrapper>
70 <styled.NewPatientRegisterButtonWrapper> 90 <styled.NewPatientRegisterButtonWrapper>
71 - <styled.NewPatientRegisterButton> 91 + <styled.NewPatientRegisterButton
92 + onClick = {props.onRegisterNewPatient}
93 + >
72 확인 94 확인
73 </styled.NewPatientRegisterButton> 95 </styled.NewPatientRegisterButton>
74 - <styled.NewPatientRegisterButton> 96 + <styled.NewPatientRegisterButton
97 + onClick = {props.onCloseModal}
98 + >
75 취소 99 취소
76 </styled.NewPatientRegisterButton> 100 </styled.NewPatientRegisterButton>
77 </styled.NewPatientRegisterButtonWrapper> 101 </styled.NewPatientRegisterButtonWrapper>
...@@ -80,6 +104,60 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { ...@@ -80,6 +104,60 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => {
80 <styled.ModalClsButtonWrapper/> 104 <styled.ModalClsButtonWrapper/>
81 </styled.ModalContainer> : null 105 </styled.ModalContainer> : null
82 } 106 }
107 + {
108 + props.editModal ?
109 + <styled.ModalContainer>
110 + <styled.ModalClsButtonWrapper>
111 + <styled.ModalClsButton
112 + onClick = {() => props.setEditModal(false)}
113 + >
114 + <styled.ModalClsButtonImg src = {closeButton}/>
115 + <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText>
116 + </styled.ModalClsButton>
117 + </styled.ModalClsButtonWrapper>
118 + <styled.ModalContentWrapper>
119 + <styled.ModalContent>
120 + <styled.PatientInfoViewContainer>
121 + <styled.PatientInfoPatientNmWrapper>
122 + <styled.PatientInfoPatientNmInfo>이름 : </styled.PatientInfoPatientNmInfo>
123 + <styled.PatientInfoPatientNm>{props.info.userNm}</styled.PatientInfoPatientNm>
124 + </styled.PatientInfoPatientNmWrapper>
125 + <styled.PatientInfoView>
126 +
127 + {
128 + props.info.patientInfo.split('\n\n').map((patientInfoText : string) => {
129 + return (
130 + <div key = {patientInfoText}>
131 + {patientInfoText}<br/><br/>
132 + </div>
133 + )
134 + })
135 + }
136 + </styled.PatientInfoView>
137 + </styled.PatientInfoViewContainer>
138 + <styled.PatientInfoEditWrapper>
139 + <styled.PatientInfoEditInput
140 + value = {props.editPatientInfo}
141 + onChange = {props.onEditPatientInfo}
142 + />
143 + </styled.PatientInfoEditWrapper>
144 + <styled.PatientInfoEditButtonWrapper>
145 + <styled.PatientInfoEditButton
146 + onClick = {props.onSubmitPatientInfo}
147 + >
148 + 확인
149 + </styled.PatientInfoEditButton>
150 + <styled.PatientInfoEditButton
151 + onClick = {props.onCloseModal}
152 + >
153 + 취소
154 + </styled.PatientInfoEditButton>
155 + </styled.PatientInfoEditButtonWrapper>
156 + </styled.ModalContent>
157 + </styled.ModalContentWrapper>
158 + <styled.ModalClsButtonWrapper/>
159 + </styled.ModalContainer> : null
160 + }
83 <styled.InfoAndSearchWrapper> 161 <styled.InfoAndSearchWrapper>
84 <styled.InfoWrapper> 162 <styled.InfoWrapper>
85 { 163 {
...@@ -99,8 +177,10 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { ...@@ -99,8 +177,10 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => {
99 </styled.InfoEachWrapper> 177 </styled.InfoEachWrapper>
100 </styled.InfoSquare> : 178 </styled.InfoSquare> :
101 <styled.InfoSquare> 179 <styled.InfoSquare>
102 - <styled.EditPatientInfoButton> 180 + <styled.EditPatientInfoButton
103 - <styled.EditPatientInfoButtonImg /> 181 + onClick = {() => props.setEditModal(true)}
182 + >
183 + <styled.EditPatientInfoButtonImg src = {edit}/>
104 <styled.EditPatientInfoButtonText>수정</styled.EditPatientInfoButtonText> 184 <styled.EditPatientInfoButtonText>수정</styled.EditPatientInfoButtonText>
105 </styled.EditPatientInfoButton> 185 </styled.EditPatientInfoButton>
106 <styled.InfoEachWrapper> 186 <styled.InfoEachWrapper>
...@@ -134,7 +214,7 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { ...@@ -134,7 +214,7 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => {
134 </styled.InfoSquare> 214 </styled.InfoSquare>
135 } 215 }
136 <styled.NewPatientButton 216 <styled.NewPatientButton
137 - onClick = {() => props.setEditModal(true)} 217 + onClick = {() => props.setNewPatientRegisterModal(true)}
138 > 218 >
139 새 환자 등록 219 새 환자 등록
140 </styled.NewPatientButton> 220 </styled.NewPatientButton>
...@@ -147,12 +227,12 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { ...@@ -147,12 +227,12 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => {
147 onChange = {props.onSetKeyword} 227 onChange = {props.onSetKeyword}
148 /> 228 />
149 <styled.SearchButton> 229 <styled.SearchButton>
150 - 검색 230 + <styled.SearchButtonImg src = {lensImg}/>
151 </styled.SearchButton> 231 </styled.SearchButton>
152 <styled.SearchButton 232 <styled.SearchButton
153 onClick = {props.onInitialize} 233 onClick = {props.onInitialize}
154 > 234 >
155 - 초기화 235 + <styled.SearchButtonImg src = {refreshing}/>
156 </styled.SearchButton> 236 </styled.SearchButton>
157 </styled.SearchBarWrapper> 237 </styled.SearchBarWrapper>
158 <styled.SearchResultWrapper> 238 <styled.SearchResultWrapper>
......
1 -import styled from 'styled-components'; 1 +import styled, { keyframes } from 'styled-components';
2 +
3 +
4 +const ModalOn = keyframes `
5 + 0% {
6 + background-color : rgba(52, 52, 52, .0);
7 + }
8 + 20% {
9 + background-color : rgba(52, 52, 52, .2);
10 + }
11 + 40% {
12 + background-color : rgba(52, 52, 52, .4);
13 + }
14 + 60% {
15 + background-color : rgba(52, 52, 52, .5);
16 + }
17 + 80% {
18 + background-color : rgba(52, 52, 52, .6);
19 + }
20 + 100% {
21 + background-color : rgba(52, 52, 52, .7);
22 + }
23 +
24 +`;
25 +
2 26
3 export const Container = styled.div ` 27 export const Container = styled.div `
4 height : 100vh; 28 height : 100vh;
...@@ -17,6 +41,8 @@ export const ModalContainer = styled.div ` ...@@ -17,6 +41,8 @@ export const ModalContainer = styled.div `
17 display : flex; 41 display : flex;
18 flex-direction : column; 42 flex-direction : column;
19 43
44 + animation : ${ModalOn} .5s;
45 +
20 background-color : rgba(52, 52, 52, .7); 46 background-color : rgba(52, 52, 52, .7);
21 47
22 `; 48 `;
...@@ -55,8 +81,8 @@ export const ModalClsButton = styled.button ` ...@@ -55,8 +81,8 @@ export const ModalClsButton = styled.button `
55 `; 81 `;
56 82
57 export const ModalClsButtonImg = styled.img ` 83 export const ModalClsButtonImg = styled.img `
58 - height : 25px; 84 + height : 20px;
59 - width : 25px; 85 + width : 20px;
60 86
61 margin : 0 10px 0 0; 87 margin : 0 10px 0 0;
62 `; 88 `;
...@@ -159,6 +185,35 @@ export const NewPatientSearchResultWrapper = styled.div ` ...@@ -159,6 +185,35 @@ export const NewPatientSearchResultWrapper = styled.div `
159 185
160 font-size : 14px; 186 font-size : 14px;
161 color : #a0a0a0; 187 color : #a0a0a0;
188 + font-weight : 600;
189 +`;
190 +
191 +export const NewPatientSearchResult = styled.div `
192 + border : none;
193 +
194 + display : flex;
195 + flex-direction : row;
196 + justify-content : center;
197 + align-items : center;
198 +`;
199 +
200 +export const NewPatientSearchResultInfoWrapper = styled.div `
201 + display : flex;
202 +`;
203 +
204 +export const NewPatientSearchResultInfo = styled.div `
205 + font-size : 13px;
206 + font-weight : 600;
207 + color : #a0a0a0;
208 +
209 + margin : 0 5px 0 0;
210 +`;
211 +
212 +export const NewPatientSearchResultInfoText = styled.div `
213 + font-size : 14px;
214 + color : #343434;
215 + font-weight : 600;
216 + letter-spacing : 1px;
162 `; 217 `;
163 218
164 export const NewPatientRegisterButtonWrapper = styled.div ` 219 export const NewPatientRegisterButtonWrapper = styled.div `
...@@ -195,6 +250,130 @@ export const NewPatientRegisterButton = styled.button ` ...@@ -195,6 +250,130 @@ export const NewPatientRegisterButton = styled.button `
195 `; 250 `;
196 251
197 252
253 +export const PatientInfoViewContainer = styled.div `
254 + overflow : scroll;
255 + flex : 6;
256 +
257 + border : none;
258 + width : 100%;
259 +
260 + display : flex;
261 + flex-direction : column;
262 +
263 + &::-webkit-scrollbar {
264 + width : 3px;
265 + background-color : transparent;
266 + height : 1px;
267 + }
268 +
269 + &::-webkit-scrollbar-thumb {
270 + background-color : #337DFF;
271 + }
272 +`;
273 +
274 +export const PatientInfoPatientNmWrapper = styled.div `
275 + display : flex;
276 + flex-direction : row;
277 +
278 + justify-content : center;
279 + align-items : center;
280 +
281 + width : 100%;
282 +
283 + padding : 15px 0;
284 +
285 + border : none;
286 + border-bottom : 1px solid #ddd;
287 +
288 +`;
289 +
290 +export const PatientInfoPatientNmInfo = styled.div `
291 + font-size : 15px;
292 + font-weight : 500;
293 + margin : 0 5px;
294 +`;
295 +
296 +export const PatientInfoPatientNm = styled.div `
297 + font-size : 20px;
298 + font-weight : 700;
299 + margin : 0 5px;
300 + letter-spacing : 1px;
301 +
302 + color : #337DFF;
303 +`;
304 +
305 +export const PatientInfoView = styled.div `
306 + padding : 15px 15px;
307 +
308 + font-size : 15px;
309 + font-weight : 500;
310 + letter-spacing : 1px;
311 +
312 +`;
313 +
314 +export const PatientInfoEditWrapper = styled.div `
315 + flex : 3;
316 +
317 + border : none;
318 + width : 100%;
319 +
320 + display : flex;
321 + justify-content : center;
322 + align-items : center;
323 +
324 +`;
325 +
326 +export const PatientInfoEditInput = styled.textarea `
327 + width : 100%;
328 +
329 + resize : none;
330 +
331 + padding : 15px;
332 + margin : 15px;
333 + border : 1px solid #ddd;
334 +`;
335 +
336 +export const PatientInfoEditButtonWrapper = styled.div `
337 + flex : 2;
338 +
339 + border : none;
340 + width : 100%;
341 +
342 + display : flex;
343 + flex-direction : row;
344 +
345 + justify-content : center;
346 + align-items : center;
347 +
348 + gap : 8%;
349 +
350 +`;
351 +
352 +export const PatientInfoEditButton = styled.button `
353 + background-color : #fff;
354 + color : #337DFF;
355 + border : 1px solid #337DFF;
356 + border-radius : 3px;
357 +
358 + cursor : pointer;
359 +
360 + transition : .25s all;
361 +
362 + font-size : 16px;
363 + font-weight : 600;
364 +
365 + padding : 10px 30px;
366 + margin : 0 10px;
367 +
368 + &:hover {
369 + background-color : #337DFF;
370 + color : #fff;
371 + border : 1px solid transparent;
372 + }
373 +`;
374 +
375 +
376 +
198 export const InfoAndSearchWrapper = styled.div ` 377 export const InfoAndSearchWrapper = styled.div `
199 flex : 3; 378 flex : 3;
200 display : flex; 379 display : flex;
...@@ -369,8 +548,10 @@ export const SearchBar = styled.input ` ...@@ -369,8 +548,10 @@ export const SearchBar = styled.input `
369 548
370 export const SearchButton = styled.button ` 549 export const SearchButton = styled.button `
371 border : 1px solid #ddd; 550 border : 1px solid #ddd;
551 + border-radius : 3px;
372 552
373 background-color : transparent; 553 background-color : transparent;
554 + opacity : .7;
374 555
375 height : 50px; 556 height : 50px;
376 width : 50px; 557 width : 50px;
...@@ -379,12 +560,20 @@ export const SearchButton = styled.button ` ...@@ -379,12 +560,20 @@ export const SearchButton = styled.button `
379 560
380 cursor : pointer; 561 cursor : pointer;
381 562
563 + display : flex;
564 + justify-content : center;
565 + align-items : center;
566 +
382 &:hover { 567 &:hover {
383 - color : #fff; 568 + opacity : 1;
384 - background-color : #343434;
385 } 569 }
386 `; 570 `;
387 571
572 +export const SearchButtonImg = styled.img `
573 + height : 20px;
574 + width : 20px;
575 +`;
576 +
388 export const SearchResultWrapper = styled.div ` 577 export const SearchResultWrapper = styled.div `
389 flex : 5; 578 flex : 5;
390 border : 1px solid #ddd; 579 border : 1px solid #ddd;
......
1 import React, { useState, useEffect } from "react"; 1 import React, { useState, useEffect } from "react";
2 import { RouteComponentProps } from 'react-router-dom'; 2 import { RouteComponentProps } from 'react-router-dom';
3 3
4 -import { useRecoilValue } from "recoil"; 4 +import { useRecoilValue, useRecoilState } from "recoil";
5 import * as recoilUtil from '../../util/recoilUtil'; 5 import * as recoilUtil from '../../util/recoilUtil';
6 6
7 +import Header from '../../components/Header';
7 import RegisterPresenter from "./RegisterPresenter"; 8 import RegisterPresenter from "./RegisterPresenter";
8 9
9 -type RegisterProps = RouteComponentProps; 10 +import { authApi } from '../../api';
11 +
12 +
13 +// eslint-disable-next-line @typescript-eslint/no-empty-interface
14 +interface RegisterProps extends RouteComponentProps {}
10 15
11 const RegisterContainer = (props : RegisterProps) => { 16 const RegisterContainer = (props : RegisterProps) => {
17 +
18 + const [token, setToken] = useRecoilState(recoilUtil.token);
19 +
20 + const fetchData = async() => {
21 + if(token && token.length) {
22 + const result = await authApi.verifyToken(token);
23 + if (result.statusText === 'OK') {
24 + props.history.push('/');
25 + }
26 + }
27 + };
28 +
29 + useEffect(() => {
30 + fetchData();
31 + }, []);
32 +
33 +
12 return ( 34 return (
35 + <>
36 + <Header {...props}/>
13 <RegisterPresenter 37 <RegisterPresenter
14 38
15 /> 39 />
40 + </>
16 ) 41 )
17 }; 42 };
18 43
......
...@@ -9,7 +9,7 @@ const RegisterPresenter = () => { ...@@ -9,7 +9,7 @@ const RegisterPresenter = () => {
9 <styled.RegisterWrapper> 9 <styled.RegisterWrapper>
10 <styled.RegisterTitle>회원 가입</styled.RegisterTitle> 10 <styled.RegisterTitle>회원 가입</styled.RegisterTitle>
11 <styled.RegisterInfo>* 의사만 회원가입이 가능합니다.</styled.RegisterInfo> 11 <styled.RegisterInfo>* 의사만 회원가입이 가능합니다.</styled.RegisterInfo>
12 - <styled.RegisterInfo style = {{fontSize : 10,}}>의사 인증을 위한 정보가 요구됩니다.</styled.RegisterInfo> 12 + <styled.RegisterInfo style = {{fontSize : 10,}}>의사 인증을 위한 정보가 요구됩니다. 해당 정보는 인증을 위한 용도로만 사용됩니다.</styled.RegisterInfo>
13 <styled.RegisterInputWrapper> 13 <styled.RegisterInputWrapper>
14 <styled.RegisterInputText>이메일</styled.RegisterInputText> 14 <styled.RegisterInputText>이메일</styled.RegisterInputText>
15 <styled.RegisterInput 15 <styled.RegisterInput
......
...@@ -35,7 +35,7 @@ export const RegisterTitle = styled.div ` ...@@ -35,7 +35,7 @@ export const RegisterTitle = styled.div `
35 35
36 export const RegisterInfo = styled.div ` 36 export const RegisterInfo = styled.div `
37 width : 90%; 37 width : 90%;
38 - font-size : 12px; 38 + font-size : 13px;
39 color : #a0a0a0; 39 color : #a0a0a0;
40 `; 40 `;
41 41
......
This diff could not be displayed because it is too large.