Showing
71 changed files
with
2545 additions
and
706 deletions
| ... | @@ -5,10 +5,20 @@ | ... | @@ -5,10 +5,20 @@ |
| 5 | "version": "0.2.0", | 5 | "version": "0.2.0", |
| 6 | "configurations": [ | 6 | "configurations": [ |
| 7 | { | 7 | { |
| 8 | - "name": "flutter_application_1", | 8 | + "name": "Attach", |
| 9 | - "cwd": "frontend\\flutter_application_1", | 9 | + "port": 9229, |
| 10 | - "request": "launch", | 10 | + "request": "attach", |
| 11 | - "type": "dart" | 11 | + "skipFiles": [ |
| 12 | - } | 12 | + "<node_internals>/**" |
| 13 | + ], | ||
| 14 | + "type": "pwa-node" | ||
| 15 | + }, | ||
| 16 | + { | ||
| 17 | + "name": "Attach to Chrome", | ||
| 18 | + "port": 9222, | ||
| 19 | + "request": "attach", | ||
| 20 | + "type": "pwa-chrome", | ||
| 21 | + "webRoot": "${workspaceFolder}" | ||
| 22 | + }, | ||
| 13 | ] | 23 | ] |
| 14 | } | 24 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | const Koa = require('koa'); | 1 | const Koa = require('koa'); |
| 2 | -const cors = require('@koa/cors'); | ||
| 3 | const Router = require('koa-router'); | 2 | const Router = require('koa-router'); |
| 3 | + | ||
| 4 | +const cors = require('@koa/cors'); | ||
| 4 | const bodyparser = require('koa-bodyparser'); | 5 | const bodyparser = require('koa-bodyparser'); |
| 5 | 6 | ||
| 6 | const Mongoose = require('mongoose'); | 7 | const Mongoose = require('mongoose'); |
| 7 | const api = require('./src/api'); | 8 | const api = require('./src/api'); |
| 8 | -const updateMedicineInfo = require('./src/lib/UpdatingMedicineInfo'); | ||
| 9 | const MqttServer = require('./src/util/MqttServer'); | 9 | const MqttServer = require('./src/util/MqttServer'); |
| 10 | +const BatchSystem = require('./src/util/Batch'); | ||
| 11 | +// const FCM = require('./src/util/FCM'); | ||
| 10 | 12 | ||
| 11 | require('dotenv').config(); | 13 | require('dotenv').config(); |
| 12 | // eslint-disable-next-line no-undef | 14 | // eslint-disable-next-line no-undef |
| ... | @@ -23,10 +25,10 @@ Mongoose.connect(MONGO_URL, { | ... | @@ -23,10 +25,10 @@ Mongoose.connect(MONGO_URL, { |
| 23 | useCreateIndex : true | 25 | useCreateIndex : true |
| 24 | }).then(() => { | 26 | }).then(() => { |
| 25 | console.log('\x1b[1;32mMongo DB is connected : ', MONGO_URL, '\x1b[0m'); | 27 | console.log('\x1b[1;32mMongo DB is connected : ', MONGO_URL, '\x1b[0m'); |
| 26 | - // updateMedicineInfo.updateMedicineInfo(); | 28 | + BatchSystem.updateMedicineData(); |
| 27 | }).catch(e => { | 29 | }).catch(e => { |
| 28 | console.log(e); | 30 | console.log(e); |
| 29 | -}) | 31 | +}); |
| 30 | 32 | ||
| 31 | app.use(bodyparser()); | 33 | app.use(bodyparser()); |
| 32 | router.use('/api', api.routes()); | 34 | router.use('/api', api.routes()); |
| ... | @@ -36,4 +38,7 @@ app.use(router.routes()).use(router.allowedMethods()); | ... | @@ -36,4 +38,7 @@ app.use(router.routes()).use(router.allowedMethods()); |
| 36 | app.listen(SERVER_PORT, () => { | 38 | app.listen(SERVER_PORT, () => { |
| 37 | console.log('\x1b[1;36mPORT : ', SERVER_PORT, 'is connected\x1b[0m'); | 39 | console.log('\x1b[1;36mPORT : ', SERVER_PORT, 'is connected\x1b[0m'); |
| 38 | MqttServer.on(); | 40 | MqttServer.on(); |
| 39 | -}) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 41 | + // FCM.initializeFCM(); | ||
| 42 | + BatchSystem.removeQrCode(); | ||
| 43 | + BatchSystem.pushNotifyByDosage(); | ||
| 44 | +}); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
This diff could not be displayed because it is too large.
| ... | @@ -17,11 +17,16 @@ | ... | @@ -17,11 +17,16 @@ |
| 17 | "author": "박권수", | 17 | "author": "박권수", |
| 18 | "license": "ISC", | 18 | "license": "ISC", |
| 19 | "dependencies": { | 19 | "dependencies": { |
| 20 | + "@google-cloud/storage": "^5.14.2", | ||
| 20 | "@koa/cors": "^3.1.0", | 21 | "@koa/cors": "^3.1.0", |
| 21 | "firebase-admin": "^9.11.1", | 22 | "firebase-admin": "^9.11.1", |
| 23 | + "google-auth-library": "^7.10.0", | ||
| 24 | + "koa-body": "^4.2.0", | ||
| 22 | "moment": "^2.29.1", | 25 | "moment": "^2.29.1", |
| 26 | + "moment-timezone": "^0.5.33", | ||
| 23 | "mqtt": "^4.2.6", | 27 | "mqtt": "^4.2.6", |
| 24 | - "node-cron": "^3.0.0" | 28 | + "node-cron": "^3.0.0", |
| 29 | + "qrcode": "^1.4.4" | ||
| 25 | }, | 30 | }, |
| 26 | "devDependencies": { | 31 | "devDependencies": { |
| 27 | "eslint": "^7.32.0" | 32 | "eslint": "^7.32.0" | ... | ... |
This diff is collapsed. Click to expand it.
| 1 | const Router = require('koa-router') | 1 | const Router = require('koa-router') |
| 2 | +const KoaBody = require('koa-body')({multipart : true}); | ||
| 2 | const authCtrl = require('./auth.ctrl') | 3 | const authCtrl = require('./auth.ctrl') |
| 3 | 4 | ||
| 4 | const auth = new Router() | 5 | const auth = new Router() |
| 5 | 6 | ||
| 6 | /** | 7 | /** |
| 7 | - * 회원가입 (email type) : 환자 회원가입 | 8 | + * 로컬 회원가입 (email type) : 환자 회원가입 |
| 8 | * url : http://localhost:4000/api/auth/register | 9 | * url : http://localhost:4000/api/auth/register |
| 9 | * request parameter : userId, password, passwordCheck | 10 | * request parameter : userId, password, passwordCheck |
| 10 | * return : null | 11 | * return : null |
| ... | @@ -12,28 +13,60 @@ const auth = new Router() | ... | @@ -12,28 +13,60 @@ const auth = new Router() |
| 12 | auth.post('/register', authCtrl.register) | 13 | auth.post('/register', authCtrl.register) |
| 13 | 14 | ||
| 14 | /** | 15 | /** |
| 15 | - * 회원가입 (email type) : 의사 회원가입 | 16 | + * 병원 검색 |
| 17 | + * url : http://localhost:4000/api/auth/hospital | ||
| 18 | + * request parameter : hospitalNm | ||
| 19 | + * return : xml type data | ||
| 20 | + */ | ||
| 21 | +auth.get('/hospital', authCtrl.searchHospital); | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 로컬 회원가입 (email type) : 의사 회원가입 | ||
| 16 | * url : http://localhost:4000/api/auth/register/doctor | 25 | * url : http://localhost:4000/api/auth/register/doctor |
| 17 | - * request parameter : userId, password, passwordCheck, doctorInfo | 26 | + * request parameter : userId, password, passwordCheck, doctorInfo(File) |
| 18 | * return : null | 27 | * return : null |
| 19 | */ | 28 | */ |
| 20 | - auth.post('/register/doctor', authCtrl.doctorRegister) | 29 | +auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister) |
| 21 | 30 | ||
| 22 | /** | 31 | /** |
| 23 | - * 로그인 (email type) | 32 | + * 로컬 로그인 (email type) |
| 24 | * url : http://localhost:4000/api/auth/login | 33 | * url : http://localhost:4000/api/auth/login |
| 25 | * request parameter : userId, password | 34 | * request parameter : userId, password |
| 26 | - * return : userId | 35 | + * return : token, userTypeCd |
| 27 | */ | 36 | */ |
| 28 | auth.post('/login', authCtrl.login) | 37 | auth.post('/login', authCtrl.login) |
| 29 | 38 | ||
| 30 | /** | 39 | /** |
| 40 | + * 소셜 회원가입(Google, Naver, Kakao) | ||
| 41 | + * url : http://localhost:4000/api/auth/register/${socialType} | ||
| 42 | + * request parameter : accessToken | ||
| 43 | + * return : status | ||
| 44 | + */ | ||
| 45 | +auth.post('/register/social/:socialType', authCtrl.socialRegister); | ||
| 46 | + | ||
| 47 | +/** | ||
| 48 | + * 소셜 로그인(Google, Naver, Kakao) | ||
| 49 | + * url : http://localhost:4000/api/auth/login/${socialType} | ||
| 50 | + * request parameter | ||
| 51 | + * return : token, userTypeCd | ||
| 52 | + */ | ||
| 53 | +auth.post('/login/social/:socialType', authCtrl.socialLogin); | ||
| 54 | + | ||
| 55 | +/** | ||
| 31 | * 로그아웃 | 56 | * 로그아웃 |
| 32 | * url : http://localhost:4000/api/auth/logout | 57 | * url : http://localhost:4000/api/auth/logout |
| 33 | * request parameter : null | 58 | * request parameter : null |
| 34 | * return : null | 59 | * return : null |
| 35 | */ | 60 | */ |
| 36 | -auth.post('/logout', authCtrl.logout) | 61 | +auth.post('/logout', authCtrl.logout); |
| 62 | + | ||
| 63 | +/** | ||
| 64 | + * 회원 탈퇴 | ||
| 65 | + * url : http://localhost:4000/api/auth | ||
| 66 | + * request parameter : password | ||
| 67 | + * return : null | ||
| 68 | + */ | ||
| 69 | +auth.delete('/', authCtrl.secession); | ||
| 37 | 70 | ||
| 38 | /** | 71 | /** |
| 39 | * 토큰이 유효한지 확인 | 72 | * 토큰이 유효한지 확인 | ... | ... |
| 1 | -/* eslint-disable no-undef */ | ||
| 2 | //어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다. | 1 | //어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다. |
| 3 | const Bottle = require('../../models/bottle'); | 2 | const Bottle = require('../../models/bottle'); |
| 4 | const Hub = require('../../models/hub'); | 3 | const Hub = require('../../models/hub'); |
| 5 | const Medicine = require('../../models/medicine'); | 4 | const Medicine = require('../../models/medicine'); |
| 6 | const User = require('../../models/user'); | 5 | const User = require('../../models/user'); |
| 6 | +const DoctorInfo = require('../../models/doctorInfo'); | ||
| 7 | const PatientInfo = require('../../models/patientInfo'); | 7 | const PatientInfo = require('../../models/patientInfo'); |
| 8 | const TakeMedicineHist = require('../../models/takeMedicineHistory'); | 8 | const TakeMedicineHist = require('../../models/takeMedicineHistory'); |
| 9 | const BottleMedicine = require('../../models/bottleMedicine'); | 9 | const BottleMedicine = require('../../models/bottleMedicine'); |
| 10 | const Feedback = require('../../models/feedback'); | 10 | const Feedback = require('../../models/feedback'); |
| 11 | -const Mqtt = require('../../lib/MqttModule'); | 11 | +const Mqtt = require('../../util/MqttModule'); |
| 12 | const jwt = require('jsonwebtoken'); | 12 | const jwt = require('jsonwebtoken'); |
| 13 | 13 | ||
| 14 | //약병 등록 | 14 | //약병 등록 |
| ... | @@ -19,6 +19,7 @@ exports.bottleConnect = async(ctx) => { | ... | @@ -19,6 +19,7 @@ exports.bottleConnect = async(ctx) => { |
| 19 | return; | 19 | return; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | + // eslint-disable-next-line no-undef | ||
| 22 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 23 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
| 23 | const user = await User.findByUserId(userId); | 24 | const user = await User.findByUserId(userId); |
| 24 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 25 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
| ... | @@ -26,7 +27,7 @@ exports.bottleConnect = async(ctx) => { | ... | @@ -26,7 +27,7 @@ exports.bottleConnect = async(ctx) => { |
| 26 | return; | 27 | return; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | - const { bottleId, hubId } = ctx.request.body; | 30 | + const { bottleId, hubId, bottleNm } = ctx.request.body; |
| 30 | 31 | ||
| 31 | const isExistBottle = await Bottle.findByBottleId(bottleId); | 32 | const isExistBottle = await Bottle.findByBottleId(bottleId); |
| 32 | if(isExistBottle) { | 33 | if(isExistBottle) { |
| ... | @@ -53,7 +54,8 @@ exports.bottleConnect = async(ctx) => { | ... | @@ -53,7 +54,8 @@ exports.bottleConnect = async(ctx) => { |
| 53 | 54 | ||
| 54 | const newBottle = new Bottle({ | 55 | const newBottle = new Bottle({ |
| 55 | bottleId, | 56 | bottleId, |
| 56 | - hubId | 57 | + hubId, |
| 58 | + bottleNm, | ||
| 57 | }); | 59 | }); |
| 58 | 60 | ||
| 59 | const client = await Mqtt.mqttOn(hosting); | 61 | const client = await Mqtt.mqttOn(hosting); |
| ... | @@ -73,6 +75,7 @@ exports.bottleDisconnect = async(ctx) => { | ... | @@ -73,6 +75,7 @@ exports.bottleDisconnect = async(ctx) => { |
| 73 | return; | 75 | return; |
| 74 | } | 76 | } |
| 75 | 77 | ||
| 78 | + // eslint-disable-next-line no-undef | ||
| 76 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 79 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
| 77 | const user = await User.findByUserId(userId); | 80 | const user = await User.findByUserId(userId); |
| 78 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 81 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
| ... | @@ -100,6 +103,7 @@ exports.bottleDisconnect = async(ctx) => { | ... | @@ -100,6 +103,7 @@ exports.bottleDisconnect = async(ctx) => { |
| 100 | const topic = 'bottle/' + bottleId + '/bts'; | 103 | const topic = 'bottle/' + bottleId + '/bts'; |
| 101 | Mqtt.mqttUnsubscribe(client, topic); | 104 | Mqtt.mqttUnsubscribe(client, topic); |
| 102 | 105 | ||
| 106 | + await BottleMedicine.updateMany({ bottleId }, { useYn : 'N' }); | ||
| 103 | await Bottle.deleteOne({ bottleId }); | 107 | await Bottle.deleteOne({ bottleId }); |
| 104 | 108 | ||
| 105 | ctx.status = 204; | 109 | ctx.status = 204; |
| ... | @@ -114,6 +118,7 @@ exports.getBottleInfo = async(ctx) => { | ... | @@ -114,6 +118,7 @@ exports.getBottleInfo = async(ctx) => { |
| 114 | return; | 118 | return; |
| 115 | } | 119 | } |
| 116 | 120 | ||
| 121 | + // eslint-disable-next-line no-undef | ||
| 117 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 122 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
| 118 | const user = await User.findByUserId(userId); | 123 | const user = await User.findByUserId(userId); |
| 119 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 124 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
| ... | @@ -146,21 +151,29 @@ exports.getBottleInfo = async(ctx) => { | ... | @@ -146,21 +151,29 @@ exports.getBottleInfo = async(ctx) => { |
| 146 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | 151 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); |
| 147 | 152 | ||
| 148 | if(bottleMedicine) { | 153 | if(bottleMedicine) { |
| 154 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
| 155 | + const doctorInfo = await DoctorInfo.findOne({ doctorId : bottleMedicine.doctorId }); | ||
| 156 | + | ||
| 149 | const takeMedicineHist = await TakeMedicineHist | 157 | const takeMedicineHist = await TakeMedicineHist |
| 150 | .find({ bmId : bottleMedicine._id }) | 158 | .find({ bmId : bottleMedicine._id }) |
| 151 | - .sort({ takeDate : 'desc' }) | 159 | + .sort({ takeDate : 'desc' }); |
| 152 | - .populate('bmId'); | ||
| 153 | 160 | ||
| 154 | ctx.status = 200; | 161 | ctx.status = 200; |
| 155 | ctx.body = { | 162 | ctx.body = { |
| 156 | - bottle, | 163 | + medicine, |
| 164 | + doctorInfo, | ||
| 165 | + dailyDosage : bottleMedicine.dailyDosage, | ||
| 166 | + totalDosage : bottleMedicine.totalDosage, | ||
| 157 | takeMedicineHist, | 167 | takeMedicineHist, |
| 158 | }; | 168 | }; |
| 159 | 169 | ||
| 160 | } else { | 170 | } else { |
| 161 | ctx.status = 200; | 171 | ctx.status = 200; |
| 162 | ctx.body = { | 172 | ctx.body = { |
| 163 | - bottle, | 173 | + medicine : null, |
| 174 | + doctorInfo : null, | ||
| 175 | + dailyDosage : null, | ||
| 176 | + totalDosage : null, | ||
| 164 | takeMedicineHist : [], | 177 | takeMedicineHist : [], |
| 165 | } | 178 | } |
| 166 | } | 179 | } |
| ... | @@ -175,6 +188,7 @@ exports.getBottleFeedback = async ctx => { | ... | @@ -175,6 +188,7 @@ exports.getBottleFeedback = async ctx => { |
| 175 | return; | 188 | return; |
| 176 | } | 189 | } |
| 177 | 190 | ||
| 191 | + // eslint-disable-next-line no-undef | ||
| 178 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 192 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
| 179 | const user = await User.findByUserId(userId); | 193 | const user = await User.findByUserId(userId); |
| 180 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 194 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
| ... | @@ -213,7 +227,9 @@ exports.getBottleFeedback = async ctx => { | ... | @@ -213,7 +227,9 @@ exports.getBottleFeedback = async ctx => { |
| 213 | .populate('bmId'); | 227 | .populate('bmId'); |
| 214 | 228 | ||
| 215 | ctx.status = 200; | 229 | ctx.status = 200; |
| 216 | - ctx.body = feedbackList; | 230 | + ctx.body = { |
| 231 | + feedbackList | ||
| 232 | + }; | ||
| 217 | } else { | 233 | } else { |
| 218 | ctx.status = 404; | 234 | ctx.status = 404; |
| 219 | ctx.body = { | 235 | ctx.body = { |
| ... | @@ -231,6 +247,7 @@ exports.setMedicine = async(ctx) => { | ... | @@ -231,6 +247,7 @@ exports.setMedicine = async(ctx) => { |
| 231 | return; | 247 | return; |
| 232 | } | 248 | } |
| 233 | 249 | ||
| 250 | + // eslint-disable-next-line no-undef | ||
| 234 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 251 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
| 235 | const user = await User.findByUserId(userId); | 252 | const user = await User.findByUserId(userId); |
| 236 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 253 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
| ... | @@ -239,7 +256,7 @@ exports.setMedicine = async(ctx) => { | ... | @@ -239,7 +256,7 @@ exports.setMedicine = async(ctx) => { |
| 239 | } | 256 | } |
| 240 | 257 | ||
| 241 | const { bottleId } = ctx.params; | 258 | const { bottleId } = ctx.params; |
| 242 | - const { medicineId, dosage, doctorId } = ctx.request.body; | 259 | + const { medicineId, doctorId, dailyDosage, totalDosage, } = ctx.request.body; |
| 243 | 260 | ||
| 244 | const bottle = await Bottle.findByBottleId(bottleId); | 261 | const bottle = await Bottle.findByBottleId(bottleId); |
| 245 | if(!bottle) { | 262 | if(!bottle) { |
| ... | @@ -273,7 +290,8 @@ exports.setMedicine = async(ctx) => { | ... | @@ -273,7 +290,8 @@ exports.setMedicine = async(ctx) => { |
| 273 | let bottleMedicine = new BottleMedicine({ | 290 | let bottleMedicine = new BottleMedicine({ |
| 274 | bottleId, | 291 | bottleId, |
| 275 | medicineId, | 292 | medicineId, |
| 276 | - dosage, | 293 | + dailyDosage, |
| 294 | + totalDosage, | ||
| 277 | }); | 295 | }); |
| 278 | 296 | ||
| 279 | if(doctorId !== undefined && doctorId !== null && doctorId !== '') { | 297 | if(doctorId !== undefined && doctorId !== null && doctorId !== '') { |
| ... | @@ -286,14 +304,109 @@ exports.setMedicine = async(ctx) => { | ... | @@ -286,14 +304,109 @@ exports.setMedicine = async(ctx) => { |
| 286 | return; | 304 | return; |
| 287 | } | 305 | } |
| 288 | 306 | ||
| 289 | - bottleMedicine.setDoctorId(doctorId); | 307 | + await bottleMedicine.setDoctorId(doctorId); |
| 290 | } | 308 | } |
| 291 | 309 | ||
| 292 | await BottleMedicine.updateMany({ bottleId }, { useYn : 'N '}); | 310 | await BottleMedicine.updateMany({ bottleId }, { useYn : 'N '}); |
| 293 | - | 311 | + await bottleMedicine.save(); |
| 294 | - bottleMedicine.save(); | 312 | + |
| 313 | + ctx.status = 200; | ||
| 314 | +}; | ||
| 315 | + | ||
| 316 | +//약 무게 세팅 | ||
| 317 | +exports.setMedicineWeight = async ctx => { | ||
| 318 | + const token = ctx.req.headers.authorization; | ||
| 319 | + if(!token || !token.length) { | ||
| 320 | + ctx.status = 401; | ||
| 321 | + return; | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + // eslint-disable-next-line no-undef | ||
| 325 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
| 326 | + const user = await User.findByUserId(userId); | ||
| 327 | + if(!user || !user.userTypeCd || user.useYn !== 'Y') { | ||
| 328 | + ctx.status = 403; | ||
| 329 | + return; | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + const { bottleId } = ctx.params; | ||
| 333 | + | ||
| 334 | + const bottle = await Bottle.findByBottleId(bottleId); | ||
| 335 | + if(!bottle) { | ||
| 336 | + ctx.status = 404; | ||
| 337 | + ctx.body = { | ||
| 338 | + error : '약병 찾을 수 없음.', | ||
| 339 | + } | ||
| 340 | + return; | ||
| 341 | + } | ||
| 342 | + | ||
| 343 | + const hub = await Hub.findByHubId(bottle.getHubId()); | ||
| 344 | + if(hub.getHub_UserId() !== userId) { | ||
| 345 | + ctx.status = 403; | ||
| 346 | + ctx.body = { | ||
| 347 | + error : '해당 허브 권한 없음', | ||
| 348 | + } | ||
| 349 | + return; | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + | ||
| 353 | + //toDo : 약병에서 가져온 무게 데이터를 이용하여, bottleMedicine값을 갱신. | ||
| 354 | + const client = await Mqtt.mqttOn(await hub.getHubHost()); | ||
| 355 | + const topic = 'bottle/' + bottleId + '/stb'; | ||
| 356 | + const message = 'weight'; | ||
| 357 | + await Mqtt.mqttPublishMessage(client, { topic, message }); | ||
| 358 | + | ||
| 359 | + | ||
| 360 | + // const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | ||
| 361 | + // const { totalWeight, totalDosage } = bottleMedicine; | ||
| 362 | + // if(totalDosage) bottleMedicine.setEachWeight(totalWeight / totalDosage); | ||
| 295 | 363 | ||
| 296 | ctx.status = 200; | 364 | ctx.status = 200; |
| 365 | + | ||
| 366 | +}; | ||
| 367 | + | ||
| 368 | +//약병 이름 변경 | ||
| 369 | +exports.setBottleName = async ctx => { | ||
| 370 | + const token = ctx.req.headers.authorization; | ||
| 371 | + if(!token || !token.length) { | ||
| 372 | + ctx.status = 401; | ||
| 373 | + return; | ||
| 374 | + } | ||
| 375 | + | ||
| 376 | + // eslint-disable-next-line no-undef | ||
| 377 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
| 378 | + const user = await User.findByUserId(userId); | ||
| 379 | + if(!user || !user.userTypeCd || user.useYn !== 'Y') { | ||
| 380 | + ctx.status = 403; | ||
| 381 | + return; | ||
| 382 | + } | ||
| 383 | + | ||
| 384 | + const { bottleId } = ctx.params; | ||
| 385 | + const { bottleNm } = ctx.request.body; | ||
| 386 | + | ||
| 387 | + const bottle = await Bottle.findByBottleId(bottleId); | ||
| 388 | + if(!bottle) { | ||
| 389 | + ctx.status = 404; | ||
| 390 | + ctx.body = { | ||
| 391 | + error : '약병 찾을 수 없음.', | ||
| 392 | + } | ||
| 393 | + return; | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + const hub = await Hub.findByHubId(bottle.getHubId()); | ||
| 397 | + if(hub.getHub_UserId() !== userId) { | ||
| 398 | + ctx.status = 403; | ||
| 399 | + ctx.body = { | ||
| 400 | + error : '해당 허브 권한 없음', | ||
| 401 | + } | ||
| 402 | + return; | ||
| 403 | + } | ||
| 404 | + | ||
| 405 | + await bottle.setBottleNm(bottleNm); | ||
| 406 | + await bottle.save(); | ||
| 407 | + | ||
| 408 | + ctx.status = 200; | ||
| 409 | + | ||
| 297 | }; | 410 | }; |
| 298 | 411 | ||
| 299 | // //비어있는 약병에 의사를 등록한다. | 412 | // //비어있는 약병에 의사를 등록한다. |
| ... | @@ -344,6 +457,7 @@ exports.getHubsBottleList = async(ctx) => { | ... | @@ -344,6 +457,7 @@ exports.getHubsBottleList = async(ctx) => { |
| 344 | return; | 457 | return; |
| 345 | } | 458 | } |
| 346 | 459 | ||
| 460 | + // eslint-disable-next-line no-undef | ||
| 347 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 461 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
| 348 | const user = await User.findByUserId(userId); | 462 | const user = await User.findByUserId(userId); |
| 349 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 463 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
| ... | @@ -385,6 +499,7 @@ exports.getAllBottleList = async ctx => { | ... | @@ -385,6 +499,7 @@ exports.getAllBottleList = async ctx => { |
| 385 | return; | 499 | return; |
| 386 | } | 500 | } |
| 387 | 501 | ||
| 502 | + // eslint-disable-next-line no-undef | ||
| 388 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 503 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
| 389 | const user = await User.findByUserId(userId); | 504 | const user = await User.findByUserId(userId); |
| 390 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 505 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
| ... | @@ -402,7 +517,7 @@ exports.getAllBottleList = async ctx => { | ... | @@ -402,7 +517,7 @@ exports.getAllBottleList = async ctx => { |
| 402 | 517 | ||
| 403 | ctx.status = 200; | 518 | ctx.status = 200; |
| 404 | ctx.body = { | 519 | ctx.body = { |
| 405 | - bottleList | 520 | + bottleList, |
| 406 | }; | 521 | }; |
| 407 | 522 | ||
| 408 | }; | 523 | }; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -44,6 +44,22 @@ bottle.get('/feedback/:bottleId', bottleCtrl.getBottleFeedback); | ... | @@ -44,6 +44,22 @@ bottle.get('/feedback/:bottleId', bottleCtrl.getBottleFeedback); |
| 44 | bottle.patch('/:bottleId', bottleCtrl.setMedicine); | 44 | bottle.patch('/:bottleId', bottleCtrl.setMedicine); |
| 45 | 45 | ||
| 46 | /** | 46 | /** |
| 47 | + * 약병에 등록된 약의 무게 갱신 | ||
| 48 | + * request parameter : bottleid | ||
| 49 | + * url : http://localhost:4000/api/bottle/weight/:bottleId | ||
| 50 | + * return : null | ||
| 51 | + */ | ||
| 52 | +bottle.patch('/weight/:bottleId', bottleCtrl.setMedicineWeight); | ||
| 53 | + | ||
| 54 | +/** | ||
| 55 | + * 약병 이름 변경 | ||
| 56 | + * request parameter : bottleid, bottleNm | ||
| 57 | + * url : http://localhost:4000/api/bottle/name/:bottleId | ||
| 58 | + * return : null | ||
| 59 | + */ | ||
| 60 | + bottle.patch('/name/:bottleId', bottleCtrl.setBottleName); | ||
| 61 | + | ||
| 62 | +/** | ||
| 47 | * 비어있는 약병에 전담의 등록 | 63 | * 비어있는 약병에 전담의 등록 |
| 48 | * request parameter : bottleId, doctorId | 64 | * request parameter : bottleId, doctorId |
| 49 | * url : http://localhost:4000/api/bottle/doctor/:bottleId | 65 | * url : http://localhost:4000/api/bottle/doctor/:bottleId | ... | ... |
| ... | @@ -9,8 +9,14 @@ const Feedback = require('../../models/feedback'); | ... | @@ -9,8 +9,14 @@ const Feedback = require('../../models/feedback'); |
| 9 | const Hub = require('../../models/hub'); | 9 | const Hub = require('../../models/hub'); |
| 10 | const PatientInfo = require('../../models/patientInfo'); | 10 | const PatientInfo = require('../../models/patientInfo'); |
| 11 | const DoctorInfo = require('../../models/doctorInfo'); | 11 | const DoctorInfo = require('../../models/doctorInfo'); |
| 12 | +const PrescribeInfo = require('../../models/prescribeInfo'); | ||
| 13 | + | ||
| 12 | const jwt = require('jsonwebtoken'); | 14 | const jwt = require('jsonwebtoken'); |
| 13 | 15 | ||
| 16 | +const { uploadQrCode, getQrCodeUrl } = require('../../util/GoogleCloudStorage'); | ||
| 17 | +const QrCodeUtil = require('../../util/QrCodeUtil'); | ||
| 18 | +const { sendPushMessage } = require('../../util/FCM'); | ||
| 19 | + | ||
| 14 | 20 | ||
| 15 | /** | 21 | /** |
| 16 | * 현재 로그인한 유저의 의사 정보를 가져온다 | 22 | * 현재 로그인한 유저의 의사 정보를 가져온다 |
| ... | @@ -150,7 +156,8 @@ exports.getPatientDetail = async ctx => { | ... | @@ -150,7 +156,8 @@ exports.getPatientDetail = async ctx => { |
| 150 | bottleId : bottle.bottleId, | 156 | bottleId : bottle.bottleId, |
| 151 | useYn : 'Y', | 157 | useYn : 'Y', |
| 152 | }); | 158 | }); |
| 153 | - reqUserBmList.push(bm); | 159 | + |
| 160 | + if(bm) reqUserBmList.push(bm); | ||
| 154 | })); | 161 | })); |
| 155 | 162 | ||
| 156 | const bottleList = await Promise.all(reqUserBmList.map(async bottleMedicine => { | 163 | const bottleList = await Promise.all(reqUserBmList.map(async bottleMedicine => { |
| ... | @@ -280,7 +287,7 @@ exports.writeReqPatientReport = async ctx => { | ... | @@ -280,7 +287,7 @@ exports.writeReqPatientReport = async ctx => { |
| 280 | } | 287 | } |
| 281 | 288 | ||
| 282 | await patientInfo.updateInfo(info); | 289 | await patientInfo.updateInfo(info); |
| 283 | - patientInfo.save(); | 290 | + await patientInfo.save(); |
| 284 | 291 | ||
| 285 | ctx.status = 200; | 292 | ctx.status = 200; |
| 286 | 293 | ||
| ... | @@ -335,13 +342,31 @@ exports.writeReqBottleFeedback = async ctx => { | ... | @@ -335,13 +342,31 @@ exports.writeReqBottleFeedback = async ctx => { |
| 335 | doctorId : userId, | 342 | doctorId : userId, |
| 336 | feedback, | 343 | feedback, |
| 337 | }); | 344 | }); |
| 338 | - newFeedback.save(); | 345 | + await newFeedback.save(); |
| 346 | + | ||
| 347 | + | ||
| 348 | + //feedback 알람 보내기 | ||
| 349 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
| 350 | + const patientProfile = await Profile.findOne({ userId : hub.userId }); | ||
| 351 | + if(patientProfile) { | ||
| 352 | + sendPushMessage({ | ||
| 353 | + deviceToken : patientProfile.deviceToken, | ||
| 354 | + title : '의사에게 새로운 알람이 도착했습니다.', | ||
| 355 | + body : feedback, | ||
| 356 | + }); | ||
| 357 | + } | ||
| 339 | 358 | ||
| 340 | ctx.status = 200; | 359 | ctx.status = 200; |
| 341 | 360 | ||
| 361 | + | ||
| 342 | }; | 362 | }; |
| 343 | 363 | ||
| 344 | -exports.searchPatientById = async ctx => { | 364 | +/** |
| 365 | + * 이메일로 환자를 검색한다. | ||
| 366 | + * @param {*} ctx | ||
| 367 | + * @returns | ||
| 368 | + */ | ||
| 369 | +exports.searchPatientByContact = async ctx => { | ||
| 345 | const token = ctx.req.headers.authorization; | 370 | const token = ctx.req.headers.authorization; |
| 346 | if (!token || !token.length) { | 371 | if (!token || !token.length) { |
| 347 | ctx.status = 401; | 372 | ctx.status = 401; |
| ... | @@ -359,22 +384,19 @@ exports.searchPatientById = async ctx => { | ... | @@ -359,22 +384,19 @@ exports.searchPatientById = async ctx => { |
| 359 | return; | 384 | return; |
| 360 | } | 385 | } |
| 361 | 386 | ||
| 362 | - const { patientId } = ctx.params; | 387 | + const { contact } = ctx.params; |
| 363 | - const patient = await User.findByUserId(patientId); | 388 | + const patientProfile = await Profile.findOne({ contact, useYn : 'Y' }); |
| 364 | - if(!patient || patient.useYn !== 'Y') { | ||
| 365 | - ctx.status = 404; | ||
| 366 | - ctx.body = { | ||
| 367 | - error : '존재하지 않는 회원', | ||
| 368 | - }; | ||
| 369 | - return; | ||
| 370 | - } | ||
| 371 | 389 | ||
| 372 | - const patientProfile = await Profile.findOne({ userId : patientId }); | 390 | + const patientInfo = { |
| 391 | + userId : patientProfile.userId, | ||
| 392 | + userNm : patientProfile.userNm, | ||
| 393 | + birth : patientProfile.birth, | ||
| 394 | + contact: patientProfile.contact, | ||
| 395 | + }; | ||
| 373 | 396 | ||
| 374 | ctx.status = 200; | 397 | ctx.status = 200; |
| 375 | ctx.body = { | 398 | ctx.body = { |
| 376 | - patientNm : patientProfile.userNm, | 399 | + patientInfo, |
| 377 | - patientId, | ||
| 378 | }; | 400 | }; |
| 379 | }; | 401 | }; |
| 380 | 402 | ||
| ... | @@ -427,8 +449,17 @@ exports.registerNewPatient = async ctx => { | ... | @@ -427,8 +449,17 @@ exports.registerNewPatient = async ctx => { |
| 427 | useYn : 'W', | 449 | useYn : 'W', |
| 428 | }); | 450 | }); |
| 429 | 451 | ||
| 430 | - patientInfo.updateInfo('환자 등록 요청'); | 452 | + await patientInfo.updateInfo('환자 등록 요청'); |
| 431 | - patientInfo.save(); | 453 | + await patientInfo.save(); |
| 454 | + | ||
| 455 | + const profile = await Profile.findByUserId(patientId); | ||
| 456 | + const { deviceToken } = profile; | ||
| 457 | + | ||
| 458 | + sendPushMessage({ | ||
| 459 | + deviceToken, | ||
| 460 | + title : '새로운 의사 등록 요청이 왔습니다.', | ||
| 461 | + body : '어플리케이션을 실행해 확인하세요.', | ||
| 462 | + }); | ||
| 432 | 463 | ||
| 433 | ctx.status = 200; | 464 | ctx.status = 200; |
| 434 | 465 | ||
| ... | @@ -477,8 +508,113 @@ exports.removeReqPatient = async ctx => { | ... | @@ -477,8 +508,113 @@ exports.removeReqPatient = async ctx => { |
| 477 | } | 508 | } |
| 478 | 509 | ||
| 479 | await patientInfo.setUseYn('N') | 510 | await patientInfo.setUseYn('N') |
| 480 | - patientInfo.save(); | 511 | + await patientInfo.save(); |
| 481 | 512 | ||
| 482 | ctx.status = 200; | 513 | ctx.status = 200; |
| 483 | 514 | ||
| 515 | +}; | ||
| 516 | + | ||
| 517 | +/** | ||
| 518 | + * 특정 환자에게 특정 약을 처방한다 | ||
| 519 | + * @param {*} ctx | ||
| 520 | + * http methods : post | ||
| 521 | + */ | ||
| 522 | +exports.prescribeMedicine = async ctx => { | ||
| 523 | + const token = ctx.req.headers.authorization; | ||
| 524 | + if(!token || !token.length) { | ||
| 525 | + ctx.status = 401; | ||
| 526 | + return; | ||
| 527 | + } | ||
| 528 | + | ||
| 529 | + // eslint-disable-next-line no-undef | ||
| 530 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
| 531 | + const user = await User.findByUserId(userId); | ||
| 532 | + if(!user || user.userTypeCd !== 'DOCTOR') { | ||
| 533 | + ctx.status = 403; | ||
| 534 | + ctx.body = { | ||
| 535 | + error : '권한 없는 유저', | ||
| 536 | + }; | ||
| 537 | + return; | ||
| 538 | + } | ||
| 539 | + | ||
| 540 | + | ||
| 541 | + const { | ||
| 542 | + patientId, | ||
| 543 | + medicineId, | ||
| 544 | + dailyDosage, | ||
| 545 | + totalDosage, | ||
| 546 | + } = ctx.request.body; | ||
| 547 | + | ||
| 548 | + | ||
| 549 | + const patientInfo = await PatientInfo.findOne({ | ||
| 550 | + patientId, | ||
| 551 | + doctorId : userId, | ||
| 552 | + useYn : 'Y', | ||
| 553 | + }) | ||
| 554 | + if(!patientInfo) { | ||
| 555 | + ctx.status = 403; | ||
| 556 | + ctx.body = { | ||
| 557 | + error : '권한 없는 환자', | ||
| 558 | + }; | ||
| 559 | + return; | ||
| 560 | + } | ||
| 561 | + | ||
| 562 | + const medicine = await Medicine.findOne({ medicineId }); | ||
| 563 | + if(!medicine) { | ||
| 564 | + ctx.status = 404; | ||
| 565 | + ctx.body = { | ||
| 566 | + error : '존재하지 않는 약', | ||
| 567 | + }; | ||
| 568 | + return; | ||
| 569 | + } | ||
| 570 | + | ||
| 571 | + | ||
| 572 | + const qrCodeResult = await QrCodeUtil.generateQrCode_prescribe({ | ||
| 573 | + medicine, | ||
| 574 | + dailyDosage, | ||
| 575 | + totalDosage, | ||
| 576 | + patientId, | ||
| 577 | + doctorId : userId, | ||
| 578 | + }); | ||
| 579 | + if(!qrCodeResult) { | ||
| 580 | + ctx.status = 400; | ||
| 581 | + ctx.body = { | ||
| 582 | + error : 'QR 코드 생성 에러', | ||
| 583 | + }; | ||
| 584 | + return; | ||
| 585 | + } | ||
| 586 | + | ||
| 587 | + const qrCodeUrl = await uploadQrCode(qrCodeResult); | ||
| 588 | + if(!qrCodeUrl) { | ||
| 589 | + ctx.status = 400; | ||
| 590 | + ctx.body = { | ||
| 591 | + error : 'QR 코드 생성 후 서버 업로드 에러', | ||
| 592 | + }; | ||
| 593 | + return; | ||
| 594 | + } | ||
| 595 | + | ||
| 596 | + const prescribeInfo = new PrescribeInfo({ | ||
| 597 | + doctorId : userId, | ||
| 598 | + patientId, | ||
| 599 | + dailyDosage, | ||
| 600 | + totalDosage, | ||
| 601 | + medicineId, | ||
| 602 | + qrCodeUrl, | ||
| 603 | + }); | ||
| 604 | + await prescribeInfo.save(); | ||
| 605 | + | ||
| 606 | + //특이사항에 처방기록 저장 | ||
| 607 | + await patientInfo.updateInfo(`${medicine.name}, 하루 ${dailyDosage}회분 처방, 총 ${totalDosage}회분 처방`); | ||
| 608 | + await patientInfo.save(); | ||
| 609 | + | ||
| 610 | + | ||
| 611 | + const { qrCodeFileName } = qrCodeResult; | ||
| 612 | + const qrCode = await getQrCodeUrl({ qrCodeFileName }); | ||
| 613 | + | ||
| 614 | + ctx.status = 200; | ||
| 615 | + ctx.body = { | ||
| 616 | + qrCode, | ||
| 617 | + }; | ||
| 618 | + | ||
| 619 | + | ||
| 484 | }; | 620 | }; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -7,7 +7,7 @@ const doctor = new Router(); | ... | @@ -7,7 +7,7 @@ const doctor = new Router(); |
| 7 | /** | 7 | /** |
| 8 | * 현재 로그인한 유저(의사)의 정보를 가져옴. | 8 | * 현재 로그인한 유저(의사)의 정보를 가져옴. |
| 9 | * request parameter : token | 9 | * request parameter : token |
| 10 | - * url : http://localhost:4000/doctor/ | 10 | + * url : http://localhost:4000/api/doctor/ |
| 11 | * return : doctor's Info | 11 | * return : doctor's Info |
| 12 | */ | 12 | */ |
| 13 | doctor.get('/', doctorCtrl.getDoctorsInfo); | 13 | doctor.get('/', doctorCtrl.getDoctorsInfo); |
| ... | @@ -15,7 +15,7 @@ doctor.get('/', doctorCtrl.getDoctorsInfo); | ... | @@ -15,7 +15,7 @@ doctor.get('/', doctorCtrl.getDoctorsInfo); |
| 15 | /** | 15 | /** |
| 16 | * 현재 로그인한 유저(의사)의 관리 환자 목록을 가져옴 | 16 | * 현재 로그인한 유저(의사)의 관리 환자 목록을 가져옴 |
| 17 | * request parameter | 17 | * request parameter |
| 18 | - * url : http://localhost:4000/doctor/patient | 18 | + * url : http://localhost:4000/api/doctor/patient |
| 19 | * return : patient List | 19 | * return : patient List |
| 20 | */ | 20 | */ |
| 21 | doctor.get('/patient', doctorCtrl.getPatientList); | 21 | doctor.get('/patient', doctorCtrl.getPatientList); |
| ... | @@ -23,7 +23,7 @@ doctor.get('/patient', doctorCtrl.getPatientList); | ... | @@ -23,7 +23,7 @@ doctor.get('/patient', doctorCtrl.getPatientList); |
| 23 | /** | 23 | /** |
| 24 | * 현재 로그인한 유저(의사)의 관리 환자 상세 정보를 가져옴 | 24 | * 현재 로그인한 유저(의사)의 관리 환자 상세 정보를 가져옴 |
| 25 | * request parameter : patient Id | 25 | * request parameter : patient Id |
| 26 | - * url : http://localhost:4000/doctor/patient/:patientId | 26 | + * url : http://localhost:4000/api/doctor/patient/:patientId |
| 27 | * return : patient Detail | 27 | * return : patient Detail |
| 28 | */ | 28 | */ |
| 29 | doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); | 29 | doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); |
| ... | @@ -31,7 +31,7 @@ doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); | ... | @@ -31,7 +31,7 @@ doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); |
| 31 | /** | 31 | /** |
| 32 | * 현재 로그인한 유저(의사)의 관리 약병 상세 정보를 가져옴 | 32 | * 현재 로그인한 유저(의사)의 관리 약병 상세 정보를 가져옴 |
| 33 | * request parameter : bottle Id | 33 | * request parameter : bottle Id |
| 34 | - * url : http://localhost:4000/doctor/bottle/:bottleId | 34 | + * url : http://localhost:4000/api/doctor/bottle/:bottleId |
| 35 | * return : bottle Detail | 35 | * return : bottle Detail |
| 36 | */ | 36 | */ |
| 37 | doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); | 37 | doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); |
| ... | @@ -39,7 +39,7 @@ doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); | ... | @@ -39,7 +39,7 @@ doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); |
| 39 | /** | 39 | /** |
| 40 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 특이사항을 기록함 | 40 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 특이사항을 기록함 |
| 41 | * request parameter : reqUserId, info | 41 | * request parameter : reqUserId, info |
| 42 | - * url : http://localhost:4000/doctor/patient | 42 | + * url : http://localhost:4000/api/doctor/patient |
| 43 | * return : null | 43 | * return : null |
| 44 | */ | 44 | */ |
| 45 | doctor.patch('/patient', doctorCtrl.writeReqPatientReport); | 45 | doctor.patch('/patient', doctorCtrl.writeReqPatientReport); |
| ... | @@ -47,7 +47,7 @@ doctor.patch('/patient', doctorCtrl.writeReqPatientReport); | ... | @@ -47,7 +47,7 @@ doctor.patch('/patient', doctorCtrl.writeReqPatientReport); |
| 47 | /** | 47 | /** |
| 48 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 약병의 피드백을 등록함. | 48 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 약병의 피드백을 등록함. |
| 49 | * request parameter : bottleId, fdbType, feedback | 49 | * request parameter : bottleId, fdbType, feedback |
| 50 | - * url : http://localhost:4000/doctor/bottle | 50 | + * url : http://localhost:4000/api/doctor/bottle |
| 51 | * return : null | 51 | * return : null |
| 52 | */ | 52 | */ |
| 53 | doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); | 53 | doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); |
| ... | @@ -55,15 +55,15 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); | ... | @@ -55,15 +55,15 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); |
| 55 | /** | 55 | /** |
| 56 | * 현재 로그인한 유저(의사)가 이메일로 유저를 검색함 | 56 | * 현재 로그인한 유저(의사)가 이메일로 유저를 검색함 |
| 57 | * request parameter : patientId | 57 | * request parameter : patientId |
| 58 | - * url : http://localhost:4000/api/doctor/patient/search/:patientId | 58 | + * url : http://localhost:4000/api/api/doctor/patient/search/:patientId |
| 59 | * return : patient Info(simple) | 59 | * return : patient Info(simple) |
| 60 | */ | 60 | */ |
| 61 | -doctor.get('/patient/search/:patientId', doctorCtrl.searchPatientById); | 61 | +doctor.get('/patient/search/:contact', doctorCtrl.searchPatientByContact); |
| 62 | 62 | ||
| 63 | /** | 63 | /** |
| 64 | * 현재 로그인한 유저(의사)의 관리 환자를 등록함. | 64 | * 현재 로그인한 유저(의사)의 관리 환자를 등록함. |
| 65 | * request parameter : patientId | 65 | * request parameter : patientId |
| 66 | - * url : http://localhost:4000/doctor/patient | 66 | + * url : http://localhost:4000/api/doctor/patient |
| 67 | * return : null | 67 | * return : null |
| 68 | */ | 68 | */ |
| 69 | doctor.post('/patient', doctorCtrl.registerNewPatient); | 69 | doctor.post('/patient', doctorCtrl.registerNewPatient); |
| ... | @@ -71,11 +71,18 @@ doctor.post('/patient', doctorCtrl.registerNewPatient); | ... | @@ -71,11 +71,18 @@ doctor.post('/patient', doctorCtrl.registerNewPatient); |
| 71 | /** | 71 | /** |
| 72 | * 현재 로그인한 유저(의사)의 특정 관리 환자를 삭제함. | 72 | * 현재 로그인한 유저(의사)의 특정 관리 환자를 삭제함. |
| 73 | * request parameter : patientId | 73 | * request parameter : patientId |
| 74 | - * url : http://localhost:4000/doctor/patient/:patientId | 74 | + * url : http://localhost:4000/api/doctor/patient/:patientId |
| 75 | * return : null | 75 | * return : null |
| 76 | */ | 76 | */ |
| 77 | doctor.delete('/patient/:patientId', doctorCtrl.removeReqPatient); | 77 | doctor.delete('/patient/:patientId', doctorCtrl.removeReqPatient); |
| 78 | 78 | ||
| 79 | +/** | ||
| 80 | + * 의사가 관리하는 환자에 대해 특정 약을 처방함 | ||
| 81 | + * request paramter : patientId, medicineId, dosage | ||
| 82 | + * url : http://localhost:4000/api/doctor/prescribe | ||
| 83 | + * return : null | ||
| 84 | + */ | ||
| 85 | +doctor.post('/prescribe', doctorCtrl.prescribeMedicine); | ||
| 86 | + | ||
| 79 | 87 | ||
| 80 | -module.exports = doctor; | ||
| 81 | - | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 88 | +module.exports = doctor; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | //허브(Mqtt Broker)등록 및 삭제 | 1 | //허브(Mqtt Broker)등록 및 삭제 |
| 2 | const Hub = require('../../models/hub'); | 2 | const Hub = require('../../models/hub'); |
| 3 | +const Bottle = require('../../models/bottle'); | ||
| 3 | const User = require('../../models/user'); | 4 | const User = require('../../models/user'); |
| 4 | -const Mqtt = require('../../lib/MqttModule'); | 5 | +const Mqtt = require('../../util/MqttModule'); |
| 5 | -const DataProcess = require('../../lib/DataProcess'); | 6 | +const DataProcess = require('../../util/DataProcess'); |
| 6 | const jwt = require('jsonwebtoken'); | 7 | const jwt = require('jsonwebtoken'); |
| 8 | +const BottleMedicine = require('../../models/bottleMedicine'); | ||
| 7 | 9 | ||
| 10 | +//허브 연결 | ||
| 8 | exports.hubConnect = async (ctx) => { | 11 | exports.hubConnect = async (ctx) => { |
| 9 | const token = ctx.req.headers.authorization; | 12 | const token = ctx.req.headers.authorization; |
| 10 | if(!token || !token.length) { | 13 | if(!token || !token.length) { |
| ... | @@ -20,7 +23,7 @@ exports.hubConnect = async (ctx) => { | ... | @@ -20,7 +23,7 @@ exports.hubConnect = async (ctx) => { |
| 20 | return; | 23 | return; |
| 21 | } | 24 | } |
| 22 | 25 | ||
| 23 | - const { hubId, host, port } = ctx.request.body; | 26 | + const { hubId, host, hubNm, } = ctx.request.body; |
| 24 | 27 | ||
| 25 | const isExistHub = await Hub.findByHubId(hubId); | 28 | const isExistHub = await Hub.findByHubId(hubId); |
| 26 | if(isExistHub) { | 29 | if(isExistHub) { |
| ... | @@ -30,7 +33,7 @@ exports.hubConnect = async (ctx) => { | ... | @@ -30,7 +33,7 @@ exports.hubConnect = async (ctx) => { |
| 30 | 33 | ||
| 31 | const hosting = { | 34 | const hosting = { |
| 32 | host, | 35 | host, |
| 33 | - port | 36 | + port : "1883", |
| 34 | }; | 37 | }; |
| 35 | 38 | ||
| 36 | Mqtt.mqttOn(hosting, DataProcess.dataPublish); | 39 | Mqtt.mqttOn(hosting, DataProcess.dataPublish); |
| ... | @@ -38,7 +41,8 @@ exports.hubConnect = async (ctx) => { | ... | @@ -38,7 +41,8 @@ exports.hubConnect = async (ctx) => { |
| 38 | const hub = new Hub({ | 41 | const hub = new Hub({ |
| 39 | hubId, | 42 | hubId, |
| 40 | hosting, | 43 | hosting, |
| 41 | - userId | 44 | + userId, |
| 45 | + hubNm, | ||
| 42 | }); | 46 | }); |
| 43 | 47 | ||
| 44 | await hub.save(); | 48 | await hub.save(); |
| ... | @@ -47,6 +51,49 @@ exports.hubConnect = async (ctx) => { | ... | @@ -47,6 +51,49 @@ exports.hubConnect = async (ctx) => { |
| 47 | 51 | ||
| 48 | }; | 52 | }; |
| 49 | 53 | ||
| 54 | +//허브 연결 해제 | ||
| 55 | +exports.hubDisconnect = async(ctx) => { | ||
| 56 | + const token = ctx.req.headers.authorization; | ||
| 57 | + if(!token || !token.length) { | ||
| 58 | + ctx.status = 401; | ||
| 59 | + return; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // eslint-disable-next-line no-undef | ||
| 63 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
| 64 | + const user = await User.findByUserId(userId); | ||
| 65 | + if(!user || !user.userTypeCd || user.useYn !== 'Y') { | ||
| 66 | + ctx.status = 403; | ||
| 67 | + return; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + const { hubId } = ctx.params; | ||
| 71 | + | ||
| 72 | + const hub = await Hub.findByHubId(hubId); | ||
| 73 | + if(!hub) { | ||
| 74 | + ctx.status = 404; | ||
| 75 | + return; | ||
| 76 | + } | ||
| 77 | + if(hub.getHub_UserId() !== userId) { | ||
| 78 | + ctx.status = 403; | ||
| 79 | + return; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + const hosting = await hub.getHubHost(); | ||
| 83 | + Mqtt.mqttOff(hosting); | ||
| 84 | + | ||
| 85 | + const bottleList = await Bottle.find({ hubId }); | ||
| 86 | + await Promise.all(bottleList.map(async bottle => { | ||
| 87 | + await BottleMedicine.updateMany({ bottleId : bottle.bottleId }, { useYn : 'N' }); | ||
| 88 | + })); | ||
| 89 | + | ||
| 90 | + await Bottle.deleteMany({ hubId }); | ||
| 91 | + await Hub.deleteOne({ hubId }); | ||
| 92 | + | ||
| 93 | + ctx.status = 204; | ||
| 94 | +}; | ||
| 95 | + | ||
| 96 | +//허브 정보 조회 | ||
| 50 | exports.getHubList = async(ctx) => { | 97 | exports.getHubList = async(ctx) => { |
| 51 | const token = ctx.req.headers.authorization; | 98 | const token = ctx.req.headers.authorization; |
| 52 | if(!token || !token.length) { | 99 | if(!token || !token.length) { |
| ... | @@ -74,7 +121,8 @@ exports.getHubList = async(ctx) => { | ... | @@ -74,7 +121,8 @@ exports.getHubList = async(ctx) => { |
| 74 | }; | 121 | }; |
| 75 | }; | 122 | }; |
| 76 | 123 | ||
| 77 | -exports.hubDisconnect = async(ctx) => { | 124 | +//허브 이름 변경 |
| 125 | +exports.setHubName = async ctx => { | ||
| 78 | const token = ctx.req.headers.authorization; | 126 | const token = ctx.req.headers.authorization; |
| 79 | if(!token || !token.length) { | 127 | if(!token || !token.length) { |
| 80 | ctx.status = 401; | 128 | ctx.status = 401; |
| ... | @@ -90,21 +138,9 @@ exports.hubDisconnect = async(ctx) => { | ... | @@ -90,21 +138,9 @@ exports.hubDisconnect = async(ctx) => { |
| 90 | } | 138 | } |
| 91 | 139 | ||
| 92 | const { hubId } = ctx.params; | 140 | const { hubId } = ctx.params; |
| 141 | + const { hubNm } = ctx.request.body; | ||
| 93 | 142 | ||
| 94 | - const hub = await Hub.findByHubId(hubId); | 143 | + await Hub.updateOne({ hubId }, { hubNm }); |
| 95 | - if(!hub) { | ||
| 96 | - ctx.status = 404; | ||
| 97 | - return; | ||
| 98 | - } | ||
| 99 | - if(hub.getHub_UserId() !== userId) { | ||
| 100 | - ctx.status = 403; | ||
| 101 | - return; | ||
| 102 | - } | ||
| 103 | 144 | ||
| 104 | - const hosting = await hub.getHubHost(); | ||
| 105 | - Mqtt.mqttOff(hosting); | ||
| 106 | - | ||
| 107 | - await Hub.deleteOne({ hubId }); | ||
| 108 | - | ||
| 109 | - ctx.status = 204; | ||
| 110 | -}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 145 | + ctx.status = 200; | ||
| 146 | +}; | ... | ... |
| ... | @@ -12,6 +12,14 @@ const hub = new Router(); | ... | @@ -12,6 +12,14 @@ const hub = new Router(); |
| 12 | hub.post('/', hubCtrl.hubConnect); | 12 | hub.post('/', hubCtrl.hubConnect); |
| 13 | 13 | ||
| 14 | /** | 14 | /** |
| 15 | + * 허브 등록 해제 | ||
| 16 | + * request parameter : x | ||
| 17 | + * url : http://localhost:4000/api/hub/:hubId | ||
| 18 | + * return : null | ||
| 19 | + */ | ||
| 20 | + hub.delete('/:hubId', hubCtrl.hubDisconnect); | ||
| 21 | + | ||
| 22 | +/** | ||
| 15 | * 로그인한 유저의 허브 목록 가져오기 | 23 | * 로그인한 유저의 허브 목록 가져오기 |
| 16 | * request parameter : X | 24 | * request parameter : X |
| 17 | * url : http://localhost:4000/api/hub | 25 | * url : http://localhost:4000/api/hub |
| ... | @@ -20,11 +28,13 @@ hub.post('/', hubCtrl.hubConnect); | ... | @@ -20,11 +28,13 @@ hub.post('/', hubCtrl.hubConnect); |
| 20 | hub.get('/', hubCtrl.getHubList); | 28 | hub.get('/', hubCtrl.getHubList); |
| 21 | 29 | ||
| 22 | /** | 30 | /** |
| 23 | - * 허브 등록 해제 | 31 | + * 로그인한 유저의 특정 허브 이름 변경 |
| 24 | - * request parameter : x | 32 | + * request parameter : hubId, hubNm |
| 25 | * url : http://localhost:4000/api/hub/:hubId | 33 | * url : http://localhost:4000/api/hub/:hubId |
| 26 | * return : null | 34 | * return : null |
| 27 | */ | 35 | */ |
| 28 | -hub.delete('/:hubId', hubCtrl.hubDisconnect); | 36 | +hub.patch('/:hubId', hubCtrl.setHubName); |
| 37 | + | ||
| 38 | + | ||
| 29 | 39 | ||
| 30 | module.exports = hub; | 40 | module.exports = hub; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -7,6 +7,7 @@ const hub = require('./hub'); | ... | @@ -7,6 +7,7 @@ const hub = require('./hub'); |
| 7 | const medicine = require('./medicine'); | 7 | const medicine = require('./medicine'); |
| 8 | const doctor = require('./doctor'); | 8 | const doctor = require('./doctor'); |
| 9 | const manage = require('./manage'); | 9 | const manage = require('./manage'); |
| 10 | +const test = require('./test'); | ||
| 10 | 11 | ||
| 11 | const api = new Router(); | 12 | const api = new Router(); |
| 12 | 13 | ||
| ... | @@ -17,5 +18,6 @@ api.use('/hub', hub.routes()); | ... | @@ -17,5 +18,6 @@ api.use('/hub', hub.routes()); |
| 17 | api.use('/medicine', medicine.routes()); | 18 | api.use('/medicine', medicine.routes()); |
| 18 | api.use('/doctor', doctor.routes()); | 19 | api.use('/doctor', doctor.routes()); |
| 19 | api.use('/manage', manage.routes()); | 20 | api.use('/manage', manage.routes()); |
| 21 | +api.use('/test', test.routes()); | ||
| 20 | 22 | ||
| 21 | module.exports = api; | 23 | module.exports = api; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -12,6 +12,14 @@ const manage = new Router(); | ... | @@ -12,6 +12,14 @@ const manage = new Router(); |
| 12 | manage.get('/doctor', manageCtrl.getDoctorRegReqList); | 12 | manage.get('/doctor', manageCtrl.getDoctorRegReqList); |
| 13 | 13 | ||
| 14 | /** | 14 | /** |
| 15 | + * 의사 회원탈퇴 요청을 한 회원들의 목록을 리턴 | ||
| 16 | + * request parameter : null | ||
| 17 | + * url : http://localhost:4000/api/manage/doctor/sec | ||
| 18 | + * return : doctor request List | ||
| 19 | + */ | ||
| 20 | + manage.get('/doctor/secession', manageCtrl.getDoctorSecReqList); | ||
| 21 | + | ||
| 22 | +/** | ||
| 15 | * 의사 회원가입 요청을 한 특정 회원의 상세정보 확인 | 23 | * 의사 회원가입 요청을 한 특정 회원의 상세정보 확인 |
| 16 | * request parameter : doctor Id | 24 | * request parameter : doctor Id |
| 17 | * url : http://localhost:4000/api/manage/doctor/:doctorId | 25 | * url : http://localhost:4000/api/manage/doctor/:doctorId |
| ... | @@ -25,7 +33,7 @@ manage.get('/doctor/:doctorId', manageCtrl.getDoctorRegReqDetail); | ... | @@ -25,7 +33,7 @@ manage.get('/doctor/:doctorId', manageCtrl.getDoctorRegReqDetail); |
| 25 | * url : http://localhost:4000/api/manage/doctor/accept | 33 | * url : http://localhost:4000/api/manage/doctor/accept |
| 26 | * return : null | 34 | * return : null |
| 27 | */ | 35 | */ |
| 28 | -manage.post('/doctor/accept', manageCtrl.acceptDoctorRegReq); | 36 | +manage.patch('/doctor/accept', manageCtrl.acceptDoctorRegReq); |
| 29 | 37 | ||
| 30 | /** | 38 | /** |
| 31 | * 의사 요청을 한 회원을 거절한다. | 39 | * 의사 요청을 한 회원을 거절한다. |
| ... | @@ -33,7 +41,15 @@ manage.post('/doctor/accept', manageCtrl.acceptDoctorRegReq); | ... | @@ -33,7 +41,15 @@ manage.post('/doctor/accept', manageCtrl.acceptDoctorRegReq); |
| 33 | * url : http://localhost:4000/api/manange/doctor/reject | 41 | * url : http://localhost:4000/api/manange/doctor/reject |
| 34 | * return : null | 42 | * return : null |
| 35 | */ | 43 | */ |
| 36 | -manage.post('/doctor/reject', manageCtrl.rejectDoctorRegReq); | 44 | +manage.patch('/doctor/reject', manageCtrl.rejectDoctorRegReq); |
| 45 | + | ||
| 46 | +/** | ||
| 47 | + * 의사 탈퇴 요청을 수락한다. | ||
| 48 | + * request parameter : doctor Id | ||
| 49 | + * url : http://localhost:4000/api/manange/doctor/secession | ||
| 50 | + * return : null | ||
| 51 | + */ | ||
| 52 | + manage.patch('/doctor/secession', manageCtrl.acceptDoctorSecReq); | ||
| 37 | 53 | ||
| 38 | /** | 54 | /** |
| 39 | * 의사 요청을 한 회원의 자격 번호가 유효한지 검증한다 | 55 | * 의사 요청을 한 회원의 자격 번호가 유효한지 검증한다 | ... | ... |
| 1 | const User = require('../../models/user'); | 1 | const User = require('../../models/user'); |
| 2 | const DoctorInfo = require('../../models/doctorInfo'); | 2 | const DoctorInfo = require('../../models/doctorInfo'); |
| 3 | -const Profile = require('../../models/profile'); | 3 | +const PatientInfo = require('../../models/patientInfo'); |
| 4 | const jwt = require('jsonwebtoken'); | 4 | const jwt = require('jsonwebtoken'); |
| 5 | +const { viewDoctorLicense } = require('../../util/GoogleCloudStorage'); | ||
| 6 | + | ||
| 5 | 7 | ||
| 6 | /** | 8 | /** |
| 7 | * 의사 회원가입을 요청한 회원 리스트를 확인한다. | 9 | * 의사 회원가입을 요청한 회원 리스트를 확인한다. |
| ... | @@ -25,13 +27,54 @@ exports.getDoctorRegReqList = async ctx => { | ... | @@ -25,13 +27,54 @@ exports.getDoctorRegReqList = async ctx => { |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | try { | 29 | try { |
| 28 | - const doctorRegReqList = await DoctorInfo.find({ | 30 | + const doctorList = await DoctorInfo.find({ |
| 29 | useYn : 'W', | 31 | useYn : 'W', |
| 30 | }); | 32 | }); |
| 31 | 33 | ||
| 32 | ctx.status = 200; | 34 | ctx.status = 200; |
| 33 | ctx.body = { | 35 | ctx.body = { |
| 34 | - doctorRegReqList | 36 | + doctorList |
| 37 | + }; | ||
| 38 | + | ||
| 39 | + } catch(e) { | ||
| 40 | + ctx.status = 500; | ||
| 41 | + ctx.body = { | ||
| 42 | + error : '알 수 없는 에러가 발생했습니다.', | ||
| 43 | + }; | ||
| 44 | + console.log(e); | ||
| 45 | + return; | ||
| 46 | + } | ||
| 47 | +}; | ||
| 48 | + | ||
| 49 | +/** | ||
| 50 | + * 의사 회원탈퇴를 요청한 회원 리스트를 확인한다. | ||
| 51 | + * http methods : get | ||
| 52 | + * @param {*} ctx | ||
| 53 | + * @returns | ||
| 54 | + */ | ||
| 55 | + exports.getDoctorSecReqList = async ctx => { | ||
| 56 | + const token = ctx.req.headers.authorization; | ||
| 57 | + if (!token || !token.length) { | ||
| 58 | + ctx.status = 401; | ||
| 59 | + return; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // eslint-disable-next-line no-undef | ||
| 63 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
| 64 | + const user = await User.findByUserId(userId); | ||
| 65 | + if(!user || user.userTypeCd !== 'MANAGER' || user.useYn !== 'Y') { | ||
| 66 | + ctx.status = 403; | ||
| 67 | + return; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + try { | ||
| 71 | + const doctorList = await DoctorInfo.find({ | ||
| 72 | + useYn : 'WS', | ||
| 73 | + }); | ||
| 74 | + | ||
| 75 | + ctx.status = 200; | ||
| 76 | + ctx.body = { | ||
| 77 | + doctorList | ||
| 35 | }; | 78 | }; |
| 36 | 79 | ||
| 37 | } catch(e) { | 80 | } catch(e) { |
| ... | @@ -40,6 +83,7 @@ exports.getDoctorRegReqList = async ctx => { | ... | @@ -40,6 +83,7 @@ exports.getDoctorRegReqList = async ctx => { |
| 40 | error : '알 수 없는 에러가 발생했습니다.', | 83 | error : '알 수 없는 에러가 발생했습니다.', |
| 41 | }; | 84 | }; |
| 42 | console.log(e); | 85 | console.log(e); |
| 86 | + return; | ||
| 43 | } | 87 | } |
| 44 | }; | 88 | }; |
| 45 | 89 | ||
| ... | @@ -108,9 +152,20 @@ exports.getDoctorRegReqDetail = async ctx => { | ... | @@ -108,9 +152,20 @@ exports.getDoctorRegReqDetail = async ctx => { |
| 108 | return; | 152 | return; |
| 109 | } | 153 | } |
| 110 | 154 | ||
| 155 | + | ||
| 156 | + const doctorLicense = await viewDoctorLicense({ | ||
| 157 | + doctorInfo | ||
| 158 | + }); | ||
| 159 | + | ||
| 111 | ctx.status = 200; | 160 | ctx.status = 200; |
| 112 | ctx.body = { | 161 | ctx.body = { |
| 113 | - doctorInfo, | 162 | + doctorInfo : { |
| 163 | + ...doctorInfo._doc, | ||
| 164 | + info : { | ||
| 165 | + ...doctorInfo.info, | ||
| 166 | + doctorLicense, | ||
| 167 | + }, | ||
| 168 | + }, | ||
| 114 | }; | 169 | }; |
| 115 | 170 | ||
| 116 | } catch (e) { | 171 | } catch (e) { |
| ... | @@ -118,12 +173,14 @@ exports.getDoctorRegReqDetail = async ctx => { | ... | @@ -118,12 +173,14 @@ exports.getDoctorRegReqDetail = async ctx => { |
| 118 | ctx.body = { | 173 | ctx.body = { |
| 119 | error : '알 수 없는 에러가 발생했습니다.', | 174 | error : '알 수 없는 에러가 발생했습니다.', |
| 120 | }; | 175 | }; |
| 176 | + console.log(e); | ||
| 177 | + return; | ||
| 121 | } | 178 | } |
| 122 | }; | 179 | }; |
| 123 | 180 | ||
| 124 | /** | 181 | /** |
| 125 | * 의사 요청이 온 회원을 수락한다. | 182 | * 의사 요청이 온 회원을 수락한다. |
| 126 | - * http methods : post | 183 | + * http methods : patch |
| 127 | * @param {*} ctx | 184 | * @param {*} ctx |
| 128 | * @returns | 185 | * @returns |
| 129 | */ | 186 | */ |
| ... | @@ -143,7 +200,7 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -143,7 +200,7 @@ exports.acceptDoctorRegReq = async ctx => { |
| 143 | } | 200 | } |
| 144 | 201 | ||
| 145 | try { | 202 | try { |
| 146 | - const { doctorId } = ctx.request.body; | 203 | + const { doctorId, validateDoctorLicense } = ctx.request.body; |
| 147 | const doctor = await User.findOne({ userId : doctorId }); | 204 | const doctor = await User.findOne({ userId : doctorId }); |
| 148 | if(!doctor) { | 205 | if(!doctor) { |
| 149 | ctx.status = 404; | 206 | ctx.status = 404; |
| ... | @@ -169,17 +226,37 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -169,17 +226,37 @@ exports.acceptDoctorRegReq = async ctx => { |
| 169 | error : '의사로 가입된 회원이 아닙니다.', | 226 | error : '의사로 가입된 회원이 아닙니다.', |
| 170 | }; | 227 | }; |
| 171 | return; | 228 | return; |
| 229 | + } else if(!validateDoctorLicense) { | ||
| 230 | + ctx.status = 400; | ||
| 231 | + ctx.body = { | ||
| 232 | + error : '유효한 자격 번호가 아닙니다.', | ||
| 233 | + }; | ||
| 234 | + return; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + const existDoctorInfo = await DoctorInfo.findOne({ | ||
| 238 | + 'info.validateDoctorLicense' : validateDoctorLicense | ||
| 239 | + }); | ||
| 240 | + if(existDoctorInfo) { | ||
| 241 | + ctx.status = 403; | ||
| 242 | + ctx.body = { | ||
| 243 | + error : '중복된 자격번호입니다.', | ||
| 244 | + }; | ||
| 245 | + return; | ||
| 172 | } | 246 | } |
| 173 | 247 | ||
| 248 | + | ||
| 174 | const doctorInfo = await DoctorInfo.findOne({ | 249 | const doctorInfo = await DoctorInfo.findOne({ |
| 175 | doctorId, | 250 | doctorId, |
| 176 | useYn : 'W', | 251 | useYn : 'W', |
| 177 | }); | 252 | }); |
| 178 | 253 | ||
| 179 | - doctor.setUseYn('Y'); | 254 | + await doctor.setUseYn('Y'); |
| 180 | - doctor.save(); | 255 | + await doctor.save(); |
| 181 | - doctorInfo.setUseYn('Y'); | 256 | + |
| 182 | - doctorInfo.save(); | 257 | + await doctorInfo.setUseYn('Y'); |
| 258 | + await doctorInfo.setValidateDoctorLicense(validateDoctorLicense); | ||
| 259 | + await doctorInfo.save(); | ||
| 183 | 260 | ||
| 184 | ctx.status = 200; | 261 | ctx.status = 200; |
| 185 | 262 | ||
| ... | @@ -189,12 +266,13 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -189,12 +266,13 @@ exports.acceptDoctorRegReq = async ctx => { |
| 189 | error : '알 수 없는 에러가 발생했습니다.', | 266 | error : '알 수 없는 에러가 발생했습니다.', |
| 190 | }; | 267 | }; |
| 191 | console.log(e); | 268 | console.log(e); |
| 269 | + return; | ||
| 192 | } | 270 | } |
| 193 | }; | 271 | }; |
| 194 | 272 | ||
| 195 | /** | 273 | /** |
| 196 | * 의사 요청이 온 회원을 거절한다. | 274 | * 의사 요청이 온 회원을 거절한다. |
| 197 | - * http methods : post | 275 | + * http methods : patch |
| 198 | * @param {*} ctx | 276 | * @param {*} ctx |
| 199 | * @returns | 277 | * @returns |
| 200 | */ | 278 | */ |
| ... | @@ -242,16 +320,79 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -242,16 +320,79 @@ exports.acceptDoctorRegReq = async ctx => { |
| 242 | return; | 320 | return; |
| 243 | } | 321 | } |
| 244 | 322 | ||
| 323 | + await DoctorInfo.updateOne({ doctorId, useYn : 'W' }, { useYn : 'N' }); | ||
| 245 | 324 | ||
| 246 | - const doctorInfo = await DoctorInfo.findOne({ | 325 | + await doctor.setUseYn('N'); |
| 247 | - doctorId, | 326 | + await doctor.save(); |
| 248 | - useYn : 'W', | ||
| 249 | - }); | ||
| 250 | 327 | ||
| 251 | - doctor.setUseYn('N'); | 328 | + ctx.status = 200; |
| 252 | - doctor.save(); | 329 | + |
| 253 | - doctorInfo.setUseYn('N'); | 330 | + } catch(e) { |
| 254 | - doctorInfo.save(); | 331 | + ctx.status = 500; |
| 332 | + ctx.body = { | ||
| 333 | + error : '알 수 없는 에러가 발생했습니다.', | ||
| 334 | + }; | ||
| 335 | + console.log(e); | ||
| 336 | + return; | ||
| 337 | + } | ||
| 338 | +}; | ||
| 339 | + | ||
| 340 | +/** | ||
| 341 | + * 의사 회원탈퇴 요청을 수락한다. | ||
| 342 | + * http methods : patch | ||
| 343 | + * @param {*} ctx | ||
| 344 | + * @returns | ||
| 345 | + */ | ||
| 346 | + exports.acceptDoctorSecReq = async ctx => { | ||
| 347 | + const token = ctx.req.headers.authorization; | ||
| 348 | + if (!token || !token.length) { | ||
| 349 | + ctx.status = 401; | ||
| 350 | + return; | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + // eslint-disable-next-line no-undef | ||
| 354 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
| 355 | + const user = await User.findByUserId(userId); | ||
| 356 | + if(!user || user.userTypeCd !== 'MANAGER' || user.useYn !== 'Y') { | ||
| 357 | + ctx.status = 403; | ||
| 358 | + return; | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + try { | ||
| 362 | + const { doctorId } = ctx.request.body; | ||
| 363 | + const doctor = await User.findOne({ userId : doctorId }); | ||
| 364 | + if(!doctor) { | ||
| 365 | + ctx.status = 404; | ||
| 366 | + ctx.body = { | ||
| 367 | + error : '존재하지 않는 회원입니다.', | ||
| 368 | + }; | ||
| 369 | + return; | ||
| 370 | + } else if(doctor.useYn === 'N') { | ||
| 371 | + ctx.status = 400; | ||
| 372 | + ctx.body = { | ||
| 373 | + error : '탈퇴된 회원입니다.', | ||
| 374 | + }; | ||
| 375 | + return; | ||
| 376 | + } else if(doctor.useYn === 'Y') { | ||
| 377 | + ctx.status = 400; | ||
| 378 | + ctx.body = { | ||
| 379 | + error : '이미 가입이 완료된 의사입니다.', | ||
| 380 | + }; | ||
| 381 | + return; | ||
| 382 | + } else if(doctor.userTypeCd !== 'DOCTOR') { | ||
| 383 | + ctx.status = 400; | ||
| 384 | + ctx.body = { | ||
| 385 | + error : '의사로 가입된 회원이 아닙니다.', | ||
| 386 | + }; | ||
| 387 | + return; | ||
| 388 | + } | ||
| 389 | + | ||
| 390 | + | ||
| 391 | + await DoctorInfo.updateOne({ doctorId, useYn : 'WS' }, { useYn : 'N' }); | ||
| 392 | + await PatientInfo.updateMany({ doctorId : userId, useYn : 'WS' }, { useYn : 'N' }); | ||
| 393 | + | ||
| 394 | + await doctor.setUseYn('N'); | ||
| 395 | + await doctor.save(); | ||
| 255 | 396 | ||
| 256 | ctx.status = 200; | 397 | ctx.status = 200; |
| 257 | 398 | ||
| ... | @@ -261,9 +402,11 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -261,9 +402,11 @@ exports.acceptDoctorRegReq = async ctx => { |
| 261 | error : '알 수 없는 에러가 발생했습니다.', | 402 | error : '알 수 없는 에러가 발생했습니다.', |
| 262 | }; | 403 | }; |
| 263 | console.log(e); | 404 | console.log(e); |
| 405 | + return; | ||
| 264 | } | 406 | } |
| 265 | }; | 407 | }; |
| 266 | 408 | ||
| 409 | + | ||
| 267 | /** | 410 | /** |
| 268 | * 회원가입을 요청한 의사의 유효한 자격 번호인지를 검증한다. | 411 | * 회원가입을 요청한 의사의 유효한 자격 번호인지를 검증한다. |
| 269 | * @param {*} ctx | 412 | * @param {*} ctx |
| ... | @@ -284,12 +427,20 @@ exports.validateDoctorLicense = async ctx => { | ... | @@ -284,12 +427,20 @@ exports.validateDoctorLicense = async ctx => { |
| 284 | return; | 427 | return; |
| 285 | } | 428 | } |
| 286 | 429 | ||
| 287 | - const { doctorLicense } = ctx.request.body; | 430 | + const { validateDoctorLicense } = ctx.request.body; |
| 288 | - const doctorInfo = await DoctorInfo.find({ 'info.doctorLicense' : doctorLicense }); | 431 | + if(!validateDoctorLicense) { |
| 432 | + ctx.status = 404; | ||
| 433 | + ctx.body = { | ||
| 434 | + error : '유효한 자격번호를 입력해주세요', | ||
| 435 | + }; | ||
| 436 | + return; | ||
| 437 | + } | ||
| 438 | + | ||
| 439 | + const doctorInfo = await DoctorInfo.findOne({ 'info.validateDoctorLicense' : validateDoctorLicense }); | ||
| 289 | 440 | ||
| 290 | ctx.status = 200; | 441 | ctx.status = 200; |
| 291 | ctx.body = { | 442 | ctx.body = { |
| 292 | - result : doctorInfo.length > 1 ? false : true, | 443 | + result : doctorInfo ? false : true, |
| 293 | }; | 444 | }; |
| 294 | 445 | ||
| 295 | }; | 446 | }; | ... | ... |
server/src/api/test/index.js
0 → 100644
server/src/api/test/test.ctrl.js
0 → 100644
| 1 | +//테스트용 api service | ||
| 2 | +const { sendPushMessage } = require('../../util/FCM'); | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +exports.sendFcmMessage = async ctx => { | ||
| 6 | + const { deviceToken, title, body } = ctx.request.body; | ||
| 7 | + | ||
| 8 | + try { | ||
| 9 | + await sendPushMessage(ctx.request.body); | ||
| 10 | + } catch(e) { | ||
| 11 | + console.log('Error at FCM Sending : ', e); | ||
| 12 | + } | ||
| 13 | +}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -11,9 +11,18 @@ const user = new Router(); | ... | @@ -11,9 +11,18 @@ const user = new Router(); |
| 11 | */ | 11 | */ |
| 12 | user.get('/', userCtrl.getMyDetail); | 12 | user.get('/', userCtrl.getMyDetail); |
| 13 | 13 | ||
| 14 | + | ||
| 14 | /** | 15 | /** |
| 15 | - * 현재 로그인한 유저에 등록된 의사 목록 가져옴 | 16 | + * 현재 유저 정보 수정 |
| 16 | * request parameter : token | 17 | * request parameter : token |
| 18 | + * url : http://localhost:4000/api/user | ||
| 19 | + * return : Object User | ||
| 20 | + */ | ||
| 21 | +user.patch('/', userCtrl.updateMyDetail); | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 현재 로그인한 유저에 등록된 의사 목록 가져옴 | ||
| 25 | + * request parameter : userNm, birth, contact, password, passwordCheck | ||
| 17 | * url : http://localhost:4000/api/user/doctor | 26 | * url : http://localhost:4000/api/user/doctor |
| 18 | * return : Doctor List | 27 | * return : Doctor List |
| 19 | */ | 28 | */ | ... | ... |
| ... | @@ -38,10 +38,66 @@ exports.getMyDetail = async ctx => { | ... | @@ -38,10 +38,66 @@ exports.getMyDetail = async ctx => { |
| 38 | /** | 38 | /** |
| 39 | * 내 정보를 업데이트한다. | 39 | * 내 정보를 업데이트한다. |
| 40 | * @param {*} ctx | 40 | * @param {*} ctx |
| 41 | - * http methods : post | 41 | + * http methods : patch |
| 42 | */ | 42 | */ |
| 43 | -exports.updateMyInfo = async ctx => { | 43 | +exports.updateMyDetail = async ctx => { |
| 44 | + const token = ctx.req.headers.authorization; | ||
| 45 | + if(!token || !token.length) { | ||
| 46 | + ctx.status = 401; | ||
| 47 | + return; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + // eslint-disable-next-line no-undef | ||
| 51 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
| 52 | + const user = await User.findByUserId(userId); | ||
| 53 | + if(!user || user.useYn !== 'Y' || user.userTypeCd !== 'NORMAL') { | ||
| 54 | + ctx.status = 403; | ||
| 55 | + return; | ||
| 56 | + } | ||
| 44 | 57 | ||
| 58 | + const profile = await Profile.findByUserId(userId); | ||
| 59 | + if(!profile || profile.useYn !== 'Y') { | ||
| 60 | + ctx.status = 403; | ||
| 61 | + return; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + const { userNm, birth, contact, password, passwordCheck, } = ctx.request.body; | ||
| 65 | + | ||
| 66 | + const existContact = await Profile.findOne({ contact, useYn : 'Y' }); | ||
| 67 | + if(existContact) { | ||
| 68 | + ctx.status = 409; | ||
| 69 | + ctx.body = { | ||
| 70 | + error : '이미 가입된 번호', | ||
| 71 | + }; | ||
| 72 | + | ||
| 73 | + return; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + //passwordCheck가 있고 로컬 회원이라면 비밀번호 변경함 | ||
| 77 | + if(passwordCheck && user.authTypeCd === 'NORMAL') { | ||
| 78 | + //passwordCheck와 password가 같아야함 | ||
| 79 | + if(passwordCheck !== password) { | ||
| 80 | + ctx.status = 401; | ||
| 81 | + ctx.body = { | ||
| 82 | + error : '비밀번호가 일치하지 않습니다.', | ||
| 83 | + }; | ||
| 84 | + return; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + await user.setPassword(password); | ||
| 88 | + await user.save(); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + await profile.updateProfileInfo({ | ||
| 92 | + userNm, | ||
| 93 | + birth, | ||
| 94 | + contact, | ||
| 95 | + }); | ||
| 96 | + | ||
| 97 | + await profile.save(); | ||
| 98 | + | ||
| 99 | + ctx.status = 200; | ||
| 100 | + | ||
| 45 | }; | 101 | }; |
| 46 | 102 | ||
| 47 | /** | 103 | /** |
| ... | @@ -76,7 +132,12 @@ exports.getMyDoctorList = async ctx => { | ... | @@ -76,7 +132,12 @@ exports.getMyDoctorList = async ctx => { |
| 76 | useYn : 'Y', | 132 | useYn : 'Y', |
| 77 | }); | 133 | }); |
| 78 | 134 | ||
| 79 | - return doctorInfo.info; | 135 | + if (doctorInfo) { |
| 136 | + return ({ | ||
| 137 | + ...doctorInfo.info, | ||
| 138 | + doctorId : doctorInfo.doctorId, | ||
| 139 | + }) | ||
| 140 | + } | ||
| 80 | })); | 141 | })); |
| 81 | 142 | ||
| 82 | ctx.status = 200; | 143 | ctx.status = 200; |
| ... | @@ -112,7 +173,10 @@ exports.viewAllDoctorRegisterReq = async ctx => { | ... | @@ -112,7 +173,10 @@ exports.viewAllDoctorRegisterReq = async ctx => { |
| 112 | }) | 173 | }) |
| 113 | 174 | ||
| 114 | const doctorReqList = await Promise.all(patientInfoList.map(async patientInfo => { | 175 | const doctorReqList = await Promise.all(patientInfoList.map(async patientInfo => { |
| 115 | - const doctor = await DoctorInfo.findOne({ doctorId : patientInfo.doctorId }); | 176 | + const doctor = await DoctorInfo.findOne({ |
| 177 | + doctorId : patientInfo.doctorId, | ||
| 178 | + useYn : 'Y', | ||
| 179 | + }); | ||
| 116 | return { | 180 | return { |
| 117 | patientId : patientInfo.patientId, | 181 | patientId : patientInfo.patientId, |
| 118 | doctorId : patientInfo.doctorId, | 182 | doctorId : patientInfo.doctorId, |
| ... | @@ -156,9 +220,9 @@ exports.acceptDoctorRegister = async ctx => { | ... | @@ -156,9 +220,9 @@ exports.acceptDoctorRegister = async ctx => { |
| 156 | return; | 220 | return; |
| 157 | } | 221 | } |
| 158 | 222 | ||
| 159 | - patientInfo.updateInfo('환자 등록 요청 수락'); | 223 | + await patientInfo.updateInfo('환자 등록 요청 수락'); |
| 160 | - patientInfo.setUseYn('Y'); | 224 | + await patientInfo.setUseYn('Y'); |
| 161 | - patientInfo.save(); | 225 | + await patientInfo.save(); |
| 162 | 226 | ||
| 163 | ctx.status = 200; | 227 | ctx.status = 200; |
| 164 | 228 | ... | ... |
| ... | @@ -11,6 +11,7 @@ exports.updateMedicineInfo = async() => { | ... | @@ -11,6 +11,7 @@ exports.updateMedicineInfo = async() => { |
| 11 | //queryUrl을 return하는 함수 : 한 페이지에 100개의 item씩 요청할 수 있다. | 11 | //queryUrl을 return하는 함수 : 한 페이지에 100개의 item씩 요청할 수 있다. |
| 12 | const getQueryURL = (i) => { | 12 | const getQueryURL = (i) => { |
| 13 | const url = 'http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList'; | 13 | const url = 'http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList'; |
| 14 | + // eslint-disable-next-line no-undef | ||
| 14 | const queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY; | 15 | const queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY; |
| 15 | const pageNum = '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(i); | 16 | const pageNum = '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(i); |
| 16 | const numOfItem = '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(100); | 17 | const numOfItem = '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(100); |
| ... | @@ -24,6 +25,7 @@ const getItemsList = async(queryUrl) => { | ... | @@ -24,6 +25,7 @@ const getItemsList = async(queryUrl) => { |
| 24 | let i = 1, getItem = null, items = null; | 25 | let i = 1, getItem = null, items = null; |
| 25 | const result = []; | 26 | const result = []; |
| 26 | 27 | ||
| 28 | + // eslint-disable-next-line no-constant-condition | ||
| 27 | while(true) { | 29 | while(true) { |
| 28 | getItem = await axios.get(queryUrl(i)); | 30 | getItem = await axios.get(queryUrl(i)); |
| 29 | items = getItem.data.body.items; | 31 | items = getItem.data.body.items; | ... | ... |
| ... | @@ -8,6 +8,7 @@ const jwtMiddleware = async (ctx, next) => { | ... | @@ -8,6 +8,7 @@ const jwtMiddleware = async (ctx, next) => { |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | try { | 10 | try { |
| 11 | + // eslint-disable-next-line no-undef | ||
| 11 | const decoded = jwt.verify(token, process.env.JWT_SECRET); | 12 | const decoded = jwt.verify(token, process.env.JWT_SECRET); |
| 12 | ctx.state.user = { | 13 | ctx.state.user = { |
| 13 | _id : decoded._id, | 14 | _id : decoded._id, | ... | ... |
| ... | @@ -5,6 +5,7 @@ const Schema = mongoose.Schema; | ... | @@ -5,6 +5,7 @@ const Schema = mongoose.Schema; |
| 5 | const BottleSchema = new Schema ({ | 5 | const BottleSchema = new Schema ({ |
| 6 | bottleId : { type : Number, required : true, unique : true }, | 6 | bottleId : { type : Number, required : true, unique : true }, |
| 7 | hubId : { type : Number, required : true, ref : 'Hub', }, | 7 | hubId : { type : Number, required : true, ref : 'Hub', }, |
| 8 | + bottleNm : { type : String, required : true, maxlength : 10, }, | ||
| 8 | }); | 9 | }); |
| 9 | 10 | ||
| 10 | BottleSchema.statics.findByBottleId = function(bottleId) { | 11 | BottleSchema.statics.findByBottleId = function(bottleId) { |
| ... | @@ -23,5 +24,9 @@ BottleSchema.methods.getHubId = function() { | ... | @@ -23,5 +24,9 @@ BottleSchema.methods.getHubId = function() { |
| 23 | return this.hubId; | 24 | return this.hubId; |
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| 27 | +BottleSchema.methods.setBottleNm = function(bottleNm) { | ||
| 28 | + this.bottleNm = bottleNm; | ||
| 29 | +} | ||
| 30 | + | ||
| 26 | 31 | ||
| 27 | module.exports = mongoose.model('Bottle', BottleSchema); | 32 | module.exports = mongoose.model('Bottle', BottleSchema); |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -16,16 +16,26 @@ const BottleMedicineSchema = new Schema({ | ... | @@ -16,16 +16,26 @@ const BottleMedicineSchema = new Schema({ |
| 16 | doctorId : { | 16 | doctorId : { |
| 17 | type : String, | 17 | type : String, |
| 18 | ref : 'User', | 18 | ref : 'User', |
| 19 | - required : true, | 19 | + lowercase : true, |
| 20 | }, | 20 | }, |
| 21 | - dosage : { | 21 | + dailyDosage : { |
| 22 | + type : Number, | ||
| 23 | + default : 1, | ||
| 24 | + }, | ||
| 25 | + totalDosage : { | ||
| 26 | + type : Number, | ||
| 27 | + default : 1, | ||
| 28 | + }, | ||
| 29 | + eachWeight : { | ||
| 30 | + type : Number, | ||
| 31 | + default : 0, | ||
| 32 | + }, | ||
| 33 | + totalWeight : { | ||
| 22 | type : Number, | 34 | type : Number, |
| 23 | - required : true, | ||
| 24 | default : 0, | 35 | default : 0, |
| 25 | }, | 36 | }, |
| 26 | regDtm : { | 37 | regDtm : { |
| 27 | type : Date, | 38 | type : Date, |
| 28 | - required : true, | ||
| 29 | default : Date.now, | 39 | default : Date.now, |
| 30 | }, | 40 | }, |
| 31 | useYn : { | 41 | useYn : { |
| ... | @@ -39,6 +49,14 @@ BottleMedicineSchema.methods.setDoctorId = function(doctorId) { | ... | @@ -39,6 +49,14 @@ BottleMedicineSchema.methods.setDoctorId = function(doctorId) { |
| 39 | this.doctorId = doctorId; | 49 | this.doctorId = doctorId; |
| 40 | }; | 50 | }; |
| 41 | 51 | ||
| 52 | +BottleMedicineSchema.methods.setEachWeight = function(eachWeight) { | ||
| 53 | + this.eachWeight = eachWeight; | ||
| 54 | +}; | ||
| 55 | + | ||
| 56 | +BottleMedicineSchema.methods.setTotalWeight = function(totalWeight) { | ||
| 57 | + this.totalWeight = totalWeight; | ||
| 58 | +}; | ||
| 59 | + | ||
| 42 | BottleMedicineSchema.methods.setUseYn = function(useYn) { | 60 | BottleMedicineSchema.methods.setUseYn = function(useYn) { |
| 43 | this.useYn = useYn; | 61 | this.useYn = useYn; |
| 44 | }; | 62 | }; | ... | ... |
| ... | @@ -3,9 +3,10 @@ const mongoose = require('mongoose'); | ... | @@ -3,9 +3,10 @@ const mongoose = require('mongoose'); |
| 3 | const Schema = mongoose.Schema; | 3 | const Schema = mongoose.Schema; |
| 4 | 4 | ||
| 5 | const DoctorInfoSchema = new Schema({ | 5 | const DoctorInfoSchema = new Schema({ |
| 6 | - doctorId : { type : String, required : true, }, | 6 | + doctorId : { type : String, required : true, lowercase : true, }, |
| 7 | info : { | 7 | info : { |
| 8 | doctorLicense : { type : String, required : true, }, | 8 | doctorLicense : { type : String, required : true, }, |
| 9 | + validateDoctorLicense : { type : String, default : null }, | ||
| 9 | hospitalNm : { type : String, default : null, }, | 10 | hospitalNm : { type : String, default : null, }, |
| 10 | hospitalAddr : { type : String, default : null, }, | 11 | hospitalAddr : { type : String, default : null, }, |
| 11 | contact : { type : String, required : true, }, | 12 | contact : { type : String, required : true, }, |
| ... | @@ -23,5 +24,9 @@ DoctorInfoSchema.methods.setUseYn = function(useYn) { | ... | @@ -23,5 +24,9 @@ DoctorInfoSchema.methods.setUseYn = function(useYn) { |
| 23 | this.useYn = useYn; | 24 | this.useYn = useYn; |
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| 27 | +DoctorInfoSchema.methods.setValidateDoctorLicense = function(validateDoctorLicense) { | ||
| 28 | + this.info.validateDoctorLicense = validateDoctorLicense; | ||
| 29 | +}; | ||
| 30 | + | ||
| 26 | 31 | ||
| 27 | module.exports = mongoose.model('DoctorInfo', DoctorInfoSchema); | 32 | module.exports = mongoose.model('DoctorInfo', DoctorInfoSchema); | ... | ... |
| ... | @@ -10,7 +10,7 @@ const FeedbackSchema = new Schema({ | ... | @@ -10,7 +10,7 @@ const FeedbackSchema = new Schema({ |
| 10 | required : true, | 10 | required : true, |
| 11 | ref : 'BottleMedicine', | 11 | ref : 'BottleMedicine', |
| 12 | }, | 12 | }, |
| 13 | - doctorId : { type : String, required : true, ref : 'User', }, | 13 | + doctorId : { type : String, required : true, ref : 'User', lowercase : true, }, |
| 14 | feedback : { type : String, required : true, }, | 14 | feedback : { type : String, required : true, }, |
| 15 | }); | 15 | }); |
| 16 | 16 | ... | ... |
| ... | @@ -5,7 +5,8 @@ const Schema = mongoose.Schema; | ... | @@ -5,7 +5,8 @@ const Schema = mongoose.Schema; |
| 5 | const HubSchema = new Schema ({ | 5 | const HubSchema = new Schema ({ |
| 6 | hubId : { type : Number, required : true, unique : true }, | 6 | hubId : { type : Number, required : true, unique : true }, |
| 7 | hosting : { type : Object, default : null }, | 7 | hosting : { type : Object, default : null }, |
| 8 | - userId : { type : String, default : null, ref : 'User' }, | 8 | + userId : { type : String, default : null, ref : 'User', lowercase : true, }, |
| 9 | + hubNm : { type : String, required : true, maxlength : 10, }, | ||
| 9 | }); | 10 | }); |
| 10 | 11 | ||
| 11 | HubSchema.statics.findByHubId = function(hubId) { | 12 | HubSchema.statics.findByHubId = function(hubId) { | ... | ... |
| 1 | const mongoose = require('mongoose'); | 1 | const mongoose = require('mongoose'); |
| 2 | const moment = require('moment'); | 2 | const moment = require('moment'); |
| 3 | +require('moment-timezone'); | ||
| 3 | 4 | ||
| 4 | const Schema = mongoose.Schema; | 5 | const Schema = mongoose.Schema; |
| 5 | 6 | ||
| 6 | const PatientInfoSchema = new Schema({ | 7 | const PatientInfoSchema = new Schema({ |
| 7 | - patientId : { type : String, required : true, ref : 'User', }, | 8 | + patientId : { type : String, required : true, ref : 'User', lowercase : true, }, |
| 8 | - doctorId : { type : String, required : true, ref : 'User', }, | 9 | + doctorId : { type : String, required : true, ref : 'User', lowercase : true, }, |
| 9 | info : { type : String, required : true, }, | 10 | info : { type : String, required : true, }, |
| 10 | useYn : { type : String, required : true, default : 'W', }, | 11 | useYn : { type : String, required : true, default : 'W', }, |
| 11 | }); | 12 | }); |
| ... | @@ -35,9 +36,9 @@ PatientInfoSchema.methods.setUseYn = function(useYn) { | ... | @@ -35,9 +36,9 @@ PatientInfoSchema.methods.setUseYn = function(useYn) { |
| 35 | }; | 36 | }; |
| 36 | 37 | ||
| 37 | PatientInfoSchema.methods.updateInfo = function(info) { | 38 | PatientInfoSchema.methods.updateInfo = function(info) { |
| 38 | - const date = moment(new Date()).format('YYYY-MM-DD hh:mm'); | 39 | + const date = moment.tz('Asia/Seoul').format('YYYY-MM-DD HH:mm'); |
| 39 | if(this.info.length) | 40 | if(this.info.length) |
| 40 | - this.info = this.info.concat('\n\n', `${date} ➡ ${info}`); | 41 | + this.info = this.info.concat('\n\n', `${date} -> ${info}`); |
| 41 | else | 42 | else |
| 42 | this.info = `${date} ➡ ${info}`; | 43 | this.info = `${date} ➡ ${info}`; |
| 43 | }; | 44 | }; | ... | ... |
server/src/models/prescribeInfo.js
0 → 100644
| 1 | +const mongoose = require('mongoose'); | ||
| 2 | + | ||
| 3 | +const Schema = mongoose.Schema; | ||
| 4 | + | ||
| 5 | +const PrescribeInfoSchema = new Schema({ | ||
| 6 | + doctorId : { type : String, require : true, }, | ||
| 7 | + patientId : { type : String, require : true, }, | ||
| 8 | + medicineId : { type : Number, require : true, }, | ||
| 9 | + dailyDosage : { type : Number, require : true, }, | ||
| 10 | + totalDosage : { type : Number, require : true, }, | ||
| 11 | + qrCodeUrl : { type : String, require : true, }, | ||
| 12 | +}); | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +module.exports = mongoose.model('PrescribeInfo', PrescribeInfoSchema); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -3,10 +3,11 @@ const mongoose = require('mongoose'); | ... | @@ -3,10 +3,11 @@ const mongoose = require('mongoose'); |
| 3 | const Schema = mongoose.Schema; | 3 | const Schema = mongoose.Schema; |
| 4 | 4 | ||
| 5 | const ProfileSchema = new Schema({ | 5 | const ProfileSchema = new Schema({ |
| 6 | - userId : { type : String, required : true, ref : 'User', }, | 6 | + userId : { type : String, required : true, ref : 'User', lowercase : true, }, |
| 7 | userNm : { type : String, required : true, }, | 7 | userNm : { type : String, required : true, }, |
| 8 | - userAge : { type : Number, required : true, }, | 8 | + birth : { type : String, required : true, }, |
| 9 | contact : { type : String, required : true, }, | 9 | contact : { type : String, required : true, }, |
| 10 | + useYn : { type : String, default : 'Y', }, | ||
| 10 | deviceToken : { type : String, default : null, }, | 11 | deviceToken : { type : String, default : null, }, |
| 11 | }); | 12 | }); |
| 12 | 13 | ||
| ... | @@ -14,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) { | ... | @@ -14,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) { |
| 14 | return this.findOne({ userId }); | 15 | return this.findOne({ userId }); |
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | -ProfileSchema.methods.updateUserContact = function(contact) { | 18 | +ProfileSchema.methods.setUseYn = function(useYn) { |
| 18 | - this.contact = contact; | 19 | + this.useYn = useYn; |
| 19 | }; | 20 | }; |
| 20 | 21 | ||
| 21 | -ProfileSchema.methods.updateUserAge = function() { | 22 | +ProfileSchema.methods.updateProfileInfo = function({ userNm, birth, contact }) { |
| 22 | - this.userAge = this.userAge + 1; | 23 | + if(userNm) { this.userNm = userNm } |
| 24 | + if(birth) { this.birth = birth } | ||
| 25 | + if(contact) { this.contact = contact } | ||
| 23 | }; | 26 | }; |
| 24 | 27 | ||
| 25 | ProfileSchema.methods.updateDeviceToken = function(deviceToken) { | 28 | ProfileSchema.methods.updateDeviceToken = function(deviceToken) { | ... | ... |
| ... | @@ -5,7 +5,6 @@ const Schema = mongoose.Schema; | ... | @@ -5,7 +5,6 @@ const Schema = mongoose.Schema; |
| 5 | const TakeMedicineHistorySchema = new Schema ({ | 5 | const TakeMedicineHistorySchema = new Schema ({ |
| 6 | takeDate : { | 6 | takeDate : { |
| 7 | type : Date, | 7 | type : Date, |
| 8 | - required : true, | ||
| 9 | default : Date.now, | 8 | default : Date.now, |
| 10 | }, | 9 | }, |
| 11 | bmId : { | 10 | bmId : { |
| ... | @@ -13,9 +12,10 @@ const TakeMedicineHistorySchema = new Schema ({ | ... | @@ -13,9 +12,10 @@ const TakeMedicineHistorySchema = new Schema ({ |
| 13 | ref : 'BottleMedicine', | 12 | ref : 'BottleMedicine', |
| 14 | required : true, | 13 | required : true, |
| 15 | }, | 14 | }, |
| 16 | - temperature : { type : Number, default : 0 }, | 15 | + temperature : { type : Number, default : 0, }, |
| 17 | - humidity : { type : Number, default : 0 }, | 16 | + humidity : { type : Number, default : 0, }, |
| 18 | - balance : { type : Number, default : 0 }, | 17 | + dosage : { type : Number, default : 0, }, |
| 18 | + balance : { type : Number, default : 0, }, | ||
| 19 | }); | 19 | }); |
| 20 | 20 | ||
| 21 | 21 | ... | ... |
| ... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); | ... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); |
| 5 | const Schema = mongoose.Schema; | 5 | const Schema = mongoose.Schema; |
| 6 | 6 | ||
| 7 | const UserSchema = new Schema ({ | 7 | const UserSchema = new Schema ({ |
| 8 | - userId : { type: String, required : true, unique : true, lowercase : true, }, | 8 | + userId : { type: String, required : true, unique : true, lowercase : true, trim : true }, |
| 9 | - hashedPassword : { type : String, required : true }, | 9 | + hashedPassword : { type : String, required : true, }, |
| 10 | - userTypeCd : { type : String, required : true, default : 'NORMAL' }, | 10 | + userTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, |
| 11 | + authTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, | ||
| 11 | useYn : { type : String, default : 'W', required : true, }, | 12 | useYn : { type : String, default : 'W', required : true, }, |
| 12 | }); | 13 | }); |
| 13 | 14 | ... | ... |
| ... | @@ -3,11 +3,136 @@ | ... | @@ -3,11 +3,136 @@ |
| 3 | * 21/09/14 | 3 | * 21/09/14 |
| 4 | * Author : 박권수 | 4 | * Author : 박권수 |
| 5 | * 배치 시스템 | 5 | * 배치 시스템 |
| 6 | - * 1) 매년 지나면 프로필의 Age를 +1 | 6 | + * 1) Dosage에 따라, Push Notification 발송 |
| 7 | - * 2) Dosage에 따라, Push Notification 발송 | ||
| 8 | */ | 7 | */ |
| 9 | 8 | ||
| 10 | const cron = require('node-cron'); | 9 | const cron = require('node-cron'); |
| 10 | +const fs = require('fs'); | ||
| 11 | 11 | ||
| 12 | const Profile = require('../models/profile'); | 12 | const Profile = require('../models/profile'); |
| 13 | -const BottleMedicine = require('../models/bottleMedicine'); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 13 | +const User = require('../models/user'); | ||
| 14 | +const Hub = require('../models/hub'); | ||
| 15 | +const Bottle = require('../models/bottle'); | ||
| 16 | +const BottleMedicine = require('../models/bottleMedicine'); | ||
| 17 | +const Medicine = require('../models/medicine'); | ||
| 18 | + | ||
| 19 | +const updateMedicineInfo = require('../lib/UpdatingMedicineInfo'); | ||
| 20 | +const { sendPushMessage } = require('./FCM'); | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +//매월 1일 0시 0분에 약 정보 업데이트 | ||
| 24 | +exports.updateMedicineData = async () => { | ||
| 25 | + cron.schedule('0 0 0 1 * *', () => { | ||
| 26 | + updateMedicineInfo.updateMedicineInfo(); | ||
| 27 | + }, { | ||
| 28 | + timezone : 'Asia/Tokyo', | ||
| 29 | + }); | ||
| 30 | +}; | ||
| 31 | + | ||
| 32 | +//매주 일요일마다 불필요한 qrcode 제거 | ||
| 33 | +exports.removeQrCode = () => { | ||
| 34 | + cron.schedule('0 0 0 * * 0', () => { | ||
| 35 | + // eslint-disable-next-line no-undef | ||
| 36 | + const qrDir = process.env.QR_DIR; | ||
| 37 | + fs.rm(qrDir, { recursive : true, force : true, }, () => { | ||
| 38 | + fs.mkdir(qrDir, (err) => { if(err) console.log(err) }); | ||
| 39 | + }); | ||
| 40 | + }, { | ||
| 41 | + timezone : 'Asia/Tokyo', | ||
| 42 | + }); | ||
| 43 | +}; | ||
| 44 | + | ||
| 45 | +//dosage에 따라, Push Notification을 발송한다. | ||
| 46 | +//아침 8시, 점심 12시, 저녁 6시에 한번씩 발송 | ||
| 47 | +exports.pushNotifyByDosage = async() => { | ||
| 48 | + | ||
| 49 | + //매일 아침 8시 : 복용량 상관 없이 보냄 | ||
| 50 | + cron.schedule('0 0 8 * * *', async () => { | ||
| 51 | + const bottleMedicineList = await BottleMedicine.find({ useYn : 'Y', dosage : { $gte : 1 } }); | ||
| 52 | + bottleMedicineList.forEach(async bottleMedicine => { | ||
| 53 | + const bottle = await Bottle.findOne({ bottleId : bottleMedicine.bottleId }); | ||
| 54 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
| 55 | + const user = await User.findOne({ userId : hub.userId, useYn : 'Y' }); | ||
| 56 | + | ||
| 57 | + if(user) { | ||
| 58 | + const profile = await Profile.findOne({ userId : user.userId }); | ||
| 59 | + const { deviceToken } = profile; | ||
| 60 | + | ||
| 61 | + if(deviceToken) { | ||
| 62 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
| 63 | + pushNotify({ | ||
| 64 | + deviceToken, | ||
| 65 | + title : '약 복용 시간입니다', | ||
| 66 | + body : medicine.name + '을 복용하셔야 합니다.', | ||
| 67 | + }); | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + }); | ||
| 71 | + }, { | ||
| 72 | + timezone : 'Asia/Tokyo', | ||
| 73 | + }); | ||
| 74 | + | ||
| 75 | + | ||
| 76 | + //매일 점심 12시 : 복용량이 3인 환자들만 | ||
| 77 | + cron.schedule('0 0 12 * * *', async () => { | ||
| 78 | + const bottleMedicineList = await BottleMedicine.find({ useYn : 'Y', dosage : { $gte : 3 } }); | ||
| 79 | + bottleMedicineList.forEach(async bottleMedicine => { | ||
| 80 | + const bottle = await Bottle.findOne({ bottleId : bottleMedicine.bottleId }); | ||
| 81 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
| 82 | + const user = await User.findOne({ userId : hub.userId, useYn : 'Y' }); | ||
| 83 | + | ||
| 84 | + if(user) { | ||
| 85 | + const profile = await Profile.findOne({ userId : user.userId }); | ||
| 86 | + const { deviceToken } = profile; | ||
| 87 | + | ||
| 88 | + if(deviceToken) { | ||
| 89 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
| 90 | + pushNotify({ | ||
| 91 | + deviceToken, | ||
| 92 | + title : '약 복용 시간입니다', | ||
| 93 | + body : medicine.name + '을 복용하셔야 합니다.', | ||
| 94 | + }); | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + }); | ||
| 98 | + }, { | ||
| 99 | + timezone : 'Asia/Tokyo', | ||
| 100 | + }); | ||
| 101 | + | ||
| 102 | + | ||
| 103 | + //매일 저녁 6시 | ||
| 104 | + cron.schedule('0 0 18 * * *', async () => { | ||
| 105 | + const bottleMedicineList = await BottleMedicine.find({ useYn : 'Y', dosage : { $gte : 2 } }); | ||
| 106 | + bottleMedicineList.forEach(async bottleMedicine => { | ||
| 107 | + const bottle = await Bottle.findOne({ bottleId : bottleMedicine.bottleId }); | ||
| 108 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
| 109 | + const user = await User.findOne({ userId : hub.userId, useYn : 'Y' }); | ||
| 110 | + | ||
| 111 | + if(user) { | ||
| 112 | + const profile = await Profile.findOne({ userId : user.userId }); | ||
| 113 | + const { deviceToken } = profile; | ||
| 114 | + | ||
| 115 | + if(deviceToken) { | ||
| 116 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
| 117 | + pushNotify({ | ||
| 118 | + deviceToken, | ||
| 119 | + title : '약 복용 시간입니다', | ||
| 120 | + body : medicine.name + '을 복용하셔야 합니다.', | ||
| 121 | + }); | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + }); | ||
| 125 | + }, { | ||
| 126 | + timezone : 'Asia/Tokyo', | ||
| 127 | + }); | ||
| 128 | + | ||
| 129 | +}; | ||
| 130 | + | ||
| 131 | +const pushNotify = ({ deviceToken, title, body }) => { | ||
| 132 | + //toDo : deviceToken을 받아서 push notification을 발송하는 함수 | ||
| 133 | + sendPushMessage({ | ||
| 134 | + deviceToken, | ||
| 135 | + title, | ||
| 136 | + body, | ||
| 137 | + }); | ||
| 138 | +}; | ... | ... |
| 1 | -const Bottle = require('../models/bottle'); | ||
| 2 | const BottleMedicine = require('../models/bottleMedicine'); | 1 | const BottleMedicine = require('../models/bottleMedicine'); |
| 3 | const TakeMedicineHist = require('../models/takeMedicineHistory'); | 2 | const TakeMedicineHist = require('../models/takeMedicineHistory'); |
| 4 | 3 | ||
| 4 | + | ||
| 5 | //message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수 | 5 | //message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수 |
| 6 | exports.dataPublish = async (topic, message) => { | 6 | exports.dataPublish = async (topic, message) => { |
| 7 | - //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴 | 7 | + if(message.includes('weight')) { |
| 8 | - const data = await factoring(topic, message); | 8 | + //무게 갱신 |
| 9 | - //가공된 데이터를 bottleId의 약병에 업데이트 | 9 | + const result = await updateBottleMedicineWeight(topic, message); |
| 10 | - await bottleInfoUpdate(data); | 10 | + |
| 11 | - //가공된 데이터를 메시지로 만들어 topic과 message 리턴 | 11 | + return result; |
| 12 | - const result = await transPublishingTopicAndMessage(data.bottleId); | 12 | + |
| 13 | + } else { | ||
| 14 | + //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴 | ||
| 15 | + const data = await factoring(topic, message); | ||
| 16 | + //가공된 데이터를 bottleId의 약병에 업데이트 | ||
| 17 | + await bottleInfoUpdate(data); | ||
| 18 | + //가공된 데이터를 메시지로 만들어 topic과 message 리턴 | ||
| 19 | + const result = await transPublishingTopicAndMessage(data.bottleId); | ||
| 13 | 20 | ||
| 14 | - return result; | 21 | + return result; |
| 15 | 22 | ||
| 23 | + } | ||
| 16 | }; | 24 | }; |
| 17 | 25 | ||
| 18 | //Hub topic : bottle/bottleId | 26 | //Hub topic : bottle/bottleId |
| 19 | -//Hub로부터 받은 message : 개폐여부/온도/습도/초음파센서 | 27 | +//Hub로부터 받은 message : 개폐여부/온도/습도/무게센서 |
| 20 | const factoring = async (topic, message) => { | 28 | const factoring = async (topic, message) => { |
| 21 | const bottleId = parseInt(topic.split('/')[1]); | 29 | const bottleId = parseInt(topic.split('/')[1]); |
| 22 | const data = message.split('/'); | 30 | const data = message.split('/'); |
| 23 | - let [isOpen, temperature, humidity, balance] = data; | 31 | + const [isOpen, temperature, totalWeight, humidity] = data; |
| 24 | 32 | ||
| 25 | - if(isOpen === '0') | ||
| 26 | - balance = await balanceFactoring(balance); | ||
| 27 | - else balance = '-1'; | ||
| 28 | - | ||
| 29 | return { | 33 | return { |
| 30 | bottleId, | 34 | bottleId, |
| 31 | isOpen, | 35 | isOpen, |
| 32 | - openDate : new Date(), | 36 | + temperature, |
| 33 | - temperature, | ||
| 34 | humidity, | 37 | humidity, |
| 35 | - balance | 38 | + totalWeight, |
| 36 | }; | 39 | }; |
| 37 | 40 | ||
| 38 | } | 41 | } |
| 39 | 42 | ||
| 40 | -const balanceFactoring = (balance) => { | ||
| 41 | - const max = 10; //Digital Lead Sensor Maximum Value | ||
| 42 | - const slicingBalance = max / 5; | ||
| 43 | - | ||
| 44 | - if(parseInt(balance) < slicingBalance || parseInt(balance) > max * 2) | ||
| 45 | - return '80'; | ||
| 46 | - else if(parseInt(balance) < slicingBalance * 2) | ||
| 47 | - return '60'; | ||
| 48 | - else if(parseInt(balance) < slicingBalance * 3) | ||
| 49 | - return '40'; | ||
| 50 | - else if(parseInt(balance) < slicingBalance * 4) | ||
| 51 | - return '20'; | ||
| 52 | - else return '0'; | ||
| 53 | - | ||
| 54 | -} | ||
| 55 | - | ||
| 56 | //bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다. | 43 | //bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다. |
| 57 | const bottleInfoUpdate = async(data) => { | 44 | const bottleInfoUpdate = async(data) => { |
| 58 | - let { bottleId, isOpen, openDate, temperature, humidity, balance } = data; | 45 | + let { bottleId, isOpen, temperature, humidity, totalWeight } = data; |
| 59 | - | ||
| 60 | - bottleId = parseInt(bottleId); | ||
| 61 | - isOpen = parseInt(isOpen); | ||
| 62 | - temperature = parseFloat(temperature); | ||
| 63 | - humidity = parseFloat(humidity); | ||
| 64 | - balance = parseInt(balance); | ||
| 65 | - | ||
| 66 | - const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | ||
| 67 | 46 | ||
| 68 | - if(bottleMedicine) { | 47 | + if(!parseInt(isOpen)) { |
| 69 | - if(isOpen) { | 48 | + bottleId = parseInt(bottleId); |
| 70 | - const takeMedicineHist = new TakeMedicineHist({ | 49 | + temperature = parseFloat(temperature); |
| 71 | - takeDate : openDate, | 50 | + humidity = parseFloat(humidity); |
| 72 | - bmId : bottleMedicine._id, | 51 | + totalWeight = parseFloat(totalWeight); |
| 73 | - temperature, | 52 | + |
| 74 | - humidity, | 53 | + const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); |
| 75 | - balance, | 54 | + |
| 76 | - }); | 55 | + if(bottleMedicine) { |
| 77 | - takeMedicineHist.save(); | 56 | + const lastTotalWeight = parseFloat(bottleMedicine.totalWeight); |
| 57 | + const { eachWeight } = bottleMedicine; | ||
| 58 | + | ||
| 59 | + const dosage = Math.round((lastTotalWeight - totalWeight) / parseFloat(eachWeight)); | ||
| 60 | + | ||
| 61 | + if(dosage > 0) { | ||
| 62 | + const balance = Math.round(totalWeight / parseFloat(eachWeight)); | ||
| 63 | + | ||
| 64 | + const takeMedicineHist = new TakeMedicineHist({ | ||
| 65 | + bmId : bottleMedicine._id, | ||
| 66 | + temperature, | ||
| 67 | + humidity, | ||
| 68 | + dosage, | ||
| 69 | + balance, | ||
| 70 | + }); | ||
| 71 | + await takeMedicineHist.save(); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + await bottleMedicine.setTotalWeight(totalWeight); | ||
| 75 | + await bottleMedicine.save(); | ||
| 78 | } | 76 | } |
| 79 | } | 77 | } |
| 80 | } | 78 | } |
| ... | @@ -84,11 +82,13 @@ const transPublishingTopicAndMessage = async(bottleId) => { | ... | @@ -84,11 +82,13 @@ const transPublishingTopicAndMessage = async(bottleId) => { |
| 84 | const topic = 'bottle/' + bottleId + '/stb'; | 82 | const topic = 'bottle/' + bottleId + '/stb'; |
| 85 | 83 | ||
| 86 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | 84 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); |
| 87 | - const takeMedicineHist = await TakeMedicineHist.find({ | 85 | + const takeMedicineHistList = await TakeMedicineHist.find({ |
| 88 | bmId : bottleMedicine._id | 86 | bmId : bottleMedicine._id |
| 89 | - }).sort((a, b) => a.takeDate < b.takeDate)[0]; | 87 | + }).sort({ takeDate : 'desc' }).limit(1); |
| 90 | 88 | ||
| 91 | - const message = 'res/' + await transDate(takeMedicineHist.takeDate) + '/' + bottleMedicine.dosage; | 89 | + const message = takeMedicineHistList && takeMedicineHistList[0] ? |
| 90 | + 'res/' + await transDate(takeMedicineHistList[0].takeDate) + '/' + takeMedicineHistList[0].dosage : | ||
| 91 | + 'res/' + await transDate(new Date()) + '/' + 0; | ||
| 92 | 92 | ||
| 93 | return { | 93 | return { |
| 94 | topic, | 94 | topic, |
| ... | @@ -100,4 +100,23 @@ const transPublishingTopicAndMessage = async(bottleId) => { | ... | @@ -100,4 +100,23 @@ const transPublishingTopicAndMessage = async(bottleId) => { |
| 100 | const transDate = (date) => { | 100 | const transDate = (date) => { |
| 101 | return (date.getMonth() + 1 < 10 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1)) | 101 | return (date.getMonth() + 1 < 10 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1)) |
| 102 | + (date.getDate() < 10 ? '0' + String(date.getDate()) : String(date.getDate())); | 102 | + (date.getDate() < 10 ? '0' + String(date.getDate()) : String(date.getDate())); |
| 103 | -} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 103 | +}; | ||
| 104 | + | ||
| 105 | + | ||
| 106 | +//무게센서를 이용하여 데이터값을 갱신하는 함수 | ||
| 107 | +const updateBottleMedicineWeight = async (topic, message) => { | ||
| 108 | + const bottleId = parseInt(topic.split('/')[1]); | ||
| 109 | + //message = weight/무게 | ||
| 110 | + const totalWeight = parseFloat(message.split('/')[1]); | ||
| 111 | + | ||
| 112 | + const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | ||
| 113 | + const totalDosage = parseInt(bottleMedicine.totalDosage); | ||
| 114 | + //받은 값으로 총 무게를 설정한 이후, 총 무게 / 총 복용량으로 개별 무게를 설정한다. | ||
| 115 | + await bottleMedicine.setTotalWeight(totalWeight); | ||
| 116 | + await bottleMedicine.setEachWeight(totalWeight / totalDosage); | ||
| 117 | + | ||
| 118 | + await bottleMedicine.save(); | ||
| 119 | + | ||
| 120 | + return null; | ||
| 121 | + | ||
| 122 | +}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
server/src/util/FCM.js
0 → 100644
| 1 | +// const fcm = require('firebase-admin'); | ||
| 2 | +const axios = require('axios'); | ||
| 3 | + | ||
| 4 | +// exports.initializeFCM = () => { | ||
| 5 | +// fcm.initializeApp({ | ||
| 6 | +// credential: fcm.credential.applicationDefault(), | ||
| 7 | +// }); | ||
| 8 | +// }; | ||
| 9 | + | ||
| 10 | +exports.sendPushMessage = async ({ deviceToken, title, body }) => { | ||
| 11 | + // const notifyMessage = { | ||
| 12 | + // notification : { | ||
| 13 | + // title, | ||
| 14 | + // body, | ||
| 15 | + // }, | ||
| 16 | + // token : deviceToken, | ||
| 17 | + // }; | ||
| 18 | + // fcm.messaging().send(notifyMessage).then(res => { | ||
| 19 | + // console.log(res); | ||
| 20 | + // }).catch(e => { | ||
| 21 | + // console.log(e); | ||
| 22 | + // }); | ||
| 23 | + | ||
| 24 | + const message = { | ||
| 25 | + to : deviceToken, | ||
| 26 | + notification : { | ||
| 27 | + title, | ||
| 28 | + body, | ||
| 29 | + priority : 'high', | ||
| 30 | + }, | ||
| 31 | + data : null, | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + const url = 'https://fcm.googleapis.com/fcm/send'; | ||
| 35 | + const result = await axios.post(url, message, { | ||
| 36 | + headers : { | ||
| 37 | + 'Content-Type' : 'application/json', | ||
| 38 | + // eslint-disable-next-line no-undef | ||
| 39 | + 'Authorization' : `key=${process.env.FCM_KEY}`, | ||
| 40 | + }, | ||
| 41 | + }); | ||
| 42 | + | ||
| 43 | + console.log(result.data); | ||
| 44 | +}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
server/src/util/GoogleCloudStorage.js
0 → 100644
| 1 | +const { Storage } = require('@google-cloud/storage'); | ||
| 2 | +const fs = require('fs'); | ||
| 3 | + | ||
| 4 | +const storage = new Storage(); | ||
| 5 | +const GoogleStorageUrl = 'https://storage.googleapis.com/'; | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +//의사 아이디, 업로드할 파일명, 업로드할 파일 경로를 인자로 받아, File을 GCS에 업로드 후 GCS 주소를 반환 | ||
| 9 | +exports.uploadDoctorLicense = async ({ userId, fileName, filePath }) => { | ||
| 10 | + const destination = userId + '_' + fileName; | ||
| 11 | + try { | ||
| 12 | + const result = await storage.bucket('doctor-info').upload(filePath, { | ||
| 13 | + destination, | ||
| 14 | + }); | ||
| 15 | + | ||
| 16 | + const doctorLicenseUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | ||
| 17 | + | ||
| 18 | + return doctorLicenseUrl; | ||
| 19 | + } catch(e) { | ||
| 20 | + console.log(e); | ||
| 21 | + return null; | ||
| 22 | + } | ||
| 23 | +}; | ||
| 24 | + | ||
| 25 | +//의사 정보를 인자로 받아 해당 Doctor License의 Signed URL을 반환 | ||
| 26 | +exports.viewDoctorLicense = async ({ doctorInfo }) => { | ||
| 27 | + try { | ||
| 28 | + const fileName = doctorInfo.info.doctorLicense.split('/').pop(); | ||
| 29 | + const file = storage.bucket('doctor-info').file(fileName); | ||
| 30 | + const option = { | ||
| 31 | + version : 'v4', | ||
| 32 | + expires : Date.now() + 1000 * 60 * 15, | ||
| 33 | + action : 'read', | ||
| 34 | + }; | ||
| 35 | + | ||
| 36 | + const [signedUrl] = file ? await file.getSignedUrl(option) : [null]; | ||
| 37 | + | ||
| 38 | + return signedUrl; | ||
| 39 | + } catch(e) { | ||
| 40 | + console.log(e); | ||
| 41 | + return null; | ||
| 42 | + } | ||
| 43 | +}; | ||
| 44 | + | ||
| 45 | +//의사 ID, 약 ID, 복용량을 인자로 받아, QR Code를 생성 | ||
| 46 | +exports.uploadQrCode = async ({ directory, qrCodeFileName }) => { | ||
| 47 | + const destination = qrCodeFileName; | ||
| 48 | + try { | ||
| 49 | + //파일을 GCS에 업로드 | ||
| 50 | + const result = await storage.bucket('prescribe-medicine-qrcode').upload(directory + '/' + qrCodeFileName, { | ||
| 51 | + destination | ||
| 52 | + }); | ||
| 53 | + | ||
| 54 | + //업로드 후 파일 삭제 | ||
| 55 | + fs.rm(directory + '/' + qrCodeFileName, () => {}); | ||
| 56 | + | ||
| 57 | + const qrCodeUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | ||
| 58 | + | ||
| 59 | + return qrCodeUrl; | ||
| 60 | + } catch(e) { | ||
| 61 | + console.log(e); | ||
| 62 | + return null; | ||
| 63 | + } | ||
| 64 | +}; | ||
| 65 | + | ||
| 66 | +//생성된 QR코드의 signedUrl을 가져옴 | ||
| 67 | +exports.getQrCodeUrl = async ({ qrCodeFileName }) => { | ||
| 68 | + try { | ||
| 69 | + const fileName = qrCodeFileName; | ||
| 70 | + const file = storage.bucket('prescribe-medicine-qrcode').file(fileName); | ||
| 71 | + const option = { | ||
| 72 | + version : 'v4', | ||
| 73 | + expires : Date.now() + 1000 * 60 * 15, | ||
| 74 | + action : 'read', | ||
| 75 | + }; | ||
| 76 | + | ||
| 77 | + const [signedUrl] = file ? await file.getSignedUrl(option) : [null]; | ||
| 78 | + | ||
| 79 | + return signedUrl; | ||
| 80 | + } catch(e) { | ||
| 81 | + console.log(e); | ||
| 82 | + return null; | ||
| 83 | + } | ||
| 84 | +}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | -const mqtt = require('mqtt') | 1 | +const mqtt = require('mqtt'); |
| 2 | -const clientList = [] | 2 | +const clientList = []; |
| 3 | 3 | ||
| 4 | exports.mqttOn = async (hosting, foo) => { | 4 | exports.mqttOn = async (hosting, foo) => { |
| 5 | const filterIndex = clientList.findIndex(client => { | 5 | const filterIndex = clientList.findIndex(client => { |
| ... | @@ -9,27 +9,29 @@ exports.mqttOn = async (hosting, foo) => { | ... | @@ -9,27 +9,29 @@ exports.mqttOn = async (hosting, foo) => { |
| 9 | }) | 9 | }) |
| 10 | 10 | ||
| 11 | if(filterIndex === -1) { | 11 | if(filterIndex === -1) { |
| 12 | - const client = mqtt.connect(hosting) | 12 | + const client = mqtt.connect(hosting); |
| 13 | - clientList.push(client) | 13 | + clientList.push(client); |
| 14 | 14 | ||
| 15 | client.on('connect', () => { | 15 | client.on('connect', () => { |
| 16 | console.log('Hub connected: ', client.connected) | 16 | console.log('Hub connected: ', client.connected) |
| 17 | - }) | 17 | + }); |
| 18 | 18 | ||
| 19 | client.on('message', async (topic, message) => { | 19 | client.on('message', async (topic, message) => { |
| 20 | - const result = await foo(topic, message.toString()) | 20 | + const result = await foo(topic, message.toString()); |
| 21 | console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m') | 21 | console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m') |
| 22 | - this.mqttPublishMessage(client, result) | 22 | + if(result) this.mqttPublishMessage(client, result); |
| 23 | - }) | 23 | + }); |
| 24 | 24 | ||
| 25 | - return client | 25 | + return client; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | - return clientList[filterIndex] | 28 | + return clientList[filterIndex]; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | exports.mqttSubscribe = (client, topic) => { | 31 | exports.mqttSubscribe = (client, topic) => { |
| 32 | - client.subscribe(topic) | 32 | + client.subscribe(topic, () => { |
| 33 | + console.log('suscribe', topic); | ||
| 34 | + }); | ||
| 33 | } | 35 | } |
| 34 | 36 | ||
| 35 | exports.mqttPublishMessage = (client, { topic, message }) => { | 37 | exports.mqttPublishMessage = (client, { topic, message }) => { |
| ... | @@ -49,10 +51,10 @@ exports.mqttOff = (hosting) => { | ... | @@ -49,10 +51,10 @@ exports.mqttOff = (hosting) => { |
| 49 | return (client.options.clientId === hosting.clientId | 51 | return (client.options.clientId === hosting.clientId |
| 50 | && client.options.host === hosting.host | 52 | && client.options.host === hosting.host |
| 51 | && client.options.port === hosting.port) | 53 | && client.options.port === hosting.port) |
| 52 | - }) | 54 | + }); |
| 53 | 55 | ||
| 54 | if(filterIndex !== -1) { | 56 | if(filterIndex !== -1) { |
| 55 | - clientList[filterIndex].end() | 57 | + clientList[filterIndex].end(); |
| 56 | - clientList.splice(filterIndex, 1) | 58 | + clientList.splice(filterIndex, 1); |
| 57 | } | 59 | } |
| 58 | } | 60 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | -const Mqtt = require('../lib/MqttModule'); | 1 | +const Mqtt = require('./MqttModule'); |
| 2 | -const DataProcess = require('../lib/DataProcess'); | 2 | +const DataProcess = require('./DataProcess'); |
| 3 | const Hub = require('../models/hub'); | 3 | const Hub = require('../models/hub'); |
| 4 | const Bottle = require('../models/bottle'); | 4 | const Bottle = require('../models/bottle'); |
| 5 | 5 | ... | ... |
server/src/util/QrCodeUtil.js
0 → 100644
| 1 | +const QrCode = require('qrcode'); | ||
| 2 | +const moment = require('moment'); | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +exports.generateQrCode_prescribe = async ({ medicine, dailyDosage, totalDosage, patientId, doctorId }) => { | ||
| 6 | + // eslint-disable-next-line no-undef | ||
| 7 | + const directory = process.env.QR_DIR; | ||
| 8 | + | ||
| 9 | + const now = moment().format('YYYY-MM-DD_HH:mm'); | ||
| 10 | + const qrCodeFileName = `${now}_${doctorId}_${patientId}_${medicine.medicineId}_${dailyDosage}_${totalDosage}.png`; | ||
| 11 | + | ||
| 12 | + try { | ||
| 13 | + await QrCode.toFile( | ||
| 14 | + directory + '/' + qrCodeFileName, | ||
| 15 | + `${medicine.medicineId}/${dailyDosage}/${totalDosage}/${doctorId}/${patientId}/${medicine.name}`, | ||
| 16 | + { | ||
| 17 | + color : { | ||
| 18 | + dark : '#337DFF', | ||
| 19 | + light : '#FFF' | ||
| 20 | + }, | ||
| 21 | + } | ||
| 22 | + ); | ||
| 23 | + | ||
| 24 | + return { | ||
| 25 | + directory, | ||
| 26 | + qrCodeFileName, | ||
| 27 | + }; | ||
| 28 | + | ||
| 29 | + } catch(e) { | ||
| 30 | + console.log(e); | ||
| 31 | + return null; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | +}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
| ... | @@ -14,6 +14,7 @@ | ... | @@ -14,6 +14,7 @@ |
| 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 | "moment": "^2.29.1", |
| 17 | + "qrcode": "^1.4.4", | ||
| 17 | "react": "^17.0.2", | 18 | "react": "^17.0.2", |
| 18 | "react-dom": "^17.0.2", | 19 | "react-dom": "^17.0.2", |
| 19 | "react-router-dom": "^5.2.0", | 20 | "react-router-dom": "^5.2.0", |
| ... | @@ -52,6 +53,7 @@ | ... | @@ -52,6 +53,7 @@ |
| 52 | ] | 53 | ] |
| 53 | }, | 54 | }, |
| 54 | "devDependencies": { | 55 | "devDependencies": { |
| 56 | + "@types/qrcode": "^1.4.1", | ||
| 55 | "@types/react-router-dom": "^5.1.8", | 57 | "@types/react-router-dom": "^5.1.8", |
| 56 | "@types/styled-components": "^5.1.12", | 58 | "@types/styled-components": "^5.1.12", |
| 57 | "@types/validator": "^13.6.3", | 59 | "@types/validator": "^13.6.3", | ... | ... |
web/public/static/img/apiLicense.png
0 → 100644
30.3 KB
web/public/static/img/next.png
0 → 100644
404 Bytes
web/public/static/img/prev.png
0 → 100644
392 Bytes
| 1 | import { client } from "./client"; | 1 | import { client } from "./client"; |
| 2 | 2 | ||
| 3 | export default { | 3 | export default { |
| 4 | - register : (Data : any) => { | 4 | + register : (Data : FormData) => { |
| 5 | return client.post('/auth/register', Data); | 5 | return client.post('/auth/register', Data); |
| 6 | }, | 6 | }, |
| 7 | 7 | ||
| 8 | + searchHospital : (hospitalNm : string, page : number) => { | ||
| 9 | + return client.get('/auth/hospital', { | ||
| 10 | + params : { | ||
| 11 | + hospitalNm, | ||
| 12 | + page, | ||
| 13 | + }, | ||
| 14 | + }); | ||
| 15 | + }, | ||
| 16 | + | ||
| 8 | registerDoctor : (Data : any) => { | 17 | registerDoctor : (Data : any) => { |
| 9 | return client.post('/auth/register/doctor', Data); | 18 | return client.post('/auth/register/doctor', Data); |
| 10 | }, | 19 | }, | ... | ... |
| ... | @@ -44,8 +44,8 @@ export default { | ... | @@ -44,8 +44,8 @@ export default { |
| 44 | }, | 44 | }, |
| 45 | }); | 45 | }); |
| 46 | }, | 46 | }, |
| 47 | - searchPatientById : (token : RecoilState<any>, patientId : string) => { | 47 | + searchPatientByContact : (token : RecoilState<any>, contact : string) => { |
| 48 | - return client.get(`/doctor/patient/search/${patientId}`, { | 48 | + return client.get(`/doctor/patient/search/${contact}`, { |
| 49 | headers : { | 49 | headers : { |
| 50 | Authorization : token, | 50 | Authorization : token, |
| 51 | }, | 51 | }, |
| ... | @@ -65,4 +65,11 @@ export default { | ... | @@ -65,4 +65,11 @@ export default { |
| 65 | }, | 65 | }, |
| 66 | }); | 66 | }); |
| 67 | }, | 67 | }, |
| 68 | + prescribeMedicine : (token : RecoilState<any>, Data : any) => { | ||
| 69 | + return client.post('/doctor/prescribe', Data, { | ||
| 70 | + headers : { | ||
| 71 | + Authorization : token, | ||
| 72 | + }, | ||
| 73 | + }); | ||
| 74 | + }, | ||
| 68 | }; | 75 | }; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -9,6 +9,13 @@ export default { | ... | @@ -9,6 +9,13 @@ export default { |
| 9 | }, | 9 | }, |
| 10 | }); | 10 | }); |
| 11 | }, | 11 | }, |
| 12 | + getDoctorSecReqList : (token : RecoilState<any>) => { | ||
| 13 | + return client.get('/manage/doctor/secession', { | ||
| 14 | + headers : { | ||
| 15 | + Authorization : token, | ||
| 16 | + }, | ||
| 17 | + }); | ||
| 18 | + }, | ||
| 12 | getDoctorRegReqDetail : (token : RecoilState<any>, doctorId : string) => { | 19 | getDoctorRegReqDetail : (token : RecoilState<any>, doctorId : string) => { |
| 13 | return client.get(`/manage/doctor/${doctorId}`, { | 20 | return client.get(`/manage/doctor/${doctorId}`, { |
| 14 | headers : { | 21 | headers : { |
| ... | @@ -17,14 +24,21 @@ export default { | ... | @@ -17,14 +24,21 @@ export default { |
| 17 | }); | 24 | }); |
| 18 | }, | 25 | }, |
| 19 | acceptDoctorRegReq : (token : RecoilState<any>, Data : any) => { | 26 | acceptDoctorRegReq : (token : RecoilState<any>, Data : any) => { |
| 20 | - return client.post('/manage/doctor/accept', Data, { | 27 | + return client.patch('/manage/doctor/accept', Data, { |
| 21 | headers : { | 28 | headers : { |
| 22 | Authorization : token, | 29 | Authorization : token, |
| 23 | }, | 30 | }, |
| 24 | }); | 31 | }); |
| 25 | }, | 32 | }, |
| 26 | rejectDoctorRegReq : (token : RecoilState<any>, Data : any) => { | 33 | rejectDoctorRegReq : (token : RecoilState<any>, Data : any) => { |
| 27 | - return client.post('/manage/doctor/reject', Data, { | 34 | + return client.patch('/manage/doctor/reject', Data, { |
| 35 | + headers : { | ||
| 36 | + Authorization : token, | ||
| 37 | + }, | ||
| 38 | + }); | ||
| 39 | + }, | ||
| 40 | + acceptDoctorSecReq : (token : RecoilState<any>, Data : any) => { | ||
| 41 | + return client.patch('/manage/doctor/secession', Data, { | ||
| 28 | headers : { | 42 | headers : { |
| 29 | Authorization : token, | 43 | Authorization : token, |
| 30 | }, | 44 | }, | ... | ... |
web/src/components/Footer/FooterStyled.tsx
0 → 100644
| 1 | +import styled from 'styled-components'; | ||
| 2 | + | ||
| 3 | +export const Container = styled.div ` | ||
| 4 | + width : 100%; | ||
| 5 | + position : relative; | ||
| 6 | + | ||
| 7 | + margin : 5px 0; | ||
| 8 | + padding : 20px 5px; | ||
| 9 | + | ||
| 10 | + border-top : 1px solid #ddd; | ||
| 11 | + | ||
| 12 | + display : flex; | ||
| 13 | + flex-direction : column; | ||
| 14 | +`; | ||
| 15 | + | ||
| 16 | +export const TermsWrapper = styled.div ` | ||
| 17 | + width : 100%: | ||
| 18 | + | ||
| 19 | + display : flex; | ||
| 20 | + flex-direction : row; | ||
| 21 | + | ||
| 22 | + padding : 0 0 20px 0; | ||
| 23 | + margin : 0 0 10px 0; | ||
| 24 | + | ||
| 25 | + background-color : transparent; | ||
| 26 | + border : none; | ||
| 27 | + | ||
| 28 | + border-bottom : 1px solid #ddd; | ||
| 29 | +`; | ||
| 30 | + | ||
| 31 | +export const EachTerms = styled.button ` | ||
| 32 | + color : #000; | ||
| 33 | + background-color : transparent; | ||
| 34 | + | ||
| 35 | + margin : 0 10px 0 0; | ||
| 36 | + padding : 0 0 5px 0; | ||
| 37 | + | ||
| 38 | + cursor : pointer; | ||
| 39 | + | ||
| 40 | + font-size : 13px; | ||
| 41 | + | ||
| 42 | + font-weight : 400; | ||
| 43 | + border : none; | ||
| 44 | + border-bottom : 1px solid; | ||
| 45 | + | ||
| 46 | + transition : .25s all; | ||
| 47 | + | ||
| 48 | + &:hover { | ||
| 49 | + color : #337DFF; | ||
| 50 | + opacity : .5; | ||
| 51 | + } | ||
| 52 | +`; | ||
| 53 | + | ||
| 54 | +export const InfoWrapper = styled.div ` | ||
| 55 | + display : flex; | ||
| 56 | + flex-direction : row; | ||
| 57 | + | ||
| 58 | + border : none; | ||
| 59 | + background-color : transparent; | ||
| 60 | +`; | ||
| 61 | + | ||
| 62 | +export const LicenseWrapper = styled.div ` | ||
| 63 | + flex : 1; | ||
| 64 | + display : flex; | ||
| 65 | + flex-direction : column; | ||
| 66 | + | ||
| 67 | + border : none; | ||
| 68 | + background-color : transparent; | ||
| 69 | + justify-content : center; | ||
| 70 | + align-items : flex-start; | ||
| 71 | + | ||
| 72 | +`; | ||
| 73 | + | ||
| 74 | +export const LicenseExplain = styled.div ` | ||
| 75 | + color : #a0a0a0; | ||
| 76 | + font-size : 14px; | ||
| 77 | + | ||
| 78 | + font-weight : 400; | ||
| 79 | + padding : 0 5px; | ||
| 80 | + | ||
| 81 | + border : none; | ||
| 82 | +`; | ||
| 83 | + | ||
| 84 | +export const LicenseImg = styled.img ` | ||
| 85 | + height : 60px; | ||
| 86 | + width : 150px; | ||
| 87 | +`; | ||
| 88 | + | ||
| 89 | +export const ServiceInfoWrapper = styled.div ` | ||
| 90 | + flex : 3; | ||
| 91 | + display : flex; | ||
| 92 | + flex-direction : column; | ||
| 93 | + justify-content : center; | ||
| 94 | + align-items : center; | ||
| 95 | + | ||
| 96 | + border : none; | ||
| 97 | + background-color : transparent; | ||
| 98 | +`; | ||
| 99 | + | ||
| 100 | +export const ServiceInfoEach = styled.div ` | ||
| 101 | + color : #d0d0d0; | ||
| 102 | + font-size : 13px; | ||
| 103 | + | ||
| 104 | + font-weight : 400 | ||
| 105 | +`; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/Footer/index.tsx
0 → 100644
| 1 | +import React from 'react'; | ||
| 2 | +import { RouteComponentProps } from 'react-router'; | ||
| 3 | + | ||
| 4 | +import * as Alert from '../../util/alertMessage'; | ||
| 5 | +import * as styled from './FooterStyled'; | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +const ApiLicense = '/static/img/apiLicense.png'; | ||
| 9 | + | ||
| 10 | +// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
| 11 | +interface FooterProps extends RouteComponentProps {} | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +const Footer = (props : FooterProps) => { | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + const onGoTerm = () => { | ||
| 18 | + Alert.onWarning('준비중입니다.', () => null); | ||
| 19 | + }; | ||
| 20 | + | ||
| 21 | + const onGoPrivateLicense = () => { | ||
| 22 | + Alert.onWarning('준비중입니다.', () => null); | ||
| 23 | + }; | ||
| 24 | + | ||
| 25 | + const onGoServiceCenter = () => { | ||
| 26 | + Alert.onWarning('준비중입니다.', () => null); | ||
| 27 | + }; | ||
| 28 | + | ||
| 29 | + return ( | ||
| 30 | + <styled.Container> | ||
| 31 | + <styled.TermsWrapper> | ||
| 32 | + <styled.EachTerms | ||
| 33 | + onClick = {onGoTerm} | ||
| 34 | + > | ||
| 35 | + 이용약관 | ||
| 36 | + </styled.EachTerms> | ||
| 37 | + <styled.EachTerms | ||
| 38 | + onClick = {onGoPrivateLicense} | ||
| 39 | + > | ||
| 40 | + 개인정보처리방침 | ||
| 41 | + </styled.EachTerms> | ||
| 42 | + <styled.EachTerms | ||
| 43 | + onClick = {onGoServiceCenter} | ||
| 44 | + > | ||
| 45 | + 고객센터 | ||
| 46 | + </styled.EachTerms> | ||
| 47 | + </styled.TermsWrapper> | ||
| 48 | + <styled.InfoWrapper> | ||
| 49 | + <styled.LicenseWrapper> | ||
| 50 | + <styled.LicenseExplain>저작권</styled.LicenseExplain> | ||
| 51 | + <styled.LicenseImg src = {ApiLicense}/> | ||
| 52 | + </styled.LicenseWrapper> | ||
| 53 | + <styled.ServiceInfoWrapper> | ||
| 54 | + <styled.ServiceInfoEach>서비스명 : Smart Medicine Box (SMB)</styled.ServiceInfoEach> | ||
| 55 | + <styled.ServiceInfoEach>서비스제공 : IoT 약병 제작회</styled.ServiceInfoEach> | ||
| 56 | + <styled.ServiceInfoEach>담당자 : 박권수</styled.ServiceInfoEach> | ||
| 57 | + <styled.ServiceInfoEach>주소 : OOO도 OOO시 OOO구 OOO OOO OOO</styled.ServiceInfoEach> | ||
| 58 | + <styled.ServiceInfoEach>연락처 : 010 - 0000 - 0000</styled.ServiceInfoEach> | ||
| 59 | + </styled.ServiceInfoWrapper> | ||
| 60 | + <styled.LicenseWrapper/> | ||
| 61 | + </styled.InfoWrapper> | ||
| 62 | + </styled.Container> | ||
| 63 | + ) | ||
| 64 | +}; | ||
| 65 | + | ||
| 66 | +export default Footer; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -10,7 +10,6 @@ import * as styled from './HeaderStyled'; | ... | @@ -10,7 +10,6 @@ import * as styled from './HeaderStyled'; |
| 10 | import { authApi } from '../../api'; | 10 | import { authApi } from '../../api'; |
| 11 | 11 | ||
| 12 | 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'; | 13 | const backButtonBlue = '/static/img/backButtonBlue.png'; |
| 15 | const logout = '/static/img/logout.png'; | 14 | const logout = '/static/img/logout.png'; |
| 16 | 15 | ... | ... |
web/src/components/Modal/ModalStyled.tsx
0 → 100644
| 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 | + | ||
| 26 | + | ||
| 27 | +export const ModalContainer = styled.div ` | ||
| 28 | + height : 100%; | ||
| 29 | + width : 100%; | ||
| 30 | + z-index : 99; | ||
| 31 | + position : absolute; | ||
| 32 | + | ||
| 33 | + display : flex; | ||
| 34 | + flex-direction : column; | ||
| 35 | + | ||
| 36 | + animation : ${ModalOn} .5s; | ||
| 37 | + | ||
| 38 | + background-color : rgba(52, 52, 52, .7); | ||
| 39 | + | ||
| 40 | +`; | ||
| 41 | + | ||
| 42 | +export const ModalClsButtonWrapper = styled.div ` | ||
| 43 | + flex : 1; | ||
| 44 | + | ||
| 45 | + display : flex; | ||
| 46 | + | ||
| 47 | + justify-content : flex-end; | ||
| 48 | + align-items : center; | ||
| 49 | + padding : 0 20px; | ||
| 50 | + | ||
| 51 | + border : none; | ||
| 52 | + background-color : transprent; | ||
| 53 | +`; | ||
| 54 | + | ||
| 55 | +export const ModalClsButton = styled.button ` | ||
| 56 | + border : none; | ||
| 57 | + background-color : transparent; | ||
| 58 | + | ||
| 59 | + cursor : pointer; | ||
| 60 | + | ||
| 61 | + color : #fff; | ||
| 62 | + | ||
| 63 | + display : flex; | ||
| 64 | + flex-direction : row; | ||
| 65 | + | ||
| 66 | + justify-content : center; | ||
| 67 | + align-items : center; | ||
| 68 | + | ||
| 69 | + transition : .25s all; | ||
| 70 | + &:hover { | ||
| 71 | + opacity : .5; | ||
| 72 | + } | ||
| 73 | +`; | ||
| 74 | + | ||
| 75 | +export const ModalClsButtonImg = styled.img ` | ||
| 76 | + height : 20px; | ||
| 77 | + width : 20px; | ||
| 78 | + | ||
| 79 | + margin : 0 10px 0 0; | ||
| 80 | +`; | ||
| 81 | + | ||
| 82 | +export const ModalClsButtonText = styled.div ` | ||
| 83 | + font-size : 18px; | ||
| 84 | + font-weight : 700; | ||
| 85 | +`; | ||
| 86 | + | ||
| 87 | +export const ModalContentWrapper = styled.div ` | ||
| 88 | + flex : 8; | ||
| 89 | + | ||
| 90 | + display : flex; | ||
| 91 | + flex-direction : column; | ||
| 92 | + | ||
| 93 | + justify-content : center; | ||
| 94 | + align-items : center; | ||
| 95 | + | ||
| 96 | + border : none; | ||
| 97 | +`; | ||
| 98 | + | ||
| 99 | +export const ModalContent = styled.div ` | ||
| 100 | + width : 600px; | ||
| 101 | + height : 400px; | ||
| 102 | + | ||
| 103 | + background-color : #fff; | ||
| 104 | + border : 1.2px solid #337DFF; | ||
| 105 | + border-radius : 5px; | ||
| 106 | + | ||
| 107 | + display : flex; | ||
| 108 | + flex-direction : column; | ||
| 109 | + | ||
| 110 | + justify-content : center; | ||
| 111 | + align-items : center; | ||
| 112 | +`; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/Modal/index.tsx
0 → 100644
| 1 | +import React from 'react'; | ||
| 2 | + | ||
| 3 | +import * as styled from './ModalStyled'; | ||
| 4 | + | ||
| 5 | +const closeButton = '/static/img/close.png'; | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +interface ModalProps { | ||
| 9 | + children : JSX.Element, | ||
| 10 | + onModalClose : () => void; | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +const ModalContainer = (props : ModalProps) => { | ||
| 14 | + return ( | ||
| 15 | + <styled.ModalContainer> | ||
| 16 | + <styled.ModalClsButtonWrapper> | ||
| 17 | + <styled.ModalClsButton | ||
| 18 | + onClick = {props.onModalClose} | ||
| 19 | + > | ||
| 20 | + <styled.ModalClsButtonImg src = {closeButton}/> | ||
| 21 | + <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | ||
| 22 | + </styled.ModalClsButton> | ||
| 23 | + </styled.ModalClsButtonWrapper> | ||
| 24 | + <styled.ModalContentWrapper> | ||
| 25 | + <styled.ModalContent> | ||
| 26 | + {props.children} | ||
| 27 | + </styled.ModalContent> | ||
| 28 | + </styled.ModalContentWrapper> | ||
| 29 | + <styled.ModalClsButtonWrapper /> | ||
| 30 | + </styled.ModalContainer> | ||
| 31 | + ); | ||
| 32 | +}; | ||
| 33 | + | ||
| 34 | +export default ModalContainer; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | import moment from 'moment'; | 1 | import moment from 'moment'; |
| 2 | 2 | ||
| 3 | -export const make = (chartData : any[], numberOfRow : number) => { | 3 | +export const make = (takeMedicineHist : any[], numberOfRow : number) => { |
| 4 | const now = new Date(); | 4 | const now = new Date(); |
| 5 | const result : any = {}; | 5 | const result : any = {}; |
| 6 | - new Array(numberOfRow).fill(null).forEach((item : any, index : number) => { | 6 | + new Array(numberOfRow).fill(null).forEach(() => { |
| 7 | const key = moment(now).format('MM/DD'); | 7 | const key = moment(now).format('MM/DD'); |
| 8 | result[key] = 0; | 8 | result[key] = 0; |
| 9 | now.setDate(now.getDate() - 1); | 9 | now.setDate(now.getDate() - 1); |
| 10 | - }) | 10 | + }); |
| 11 | 11 | ||
| 12 | - chartData.forEach((data : any) => { | 12 | + takeMedicineHist.forEach((data : any) => { |
| 13 | const key : string = moment(data.takeDate).format('MM/DD'); | 13 | const key : string = moment(data.takeDate).format('MM/DD'); |
| 14 | - result[key] = result[key] + 1; | 14 | + !isNaN(result[key]) ? result[key] = result[key] + data.dosage : null; |
| 15 | }); | 15 | }); |
| 16 | 16 | ||
| 17 | const categories : any = []; | 17 | const categories : any = []; |
| ... | @@ -28,5 +28,5 @@ export const make = (chartData : any[], numberOfRow : number) => { | ... | @@ -28,5 +28,5 @@ export const make = (chartData : any[], numberOfRow : number) => { |
| 28 | return { | 28 | return { |
| 29 | categories, | 29 | categories, |
| 30 | data, | 30 | data, |
| 31 | - } | 31 | + }; |
| 32 | }; | 32 | }; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -9,6 +9,12 @@ export const token = atom({ | ... | @@ -9,6 +9,12 @@ export const token = atom({ |
| 9 | effects_UNSTABLE : [persistAtom], | 9 | effects_UNSTABLE : [persistAtom], |
| 10 | }); | 10 | }); |
| 11 | 11 | ||
| 12 | +export const userId = atom({ | ||
| 13 | + key : 'userId', | ||
| 14 | + default : null, | ||
| 15 | + effects_UNSTABLE : [persistAtom], | ||
| 16 | +}); | ||
| 17 | + | ||
| 12 | export const userTypeCd = atom({ | 18 | export const userTypeCd = atom({ |
| 13 | key : 'userTypeCd', | 19 | key : 'userTypeCd', |
| 14 | default : 'NORMAL', | 20 | default : 'NORMAL', | ... | ... |
| ... | @@ -3,6 +3,7 @@ import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'; | ... | @@ -3,6 +3,7 @@ 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 Loading from '../components/Loading'; | 5 | import Loading from '../components/Loading'; |
| 6 | +import Footer from '../components/Footer'; | ||
| 6 | import { LoginContainer } from "./login"; | 7 | import { LoginContainer } from "./login"; |
| 7 | import { RegisterContainer } from './register'; | 8 | import { RegisterContainer } from './register'; |
| 8 | import { MainContainer } from "./main"; | 9 | import { MainContainer } from "./main"; | ... | ... |
| ... | @@ -9,6 +9,7 @@ import * as Alert from '../../util/alertMessage'; | ... | @@ -9,6 +9,7 @@ import * as Alert from '../../util/alertMessage'; |
| 9 | import moment from 'moment'; | 9 | import moment from 'moment'; |
| 10 | 10 | ||
| 11 | import Header from '../../components/Header'; | 11 | import Header from '../../components/Header'; |
| 12 | +import Footer from '../../components/Footer'; | ||
| 12 | import BottleInfoPresenter from './BottleInfoPresenter'; | 13 | import BottleInfoPresenter from './BottleInfoPresenter'; |
| 13 | 14 | ||
| 14 | import { doctorApi } from '../../api'; | 15 | import { doctorApi } from '../../api'; |
| ... | @@ -35,6 +36,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -35,6 +36,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 35 | takeMedicineHist : [], | 36 | takeMedicineHist : [], |
| 36 | }); | 37 | }); |
| 37 | 38 | ||
| 39 | + //차트에 표시되는 행의 개수 | ||
| 38 | const numberOfChartItem = 7; | 40 | const numberOfChartItem = 7; |
| 39 | const [chartOption, setChartOption] = useState<any>({ | 41 | const [chartOption, setChartOption] = useState<any>({ |
| 40 | chart : { | 42 | chart : { |
| ... | @@ -51,19 +53,22 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -51,19 +53,22 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 51 | categories : [], | 53 | categories : [], |
| 52 | }, | 54 | }, |
| 53 | series : [{ | 55 | series : [{ |
| 54 | - name : '약 복용 횟수', | 56 | + name : '약 복용 회분', |
| 55 | color : '#337DFF', | 57 | color : '#337DFF', |
| 56 | data : [], | 58 | data : [], |
| 57 | }], | 59 | }], |
| 58 | }); | 60 | }); |
| 61 | + const [takeMedicineHist, setTakeMedicineHist] = useState<any[]>([]); | ||
| 59 | 62 | ||
| 60 | const [feedback, setFeedback] = useState<string>(''); | 63 | const [feedback, setFeedback] = useState<string>(''); |
| 61 | const [fdbType, setFdbType] = useState<string>('RECOMMEND'); | 64 | const [fdbType, setFdbType] = useState<string>('RECOMMEND'); |
| 62 | 65 | ||
| 63 | const [medicineInfoModal, setMedicineInfoModal] = useState<boolean>(false); | 66 | const [medicineInfoModal, setMedicineInfoModal] = useState<boolean>(false); |
| 67 | + const [modalType, setModalType] = useState<string>('hist'); //hist , info | ||
| 64 | 68 | ||
| 65 | 69 | ||
| 66 | const fetchData = async () => { | 70 | const fetchData = async () => { |
| 71 | + setModalType('hist'); | ||
| 67 | setFeedback(''); | 72 | setFeedback(''); |
| 68 | setFdbType('RECOMMEND'); | 73 | setFdbType('RECOMMEND'); |
| 69 | setMedicineInfoModal(false); | 74 | setMedicineInfoModal(false); |
| ... | @@ -71,6 +76,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -71,6 +76,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 71 | try { | 76 | try { |
| 72 | const result = await doctorApi.getPatientBottleDetail(token, bottleId); | 77 | const result = await doctorApi.getPatientBottleDetail(token, bottleId); |
| 73 | if (result.statusText === 'OK') { | 78 | if (result.statusText === 'OK') { |
| 79 | + setTakeMedicineHist(result.data.takeMedicineHist.map((takeMedicine : any) => { | ||
| 80 | + return ({ | ||
| 81 | + ...takeMedicine, | ||
| 82 | + takeDate : moment(takeMedicine.takeDate).format('YYYY년 MM월 DD일 hh시 mm분'), | ||
| 83 | + }); | ||
| 84 | + })); | ||
| 74 | const { categories, data } = makeChart.make(result.data.takeMedicineHist, numberOfChartItem); | 85 | const { categories, data } = makeChart.make(result.data.takeMedicineHist, numberOfChartItem); |
| 75 | setBottleInfo({ | 86 | setBottleInfo({ |
| 76 | ...result.data, | 87 | ...result.data, |
| ... | @@ -97,9 +108,8 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -97,9 +108,8 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 97 | Alert.onError('접근 권한이 없습니다.', () => props.history.push('/')); | 108 | Alert.onError('접근 권한이 없습니다.', () => props.history.push('/')); |
| 98 | } | 109 | } |
| 99 | } catch(e : any) { | 110 | } catch(e : any) { |
| 100 | - Alert.onError(e.response.data.error, () => props.history.push('/')); | ||
| 101 | - | ||
| 102 | console.log(e); | 111 | console.log(e); |
| 112 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => props.history.push('/')); | ||
| 103 | } | 113 | } |
| 104 | }; | 114 | }; |
| 105 | 115 | ||
| ... | @@ -123,7 +133,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -123,7 +133,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 123 | Alert.onError('피드백 등록에 실패했습니다.', () => null); | 133 | Alert.onError('피드백 등록에 실패했습니다.', () => null); |
| 124 | } | 134 | } |
| 125 | } catch(e : any) { | 135 | } catch(e : any) { |
| 126 | - Alert.onError(e.response.data.error, () => fetchData()); | 136 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => fetchData()); |
| 127 | } | 137 | } |
| 128 | } else { | 138 | } else { |
| 129 | Alert.onError('피드백 내용을 입력하세요.', () => null); | 139 | Alert.onError('피드백 내용을 입력하세요.', () => null); |
| ... | @@ -134,6 +144,14 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -134,6 +144,14 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 134 | 144 | ||
| 135 | }; | 145 | }; |
| 136 | 146 | ||
| 147 | + const onViewTakeHist = () => { | ||
| 148 | + if(modalType === 'info') setModalType('hist'); | ||
| 149 | + }; | ||
| 150 | + | ||
| 151 | + const onViewMedicineInfo = () => { | ||
| 152 | + if(modalType === 'hist') setModalType('info'); | ||
| 153 | + }; | ||
| 154 | + | ||
| 137 | 155 | ||
| 138 | useEffect(() => { | 156 | useEffect(() => { |
| 139 | if(userTypeCd !== 'DOCTOR') { | 157 | if(userTypeCd !== 'DOCTOR') { |
| ... | @@ -148,8 +166,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -148,8 +166,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 148 | <BottleInfoPresenter | 166 | <BottleInfoPresenter |
| 149 | bottleInfo = {bottleInfo} | 167 | bottleInfo = {bottleInfo} |
| 150 | chartOption = {chartOption} | 168 | chartOption = {chartOption} |
| 169 | + takeMedicineHist = {takeMedicineHist} | ||
| 151 | 170 | ||
| 152 | medicineInfoModal = {medicineInfoModal} | 171 | medicineInfoModal = {medicineInfoModal} |
| 172 | + modalType = {modalType} | ||
| 173 | + onViewTakeHist = {onViewTakeHist} | ||
| 174 | + onViewMedicineInfo = {onViewMedicineInfo} | ||
| 153 | setMedicineInfoModal = {setMedicineInfoModal} | 175 | setMedicineInfoModal = {setMedicineInfoModal} |
| 154 | 176 | ||
| 155 | feedback = {feedback} | 177 | feedback = {feedback} |
| ... | @@ -158,6 +180,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -158,6 +180,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
| 158 | setFdbType = {setFdbType} | 180 | setFdbType = {setFdbType} |
| 159 | onSubmitFeedback = {onSubmitFeedback} | 181 | onSubmitFeedback = {onSubmitFeedback} |
| 160 | /> | 182 | /> |
| 183 | + <Footer {...props}/> | ||
| 161 | </> | 184 | </> |
| 162 | ); | 185 | ); |
| 163 | }; | 186 | }; | ... | ... |
| ... | @@ -2,10 +2,10 @@ import React from 'react'; | ... | @@ -2,10 +2,10 @@ import React from 'react'; |
| 2 | import HighCharts from 'highcharts'; | 2 | import HighCharts from 'highcharts'; |
| 3 | import HighchartsReact from 'highcharts-react-official'; | 3 | import HighchartsReact from 'highcharts-react-official'; |
| 4 | 4 | ||
| 5 | +import Modal from '../../components/Modal'; | ||
| 5 | import * as styled from './BottleInfoStyled'; | 6 | import * as styled from './BottleInfoStyled'; |
| 6 | 7 | ||
| 7 | const plus = '/static/img/plus.png'; | 8 | const plus = '/static/img/plus.png'; |
| 8 | -const closeButton = '/static/img/close.png'; | ||
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | interface BottleInfoProps { | 11 | interface BottleInfoProps { |
| ... | @@ -16,8 +16,12 @@ interface BottleInfoProps { | ... | @@ -16,8 +16,12 @@ interface BottleInfoProps { |
| 16 | takeMedicineHist : any[]; | 16 | takeMedicineHist : any[]; |
| 17 | }; | 17 | }; |
| 18 | chartOption : any; | 18 | chartOption : any; |
| 19 | + takeMedicineHist : any[]; | ||
| 19 | 20 | ||
| 20 | medicineInfoModal : boolean; | 21 | medicineInfoModal : boolean; |
| 22 | + modalType : string; | ||
| 23 | + onViewTakeHist : () => void; | ||
| 24 | + onViewMedicineInfo : () => void; | ||
| 21 | setMedicineInfoModal : (arg0 : boolean) => void; | 25 | setMedicineInfoModal : (arg0 : boolean) => void; |
| 22 | 26 | ||
| 23 | feedback : string; | 27 | feedback : string; |
| ... | @@ -32,42 +36,87 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { | ... | @@ -32,42 +36,87 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { |
| 32 | <styled.Container> | 36 | <styled.Container> |
| 33 | { | 37 | { |
| 34 | props.medicineInfoModal ? | 38 | props.medicineInfoModal ? |
| 35 | - <styled.ModalContainer> | 39 | + <Modal onModalClose = {() => props.setMedicineInfoModal(false)}> |
| 36 | - <styled.ModalClsButtonWrapper> | 40 | + <> |
| 37 | - <styled.ModalClsButton | 41 | + <styled.ModalTypeButtonWrapper> |
| 38 | - onClick = {() => props.setMedicineInfoModal(false)} | 42 | + <styled.ModalTypeButton |
| 43 | + isSelect = {props.modalType === 'hist'} | ||
| 44 | + onClick = {props.onViewTakeHist} | ||
| 39 | > | 45 | > |
| 40 | - <styled.ModalClsButtonImg src = {closeButton}/> | 46 | + 복용 기록 |
| 41 | - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | 47 | + </styled.ModalTypeButton> |
| 42 | - </styled.ModalClsButton> | 48 | + <styled.ModalTypeButton |
| 43 | - </styled.ModalClsButtonWrapper> | 49 | + isSelect = {props.modalType === 'info'} |
| 44 | - <styled.ModalContentWrapper> | 50 | + onClick = {props.onViewMedicineInfo} |
| 45 | - <styled.ModalContent> | 51 | + > |
| 46 | - <styled.MedicineNameWrapper> | 52 | + 약 정보 |
| 47 | - <styled.MedicineName>{props.bottleInfo.medicine.name}</styled.MedicineName> | 53 | + </styled.ModalTypeButton> |
| 48 | - <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{props.bottleInfo.medicine.company}</styled.MedicineName> | 54 | + </styled.ModalTypeButtonWrapper> |
| 49 | - </styled.MedicineNameWrapper> | 55 | + { |
| 50 | - <styled.MedicineInfoWrapper> | 56 | + props.modalType === 'hist' ? |
| 51 | - <styled.MedicineEachInfoWrapper> | 57 | + <> |
| 52 | - <styled.MedicineEachInfoTitle>효능</styled.MedicineEachInfoTitle> | 58 | + <styled.MedicineNameWrapper> |
| 53 | - <styled.MedicineEachInfo>{props.bottleInfo.medicine.target}</styled.MedicineEachInfo> | 59 | + <styled.MedicineName>{`복용 기록`}</styled.MedicineName> |
| 54 | - </styled.MedicineEachInfoWrapper> | 60 | + <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{`전체 : ${props.takeMedicineHist.length}건`}</styled.MedicineName> |
| 55 | - <styled.MedicineEachInfoWrapper> | 61 | + </styled.MedicineNameWrapper> |
| 56 | - <styled.MedicineEachInfoTitle>복용 정보</styled.MedicineEachInfoTitle> | 62 | + <styled.MedicineInfoWrapper> |
| 57 | - <styled.MedicineEachInfo>{props.bottleInfo.medicine.dosage}</styled.MedicineEachInfo> | 63 | + { |
| 58 | - </styled.MedicineEachInfoWrapper> | 64 | + props.takeMedicineHist.map((hist : any) => { |
| 59 | - <styled.MedicineEachInfoWrapper> | 65 | + return ( |
| 60 | - <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>주의 사항</styled.MedicineEachInfoTitle> | 66 | + <styled.HistWrapper |
| 61 | - <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.warn}</styled.MedicineEachInfo> | 67 | + key = {hist._id} |
| 62 | - </styled.MedicineEachInfoWrapper> | 68 | + > |
| 63 | - <styled.MedicineEachInfoWrapper> | 69 | + <styled.HistDtmWrapper> |
| 64 | - <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>부작용</styled.MedicineEachInfoTitle> | 70 | + <styled.HistInfoEachWrapper style = {{fontSize : 11}}>복용 날짜</styled.HistInfoEachWrapper> |
| 65 | - <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.antiEffect}</styled.MedicineEachInfo> | 71 | + <styled.HistDtm>{hist.takeDate}</styled.HistDtm> |
| 66 | - </styled.MedicineEachInfoWrapper> | 72 | + </styled.HistDtmWrapper> |
| 67 | - </styled.MedicineInfoWrapper> | 73 | + <styled.HistInfoWrapper> |
| 68 | - </styled.ModalContent> | 74 | + <styled.HistInfoEachWrapper> |
| 69 | - </styled.ModalContentWrapper> | 75 | + 복용량 |
| 70 | - </styled.ModalContainer> : null | 76 | + <styled.HistInfoEach>{hist.dosage}회분</styled.HistInfoEach> |
| 77 | + </styled.HistInfoEachWrapper> | ||
| 78 | + <styled.HistInfoEachWrapper> | ||
| 79 | + 약병 내 온도 | ||
| 80 | + <styled.HistInfoEach>{hist.temperature}℃</styled.HistInfoEach> | ||
| 81 | + </styled.HistInfoEachWrapper> | ||
| 82 | + <styled.HistInfoEachWrapper> | ||
| 83 | + 약병 내 습도 | ||
| 84 | + <styled.HistInfoEach>{hist.humidity}%</styled.HistInfoEach> | ||
| 85 | + </styled.HistInfoEachWrapper> | ||
| 86 | + </styled.HistInfoWrapper> | ||
| 87 | + </styled.HistWrapper> | ||
| 88 | + ) | ||
| 89 | + }) | ||
| 90 | + } | ||
| 91 | + </styled.MedicineInfoWrapper> | ||
| 92 | + </> : | ||
| 93 | + <> | ||
| 94 | + <styled.MedicineNameWrapper> | ||
| 95 | + <styled.MedicineName>{props.bottleInfo.medicine.name}</styled.MedicineName> | ||
| 96 | + <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{props.bottleInfo.medicine.company}</styled.MedicineName> | ||
| 97 | + </styled.MedicineNameWrapper> | ||
| 98 | + <styled.MedicineInfoWrapper> | ||
| 99 | + <styled.MedicineEachInfoWrapper> | ||
| 100 | + <styled.MedicineEachInfoTitle>효능</styled.MedicineEachInfoTitle> | ||
| 101 | + <styled.MedicineEachInfo>{props.bottleInfo.medicine.target}</styled.MedicineEachInfo> | ||
| 102 | + </styled.MedicineEachInfoWrapper> | ||
| 103 | + <styled.MedicineEachInfoWrapper> | ||
| 104 | + <styled.MedicineEachInfoTitle>복용 정보</styled.MedicineEachInfoTitle> | ||
| 105 | + <styled.MedicineEachInfo>{props.bottleInfo.medicine.dosage}</styled.MedicineEachInfo> | ||
| 106 | + </styled.MedicineEachInfoWrapper> | ||
| 107 | + <styled.MedicineEachInfoWrapper> | ||
| 108 | + <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>주의 사항</styled.MedicineEachInfoTitle> | ||
| 109 | + <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.warn}</styled.MedicineEachInfo> | ||
| 110 | + </styled.MedicineEachInfoWrapper> | ||
| 111 | + <styled.MedicineEachInfoWrapper> | ||
| 112 | + <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>부작용</styled.MedicineEachInfoTitle> | ||
| 113 | + <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.antiEffect}</styled.MedicineEachInfo> | ||
| 114 | + </styled.MedicineEachInfoWrapper> | ||
| 115 | + </styled.MedicineInfoWrapper> | ||
| 116 | + </> | ||
| 117 | + } | ||
| 118 | + </> | ||
| 119 | + </Modal> : null | ||
| 71 | } | 120 | } |
| 72 | <styled.ChartAndFeedbackWrapper> | 121 | <styled.ChartAndFeedbackWrapper> |
| 73 | <styled.ChartWrapper> | 122 | <styled.ChartWrapper> |
| ... | @@ -107,7 +156,7 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { | ... | @@ -107,7 +156,7 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { |
| 107 | /> | 156 | /> |
| 108 | <styled.NewFeedbackButtonWrapper> | 157 | <styled.NewFeedbackButtonWrapper> |
| 109 | <styled.NewFeedbackTypeButtonWrapper> | 158 | <styled.NewFeedbackTypeButtonWrapper> |
| 110 | - <styled.NewFeedbackTypeButtonEachWrapper> | 159 | + <styled.NewFeedbackTypeButtonEachWrapper> |
| 111 | <styled.NewFeedbackTypeButton | 160 | <styled.NewFeedbackTypeButton |
| 112 | valueType = 'RECOMMEND' | 161 | valueType = 'RECOMMEND' |
| 113 | selected = {props.fdbType === 'RECOMMEND'} | 162 | selected = {props.fdbType === 'RECOMMEND'} |
| ... | @@ -143,7 +192,6 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { | ... | @@ -143,7 +192,6 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { |
| 143 | <styled.NewFeedbackRegButton | 192 | <styled.NewFeedbackRegButton |
| 144 | onClick = {props.onSubmitFeedback} | 193 | onClick = {props.onSubmitFeedback} |
| 145 | > | 194 | > |
| 146 | - {/* <styled.NewFeedbackRegButtonImg /> */} | ||
| 147 | <styled.NewFeedbackRegButtonText>피드백<br/>등록</styled.NewFeedbackRegButtonText> | 195 | <styled.NewFeedbackRegButtonText>피드백<br/>등록</styled.NewFeedbackRegButtonText> |
| 148 | </styled.NewFeedbackRegButton> | 196 | </styled.NewFeedbackRegButton> |
| 149 | </styled.NewFeedbackButtonWrapper> | 197 | </styled.NewFeedbackButtonWrapper> | ... | ... |
| 1 | -import styled, { keyframes } from 'styled-components'; | 1 | +import styled 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 | ||
| 27 | export const Container = styled.div ` | 4 | export const Container = styled.div ` |
| ... | @@ -32,91 +9,106 @@ export const Container = styled.div ` | ... | @@ -32,91 +9,106 @@ export const Container = styled.div ` |
| 32 | justify-content : center; | 9 | justify-content : center; |
| 33 | `; | 10 | `; |
| 34 | 11 | ||
| 35 | -export const ModalContainer = styled.div ` | 12 | +export const ModalTypeButtonWrapper = styled.div ` |
| 36 | - height : 100%; | 13 | + border : none; |
| 37 | - width : 100%; | ||
| 38 | - z-index : 99; | ||
| 39 | - position : absolute; | ||
| 40 | - | ||
| 41 | display : flex; | 14 | display : flex; |
| 42 | - flex-direction : column; | 15 | + flex-direction : row; |
| 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 | 16 | ||
| 53 | - display : flex; | 17 | + width : 100%; |
| 54 | 18 | ||
| 55 | - justify-content : flex-end; | 19 | + justify-content : center; |
| 56 | align-items : center; | 20 | align-items : center; |
| 57 | - padding : 0 20px; | ||
| 58 | 21 | ||
| 59 | - border : none; | ||
| 60 | - background-color : transprent; | ||
| 61 | -`; | ||
| 62 | - | ||
| 63 | -export const ModalClsButton = styled.button ` | ||
| 64 | - border : none; | ||
| 65 | background-color : transparent; | 22 | background-color : transparent; |
| 23 | + | ||
| 24 | + gap : 5%; | ||
| 66 | 25 | ||
| 67 | - cursor : pointer; | 26 | + padding : 3% 0 0 0; |
| 27 | +`; | ||
| 68 | 28 | ||
| 69 | - color : #fff; | 29 | +export const ModalTypeButton = styled.button<{isSelect : boolean}> ` |
| 30 | + border : 1px solid #337DFF; | ||
| 31 | + border-radius : 3px; | ||
| 32 | + color : ${props => props.isSelect ? '#fff' : '#337DFF'}; | ||
| 33 | + background-color : ${props => props.isSelect ? '#337DFF' : '#fff'}; | ||
| 70 | 34 | ||
| 71 | - display : flex; | 35 | + padding : 1% 3%; |
| 72 | - flex-direction : row; | ||
| 73 | 36 | ||
| 74 | - justify-content : center; | 37 | + cursor : pointer; |
| 75 | - align-items : center; | 38 | + |
| 39 | + font-size : 16px; | ||
| 40 | + font-weight : ${props => props.isSelect ? '600' : '500'}; | ||
| 76 | 41 | ||
| 77 | transition : .25s all; | 42 | transition : .25s all; |
| 43 | + | ||
| 78 | &:hover { | 44 | &:hover { |
| 79 | opacity : .5; | 45 | opacity : .5; |
| 80 | } | 46 | } |
| 47 | + | ||
| 81 | `; | 48 | `; |
| 82 | 49 | ||
| 83 | -export const ModalClsButtonImg = styled.img ` | 50 | +export const HistWrapper = styled.div ` |
| 84 | - height : 20px; | 51 | + display : flex; |
| 85 | - width : 20px; | 52 | + flex-direction : column; |
| 86 | 53 | ||
| 87 | - margin : 0 10px 0 0; | 54 | + height : 65px; |
| 88 | -`; | 55 | + width : 100%; |
| 89 | 56 | ||
| 90 | -export const ModalClsButtonText = styled.div ` | 57 | + border : none; |
| 91 | - font-size : 18px; | 58 | + border-bottom : 1px solid #ddd; |
| 92 | - font-weight : 700; | ||
| 93 | `; | 59 | `; |
| 94 | 60 | ||
| 95 | -export const ModalContentWrapper = styled.div ` | 61 | +export const HistDtmWrapper = styled.div ` |
| 96 | - flex : 8; | 62 | + flex : 2; |
| 63 | + padding : 0 3%; | ||
| 64 | + | ||
| 65 | + border : none; | ||
| 97 | 66 | ||
| 98 | display : flex; | 67 | display : flex; |
| 99 | flex-direction : column; | 68 | flex-direction : column; |
| 100 | 69 | ||
| 101 | justify-content : center; | 70 | justify-content : center; |
| 102 | - align-items : center; | ||
| 103 | 71 | ||
| 104 | - border : none; | ||
| 105 | `; | 72 | `; |
| 106 | 73 | ||
| 107 | -export const ModalContent = styled.div ` | 74 | +export const HistDtm = styled.div ` |
| 108 | - width : 700px; | 75 | + font-size : 16px; |
| 109 | - height : 500px; | 76 | + color : #000; |
| 77 | +`; | ||
| 78 | + | ||
| 79 | +export const HistInfoWrapper = styled.div ` | ||
| 80 | + flex : 1; | ||
| 81 | + padding : 0 3%; | ||
| 82 | + | ||
| 83 | + border : none; | ||
| 110 | 84 | ||
| 111 | - background-color : #fff; | ||
| 112 | - border : 1.2px solid #337DFF; | ||
| 113 | - border-radius : 5px; | ||
| 114 | - | ||
| 115 | display : flex; | 85 | display : flex; |
| 116 | - flex-direction : column; | 86 | + flex-direction : row; |
| 87 | + align-items : flex-start; | ||
| 88 | + | ||
| 89 | +`; | ||
| 90 | + | ||
| 91 | +export const HistInfoEachWrapper = styled.div ` | ||
| 92 | + flex : 1; | ||
| 93 | + border : none; | ||
| 117 | 94 | ||
| 118 | - // justify-content : center; | 95 | + display : flex; |
| 96 | + flex-direction : row; | ||
| 119 | align-items : center; | 97 | align-items : center; |
| 98 | + | ||
| 99 | + gap : 5%; | ||
| 100 | + | ||
| 101 | + font-size : 14px; | ||
| 102 | + font-weight : 500; | ||
| 103 | + | ||
| 104 | + color : #a0a0a0; | ||
| 105 | +`; | ||
| 106 | + | ||
| 107 | +export const HistInfoEach = styled.div ` | ||
| 108 | + font-size : 15px; | ||
| 109 | + font-weight : 600; | ||
| 110 | + | ||
| 111 | + color : #337DFF; | ||
| 120 | `; | 112 | `; |
| 121 | 113 | ||
| 122 | export const MedicineNameWrapper = styled.div ` | 114 | export const MedicineNameWrapper = styled.div ` |
| ... | @@ -169,6 +161,7 @@ export const MedicineEachInfoWrapper = styled.div ` | ... | @@ -169,6 +161,7 @@ export const MedicineEachInfoWrapper = styled.div ` |
| 169 | display : flex; | 161 | display : flex; |
| 170 | flex-direction : column; | 162 | flex-direction : column; |
| 171 | 163 | ||
| 164 | + | ||
| 172 | width : 80%; | 165 | width : 80%; |
| 173 | padding : 20px 10%; | 166 | padding : 20px 10%; |
| 174 | border : none; | 167 | border : none; | ... | ... |
| ... | @@ -23,6 +23,7 @@ const LoginContainer = (props : LoginProps) => { | ... | @@ -23,6 +23,7 @@ const LoginContainer = (props : LoginProps) => { |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | const [token, setToken] = useRecoilState(recoilUtil.token); | 25 | const [token, setToken] = useRecoilState(recoilUtil.token); |
| 26 | + const [userId, setUserId] = useRecoilState(recoilUtil.userId); | ||
| 26 | const [userTypeCd, setUserTypeCd] = useRecoilState(recoilUtil.userTypeCd); | 27 | const [userTypeCd, setUserTypeCd] = useRecoilState(recoilUtil.userTypeCd); |
| 27 | 28 | ||
| 28 | 29 | ||
| ... | @@ -58,13 +59,14 @@ const LoginContainer = (props : LoginProps) => { | ... | @@ -58,13 +59,14 @@ const LoginContainer = (props : LoginProps) => { |
| 58 | const result : any = await authApi.login(loginForm); | 59 | const result : any = await authApi.login(loginForm); |
| 59 | if(result.statusText === 'OK' && result.data.userTypeCd !== 'NORMAL') { | 60 | if(result.statusText === 'OK' && result.data.userTypeCd !== 'NORMAL') { |
| 60 | setToken(result.data.token); | 61 | setToken(result.data.token); |
| 62 | + setUserId(loginForm.userId); | ||
| 61 | setUserTypeCd(result.data.userTypeCd); | 63 | setUserTypeCd(result.data.userTypeCd); |
| 62 | Alert.onSuccess('로그인 성공, 메인 화면으로 이동합니다.', () => props.history.push('/')); | 64 | Alert.onSuccess('로그인 성공, 메인 화면으로 이동합니다.', () => props.history.push('/')); |
| 63 | } else if(result.data.userTypeCd === 'NORMAL') { | 65 | } else if(result.data.userTypeCd === 'NORMAL') { |
| 64 | Alert.onError('권한이 없는 유저입니다.', () => props.history.push('/')); | 66 | Alert.onError('권한이 없는 유저입니다.', () => props.history.push('/')); |
| 65 | } | 67 | } |
| 66 | - } catch(e) { | 68 | + } catch(e : any) { |
| 67 | - Alert.onError(e.response.data.error, () => null); | 69 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
| 68 | } | 70 | } |
| 69 | 71 | ||
| 70 | }; | 72 | }; | ... | ... |
| ... | @@ -6,6 +6,7 @@ import * as recoilUtil from '../../util/recoilUtil'; | ... | @@ -6,6 +6,7 @@ import * as recoilUtil from '../../util/recoilUtil'; |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | import Header from '../../components/Header'; | 8 | import Header from '../../components/Header'; |
| 9 | +import Footer from '../../components/Footer'; | ||
| 9 | import DoctorMenuContainer from './doctor'; | 10 | import DoctorMenuContainer from './doctor'; |
| 10 | import ManagerMenuContainer from './manager'; | 11 | import ManagerMenuContainer from './manager'; |
| 11 | 12 | ||
| ... | @@ -32,6 +33,7 @@ const MainContainer = (props : MainProps) => { | ... | @@ -32,6 +33,7 @@ const MainContainer = (props : MainProps) => { |
| 32 | userTypeCd === 'MANAGER' ? | 33 | userTypeCd === 'MANAGER' ? |
| 33 | <ManagerMenuContainer {...props}/> : null | 34 | <ManagerMenuContainer {...props}/> : null |
| 34 | } | 35 | } |
| 36 | + <Footer {...props}/> | ||
| 35 | </> | 37 | </> |
| 36 | ); | 38 | ); |
| 37 | }; | 39 | }; | ... | ... |
web/src/views/main/MainPresenter.tsx
deleted
100644 → 0
| 1 | -import React from 'react'; | ||
| 2 | - | ||
| 3 | -import * as styled from './MainStyled'; | ||
| 4 | - | ||
| 5 | - | ||
| 6 | -interface MainProps { | ||
| 7 | - userTypeCd : string; | ||
| 8 | -} | ||
| 9 | - | ||
| 10 | -const MainPresenter = (props : MainProps) => { | ||
| 11 | - return ( | ||
| 12 | - <styled.Container> | ||
| 13 | - <styled.InfoAndSearchWrapper> | ||
| 14 | - <styled.InfoWrapper> | ||
| 15 | - <styled.InfoSquare> | ||
| 16 | - | ||
| 17 | - </styled.InfoSquare> | ||
| 18 | - <styled.NewPatientButton>새 환자 등록</styled.NewPatientButton> | ||
| 19 | - </styled.InfoWrapper> | ||
| 20 | - <styled.SearchAndDetailWrapper> | ||
| 21 | - <styled.SearchBarWrapper> | ||
| 22 | - <styled.SearchBar | ||
| 23 | - placeholder = '환자 이름' | ||
| 24 | - /> | ||
| 25 | - <styled.SearchButton> | ||
| 26 | - 검색 | ||
| 27 | - </styled.SearchButton> | ||
| 28 | - </styled.SearchBarWrapper> | ||
| 29 | - <styled.SearchResultWrapper> | ||
| 30 | - | ||
| 31 | - </styled.SearchResultWrapper> | ||
| 32 | - </styled.SearchAndDetailWrapper> | ||
| 33 | - </styled.InfoAndSearchWrapper> | ||
| 34 | - <styled.BottleListWrapper> | ||
| 35 | - bottleListWrapper | ||
| 36 | - </styled.BottleListWrapper> | ||
| 37 | - </styled.Container> | ||
| 38 | - ) | ||
| 39 | -}; | ||
| 40 | - | ||
| 41 | -export default MainPresenter; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
| 1 | -import styled, { keyframes } from 'styled-components'; | 1 | +import styled 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 | ||
| 27 | export const Container = styled.div ` | 4 | export const Container = styled.div ` |
| ... | @@ -32,93 +9,6 @@ export const Container = styled.div ` | ... | @@ -32,93 +9,6 @@ export const Container = styled.div ` |
| 32 | justify-content : center; | 9 | justify-content : center; |
| 33 | `; | 10 | `; |
| 34 | 11 | ||
| 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 : 600px; | ||
| 109 | - height : 400px; | ||
| 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 NewPatientRegisterTitle = styled.div ` | 12 | export const NewPatientRegisterTitle = styled.div ` |
| 123 | font-size : 20px; | 13 | font-size : 20px; |
| 124 | font-weight : 700; | 14 | font-weight : 700; |
| ... | @@ -199,14 +89,18 @@ export const NewPatientSearchResult = styled.div ` | ... | @@ -199,14 +89,18 @@ export const NewPatientSearchResult = styled.div ` |
| 199 | 89 | ||
| 200 | export const NewPatientSearchResultInfoWrapper = styled.div ` | 90 | export const NewPatientSearchResultInfoWrapper = styled.div ` |
| 201 | display : flex; | 91 | display : flex; |
| 92 | + flex-direction : column; | ||
| 202 | `; | 93 | `; |
| 203 | 94 | ||
| 204 | export const NewPatientSearchResultInfo = styled.div ` | 95 | export const NewPatientSearchResultInfo = styled.div ` |
| 96 | + display : flex; | ||
| 97 | + flex-direction : row; | ||
| 98 | + | ||
| 205 | font-size : 13px; | 99 | font-size : 13px; |
| 206 | font-weight : 600; | 100 | font-weight : 600; |
| 207 | color : #a0a0a0; | 101 | color : #a0a0a0; |
| 208 | 102 | ||
| 209 | - margin : 0 5px 0 0; | 103 | + margin : 0 5px 0 5px; |
| 210 | `; | 104 | `; |
| 211 | 105 | ||
| 212 | export const NewPatientSearchResultInfoText = styled.div ` | 106 | export const NewPatientSearchResultInfoText = styled.div ` |
| ... | @@ -214,6 +108,8 @@ export const NewPatientSearchResultInfoText = styled.div ` | ... | @@ -214,6 +108,8 @@ export const NewPatientSearchResultInfoText = styled.div ` |
| 214 | color : #343434; | 108 | color : #343434; |
| 215 | font-weight : 600; | 109 | font-weight : 600; |
| 216 | letter-spacing : 1px; | 110 | letter-spacing : 1px; |
| 111 | + | ||
| 112 | + margin : 0 0 0 5px; | ||
| 217 | `; | 113 | `; |
| 218 | 114 | ||
| 219 | export const NewPatientRegisterButtonWrapper = styled.div ` | 115 | export const NewPatientRegisterButtonWrapper = styled.div ` |
| ... | @@ -436,6 +332,7 @@ export const MedicineSearchButtonImg = styled.img ` | ... | @@ -436,6 +332,7 @@ export const MedicineSearchButtonImg = styled.img ` |
| 436 | height : 15px; | 332 | height : 15px; |
| 437 | width : 15px; | 333 | width : 15px; |
| 438 | 334 | ||
| 335 | + transition : .25s all; | ||
| 439 | `; | 336 | `; |
| 440 | 337 | ||
| 441 | export const MedicineSearchResultWrapper = styled.div ` | 338 | export const MedicineSearchResultWrapper = styled.div ` |
| ... | @@ -499,6 +396,91 @@ export const MedicineSelectButtonImg = styled.img ` | ... | @@ -499,6 +396,91 @@ export const MedicineSelectButtonImg = styled.img ` |
| 499 | width : 15px; | 396 | width : 15px; |
| 500 | `; | 397 | `; |
| 501 | 398 | ||
| 399 | +export const MedicineDosageSetWrapper = styled.div ` | ||
| 400 | + width : 80%; | ||
| 401 | + | ||
| 402 | + display : flex; | ||
| 403 | + flex-direction : column; | ||
| 404 | + | ||
| 405 | + justify-content : center; | ||
| 406 | + align-items : center; | ||
| 407 | + | ||
| 408 | + border : none; | ||
| 409 | + | ||
| 410 | + margin : 20px 0; | ||
| 411 | + | ||
| 412 | + height : 200px; | ||
| 413 | +`; | ||
| 414 | + | ||
| 415 | +export const MedicineDosageInfo = styled.div ` | ||
| 416 | + font-size : 15px; | ||
| 417 | + font-weight : 500; | ||
| 418 | + | ||
| 419 | + color : #a0a0a0; | ||
| 420 | + | ||
| 421 | + width : 100%; | ||
| 422 | + margin : 10px 0 10px 0; | ||
| 423 | + | ||
| 424 | + border : none; | ||
| 425 | + background-color : transparent; | ||
| 426 | + | ||
| 427 | + text-align : center; | ||
| 428 | +`; | ||
| 429 | + | ||
| 430 | +export const MedicineDosageInput = styled.input.attrs({ | ||
| 431 | + type : 'number', | ||
| 432 | +}) ` | ||
| 433 | + margin : 0 0 10px 0; | ||
| 434 | + width : 40%; | ||
| 435 | + | ||
| 436 | + padding : 10px 20px; | ||
| 437 | + color : #337DFF; | ||
| 438 | + font-size : 20px; | ||
| 439 | + | ||
| 440 | + font-weight : 700; | ||
| 441 | + | ||
| 442 | + border : none; | ||
| 443 | + border-bottom : 1px solid #337DFF; | ||
| 444 | + | ||
| 445 | + display : flex; | ||
| 446 | + flex-direction : row; | ||
| 447 | + | ||
| 448 | + text-align : center; | ||
| 449 | + | ||
| 450 | + transition : .25s all; | ||
| 451 | +`; | ||
| 452 | + | ||
| 453 | +export const MedicineQRCodeWrapper = styled.div ` | ||
| 454 | + width : 80%; | ||
| 455 | + height : 200px; | ||
| 456 | + | ||
| 457 | + display : flex; | ||
| 458 | + flex-direction : column; | ||
| 459 | + | ||
| 460 | + justify-content : center; | ||
| 461 | + align-items : center; | ||
| 462 | + | ||
| 463 | + margin : 20px 0; | ||
| 464 | + | ||
| 465 | + border : none; | ||
| 466 | +`; | ||
| 467 | + | ||
| 468 | +export const MedicineQRCodeInfo = styled.div ` | ||
| 469 | + font-size : 15px; | ||
| 470 | + font-weight : 500; | ||
| 471 | + | ||
| 472 | + color : #a0a0a0; | ||
| 473 | + | ||
| 474 | + text-align : center; | ||
| 475 | +`; | ||
| 476 | + | ||
| 477 | +export const MedicineQRCode = styled.img ` | ||
| 478 | + margin : 10px 0 0 0; | ||
| 479 | + | ||
| 480 | + height : 170px; | ||
| 481 | + width : 170px; | ||
| 482 | +`; | ||
| 483 | + | ||
| 502 | export const MedicinePrescribeButtonWrapper = styled.div ` | 484 | export const MedicinePrescribeButtonWrapper = styled.div ` |
| 503 | margin : 20px 0 0 0; | 485 | margin : 20px 0 0 0; |
| 504 | 486 | ... | ... |
| ... | @@ -17,11 +17,14 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -17,11 +17,14 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
| 17 | 17 | ||
| 18 | const token = useRecoilValue(recoilUtil.token); | 18 | const token = useRecoilValue(recoilUtil.token); |
| 19 | 19 | ||
| 20 | - const [doctorRegReqList, setDoctorRegReqList] = useState<any>([]); | 20 | + const [doctorList, setDoctorList] = useState<any>([]); |
| 21 | + | ||
| 22 | + const [viewType, setViewType] = useState<string>('reg'); | ||
| 21 | 23 | ||
| 22 | const [doctorDetail, setDoctorDetail] = useState<any>({}); | 24 | const [doctorDetail, setDoctorDetail] = useState<any>({}); |
| 23 | const [modalUp, setModalUp] = useState<boolean>(false); | 25 | const [modalUp, setModalUp] = useState<boolean>(false); |
| 24 | const [validate, setValidate] = useState<string>('W'); | 26 | const [validate, setValidate] = useState<string>('W'); |
| 27 | + const [validateDoctorLicense, setValidateDoctorLicense] = useState<string>(''); | ||
| 25 | 28 | ||
| 26 | 29 | ||
| 27 | 30 | ||
| ... | @@ -30,20 +33,29 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -30,20 +33,29 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
| 30 | setValidate('W'); | 33 | setValidate('W'); |
| 31 | 34 | ||
| 32 | try { | 35 | try { |
| 33 | - await managerApi.getDoctorRegReqList(token) | 36 | + const res = viewType === 'reg' ? await managerApi.getDoctorRegReqList(token) : await managerApi.getDoctorSecReqList(token); |
| 34 | - .then((res : any) => { | 37 | + if(res.statusText === 'OK') { |
| 35 | - if(res.statusText === 'OK') { | 38 | + setDoctorList(res.data.doctorList); |
| 36 | - setDoctorRegReqList(res.data.doctorRegReqList); | 39 | + } else { |
| 37 | - } | 40 | + Alert.onError(res.data.error, () => null); |
| 38 | - }).catch(err => { | 41 | + } |
| 39 | - Alert.onError(err.response.data.error, () => null); | 42 | + } catch(e : any) { |
| 40 | - }) | 43 | + console.log(e); |
| 41 | - } catch(e) { | 44 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
| 42 | - Alert.onError(e.response.data.error, () => null); | ||
| 43 | } | 45 | } |
| 44 | }; | 46 | }; |
| 45 | 47 | ||
| 48 | + //가입요청 보기 | ||
| 49 | + const onViewRegList = () => { | ||
| 50 | + setViewType('reg'); | ||
| 51 | + }; | ||
| 52 | + | ||
| 53 | + //탈퇴요청 보기 | ||
| 54 | + const onViewSecList = () => { | ||
| 55 | + setViewType('sec'); | ||
| 56 | + }; | ||
| 46 | 57 | ||
| 58 | + //가입요청 상세보기 | ||
| 47 | const onViewDetailReq = async (doctorId : string) => { | 59 | const onViewDetailReq = async (doctorId : string) => { |
| 48 | setValidate('W'); | 60 | setValidate('W'); |
| 49 | 61 | ||
| ... | @@ -55,13 +67,23 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -55,13 +67,23 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
| 55 | setModalUp(true); | 67 | setModalUp(true); |
| 56 | } | 68 | } |
| 57 | }) | 69 | }) |
| 58 | - } catch(e) { | 70 | + } catch(e : any) { |
| 59 | - Alert.onError(e.response.data.error, () => setModalUp(false)); | 71 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => setModalUp(false)); |
| 60 | } | 72 | } |
| 61 | }; | 73 | }; |
| 62 | 74 | ||
| 75 | + const onViewLicenseDetail = async (url : string) => { | ||
| 76 | + const licensePage : any = window.open(url); | ||
| 77 | + licensePage.focus(); | ||
| 78 | + }; | ||
| 79 | + | ||
| 80 | + //자격 확인 문서를 보고, 면허 번호를 입력하는 함수 | ||
| 81 | + const onSetValidateDoctorLicense = (e : React.ChangeEvent<HTMLInputElement>) => { | ||
| 82 | + setValidateDoctorLicense(e.target.value); | ||
| 83 | + }; | ||
| 84 | + | ||
| 63 | //회원 가입 수락 | 85 | //회원 가입 수락 |
| 64 | - const onAcceptRequest = () => { | 86 | + const onAcceptRegReq = () => { |
| 65 | if(validate === 'W') { | 87 | if(validate === 'W') { |
| 66 | Alert.onError('먼저 의사의 자격번호가 유효한지 검증해주세요.', () => null); | 88 | Alert.onError('먼저 의사의 자격번호가 유효한지 검증해주세요.', () => null); |
| 67 | return; | 89 | return; |
| ... | @@ -72,14 +94,16 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -72,14 +94,16 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
| 72 | 94 | ||
| 73 | const onAccept = async() => { | 95 | const onAccept = async() => { |
| 74 | try { | 96 | try { |
| 75 | - await managerApi.acceptDoctorRegReq(token, doctorDetail) | 97 | + await managerApi.acceptDoctorRegReq(token, { |
| 76 | - .then((res : any) => { | 98 | + doctorId : doctorDetail.doctorId, |
| 77 | - if(res.statusText === 'OK') { | 99 | + validateDoctorLicense, |
| 78 | - Alert.onSuccess('회원 등록이 완료되었습니다.', fetchData); | 100 | + }).then((res : any) => { |
| 79 | - } | 101 | + if(res.statusText === 'OK') { |
| 80 | - }) | 102 | + Alert.onSuccess('회원 등록이 완료되었습니다.', fetchData); |
| 81 | - } catch(e) { | 103 | + } |
| 82 | - Alert.onError(e.response.data.error, () => setModalUp(false)); | 104 | + }); |
| 105 | + } catch(e : any) { | ||
| 106 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => setModalUp(false)); | ||
| 83 | } | 107 | } |
| 84 | }; | 108 | }; |
| 85 | 109 | ||
| ... | @@ -90,58 +114,95 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -90,58 +114,95 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
| 90 | const onRejectRequest = () => { | 114 | const onRejectRequest = () => { |
| 91 | const onReject = async() => { | 115 | const onReject = async() => { |
| 92 | try { | 116 | try { |
| 93 | - await managerApi.rejectDoctorRegReq(token, doctorDetail) | 117 | + await managerApi.rejectDoctorRegReq(token, { |
| 94 | - .then((res : any) => { | 118 | + doctorId : doctorDetail.doctorId, |
| 95 | - if(res.statusText === 'OK') { | 119 | + }).then((res : any) => { |
| 96 | - Alert.onSuccess('회원 등록이 취소되었습니다.', fetchData); | 120 | + if(res.statusText === 'OK') { |
| 97 | - } | 121 | + Alert.onSuccess('회원 등록이 취소되었습니다.', fetchData); |
| 98 | - }) | 122 | + } |
| 99 | - } catch(e) { | 123 | + }); |
| 100 | - Alert.onError(e.response.data.error, () => setModalUp(false)); | 124 | + } catch(e : any) { |
| 125 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => setModalUp(false)); | ||
| 101 | } | 126 | } |
| 102 | }; | 127 | }; |
| 103 | 128 | ||
| 104 | Alert.onCheck('회원 가입 요청을 취소하시겠습니까?', onReject, () => null); | 129 | Alert.onCheck('회원 가입 요청을 취소하시겠습니까?', onReject, () => null); |
| 105 | }; | 130 | }; |
| 106 | 131 | ||
| 132 | + //회원 자격번호 유효 검증 api | ||
| 107 | const onValidate = async () => { | 133 | const onValidate = async () => { |
| 108 | try { | 134 | try { |
| 109 | await managerApi.validateDoctorLicense(token, { | 135 | await managerApi.validateDoctorLicense(token, { |
| 110 | - doctorLicense : doctorDetail.info.doctorLicense, | 136 | + validateDoctorLicense, |
| 111 | }).then(res => { | 137 | }).then(res => { |
| 112 | if(res.statusText === 'OK') { | 138 | if(res.statusText === 'OK') { |
| 113 | setValidate(res.data.result ? 'Y' : 'N'); | 139 | setValidate(res.data.result ? 'Y' : 'N'); |
| 114 | } | 140 | } |
| 115 | }).catch(err => { | 141 | }).catch(err => { |
| 116 | - Alert.onError(err.response.data, () => { | 142 | + console.log(err); |
| 143 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => { | ||
| 117 | setModalUp(false); | 144 | setModalUp(false); |
| 118 | setValidate('W'); | 145 | setValidate('W'); |
| 119 | }); | 146 | }); |
| 120 | }) | 147 | }) |
| 121 | - } catch(e) { | 148 | + } catch(e : any) { |
| 122 | - Alert.onError(e.response.data, () => { | 149 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => { |
| 123 | setModalUp(false); | 150 | setModalUp(false); |
| 124 | setValidate('W'); | 151 | setValidate('W'); |
| 125 | }); | 152 | }); |
| 126 | } | 153 | } |
| 127 | }; | 154 | }; |
| 128 | 155 | ||
| 156 | + const onAcceptSecReq = (doctorId : string) => { | ||
| 157 | + const onAccept = async () => { | ||
| 158 | + try { | ||
| 159 | + const res = await managerApi.acceptDoctorSecReq(token, { | ||
| 160 | + doctorId, | ||
| 161 | + }); | ||
| 162 | + if(res.statusText === 'OK') { | ||
| 163 | + Alert.onSuccess('탈퇴를 승인했습니다.', fetchData); | ||
| 164 | + } | ||
| 165 | + } catch (e : any) { | ||
| 166 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); | ||
| 167 | + } | ||
| 168 | + }; | ||
| 169 | + | ||
| 170 | + Alert.onCheck('회원 탈퇴를 승인하시겠습니까?\n이 작업은 되돌릴 수 없습니다.', onAccept, () => null); | ||
| 171 | + }; | ||
| 172 | + | ||
| 173 | + | ||
| 174 | + | ||
| 175 | + useEffect(() => { | ||
| 176 | + setValidate('W'); | ||
| 177 | + setValidateDoctorLicense(''); | ||
| 178 | + }, [modalUp]); | ||
| 179 | + | ||
| 129 | useEffect(() => { | 180 | useEffect(() => { |
| 130 | fetchData(); | 181 | fetchData(); |
| 131 | - }, []); | 182 | + }, [viewType]); |
| 132 | 183 | ||
| 133 | return ( | 184 | return ( |
| 134 | <ManagerMenuPresenter | 185 | <ManagerMenuPresenter |
| 135 | - doctorRegReqList = {doctorRegReqList} | 186 | + viewType = {viewType} |
| 187 | + onViewRegList = {onViewRegList} | ||
| 188 | + onViewSecList = {onViewSecList} | ||
| 189 | + | ||
| 190 | + doctorList = {doctorList} | ||
| 136 | 191 | ||
| 137 | doctorDetail = {doctorDetail} | 192 | doctorDetail = {doctorDetail} |
| 138 | modalUp = {modalUp} | 193 | modalUp = {modalUp} |
| 139 | setModalUp = {setModalUp} | 194 | setModalUp = {setModalUp} |
| 140 | onViewDetailReq = {onViewDetailReq} | 195 | onViewDetailReq = {onViewDetailReq} |
| 196 | + onViewLicenseDetail = {onViewLicenseDetail} | ||
| 197 | + | ||
| 141 | validate = {validate} | 198 | validate = {validate} |
| 142 | onValidate = {onValidate} | 199 | onValidate = {onValidate} |
| 143 | 200 | ||
| 144 | - onAcceptRequest = {onAcceptRequest} | 201 | + validateDoctorLicense = {validateDoctorLicense} |
| 202 | + onSetValidateDoctorLicense = {onSetValidateDoctorLicense} | ||
| 203 | + | ||
| 204 | + onAcceptRegReq = {onAcceptRegReq} | ||
| 205 | + onAcceptSecReq = {onAcceptSecReq} | ||
| 145 | onRejectRequest = {onRejectRequest} | 206 | onRejectRequest = {onRejectRequest} |
| 146 | /> | 207 | /> |
| 147 | ); | 208 | ); | ... | ... |
This diff is collapsed. Click to expand it.
| 1 | -import styled, { keyframes } from 'styled-components'; | 1 | +import styled from 'styled-components'; |
| 2 | 2 | ||
| 3 | 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 | - | ||
| 26 | export const Container = styled.div ` | 4 | export const Container = styled.div ` |
| 27 | width : 100%; | 5 | width : 100%; |
| 28 | height : 80vh; | 6 | height : 80vh; |
| ... | @@ -33,92 +11,6 @@ export const Container = styled.div ` | ... | @@ -33,92 +11,6 @@ export const Container = styled.div ` |
| 33 | `; | 11 | `; |
| 34 | 12 | ||
| 35 | 13 | ||
| 36 | -export const ModalContainer = styled.div ` | ||
| 37 | - height : 100%; | ||
| 38 | - width : 100%; | ||
| 39 | - z-index : 99; | ||
| 40 | - position : absolute; | ||
| 41 | - | ||
| 42 | - display : flex; | ||
| 43 | - flex-direction : column; | ||
| 44 | - | ||
| 45 | - animation : ${ModalOn} .5s; | ||
| 46 | - | ||
| 47 | - background-color : rgba(52, 52, 52, .7); | ||
| 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 ModalTitleWrapper = styled.div ` | 14 | export const ModalTitleWrapper = styled.div ` |
| 123 | flex : 1; | 15 | flex : 1; |
| 124 | border : none; | 16 | border : none; |
| ... | @@ -183,25 +75,96 @@ export const ModalInfoExplain = styled.div ` | ... | @@ -183,25 +75,96 @@ export const ModalInfoExplain = styled.div ` |
| 183 | 75 | ||
| 184 | letter-spacing : 1px; | 76 | letter-spacing : 1px; |
| 185 | 77 | ||
| 78 | + display : flex; | ||
| 79 | + flex-direction : row; | ||
| 80 | + justify-content : center; | ||
| 81 | + align-items : center; | ||
| 82 | + | ||
| 186 | border : none; | 83 | border : none; |
| 187 | border-bottom : 1px solid #337DFF; | 84 | border-bottom : 1px solid #337DFF; |
| 188 | 85 | ||
| 189 | color : #337DFF; | 86 | color : #337DFF; |
| 190 | - padding : 2px 5px; | 87 | + padding : 2px 1px; |
| 191 | `; | 88 | `; |
| 192 | 89 | ||
| 193 | export const ModalInfo = styled.div ` | 90 | export const ModalInfo = styled.div ` |
| 194 | - margin : 5px 0 20px 0; | 91 | + margin : 5px 0 10px 0; |
| 195 | - font-size : 20px; | 92 | + font-size : 13px; |
| 196 | - font-weight : 700; | 93 | + font-weight : 600; |
| 94 | + | ||
| 95 | + letter-spacing : 1px; | ||
| 96 | + | ||
| 97 | + display : flex; | ||
| 98 | + flex-direction : row; | ||
| 99 | + align-items : center; | ||
| 100 | +`; | ||
| 101 | + | ||
| 102 | +export const ModalInfoNotice = styled.div ` | ||
| 103 | + font-size : 11px; | ||
| 104 | + color : #bbb; | ||
| 105 | + | ||
| 106 | + font-weight : 400; | ||
| 107 | + | ||
| 108 | + letter-spacing : 0px; | ||
| 109 | + | ||
| 110 | + margin : 5px 0 0px 0; | ||
| 111 | +`; | ||
| 112 | + | ||
| 113 | +export const DoctorLicenseViewWrapper = styled.div ` | ||
| 114 | + display : flex; | ||
| 115 | + flex-direction : row; | ||
| 116 | + | ||
| 117 | + justify-content : center; | ||
| 118 | + align-items : center; | ||
| 119 | + | ||
| 120 | + border : none; | ||
| 121 | + background-color : transparent; | ||
| 122 | +`; | ||
| 123 | + | ||
| 124 | +export const DoctorLicenseViewButton = styled.button ` | ||
| 125 | + margin : 5px 0 0 7px; | ||
| 126 | + | ||
| 127 | + border : 1px solid #343434; | ||
| 128 | + border-radius : 3px; | ||
| 129 | + background-color : #EAF2FF; | ||
| 130 | + padding : 2px 5px; | ||
| 197 | 131 | ||
| 198 | display : flex; | 132 | display : flex; |
| 199 | flex-direction : row; | 133 | flex-direction : row; |
| 134 | + justify-content : center; | ||
| 200 | align-items : center; | 135 | align-items : center; |
| 136 | + | ||
| 137 | + cursor : pointer; | ||
| 138 | + transition : .25s all; | ||
| 139 | + | ||
| 140 | + &:hover { | ||
| 141 | + border : 1px solid #337DFF; | ||
| 142 | + background-color : #337DFF; | ||
| 143 | + color : #fff; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + font-size : 11px; | ||
| 147 | +`; | ||
| 148 | + | ||
| 149 | +export const DoctorLicenseViewInput = styled.input ` | ||
| 150 | + padding : 2px 1px; | ||
| 151 | + | ||
| 152 | + font-size : 11px; | ||
| 153 | + letter-spacing : 1px; | ||
| 154 | + color : #343434; | ||
| 155 | + | ||
| 156 | + border : none; | ||
| 157 | + border-bottom : 1px solid #343434; | ||
| 158 | + | ||
| 159 | + &::placeholder { | ||
| 160 | + color : #ccc; | ||
| 161 | + } | ||
| 201 | `; | 162 | `; |
| 202 | 163 | ||
| 203 | export const ValidateButton = styled.button<{validate : string}> ` | 164 | export const ValidateButton = styled.button<{validate : string}> ` |
| 204 | - margin : 0 0 0 15px; | 165 | + font-size : 11px; |
| 166 | + | ||
| 167 | + margin : 0 0 0 5px; | ||
| 205 | padding : 2px 5px; | 168 | padding : 2px 5px; |
| 206 | 169 | ||
| 207 | border-radius : 3px; | 170 | border-radius : 3px; |
| ... | @@ -288,8 +251,45 @@ export const ContentWrapper = styled.div ` | ... | @@ -288,8 +251,45 @@ export const ContentWrapper = styled.div ` |
| 288 | 251 | ||
| 289 | `; | 252 | `; |
| 290 | 253 | ||
| 254 | +export const ContentButtonWrapper = styled.div ` | ||
| 255 | + width : 100%; | ||
| 256 | + height : 10%; | ||
| 257 | + border : none; | ||
| 258 | + | ||
| 259 | + display : flex; | ||
| 260 | + flex-direction : row; | ||
| 261 | + justify-content : center; | ||
| 262 | + align-items : flex-end; | ||
| 263 | + | ||
| 264 | + gap : 10%; | ||
| 265 | + | ||
| 266 | + background-color : transparent; | ||
| 267 | +`; | ||
| 268 | + | ||
| 269 | +export const ContentButton = styled.button<{isSelect : boolean}> ` | ||
| 270 | + background-color : ${props => props.isSelect ? '#337DFF' : 'transparent'}; | ||
| 271 | + color : ${props => props.isSelect ? '#fff' : '#337DFF'}; | ||
| 272 | + border : 1px solid #337DFF; | ||
| 273 | + border-radius : 4px; | ||
| 274 | + | ||
| 275 | + padding : 4px 10px; | ||
| 276 | + | ||
| 277 | + cursor : pointer; | ||
| 278 | + | ||
| 279 | + display : flex; | ||
| 280 | + justify-content : center; | ||
| 281 | + align-items : center; | ||
| 282 | + | ||
| 283 | + transition : .25s all; | ||
| 284 | + | ||
| 285 | + &:hover { | ||
| 286 | + opacity : .5; | ||
| 287 | + } | ||
| 288 | +`; | ||
| 289 | + | ||
| 291 | export const ContentTitle = styled.div ` | 290 | export const ContentTitle = styled.div ` |
| 292 | width : 100%; | 291 | width : 100%; |
| 292 | + height : 20%; | ||
| 293 | border : none; | 293 | border : none; |
| 294 | border-bottom : 1px solid #ddd; | 294 | border-bottom : 1px solid #ddd; |
| 295 | 295 | ||
| ... | @@ -298,7 +298,6 @@ export const ContentTitle = styled.div ` | ... | @@ -298,7 +298,6 @@ export const ContentTitle = styled.div ` |
| 298 | justify-content : center; | 298 | justify-content : center; |
| 299 | align-items : center; | 299 | align-items : center; |
| 300 | 300 | ||
| 301 | - padding : 4% 0; | ||
| 302 | font-size : 22px; | 301 | font-size : 22px; |
| 303 | font-weight : 600; | 302 | font-weight : 600; |
| 304 | letter-spacing : 1px; | 303 | letter-spacing : 1px; |
| ... | @@ -318,16 +317,17 @@ export const ContentExplain = styled.div ` | ... | @@ -318,16 +317,17 @@ export const ContentExplain = styled.div ` |
| 318 | export const ContentBody = styled.div ` | 317 | export const ContentBody = styled.div ` |
| 319 | overflow : scroll; | 318 | overflow : scroll; |
| 320 | 319 | ||
| 321 | - height : 79%; | 320 | + min-height : 60%; |
| 321 | + max-height : 60%; | ||
| 322 | 322 | ||
| 323 | border : none; | 323 | border : none; |
| 324 | 324 | ||
| 325 | - padding : 0 0 0 3px; | ||
| 326 | - | ||
| 327 | display : flex; | 325 | display : flex; |
| 328 | flex-direction : column; | 326 | flex-direction : column; |
| 329 | align-items : center; | 327 | align-items : center; |
| 330 | 328 | ||
| 329 | + padding : 0 0 0 3px; | ||
| 330 | + | ||
| 331 | &::-webkit-scrollbar { | 331 | &::-webkit-scrollbar { |
| 332 | width : 3px; | 332 | width : 3px; |
| 333 | background-color : transparent; | 333 | background-color : transparent; |
| ... | @@ -341,6 +341,7 @@ export const ContentBody = styled.div ` | ... | @@ -341,6 +341,7 @@ export const ContentBody = styled.div ` |
| 341 | 341 | ||
| 342 | export const ContentInfoWrapper = styled.div ` | 342 | export const ContentInfoWrapper = styled.div ` |
| 343 | width : 100%; | 343 | width : 100%; |
| 344 | + height : 10%; | ||
| 344 | border : none; | 345 | border : none; |
| 345 | border-bottom : 1px solid #a0a0a0; | 346 | border-bottom : 1px solid #a0a0a0; |
| 346 | 347 | ||
| ... | @@ -350,7 +351,6 @@ export const ContentInfoWrapper = styled.div ` | ... | @@ -350,7 +351,6 @@ export const ContentInfoWrapper = styled.div ` |
| 350 | justify-content : center; | 351 | justify-content : center; |
| 351 | align-items : center; | 352 | align-items : center; |
| 352 | 353 | ||
| 353 | - padding : 12px 0px; | ||
| 354 | `; | 354 | `; |
| 355 | 355 | ||
| 356 | export const ContentInfo = styled.div<{isLast : boolean}> ` | 356 | export const ContentInfo = styled.div<{isLast : boolean}> ` |
| ... | @@ -388,13 +388,15 @@ export const EachContentWrapper = styled.button ` | ... | @@ -388,13 +388,15 @@ export const EachContentWrapper = styled.button ` |
| 388 | 388 | ||
| 389 | padding : 10px 0px; | 389 | padding : 10px 0px; |
| 390 | 390 | ||
| 391 | - cursor : pointer; | 391 | + :not(:disabled) { |
| 392 | + cursor : pointer; | ||
| 392 | 393 | ||
| 393 | - transition : .1s all; | 394 | + transition : .1s all; |
| 394 | 395 | ||
| 395 | - &:hover { | 396 | + &:hover { |
| 396 | - background-color : #337DFF; | 397 | + background-color : #337DFF; |
| 397 | - color : #fff; | 398 | + color : #fff; |
| 399 | + } | ||
| 398 | } | 400 | } |
| 399 | 401 | ||
| 400 | `; | 402 | `; |
| ... | @@ -417,10 +419,33 @@ export const EachContentNm = styled.div<{isLast : boolean}> ` | ... | @@ -417,10 +419,33 @@ export const EachContentNm = styled.div<{isLast : boolean}> ` |
| 417 | 419 | ||
| 418 | `; | 420 | `; |
| 419 | 421 | ||
| 422 | +export const AcceptButton = styled.button ` | ||
| 423 | + background-color : transparent; | ||
| 424 | + color : #337DFF; | ||
| 425 | + border : 1px solid #337DFF; | ||
| 426 | + border-radius : 3px; | ||
| 427 | + padding : 2px 10px; | ||
| 428 | + | ||
| 429 | + display : flex; | ||
| 430 | + justify-content : center; | ||
| 431 | + align-items : center; | ||
| 432 | + | ||
| 433 | + cursor : pointer; | ||
| 434 | + | ||
| 435 | + transition : .25s all; | ||
| 436 | + | ||
| 437 | + &:hover { | ||
| 438 | + background-color : #337DFF; | ||
| 439 | + color : #fff; | ||
| 440 | + } | ||
| 441 | +`; | ||
| 442 | + | ||
| 420 | export const NothingWrapper = styled.div ` | 443 | export const NothingWrapper = styled.div ` |
| 421 | height : 100%; | 444 | height : 100%; |
| 422 | width : 100%; | 445 | width : 100%; |
| 423 | 446 | ||
| 447 | + border : none; | ||
| 448 | + | ||
| 424 | display : flex; | 449 | display : flex; |
| 425 | justify-content : center; | 450 | justify-content : center; |
| 426 | align-items : center; | 451 | align-items : center; | ... | ... |
| 1 | -import React, { useState, useEffect } from "react"; | 1 | +import React, { useState, useEffect, useRef } 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 validator from 'validator'; | 7 | import validator from 'validator'; |
| ... | @@ -11,7 +11,6 @@ import Header from '../../components/Header'; | ... | @@ -11,7 +11,6 @@ import Header from '../../components/Header'; |
| 11 | import RegisterPresenter from "./RegisterPresenter"; | 11 | import RegisterPresenter from "./RegisterPresenter"; |
| 12 | 12 | ||
| 13 | import { authApi } from '../../api'; | 13 | import { authApi } from '../../api'; |
| 14 | -import { resourceLimits } from "worker_threads"; | ||
| 15 | 14 | ||
| 16 | 15 | ||
| 17 | // eslint-disable-next-line @typescript-eslint/no-empty-interface | 16 | // eslint-disable-next-line @typescript-eslint/no-empty-interface |
| ... | @@ -20,13 +19,13 @@ interface RegisterProps extends RouteComponentProps {} | ... | @@ -20,13 +19,13 @@ interface RegisterProps extends RouteComponentProps {} |
| 20 | const RegisterContainer = (props : RegisterProps) => { | 19 | const RegisterContainer = (props : RegisterProps) => { |
| 21 | 20 | ||
| 22 | const token = useRecoilValue(recoilUtil.token); | 21 | const token = useRecoilValue(recoilUtil.token); |
| 22 | + const [loading, setLoading] = useRecoilState(recoilUtil.loading); | ||
| 23 | 23 | ||
| 24 | const [registerForm, setRegisterForm] = useState<any>({ | 24 | const [registerForm, setRegisterForm] = useState<any>({ |
| 25 | userId : '', | 25 | userId : '', |
| 26 | password : '', | 26 | password : '', |
| 27 | passwordCheck : '', | 27 | passwordCheck : '', |
| 28 | info : { | 28 | info : { |
| 29 | - doctorLicense : '', | ||
| 30 | hospitalNm : '', | 29 | hospitalNm : '', |
| 31 | hospitalAddr : '', | 30 | hospitalAddr : '', |
| 32 | contact : '', | 31 | contact : '', |
| ... | @@ -34,10 +33,18 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -34,10 +33,18 @@ const RegisterContainer = (props : RegisterProps) => { |
| 34 | doctorNm : '', | 33 | doctorNm : '', |
| 35 | }, | 34 | }, |
| 36 | }); | 35 | }); |
| 36 | + const [doctorInfoFile, setDoctorInfoFile] = useState<FileList | null>(null); | ||
| 37 | + const doctorInfoFile_Select = useRef(null); | ||
| 37 | 38 | ||
| 38 | const [page, setPage] = useState<number>(1); | 39 | const [page, setPage] = useState<number>(1); |
| 39 | const [error, setError] = useState<string | null>(null); | 40 | const [error, setError] = useState<string | null>(null); |
| 40 | 41 | ||
| 42 | + const [searchHospital, setSearchHospital] = useState<boolean>(false); | ||
| 43 | + const [hospitalNm, setHospitalNm] = useState<string>(''); | ||
| 44 | + const [hospitalSearchPage, setHospitalSearchPage] = useState<number>(1); | ||
| 45 | + const [hospitalSearchPageList, setHospitalSearchPageList] = useState<number[]>([1]); | ||
| 46 | + const [hospitalList, setHospitalList] = useState<any[]>([]); | ||
| 47 | + const [selectHospital, setSelectHospital] = useState<any>(null); | ||
| 41 | 48 | ||
| 42 | 49 | ||
| 43 | const fetchData = async() => { | 50 | const fetchData = async() => { |
| ... | @@ -46,7 +53,7 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -46,7 +53,7 @@ const RegisterContainer = (props : RegisterProps) => { |
| 46 | if (result.statusText === 'OK') { | 53 | if (result.statusText === 'OK') { |
| 47 | props.history.push('/'); | 54 | props.history.push('/'); |
| 48 | } | 55 | } |
| 49 | - } | 56 | + } |
| 50 | }; | 57 | }; |
| 51 | 58 | ||
| 52 | const onCancleRegister = () => { | 59 | const onCancleRegister = () => { |
| ... | @@ -61,7 +68,6 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -61,7 +68,6 @@ const RegisterContainer = (props : RegisterProps) => { |
| 61 | } | 68 | } |
| 62 | }; | 69 | }; |
| 63 | 70 | ||
| 64 | - | ||
| 65 | const validateRegisterForm = () => { | 71 | const validateRegisterForm = () => { |
| 66 | if(page === 1) { | 72 | if(page === 1) { |
| 67 | if (!validator.isEmail(registerForm.userId)) { | 73 | if (!validator.isEmail(registerForm.userId)) { |
| ... | @@ -74,9 +80,8 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -74,9 +80,8 @@ const RegisterContainer = (props : RegisterProps) => { |
| 74 | setError('비밀번호가 일치하지 않습니다.') | 80 | setError('비밀번호가 일치하지 않습니다.') |
| 75 | } else setError(null); | 81 | } else setError(null); |
| 76 | } else if(page === 2) { | 82 | } else if(page === 2) { |
| 77 | - if(!registerForm.info.doctorLicense.length && | 83 | + if(!doctorInfoFile) { |
| 78 | - !validator.isAlphanumeric(registerForm.info.doctorLicense)) { | 84 | + setError('의사 자격 인증 파일을 첨부해야 합니다.'); |
| 79 | - setError('의사 자격 번호를 입력해야 합니다.'); | ||
| 80 | } else if(registerForm.info.doctorNm.length < 2) { | 85 | } else if(registerForm.info.doctorNm.length < 2) { |
| 81 | setError('의사 이름을 올바르게 입력해야 합니다.'); | 86 | setError('의사 이름을 올바르게 입력해야 합니다.'); |
| 82 | } else if(!registerForm.info.contact) { | 87 | } else if(!registerForm.info.contact) { |
| ... | @@ -116,33 +121,11 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -116,33 +121,11 @@ const RegisterContainer = (props : RegisterProps) => { |
| 116 | }; | 121 | }; |
| 117 | 122 | ||
| 118 | const onSetDoctorLicense = (e : React.ChangeEvent<HTMLInputElement>) => { | 123 | const onSetDoctorLicense = (e : React.ChangeEvent<HTMLInputElement>) => { |
| 119 | - setRegisterForm({ | 124 | + setDoctorInfoFile(e.target.files); |
| 120 | - ...registerForm, | ||
| 121 | - info : { | ||
| 122 | - ...registerForm.info, | ||
| 123 | - doctorLicense : e.target.value, | ||
| 124 | - }, | ||
| 125 | - }); | ||
| 126 | }; | 125 | }; |
| 127 | 126 | ||
| 128 | const onSetHospitalNm = (e : React.ChangeEvent<HTMLInputElement>) => { | 127 | const onSetHospitalNm = (e : React.ChangeEvent<HTMLInputElement>) => { |
| 129 | - setRegisterForm({ | 128 | + setHospitalNm(e.target.value); |
| 130 | - ...registerForm, | ||
| 131 | - info : { | ||
| 132 | - ...registerForm.info, | ||
| 133 | - hospitalNm : e.target.value, | ||
| 134 | - }, | ||
| 135 | - }); | ||
| 136 | - }; | ||
| 137 | - | ||
| 138 | - const onSetHospitalAddr = (e : React.ChangeEvent<HTMLInputElement>) => { | ||
| 139 | - setRegisterForm({ | ||
| 140 | - ...registerForm, | ||
| 141 | - info : { | ||
| 142 | - ...registerForm.info, | ||
| 143 | - hospitalAddr : e.target.value, | ||
| 144 | - }, | ||
| 145 | - }); | ||
| 146 | }; | 129 | }; |
| 147 | 130 | ||
| 148 | const onSetContact = (e : React.ChangeEvent<HTMLInputElement>) => { | 131 | const onSetContact = (e : React.ChangeEvent<HTMLInputElement>) => { |
| ... | @@ -175,6 +158,56 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -175,6 +158,56 @@ const RegisterContainer = (props : RegisterProps) => { |
| 175 | }); | 158 | }); |
| 176 | }; | 159 | }; |
| 177 | 160 | ||
| 161 | + const onSearchHospital = async () => { | ||
| 162 | + try { | ||
| 163 | + setLoading(true); | ||
| 164 | + setSearchHospital(true); | ||
| 165 | + const result = await authApi.searchHospital(hospitalNm, hospitalSearchPage); | ||
| 166 | + if(result.statusText === 'OK') { | ||
| 167 | + setLoading(false); | ||
| 168 | + setHospitalSearchPageList(new Array(result.data.totalPage).fill(null).map((item : null, index : number) => index + 1)); | ||
| 169 | + setHospitalList(result.data.hospitalList.length ? result.data.hospitalList : [result.data.hospitalList]); | ||
| 170 | + } | ||
| 171 | + } catch(e : any) { | ||
| 172 | + setLoading(false); | ||
| 173 | + Alert.onError('알 수 없는 에러로 검색에 실패했습니다.', () => null); | ||
| 174 | + } | ||
| 175 | + }; | ||
| 176 | + | ||
| 177 | + const onSetSearchPrevPage = () => { | ||
| 178 | + //set Prev Page | ||
| 179 | + const pageSlice = 5; | ||
| 180 | + if(hospitalSearchPage > pageSlice) { | ||
| 181 | + setHospitalSearchPage(Math.floor((hospitalSearchPage - 1) / pageSlice) * pageSlice); | ||
| 182 | + } | ||
| 183 | + }; | ||
| 184 | + | ||
| 185 | + const onSetSearchNextPage = () => { | ||
| 186 | + //set Next Page | ||
| 187 | + const pageSlice = 5; | ||
| 188 | + if(hospitalSearchPage <= Math.floor((hospitalSearchPageList.length - 1) / pageSlice) * pageSlice) { | ||
| 189 | + setHospitalSearchPage(Math.ceil(hospitalSearchPage / pageSlice) * pageSlice + 1); | ||
| 190 | + } | ||
| 191 | + }; | ||
| 192 | + | ||
| 193 | + const onConfirmSelectHospital = () => { | ||
| 194 | + setSearchHospital(false); | ||
| 195 | + setHospitalSearchPage(1); | ||
| 196 | + setHospitalSearchPageList([1]); | ||
| 197 | + setHospitalList([]); | ||
| 198 | + }; | ||
| 199 | + | ||
| 200 | + const onCancelSelectHospital = () => { | ||
| 201 | + Alert.onCheck('병원 등록이 취소됩니다. 계속하시겠습니까?', () => { | ||
| 202 | + setSearchHospital(false); | ||
| 203 | + setHospitalNm(''); | ||
| 204 | + setHospitalSearchPage(1); | ||
| 205 | + setHospitalSearchPageList([1]); | ||
| 206 | + setHospitalList([]); | ||
| 207 | + setSelectHospital(null); | ||
| 208 | + }, () => null); | ||
| 209 | + }; | ||
| 210 | + | ||
| 178 | const onSubmitButton = () => { | 211 | const onSubmitButton = () => { |
| 179 | if(error) { | 212 | if(error) { |
| 180 | Alert.onError(error, () => null); | 213 | Alert.onError(error, () => null); |
| ... | @@ -186,26 +219,80 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -186,26 +219,80 @@ const RegisterContainer = (props : RegisterProps) => { |
| 186 | } else if(page === 2) { | 219 | } else if(page === 2) { |
| 187 | setPage(3); | 220 | setPage(3); |
| 188 | } else if(page === 3) { | 221 | } else if(page === 3) { |
| 222 | + | ||
| 223 | + const Data = new FormData(); | ||
| 224 | + Data.append('userId', registerForm.userId); | ||
| 225 | + Data.append('password', registerForm.password); | ||
| 226 | + Data.append('passwordCheck', registerForm.passwordCheck); | ||
| 227 | + | ||
| 228 | + Data.append('hospitalNm', registerForm.info.hospitalNm); | ||
| 229 | + Data.append('hospitalAddr', registerForm.info.hospitalAddr); | ||
| 230 | + Data.append('contact', registerForm.info.contact); | ||
| 231 | + Data.append('doctorNm', registerForm.info.doctorNm); | ||
| 232 | + Data.append('doctorType', registerForm.info.doctorType); | ||
| 233 | + | ||
| 234 | + Data.append('doctorInfoFile', doctorInfoFile ? doctorInfoFile[0] : ''); | ||
| 235 | + | ||
| 236 | + | ||
| 189 | const onRegisterDoctor = async () => { | 237 | const onRegisterDoctor = async () => { |
| 238 | + //로딩 진행 | ||
| 239 | + setLoading(true); | ||
| 240 | + | ||
| 190 | try { | 241 | try { |
| 191 | - const result = await authApi.registerDoctor(registerForm); | 242 | + const result = await authApi.registerDoctor(Data); |
| 192 | if(result.data === 'Created') { | 243 | if(result.data === 'Created') { |
| 244 | + setLoading(false); | ||
| 193 | Alert.onSuccess('회원가입 성공, 관리자의 승인을 대기하세요.', () => props.history.push('/login')); | 245 | Alert.onSuccess('회원가입 성공, 관리자의 승인을 대기하세요.', () => props.history.push('/login')); |
| 194 | } | 246 | } |
| 195 | - } catch(e) { | 247 | + } catch(e : any) { |
| 248 | + setLoading(false); | ||
| 196 | Alert.onError(e.response.data.error, () => null); | 249 | Alert.onError(e.response.data.error, () => null); |
| 197 | } | 250 | } |
| 198 | }; | 251 | }; |
| 199 | 252 | ||
| 200 | - Alert.onCheck('입력하신 정보로 회원가입을 진행하시겠습니까?', onRegisterDoctor, () => null); | 253 | + if(selectHospital) { |
| 254 | + Alert.onCheck('입력하신 정보로 회원가입을 진행하시겠습니까?', onRegisterDoctor, () => null); | ||
| 255 | + } else { | ||
| 256 | + Alert.onError('검색 버튼을 눌러 병원을 선택해주세요.', () => null); | ||
| 257 | + } | ||
| 258 | + | ||
| 201 | } | 259 | } |
| 202 | 260 | ||
| 203 | }; | 261 | }; |
| 204 | 262 | ||
| 263 | + | ||
| 264 | + | ||
| 205 | useEffect(() => { | 265 | useEffect(() => { |
| 206 | validateRegisterForm(); | 266 | validateRegisterForm(); |
| 207 | - }, [registerForm, page]); | 267 | + }, [registerForm, doctorInfoFile, page]); |
| 268 | + | ||
| 269 | + useEffect(() => { | ||
| 270 | + if(selectHospital) { | ||
| 271 | + setHospitalNm(selectHospital.yadmNm); | ||
| 272 | + setRegisterForm({ | ||
| 273 | + ...registerForm, | ||
| 274 | + info : { | ||
| 275 | + ...registerForm.info, | ||
| 276 | + hospitalNm : selectHospital.yadmNm, | ||
| 277 | + hospitalAddr : selectHospital.addr, | ||
| 278 | + }, | ||
| 279 | + }); | ||
| 280 | + } else { | ||
| 281 | + setHospitalNm(''); | ||
| 282 | + setRegisterForm({ | ||
| 283 | + ...registerForm, | ||
| 284 | + info : { | ||
| 285 | + ...registerForm.info, | ||
| 286 | + hospitalNm : '', | ||
| 287 | + hospitalAddr : '', | ||
| 288 | + }, | ||
| 289 | + }); | ||
| 290 | + } | ||
| 291 | + }, [selectHospital]); | ||
| 208 | 292 | ||
| 293 | + useEffect(() => { | ||
| 294 | + if(searchHospital) onSearchHospital(); | ||
| 295 | + }, [hospitalSearchPage]); | ||
| 209 | 296 | ||
| 210 | useEffect(() => { | 297 | useEffect(() => { |
| 211 | fetchData(); | 298 | fetchData(); |
| ... | @@ -218,6 +305,8 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -218,6 +305,8 @@ const RegisterContainer = (props : RegisterProps) => { |
| 218 | <Header {...props}/> | 305 | <Header {...props}/> |
| 219 | <RegisterPresenter | 306 | <RegisterPresenter |
| 220 | registerForm = {registerForm} | 307 | registerForm = {registerForm} |
| 308 | + doctorInfoFile = {doctorInfoFile} | ||
| 309 | + doctorInfoFile_Select = {doctorInfoFile_Select} | ||
| 221 | page = {page} | 310 | page = {page} |
| 222 | error = {error} | 311 | error = {error} |
| 223 | 312 | ||
| ... | @@ -228,12 +317,28 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -228,12 +317,28 @@ const RegisterContainer = (props : RegisterProps) => { |
| 228 | onSetPassword = {onSetPassword} | 317 | onSetPassword = {onSetPassword} |
| 229 | onSetPasswordCheck = {onSetPasswordCheck} | 318 | onSetPasswordCheck = {onSetPasswordCheck} |
| 230 | onSetDoctorLicense = {onSetDoctorLicense} | 319 | onSetDoctorLicense = {onSetDoctorLicense} |
| 320 | + hospitalNm = {hospitalNm} | ||
| 231 | onSetHospitalNm = {onSetHospitalNm} | 321 | onSetHospitalNm = {onSetHospitalNm} |
| 232 | - onSetHospitalAddr = {onSetHospitalAddr} | ||
| 233 | onSetContact = {onSetContact} | 322 | onSetContact = {onSetContact} |
| 234 | onSetDoctorType = {onSetDoctorType} | 323 | onSetDoctorType = {onSetDoctorType} |
| 235 | onSetDoctorNm = {onSetDoctorNm} | 324 | onSetDoctorNm = {onSetDoctorNm} |
| 236 | onSubmitButton = {onSubmitButton} | 325 | onSubmitButton = {onSubmitButton} |
| 326 | + | ||
| 327 | + searchHospital = {searchHospital} | ||
| 328 | + setSearchHospital = {setSearchHospital} | ||
| 329 | + onSearchHospital = {onSearchHospital} | ||
| 330 | + hospitalSearchPage = {hospitalSearchPage} | ||
| 331 | + setHospitalSearchPage = {setHospitalSearchPage} | ||
| 332 | + hospitalSearchPageList = {hospitalSearchPageList} | ||
| 333 | + onSetSearchPrevPage = {onSetSearchPrevPage} | ||
| 334 | + onSetSearchNextPage = {onSetSearchNextPage} | ||
| 335 | + | ||
| 336 | + onConfirmSelectHospital = {onConfirmSelectHospital} | ||
| 337 | + onCancelSelectHospital = {onCancelSelectHospital} | ||
| 338 | + | ||
| 339 | + hospitalList = {hospitalList} | ||
| 340 | + selectHospital = {selectHospital} | ||
| 341 | + setSelectHospital = {setSelectHospital} | ||
| 237 | /> | 342 | /> |
| 238 | </> | 343 | </> |
| 239 | ) | 344 | ) | ... | ... |
This diff is collapsed. Click to expand it.
| ... | @@ -10,6 +10,198 @@ export const Container = styled.div ` | ... | @@ -10,6 +10,198 @@ export const Container = styled.div ` |
| 10 | align-items : center; | 10 | align-items : center; |
| 11 | `; | 11 | `; |
| 12 | 12 | ||
| 13 | +export const SearchTitle = styled.div ` | ||
| 14 | + font-weight : 600; | ||
| 15 | + font-size : 20; | ||
| 16 | + | ||
| 17 | + color : #337DFF; | ||
| 18 | + | ||
| 19 | + display : flex; | ||
| 20 | + flex-direction : row; | ||
| 21 | + | ||
| 22 | + align-items : center; | ||
| 23 | + justify-content : center; | ||
| 24 | + | ||
| 25 | +`; | ||
| 26 | + | ||
| 27 | +export const SearchResultCount = styled.div ` | ||
| 28 | + color : #343434; | ||
| 29 | +`; | ||
| 30 | + | ||
| 31 | +export const HospitalListWrapper = styled.div ` | ||
| 32 | + margin : 20px 0; | ||
| 33 | + | ||
| 34 | + height : 200px; | ||
| 35 | + width : 80%; | ||
| 36 | + | ||
| 37 | + border : 1px solid #337DFF; | ||
| 38 | + border-radius : 3px; | ||
| 39 | + | ||
| 40 | + display : flex; | ||
| 41 | + flex-direction : column; | ||
| 42 | +`; | ||
| 43 | + | ||
| 44 | +export const HospitalListInfo = styled.div ` | ||
| 45 | + | ||
| 46 | + height : 25px; | ||
| 47 | + width : 100%; | ||
| 48 | + | ||
| 49 | + border : none; | ||
| 50 | + border-bottom : 2px solid #ddd; | ||
| 51 | + | ||
| 52 | + display : flex; | ||
| 53 | + flex-direction : row; | ||
| 54 | +`; | ||
| 55 | + | ||
| 56 | +export const HospitalListInfoEach = styled.div<{isLast : boolean}> ` | ||
| 57 | + flex : ${props => props.isLast ? '1' : '3'}; | ||
| 58 | + | ||
| 59 | + display : flex; | ||
| 60 | + align-items : center; | ||
| 61 | + justify-content : center; | ||
| 62 | + | ||
| 63 | + font-size : 14px; | ||
| 64 | + font-weight : 500; | ||
| 65 | + | ||
| 66 | + color : #343434; | ||
| 67 | + | ||
| 68 | + border : none; | ||
| 69 | + border-right : ${props => props.isLast ? 'none' : '1px solid #ddd'}; | ||
| 70 | + | ||
| 71 | + padding : 3px 5px; | ||
| 72 | +`; | ||
| 73 | + | ||
| 74 | +export const HospitalListEach = styled.div ` | ||
| 75 | + min-height : 34px; | ||
| 76 | + max-height : 34px; | ||
| 77 | + width : 100%; | ||
| 78 | + | ||
| 79 | + display : flex; | ||
| 80 | + flex-direction : row; | ||
| 81 | + | ||
| 82 | + border : none; | ||
| 83 | + border-bottom : 1px solid #ddd; | ||
| 84 | +`; | ||
| 85 | + | ||
| 86 | +export const HospitalListEachInfo = styled.div<{isLast : boolean}> ` | ||
| 87 | + flex : ${props => props.isLast ? '1' : '3'}; | ||
| 88 | + | ||
| 89 | + display : flex; | ||
| 90 | + | ||
| 91 | + font-size : 12px; | ||
| 92 | + font-weight : 500; | ||
| 93 | + | ||
| 94 | + justify-content : center; | ||
| 95 | + align-items : center; | ||
| 96 | + | ||
| 97 | + border : none; | ||
| 98 | + border-right : ${props => props.isLast ? 'none' : '1px solid #ddd'}; | ||
| 99 | + | ||
| 100 | + padding : 3px 5px; | ||
| 101 | +`; | ||
| 102 | + | ||
| 103 | +export const CheckButton = styled.button ` | ||
| 104 | + border : none; | ||
| 105 | + background-color : transparent; | ||
| 106 | + | ||
| 107 | + height : 15px; | ||
| 108 | + width : 15px; | ||
| 109 | + | ||
| 110 | + display : flex; | ||
| 111 | + flex-direction : row; | ||
| 112 | + justify-content : center; | ||
| 113 | + | ||
| 114 | + cursor : pointer; | ||
| 115 | + transition : .25s all; | ||
| 116 | + | ||
| 117 | + &:hover { | ||
| 118 | + opacity : .5; | ||
| 119 | + } | ||
| 120 | +`; | ||
| 121 | + | ||
| 122 | +export const CheckButtonImg = styled.img ` | ||
| 123 | + height : 15px; | ||
| 124 | + width : 15px; | ||
| 125 | +`; | ||
| 126 | + | ||
| 127 | +export const PageWrapper = styled.div ` | ||
| 128 | + width : 50%; | ||
| 129 | + display : flex; | ||
| 130 | + flex-direction : row; | ||
| 131 | + | ||
| 132 | + justify-content : center; | ||
| 133 | + align-items : center; | ||
| 134 | + | ||
| 135 | + gap : 2%; | ||
| 136 | +`; | ||
| 137 | + | ||
| 138 | +export const PageButton = styled.button<{isSelect : boolean}> ` | ||
| 139 | + height : 18px; | ||
| 140 | + width : 18px; | ||
| 141 | + | ||
| 142 | + display : flex; | ||
| 143 | + align-items : center; | ||
| 144 | + justify-content : center; | ||
| 145 | + | ||
| 146 | + border : none; | ||
| 147 | + border-radius : 4px; | ||
| 148 | + background-color : ${props => props.isSelect ? '#337DFF' : 'transparent'}; | ||
| 149 | + color : ${props => props.isSelect ? '#fff' : '#343434'}; | ||
| 150 | + | ||
| 151 | + font-size : 12px; | ||
| 152 | + font-weight : 600; | ||
| 153 | + | ||
| 154 | + cursor : pointer; | ||
| 155 | + | ||
| 156 | + transition : .25s all; | ||
| 157 | + | ||
| 158 | + &:hover { | ||
| 159 | + opacity : .7; | ||
| 160 | + } | ||
| 161 | +`; | ||
| 162 | + | ||
| 163 | +export const PageArrowImg = styled.img ` | ||
| 164 | + height : 15px; | ||
| 165 | + width : 15px; | ||
| 166 | +`; | ||
| 167 | + | ||
| 168 | +export const ModalButtonWrapper = styled.div ` | ||
| 169 | + margin : 20px 0 0 0; | ||
| 170 | + width : 50%; | ||
| 171 | + | ||
| 172 | + display : flex; | ||
| 173 | + flex-direction : row; | ||
| 174 | + | ||
| 175 | + justify-content : center; | ||
| 176 | + align-items : center; | ||
| 177 | + | ||
| 178 | + border : none; | ||
| 179 | + | ||
| 180 | + gap : 10%; | ||
| 181 | +`; | ||
| 182 | + | ||
| 183 | +export const ModalButton = styled.div<{isCloseButton : boolean}> ` | ||
| 184 | + padding : 2.5% 10%; | ||
| 185 | + cursor : pointer; | ||
| 186 | + | ||
| 187 | + border : 1px solid ${props => props.isCloseButton ? '#343434' : '#337DFF'}; | ||
| 188 | + background-color : ${props => props.isCloseButton ? 'transparent' : '#337DFF'}; | ||
| 189 | + | ||
| 190 | + border-radius : 5px; | ||
| 191 | + | ||
| 192 | + color : ${props => props.isCloseButton ? '#343434' : '#fff'}; | ||
| 193 | + font-weight : 600; | ||
| 194 | + font-size : 16px; | ||
| 195 | + | ||
| 196 | + transition : .25s all; | ||
| 197 | + | ||
| 198 | + &:hover { | ||
| 199 | + opacity : .7; | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | +` | ||
| 203 | + | ||
| 204 | + | ||
| 13 | export const RegisterWrapper = styled.div ` | 205 | export const RegisterWrapper = styled.div ` |
| 14 | width : 35%; | 206 | width : 35%; |
| 15 | border : none; | 207 | border : none; |
| ... | @@ -24,10 +216,9 @@ export const RegisterWrapper = styled.div ` | ... | @@ -24,10 +216,9 @@ export const RegisterWrapper = styled.div ` |
| 24 | padding : 30px 3px; | 216 | padding : 30px 3px; |
| 25 | 217 | ||
| 26 | box-shadow: 0px 0px 10px #a0a0a0; | 218 | box-shadow: 0px 0px 10px #a0a0a0; |
| 27 | - | ||
| 28 | - | ||
| 29 | `; | 219 | `; |
| 30 | 220 | ||
| 221 | + | ||
| 31 | export const RegisterBackButtonWrapper = styled.div ` | 222 | export const RegisterBackButtonWrapper = styled.div ` |
| 32 | width : 100%; | 223 | width : 100%; |
| 33 | border : none; | 224 | border : none; |
| ... | @@ -97,6 +288,18 @@ export const RegisterInputText = styled.div ` | ... | @@ -97,6 +288,18 @@ export const RegisterInputText = styled.div ` |
| 97 | 288 | ||
| 98 | `; | 289 | `; |
| 99 | 290 | ||
| 291 | +export const RegisterInputWrapperForSearch = styled.div ` | ||
| 292 | + display : flex; | ||
| 293 | + flex-direction : row; | ||
| 294 | + | ||
| 295 | + justify-content : center; | ||
| 296 | + | ||
| 297 | + width : 100%; | ||
| 298 | + | ||
| 299 | + border : none; | ||
| 300 | + background-color : transparent; | ||
| 301 | +`; | ||
| 302 | + | ||
| 100 | export const RegisterInput = styled.input ` | 303 | export const RegisterInput = styled.input ` |
| 101 | width : 80%; | 304 | width : 80%; |
| 102 | padding : 5px 10px; | 305 | padding : 5px 10px; |
| ... | @@ -111,6 +314,87 @@ export const RegisterInput = styled.input ` | ... | @@ -111,6 +314,87 @@ export const RegisterInput = styled.input ` |
| 111 | } | 314 | } |
| 112 | `; | 315 | `; |
| 113 | 316 | ||
| 317 | +export const RegisterFileUploadWrapper = styled.div ` | ||
| 318 | + width : 80%; | ||
| 319 | + display : flex; | ||
| 320 | + flex-direction : row; | ||
| 321 | + | ||
| 322 | + justify-content : flex-start; | ||
| 323 | + align-items : center; | ||
| 324 | + | ||
| 325 | + border : none; | ||
| 326 | + background-color : transparent; | ||
| 327 | +`; | ||
| 328 | + | ||
| 329 | +export const RegisterFileUploadButton = styled.button ` | ||
| 330 | + display : flex; | ||
| 331 | + flex-direction : row; | ||
| 332 | + | ||
| 333 | + justify-content : center; | ||
| 334 | + align-items : center; | ||
| 335 | + | ||
| 336 | + border-radius : 3px; | ||
| 337 | + border : 1px solid #343434; | ||
| 338 | + background-color : transparent; | ||
| 339 | + color : #343434; | ||
| 340 | + | ||
| 341 | + padding : 3px 4px; | ||
| 342 | + | ||
| 343 | + font-size : 12px; | ||
| 344 | + font-weight : 600; | ||
| 345 | + | ||
| 346 | + cursor : pointer; | ||
| 347 | + transition : .25s all; | ||
| 348 | + | ||
| 349 | + &:hover { | ||
| 350 | + border : 1px solid #337DFF; | ||
| 351 | + color : #fff; | ||
| 352 | + background-color : #337DFF; | ||
| 353 | + } | ||
| 354 | + | ||
| 355 | + margin : 0 5% 0 0; | ||
| 356 | + | ||
| 357 | +`; | ||
| 358 | + | ||
| 359 | +export const RegisterFileUploadInfoText = styled.div ` | ||
| 360 | + font-size : 12px; | ||
| 361 | + font-weight : 600; | ||
| 362 | + | ||
| 363 | + color : #337DFF; | ||
| 364 | +`; | ||
| 365 | + | ||
| 366 | +export const RegisterInputSearchButton = styled.button ` | ||
| 367 | + position : absolute; | ||
| 368 | + | ||
| 369 | + height : 25px; | ||
| 370 | + width : 25px; | ||
| 371 | + | ||
| 372 | + align-self : end; | ||
| 373 | + | ||
| 374 | + margin : 0 0 1px 24%; | ||
| 375 | + | ||
| 376 | + background-color : transparent; | ||
| 377 | + border : none; | ||
| 378 | + | ||
| 379 | + transition : .25s all; | ||
| 380 | + &:hover { | ||
| 381 | + opacity : .5; | ||
| 382 | + } | ||
| 383 | + | ||
| 384 | + display : flex; | ||
| 385 | + flex-direction : row; | ||
| 386 | + | ||
| 387 | + justify-content : center; | ||
| 388 | + align-items : center; | ||
| 389 | + | ||
| 390 | + cursor : pointer; | ||
| 391 | +`; | ||
| 392 | + | ||
| 393 | +export const RegisterInputSearchButtonImg = styled.img ` | ||
| 394 | + height : 20px; | ||
| 395 | + width : 20px; | ||
| 396 | +`; | ||
| 397 | + | ||
| 114 | export const RegisterButtonWrapper = styled.div ` | 398 | export const RegisterButtonWrapper = styled.div ` |
| 115 | margin : 20px 0 0 0; | 399 | margin : 20px 0 0 0; |
| 116 | 400 | ... | ... |
This diff could not be displayed because it is too large.
-
Please register or login to post a comment