feat. bottle view -> medicine Modal, User Feedback, User Register and Header button, etc...
Showing
31 changed files
with
1520 additions
and
93 deletions
| ... | @@ -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 | }, | ... | ... |
web/public/static/img/backButton.png
0 → 100644
11.6 KB
web/public/static/img/backButtonBlue.png
0 → 100644
5.63 KB
web/public/static/img/backButtonWhite.png
0 → 100644
2.66 KB
web/public/static/img/edit.png
0 → 100644
9.84 KB
web/public/static/img/logout.png
0 → 100644
6.03 KB
web/public/static/img/plus.png
0 → 100644
490 Bytes
web/public/static/img/refreshing.png
0 → 100644
7.05 KB
| ... | @@ -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 | ... | ... |
| ... | @@ -8,17 +8,3 @@ export const client : AxiosInstance = axios.create({ | ... | @@ -8,17 +8,3 @@ export const client : AxiosInstance = axios.create({ |
| 8 | 'Access-Control-Allow-Origin': '*', | 8 | 'Access-Control-Allow-Origin': '*', |
| 9 | }, | 9 | }, |
| 10 | }); | 10 | }); |
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 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 | -); | ... | ... |
| ... | @@ -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; |
| 38 | `; | 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; | ||
| 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 | - <> | ||
| 18 | - { | ||
| 19 | - error ? | ||
| 20 | <styled.Container> | 8 | <styled.Container> |
| 21 | - {error} | 9 | + </styled.Container> |
| 22 | - </styled.Container> : null | ||
| 23 | - } | ||
| 24 | - </> | ||
| 25 | ); | 10 | ); |
| 26 | }; | 11 | }; |
| 27 | 12 | ... | ... |
web/src/util/alertMessage.ts
0 → 100644
| 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 |
web/src/util/makeChart.ts
0 → 100644
| 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 |
| ... | @@ -14,9 +14,3 @@ export const userTypeCd = atom({ | ... | @@ -14,9 +14,3 @@ export const userTypeCd = atom({ |
| 14 | default : 'NORMAL', | 14 | default : 'NORMAL', |
| 15 | effects_UNSTABLE : [persistAtom], | 15 | effects_UNSTABLE : [persistAtom], |
| 16 | }); | 16 | }); |
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 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 | ... | ... |
| 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); |
| 20 | 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 | + | ||
| 65 | + | ||
| 66 | + const fetchData = async () => { | ||
| 67 | + setFeedback(''); | ||
| 68 | + setFdbType('RECOMMEND'); | ||
| 69 | + setMedicineInfoModal(false); | ||
| 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 | + | ||
| 21 | 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,9 +140,9 @@ export const LoginButton = styled.button<{isLoginButton : boolean}> ` | ... | @@ -97,9 +140,9 @@ 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 | ||
| 100 | - cursor : pointer; | 143 | + width : 80%; |
| 101 | 144 | ||
| 102 | - transition : .25s all; | 145 | + cursor : pointer; |
| 103 | 146 | ||
| 104 | color : #343434; | 147 | color : #343434; |
| 105 | font-weight : 600; | 148 | font-weight : 600; |
| ... | @@ -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 ( |
| 27 | + <> | ||
| 28 | + <Header {...props}/> | ||
| 29 | + { | ||
| 25 | userTypeCd === 'DOCTOR' ? | 30 | userTypeCd === 'DOCTOR' ? |
| 26 | <DoctorMenuContainer {...props}/> : | 31 | <DoctorMenuContainer {...props}/> : |
| 27 | - <ManagerMenuContainer {...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 | ||
| ... | @@ -40,9 +42,11 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -40,9 +42,11 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
| 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); |
| 121 | } | 167 | } |
| 122 | }; | 168 | }; |
| 123 | 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); | ||
| 191 | + } | ||
| 192 | + }; | ||
| 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.
-
Please register or login to post a comment