박권수

Merge branch 'web'

Showing 71 changed files with 2567 additions and 669 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 '});
311 + await 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;
293 333
294 - bottleMedicine.save(); 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);
363 +
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();
295 407
296 ctx.status = 200; 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
484 }; 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 +
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);
79 86
80 -module.exports = doctor;
81 87
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 -
104 - const hosting = await hub.getHubHost();
105 - Mqtt.mqttOff(hosting);
106 -
107 - await Hub.deleteOne({ hubId });
108 144
109 - ctx.status = 204; 145 + ctx.status = 200;
110 }; 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;
172 } 235 }
173 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;
246 + }
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 };
......
1 +//test용 api입니다.
2 +const Router = require('koa-router');
3 +const testCtrl = require('./test.ctrl');
4 +
5 +const test = new Router();
6 +
7 +//푸쉬메시지 테스트
8 +test.post('/fcm', testCtrl.sendFcmMessage);
9 +
10 +
11 +
12 +module.exports = test;
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,9 +38,65 @@ exports.getMyDetail = async ctx => { ...@@ -38,9 +38,65 @@ 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 + }
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;
44 100
45 }; 101 };
46 102
...@@ -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 };
......
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 User = require('../models/user');
14 +const Hub = require('../models/hub');
15 +const Bottle = require('../models/bottle');
13 const BottleMedicine = require('../models/bottleMedicine'); 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 + if(message.includes('weight')) {
8 + //무게 갱신
9 + const result = await updateBottleMedicineWeight(topic, message);
10 +
11 + return result;
12 +
13 + } else {
7 //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴 14 //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴
8 const data = await factoring(topic, message); 15 const data = await factoring(topic, message);
9 //가공된 데이터를 bottleId의 약병에 업데이트 16 //가공된 데이터를 bottleId의 약병에 업데이트
...@@ -13,68 +20,59 @@ exports.dataPublish = async (topic, message) => { ...@@ -13,68 +20,59 @@ exports.dataPublish = async (topic, message) => {
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 -
25 - if(isOpen === '0')
26 - balance = await balanceFactoring(balance);
27 - else balance = '-1';
28 32
29 return { 33 return {
30 bottleId, 34 bottleId,
31 isOpen, 35 isOpen,
32 - openDate : new Date(),
33 temperature, 36 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 46
47 + if(!parseInt(isOpen)) {
60 bottleId = parseInt(bottleId); 48 bottleId = parseInt(bottleId);
61 - isOpen = parseInt(isOpen);
62 temperature = parseFloat(temperature); 49 temperature = parseFloat(temperature);
63 humidity = parseFloat(humidity); 50 humidity = parseFloat(humidity);
64 - balance = parseInt(balance); 51 + totalWeight = parseFloat(totalWeight);
65 52
66 const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); 53 const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' });
67 54
68 if(bottleMedicine) { 55 if(bottleMedicine) {
69 - if(isOpen) { 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 +
70 const takeMedicineHist = new TakeMedicineHist({ 64 const takeMedicineHist = new TakeMedicineHist({
71 - takeDate : openDate,
72 bmId : bottleMedicine._id, 65 bmId : bottleMedicine._id,
73 temperature, 66 temperature,
74 humidity, 67 humidity,
68 + dosage,
75 balance, 69 balance,
76 }); 70 });
77 - takeMedicineHist.save(); 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
......
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
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
......
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",
......
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 },
......
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
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
......
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
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,17 +36,61 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { ...@@ -32,17 +36,61 @@ 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}
45 + >
46 + 복용 기록
47 + </styled.ModalTypeButton>
48 + <styled.ModalTypeButton
49 + isSelect = {props.modalType === 'info'}
50 + onClick = {props.onViewMedicineInfo}
51 + >
52 + 약 정보
53 + </styled.ModalTypeButton>
54 + </styled.ModalTypeButtonWrapper>
55 + {
56 + props.modalType === 'hist' ?
57 + <>
58 + <styled.MedicineNameWrapper>
59 + <styled.MedicineName>{`복용 기록`}</styled.MedicineName>
60 + <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{`전체 : ${props.takeMedicineHist.length}건`}</styled.MedicineName>
61 + </styled.MedicineNameWrapper>
62 + <styled.MedicineInfoWrapper>
63 + {
64 + props.takeMedicineHist.map((hist : any) => {
65 + return (
66 + <styled.HistWrapper
67 + key = {hist._id}
39 > 68 >
40 - <styled.ModalClsButtonImg src = {closeButton}/> 69 + <styled.HistDtmWrapper>
41 - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> 70 + <styled.HistInfoEachWrapper style = {{fontSize : 11}}>복용 날짜</styled.HistInfoEachWrapper>
42 - </styled.ModalClsButton> 71 + <styled.HistDtm>{hist.takeDate}</styled.HistDtm>
43 - </styled.ModalClsButtonWrapper> 72 + </styled.HistDtmWrapper>
44 - <styled.ModalContentWrapper> 73 + <styled.HistInfoWrapper>
45 - <styled.ModalContent> 74 + <styled.HistInfoEachWrapper>
75 + 복용량
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 + <>
46 <styled.MedicineNameWrapper> 94 <styled.MedicineNameWrapper>
47 <styled.MedicineName>{props.bottleInfo.medicine.name}</styled.MedicineName> 95 <styled.MedicineName>{props.bottleInfo.medicine.name}</styled.MedicineName>
48 <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{props.bottleInfo.medicine.company}</styled.MedicineName> 96 <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{props.bottleInfo.medicine.company}</styled.MedicineName>
...@@ -65,9 +113,10 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { ...@@ -65,9 +113,10 @@ const BottleInfoPresenter = (props : BottleInfoProps) => {
65 <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.antiEffect}</styled.MedicineEachInfo> 113 <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.antiEffect}</styled.MedicineEachInfo>
66 </styled.MedicineEachInfoWrapper> 114 </styled.MedicineEachInfoWrapper>
67 </styled.MedicineInfoWrapper> 115 </styled.MedicineInfoWrapper>
68 - </styled.ModalContent> 116 + </>
69 - </styled.ModalContentWrapper> 117 + }
70 - </styled.ModalContainer> : null 118 + </>
119 + </Modal> : null
71 } 120 }
72 <styled.ChartAndFeedbackWrapper> 121 <styled.ChartAndFeedbackWrapper>
73 <styled.ChartWrapper> 122 <styled.ChartWrapper>
...@@ -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 16
48 -`; 17 + width : 100%;
49 18
50 -export const ModalClsButtonWrapper = styled.div ` 19 + justify-content : center;
51 - flex : 1; 20 + align-items : center;
52 21
53 - display : flex; 22 + background-color : transparent;
54 23
55 - justify-content : flex-end; 24 + gap : 5%;
56 - align-items : center;
57 - padding : 0 20px;
58 25
59 - border : none; 26 + padding : 3% 0 0 0;
60 - background-color : transprent;
61 `; 27 `;
62 28
63 -export const ModalClsButton = styled.button ` 29 +export const ModalTypeButton = styled.button<{isSelect : boolean}> `
64 - border : none; 30 + border : 1px solid #337DFF;
65 - background-color : transparent; 31 + border-radius : 3px;
66 - 32 + color : ${props => props.isSelect ? '#fff' : '#337DFF'};
67 - cursor : pointer; 33 + background-color : ${props => props.isSelect ? '#337DFF' : '#fff'};
68 34
69 - color : #fff; 35 + padding : 1% 3%;
70 36
71 - display : flex; 37 + cursor : pointer;
72 - flex-direction : row;
73 38
74 - justify-content : center; 39 + font-size : 16px;
75 - align-items : center; 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%;
110 82
111 - background-color : #fff; 83 + border : none;
112 - border : 1.2px solid #337DFF;
113 - border-radius : 5px;
114 84
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 };
......
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
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) => {
35 if(res.statusText === 'OK') { 37 if(res.statusText === 'OK') {
36 - setDoctorRegReqList(res.data.doctorRegReqList); 38 + setDoctorList(res.data.doctorList);
39 + } else {
40 + Alert.onError(res.data.error, () => null);
37 } 41 }
38 - }).catch(err => { 42 + } catch(e : any) {
39 - Alert.onError(err.response.data.error, () => null); 43 + console.log(e);
40 - }) 44 + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null);
41 - } catch(e) {
42 - Alert.onError(e.response.data.error, () => null);
43 } 45 }
44 }; 46 };
45 47
48 + //가입요청 보기
49 + const onViewRegList = () => {
50 + setViewType('reg');
51 + };
46 52
53 + //탈퇴요청 보기
54 + const onViewSecList = () => {
55 + setViewType('sec');
56 + };
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,
99 + validateDoctorLicense,
100 + }).then((res : any) => {
77 if(res.statusText === 'OK') { 101 if(res.statusText === 'OK') {
78 Alert.onSuccess('회원 등록이 완료되었습니다.', fetchData); 102 Alert.onSuccess('회원 등록이 완료되었습니다.', fetchData);
79 } 103 }
80 - }) 104 + });
81 - } catch(e) { 105 + } catch(e : any) {
82 - Alert.onError(e.response.data.error, () => setModalUp(false)); 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,
119 + }).then((res : any) => {
95 if(res.statusText === 'OK') { 120 if(res.statusText === 'OK') {
96 Alert.onSuccess('회원 등록이 취소되었습니다.', fetchData); 121 Alert.onSuccess('회원 등록이 취소되었습니다.', fetchData);
97 } 122 }
98 - }) 123 + });
99 - } catch(e) { 124 + } catch(e : any) {
100 - Alert.onError(e.response.data.error, () => setModalUp(false)); 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 );
......
1 import React from 'react'; 1 import React from 'react';
2 2
3 +import Modal from '../../../components/Modal';
3 import * as styled from './ManagerMenuStyled'; 4 import * as styled from './ManagerMenuStyled';
4 5
5 -const closeButton = '/static/img/close.png';
6 -
7 -
8 6
9 interface ManagerMenuProps { 7 interface ManagerMenuProps {
10 - doctorRegReqList : any[]; 8 + viewType : string;
9 + onViewRegList : () => void;
10 + onViewSecList : () => void;
11 +
12 + doctorList : any[];
11 13
12 doctorDetail : any; 14 doctorDetail : any;
13 modalUp : boolean; 15 modalUp : boolean;
14 setModalUp : any; 16 setModalUp : any;
15 onViewDetailReq : (arg0 : string) => void; 17 onViewDetailReq : (arg0 : string) => void;
18 + onViewLicenseDetail : (arg0 : string) => void;
19 +
16 validate : string; 20 validate : string;
17 onValidate : () => void; 21 onValidate : () => void;
18 22
19 - onAcceptRequest : () => void; 23 + validateDoctorLicense : string;
24 + onSetValidateDoctorLicense : React.ChangeEventHandler<HTMLInputElement>;
25 +
26 + onAcceptRegReq : () => void;
27 + onAcceptSecReq : (arg0 : string) => void;
20 onRejectRequest : () => void; 28 onRejectRequest : () => void;
21 29
22 } 30 }
...@@ -26,26 +34,33 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => { ...@@ -26,26 +34,33 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => {
26 <styled.Container> 34 <styled.Container>
27 { 35 {
28 props.modalUp ? 36 props.modalUp ?
29 - <styled.ModalContainer> 37 + <Modal onModalClose = {() => props.setModalUp(false)}>
30 - <styled.ModalClsButtonWrapper> 38 + <>
31 - <styled.ModalClsButton
32 - onClick = {() => props.setModalUp(false)}
33 - >
34 - <styled.ModalClsButtonImg src = {closeButton}/>
35 - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText>
36 - </styled.ModalClsButton>
37 - </styled.ModalClsButtonWrapper>
38 - <styled.ModalContentWrapper>
39 - <styled.ModalContent>
40 <styled.ModalTitleWrapper> 39 <styled.ModalTitleWrapper>
41 <styled.ModalTitle>가입 요청 정보</styled.ModalTitle> 40 <styled.ModalTitle>가입 요청 정보</styled.ModalTitle>
42 </styled.ModalTitleWrapper> 41 </styled.ModalTitleWrapper>
43 <styled.ModalBodyWrapper> 42 <styled.ModalBodyWrapper>
44 <styled.ModalBodyLeftAndRight> 43 <styled.ModalBodyLeftAndRight>
45 <styled.ModalInfoWrapper> 44 <styled.ModalInfoWrapper>
46 - <styled.ModalInfoExplain>의사 자격 번호</styled.ModalInfoExplain> 45 + <styled.DoctorLicenseViewWrapper>
46 + <styled.ModalInfoExplain>
47 + 의사 자격 번호
48 + </styled.ModalInfoExplain>
49 + <styled.DoctorLicenseViewButton onClick = {() => props.onViewLicenseDetail(props.doctorDetail.info.doctorLicense)}>
50 + 자격정보 확인
51 + </styled.DoctorLicenseViewButton>
52 + </styled.DoctorLicenseViewWrapper>
53 + <styled.ModalInfoNotice>
54 + * 자격 정보 확인 버튼을 눌러 정보를 확인하세요.
55 + <br/>* 정보 확인은 15분간 유효합니다.
56 + <br/>* 확인한 면허 번호를 입력 후 검증하세요.
57 + </styled.ModalInfoNotice>
47 <styled.ModalInfo> 58 <styled.ModalInfo>
48 - {props.doctorDetail.info.doctorLicense} 59 + <styled.DoctorLicenseViewInput
60 + placeholder = '의사 면허 번호'
61 + value = {props.validateDoctorLicense}
62 + onChange = {props.onSetValidateDoctorLicense}
63 + />
49 <styled.ValidateButton 64 <styled.ValidateButton
50 onClick = {props.onValidate} 65 onClick = {props.onValidate}
51 disabled = {props.validate !== 'W'} 66 disabled = {props.validate !== 'W'}
...@@ -88,7 +103,7 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => { ...@@ -88,7 +103,7 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => {
88 </styled.ModalBodyWrapper> 103 </styled.ModalBodyWrapper>
89 <styled.ModalButtonWrapper> 104 <styled.ModalButtonWrapper>
90 <styled.ModalButton 105 <styled.ModalButton
91 - onClick = {props.onAcceptRequest} 106 + onClick = {props.onAcceptRegReq}
92 isAccept = {true} 107 isAccept = {true}
93 > 108 >
94 수락 109 수락
...@@ -100,19 +115,41 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => { ...@@ -100,19 +115,41 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => {
100 거절 115 거절
101 </styled.ModalButton> 116 </styled.ModalButton>
102 </styled.ModalButtonWrapper> 117 </styled.ModalButtonWrapper>
103 - </styled.ModalContent> 118 + </>
104 - </styled.ModalContentWrapper> 119 + </Modal> : null
105 - <styled.ModalClsButtonWrapper/>
106 - </styled.ModalContainer> : null
107 } 120 }
108 <styled.ContentWrapper> 121 <styled.ContentWrapper>
122 + <styled.ContentButtonWrapper>
123 + <styled.ContentButton
124 + isSelect = {props.viewType === 'reg'}
125 + onClick = {props.onViewRegList}
126 + >
127 + 가입 대기
128 + </styled.ContentButton>
129 + <styled.ContentButton
130 + isSelect = {props.viewType === 'sec'}
131 + onClick = {props.onViewSecList}
132 + >
133 + 탈퇴 요청
134 + </styled.ContentButton>
135 + </styled.ContentButtonWrapper>
109 <styled.ContentTitle> 136 <styled.ContentTitle>
137 + {
138 + props.viewType === 'sec' ?
139 + <>
140 + 탈퇴 대기 중 의사 회원
141 + <styled.ContentExplain>
142 + *승인을 누르면 탈퇴를 승인합니다.
143 + </styled.ContentExplain>
144 + </> :
145 + <>
110 가입 대기 중 의사 회원 146 가입 대기 중 의사 회원
111 <styled.ContentExplain> 147 <styled.ContentExplain>
112 *클릭하면 상세정보를 확인할 수 있습니다. 148 *클릭하면 상세정보를 확인할 수 있습니다.
113 </styled.ContentExplain> 149 </styled.ContentExplain>
150 + </>
151 + }
114 </styled.ContentTitle> 152 </styled.ContentTitle>
115 - <styled.ContentBody>
116 <styled.ContentInfoWrapper> 153 <styled.ContentInfoWrapper>
117 <styled.ContentInfo 154 <styled.ContentInfo
118 isLast = {false} 155 isLast = {false}
...@@ -125,18 +162,28 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => { ...@@ -125,18 +162,28 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => {
125 이름 162 이름
126 </styled.ContentInfo> 163 </styled.ContentInfo>
127 <styled.ContentInfo 164 <styled.ContentInfo
128 - isLast = {true} 165 + isLast = {props.viewType !== 'sec'}
129 > 166 >
130 이메일 167 이메일
131 </styled.ContentInfo> 168 </styled.ContentInfo>
169 + {
170 + props.viewType === 'sec' ?
171 + <styled.ContentInfo
172 + isLast = {true}
173 + >
174 + 탈퇴 수락
175 + </styled.ContentInfo> : null
176 + }
132 </styled.ContentInfoWrapper> 177 </styled.ContentInfoWrapper>
178 + <styled.ContentBody>
133 { 179 {
134 - props.doctorRegReqList.length ? 180 + props.doctorList.length ?
135 - props.doctorRegReqList.map((doctor : any) => { 181 + props.doctorList.map((doctor : any) => {
136 return ( 182 return (
137 <styled.EachContentWrapper 183 <styled.EachContentWrapper
138 key = {doctor.doctorId} 184 key = {doctor.doctorId}
139 onClick = {() => props.onViewDetailReq(doctor.doctorId)} 185 onClick = {() => props.onViewDetailReq(doctor.doctorId)}
186 + disabled = {props.viewType === 'sec'}
140 > 187 >
141 <styled.EachContentNm 188 <styled.EachContentNm
142 isLast = {false} 189 isLast = {false}
...@@ -149,10 +196,22 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => { ...@@ -149,10 +196,22 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => {
149 {doctor.info.doctorNm} 196 {doctor.info.doctorNm}
150 </styled.EachContentNm> 197 </styled.EachContentNm>
151 <styled.EachContentNm 198 <styled.EachContentNm
152 - isLast = {true} 199 + isLast = {props.viewType !== 'sec'}
153 > 200 >
154 {doctor.doctorId} 201 {doctor.doctorId}
155 </styled.EachContentNm> 202 </styled.EachContentNm>
203 + {
204 + props.viewType === 'sec' ?
205 + <styled.EachContentNm
206 + isLast = {true}
207 + >
208 + <styled.AcceptButton
209 + onClick = {() => props.onAcceptSecReq(doctor.doctorId)}
210 + >
211 + 승인
212 + </styled.AcceptButton>
213 + </styled.EachContentNm> : null
214 + }
156 </styled.EachContentWrapper> 215 </styled.EachContentWrapper>
157 ) 216 )
158 }) : 217 }) :
......
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;
197 96
198 display : flex; 97 display : flex;
199 flex-direction : row; 98 flex-direction : row;
200 align-items : center; 99 align-items : center;
201 `; 100 `;
202 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;
131 +
132 + display : flex;
133 + flex-direction : row;
134 + justify-content : 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 + }
162 +`;
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,6 +388,7 @@ export const EachContentWrapper = styled.button ` ...@@ -388,6 +388,7 @@ export const EachContentWrapper = styled.button `
388 388
389 padding : 10px 0px; 389 padding : 10px 0px;
390 390
391 + :not(:disabled) {
391 cursor : pointer; 392 cursor : pointer;
392 393
393 transition : .1s all; 394 transition : .1s all;
...@@ -396,6 +397,7 @@ export const EachContentWrapper = styled.button ` ...@@ -396,6 +397,7 @@ export const EachContentWrapper = styled.button `
396 background-color : #337DFF; 397 background-color : #337DFF;
397 color : #fff; 398 color : #fff;
398 } 399 }
400 + }
399 401
400 `; 402 `;
401 403
...@@ -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() => {
...@@ -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
253 + if(selectHospital) {
200 Alert.onCheck('입력하신 정보로 회원가입을 진행하시겠습니까?', onRegisterDoctor, () => null); 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.