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 | }; | ... | ... |
server/src/api/test/index.js
0 → 100644
server/src/api/test/test.ctrl.js
0 → 100644
1 | +//테스트용 api service | ||
2 | +const { sendPushMessage } = require('../../util/FCM'); | ||
3 | + | ||
4 | + | ||
5 | +exports.sendFcmMessage = async ctx => { | ||
6 | + const { deviceToken, title, body } = ctx.request.body; | ||
7 | + | ||
8 | + try { | ||
9 | + await sendPushMessage(ctx.request.body); | ||
10 | + } catch(e) { | ||
11 | + console.log('Error at FCM Sending : ', e); | ||
12 | + } | ||
13 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -11,9 +11,18 @@ const user = new Router(); | ... | @@ -11,9 +11,18 @@ const user = new Router(); |
11 | */ | 11 | */ |
12 | user.get('/', userCtrl.getMyDetail); | 12 | user.get('/', userCtrl.getMyDetail); |
13 | 13 | ||
14 | + | ||
14 | /** | 15 | /** |
15 | - * 현재 로그인한 유저에 등록된 의사 목록 가져옴 | 16 | + * 현재 유저 정보 수정 |
16 | * request parameter : token | 17 | * request parameter : token |
18 | + * url : http://localhost:4000/api/user | ||
19 | + * return : Object User | ||
20 | + */ | ||
21 | +user.patch('/', userCtrl.updateMyDetail); | ||
22 | + | ||
23 | +/** | ||
24 | + * 현재 로그인한 유저에 등록된 의사 목록 가져옴 | ||
25 | + * request parameter : userNm, birth, contact, password, passwordCheck | ||
17 | * url : http://localhost:4000/api/user/doctor | 26 | * url : http://localhost:4000/api/user/doctor |
18 | * return : Doctor List | 27 | * return : Doctor List |
19 | */ | 28 | */ | ... | ... |
... | @@ -38,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 | }; | ... | ... |
server/src/models/prescribeInfo.js
0 → 100644
1 | +const mongoose = require('mongoose'); | ||
2 | + | ||
3 | +const Schema = mongoose.Schema; | ||
4 | + | ||
5 | +const PrescribeInfoSchema = new Schema({ | ||
6 | + doctorId : { type : String, require : true, }, | ||
7 | + patientId : { type : String, require : true, }, | ||
8 | + medicineId : { type : Number, require : true, }, | ||
9 | + dailyDosage : { type : Number, require : true, }, | ||
10 | + totalDosage : { type : Number, require : true, }, | ||
11 | + qrCodeUrl : { type : String, require : true, }, | ||
12 | +}); | ||
13 | + | ||
14 | + | ||
15 | +module.exports = mongoose.model('PrescribeInfo', PrescribeInfoSchema); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -3,10 +3,11 @@ const mongoose = require('mongoose'); | ... | @@ -3,10 +3,11 @@ const mongoose = require('mongoose'); |
3 | const Schema = mongoose.Schema; | 3 | const Schema = mongoose.Schema; |
4 | 4 | ||
5 | const ProfileSchema = new Schema({ | 5 | const ProfileSchema = new Schema({ |
6 | - userId : { type : String, required : true, ref : 'User', }, | 6 | + userId : { type : String, required : true, ref : 'User', lowercase : true, }, |
7 | userNm : { type : String, required : true, }, | 7 | userNm : { type : String, required : true, }, |
8 | - userAge : { type : Number, required : true, }, | 8 | + birth : { type : String, required : true, }, |
9 | contact : { type : String, required : true, }, | 9 | contact : { type : String, required : true, }, |
10 | + useYn : { type : String, default : 'Y', }, | ||
10 | deviceToken : { type : String, default : null, }, | 11 | deviceToken : { type : String, default : null, }, |
11 | }); | 12 | }); |
12 | 13 | ||
... | @@ -14,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) { | ... | @@ -14,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) { |
14 | return this.findOne({ userId }); | 15 | return this.findOne({ userId }); |
15 | }; | 16 | }; |
16 | 17 | ||
17 | -ProfileSchema.methods.updateUserContact = function(contact) { | 18 | +ProfileSchema.methods.setUseYn = function(useYn) { |
18 | - this.contact = contact; | 19 | + this.useYn = useYn; |
19 | }; | 20 | }; |
20 | 21 | ||
21 | -ProfileSchema.methods.updateUserAge = function() { | 22 | +ProfileSchema.methods.updateProfileInfo = function({ userNm, birth, contact }) { |
22 | - this.userAge = this.userAge + 1; | 23 | + if(userNm) { this.userNm = userNm } |
24 | + if(birth) { this.birth = birth } | ||
25 | + if(contact) { this.contact = contact } | ||
23 | }; | 26 | }; |
24 | 27 | ||
25 | ProfileSchema.methods.updateDeviceToken = function(deviceToken) { | 28 | ProfileSchema.methods.updateDeviceToken = function(deviceToken) { | ... | ... |
... | @@ -5,7 +5,6 @@ const Schema = mongoose.Schema; | ... | @@ -5,7 +5,6 @@ const Schema = mongoose.Schema; |
5 | const TakeMedicineHistorySchema = new Schema ({ | 5 | const TakeMedicineHistorySchema = new Schema ({ |
6 | takeDate : { | 6 | takeDate : { |
7 | type : Date, | 7 | type : Date, |
8 | - required : true, | ||
9 | default : Date.now, | 8 | default : Date.now, |
10 | }, | 9 | }, |
11 | bmId : { | 10 | bmId : { |
... | @@ -13,9 +12,10 @@ const TakeMedicineHistorySchema = new Schema ({ | ... | @@ -13,9 +12,10 @@ const TakeMedicineHistorySchema = new Schema ({ |
13 | ref : 'BottleMedicine', | 12 | ref : 'BottleMedicine', |
14 | required : true, | 13 | required : true, |
15 | }, | 14 | }, |
16 | - temperature : { type : Number, default : 0 }, | 15 | + temperature : { type : Number, default : 0, }, |
17 | - humidity : { type : Number, default : 0 }, | 16 | + humidity : { type : Number, default : 0, }, |
18 | - balance : { type : Number, default : 0 }, | 17 | + dosage : { type : Number, default : 0, }, |
18 | + balance : { type : Number, default : 0, }, | ||
19 | }); | 19 | }); |
20 | 20 | ||
21 | 21 | ... | ... |
... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); | ... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); |
5 | const Schema = mongoose.Schema; | 5 | const Schema = mongoose.Schema; |
6 | 6 | ||
7 | const UserSchema = new Schema ({ | 7 | const UserSchema = new Schema ({ |
8 | - userId : { type: String, required : true, unique : true, lowercase : true, }, | 8 | + userId : { type: String, required : true, unique : true, lowercase : true, trim : true }, |
9 | - hashedPassword : { type : String, required : true }, | 9 | + hashedPassword : { type : String, required : true, }, |
10 | - userTypeCd : { type : String, required : true, default : 'NORMAL' }, | 10 | + userTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, |
11 | + authTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, | ||
11 | useYn : { type : String, default : 'W', required : true, }, | 12 | useYn : { type : String, default : 'W', required : true, }, |
12 | }); | 13 | }); |
13 | 14 | ... | ... |
... | @@ -3,11 +3,136 @@ | ... | @@ -3,11 +3,136 @@ |
3 | * 21/09/14 | 3 | * 21/09/14 |
4 | * Author : 박권수 | 4 | * Author : 박권수 |
5 | * 배치 시스템 | 5 | * 배치 시스템 |
6 | - * 1) 매년 지나면 프로필의 Age를 +1 | 6 | + * 1) Dosage에 따라, Push Notification 발송 |
7 | - * 2) Dosage에 따라, Push Notification 발송 | ||
8 | */ | 7 | */ |
9 | 8 | ||
10 | const cron = require('node-cron'); | 9 | const cron = require('node-cron'); |
10 | +const fs = require('fs'); | ||
11 | 11 | ||
12 | const Profile = require('../models/profile'); | 12 | const Profile = require('../models/profile'); |
13 | +const 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 | ... | ... |
server/src/util/FCM.js
0 → 100644
1 | +// const fcm = require('firebase-admin'); | ||
2 | +const axios = require('axios'); | ||
3 | + | ||
4 | +// exports.initializeFCM = () => { | ||
5 | +// fcm.initializeApp({ | ||
6 | +// credential: fcm.credential.applicationDefault(), | ||
7 | +// }); | ||
8 | +// }; | ||
9 | + | ||
10 | +exports.sendPushMessage = async ({ deviceToken, title, body }) => { | ||
11 | + // const notifyMessage = { | ||
12 | + // notification : { | ||
13 | + // title, | ||
14 | + // body, | ||
15 | + // }, | ||
16 | + // token : deviceToken, | ||
17 | + // }; | ||
18 | + // fcm.messaging().send(notifyMessage).then(res => { | ||
19 | + // console.log(res); | ||
20 | + // }).catch(e => { | ||
21 | + // console.log(e); | ||
22 | + // }); | ||
23 | + | ||
24 | + const message = { | ||
25 | + to : deviceToken, | ||
26 | + notification : { | ||
27 | + title, | ||
28 | + body, | ||
29 | + priority : 'high', | ||
30 | + }, | ||
31 | + data : null, | ||
32 | + } | ||
33 | + | ||
34 | + const url = 'https://fcm.googleapis.com/fcm/send'; | ||
35 | + const result = await axios.post(url, message, { | ||
36 | + headers : { | ||
37 | + 'Content-Type' : 'application/json', | ||
38 | + // eslint-disable-next-line no-undef | ||
39 | + 'Authorization' : `key=${process.env.FCM_KEY}`, | ||
40 | + }, | ||
41 | + }); | ||
42 | + | ||
43 | + console.log(result.data); | ||
44 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
server/src/util/GoogleCloudStorage.js
0 → 100644
1 | +const { Storage } = require('@google-cloud/storage'); | ||
2 | +const fs = require('fs'); | ||
3 | + | ||
4 | +const storage = new Storage(); | ||
5 | +const GoogleStorageUrl = 'https://storage.googleapis.com/'; | ||
6 | + | ||
7 | + | ||
8 | +//의사 아이디, 업로드할 파일명, 업로드할 파일 경로를 인자로 받아, File을 GCS에 업로드 후 GCS 주소를 반환 | ||
9 | +exports.uploadDoctorLicense = async ({ userId, fileName, filePath }) => { | ||
10 | + const destination = userId + '_' + fileName; | ||
11 | + try { | ||
12 | + const result = await storage.bucket('doctor-info').upload(filePath, { | ||
13 | + destination, | ||
14 | + }); | ||
15 | + | ||
16 | + const doctorLicenseUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | ||
17 | + | ||
18 | + return doctorLicenseUrl; | ||
19 | + } catch(e) { | ||
20 | + console.log(e); | ||
21 | + return null; | ||
22 | + } | ||
23 | +}; | ||
24 | + | ||
25 | +//의사 정보를 인자로 받아 해당 Doctor License의 Signed URL을 반환 | ||
26 | +exports.viewDoctorLicense = async ({ doctorInfo }) => { | ||
27 | + try { | ||
28 | + const fileName = doctorInfo.info.doctorLicense.split('/').pop(); | ||
29 | + const file = storage.bucket('doctor-info').file(fileName); | ||
30 | + const option = { | ||
31 | + version : 'v4', | ||
32 | + expires : Date.now() + 1000 * 60 * 15, | ||
33 | + action : 'read', | ||
34 | + }; | ||
35 | + | ||
36 | + const [signedUrl] = file ? await file.getSignedUrl(option) : [null]; | ||
37 | + | ||
38 | + return signedUrl; | ||
39 | + } catch(e) { | ||
40 | + console.log(e); | ||
41 | + return null; | ||
42 | + } | ||
43 | +}; | ||
44 | + | ||
45 | +//의사 ID, 약 ID, 복용량을 인자로 받아, QR Code를 생성 | ||
46 | +exports.uploadQrCode = async ({ directory, qrCodeFileName }) => { | ||
47 | + const destination = qrCodeFileName; | ||
48 | + try { | ||
49 | + //파일을 GCS에 업로드 | ||
50 | + const result = await storage.bucket('prescribe-medicine-qrcode').upload(directory + '/' + qrCodeFileName, { | ||
51 | + destination | ||
52 | + }); | ||
53 | + | ||
54 | + //업로드 후 파일 삭제 | ||
55 | + fs.rm(directory + '/' + qrCodeFileName, () => {}); | ||
56 | + | ||
57 | + const qrCodeUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | ||
58 | + | ||
59 | + return qrCodeUrl; | ||
60 | + } catch(e) { | ||
61 | + console.log(e); | ||
62 | + return null; | ||
63 | + } | ||
64 | +}; | ||
65 | + | ||
66 | +//생성된 QR코드의 signedUrl을 가져옴 | ||
67 | +exports.getQrCodeUrl = async ({ qrCodeFileName }) => { | ||
68 | + try { | ||
69 | + const fileName = qrCodeFileName; | ||
70 | + const file = storage.bucket('prescribe-medicine-qrcode').file(fileName); | ||
71 | + const option = { | ||
72 | + version : 'v4', | ||
73 | + expires : Date.now() + 1000 * 60 * 15, | ||
74 | + action : 'read', | ||
75 | + }; | ||
76 | + | ||
77 | + const [signedUrl] = file ? await file.getSignedUrl(option) : [null]; | ||
78 | + | ||
79 | + return signedUrl; | ||
80 | + } catch(e) { | ||
81 | + console.log(e); | ||
82 | + return null; | ||
83 | + } | ||
84 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -const mqtt = require('mqtt') | 1 | +const mqtt = require('mqtt'); |
2 | -const clientList = [] | 2 | +const clientList = []; |
3 | 3 | ||
4 | exports.mqttOn = async (hosting, foo) => { | 4 | exports.mqttOn = async (hosting, foo) => { |
5 | const filterIndex = clientList.findIndex(client => { | 5 | const filterIndex = clientList.findIndex(client => { |
... | @@ -9,27 +9,29 @@ exports.mqttOn = async (hosting, foo) => { | ... | @@ -9,27 +9,29 @@ exports.mqttOn = async (hosting, foo) => { |
9 | }) | 9 | }) |
10 | 10 | ||
11 | if(filterIndex === -1) { | 11 | if(filterIndex === -1) { |
12 | - const client = mqtt.connect(hosting) | 12 | + const client = mqtt.connect(hosting); |
13 | - clientList.push(client) | 13 | + clientList.push(client); |
14 | 14 | ||
15 | client.on('connect', () => { | 15 | client.on('connect', () => { |
16 | console.log('Hub connected: ', client.connected) | 16 | console.log('Hub connected: ', client.connected) |
17 | - }) | 17 | + }); |
18 | 18 | ||
19 | client.on('message', async (topic, message) => { | 19 | client.on('message', async (topic, message) => { |
20 | - const result = await foo(topic, message.toString()) | 20 | + const result = await foo(topic, message.toString()); |
21 | console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m') | 21 | console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m') |
22 | - this.mqttPublishMessage(client, result) | 22 | + if(result) this.mqttPublishMessage(client, result); |
23 | - }) | 23 | + }); |
24 | 24 | ||
25 | - return client | 25 | + return client; |
26 | } | 26 | } |
27 | 27 | ||
28 | - return clientList[filterIndex] | 28 | + return clientList[filterIndex]; |
29 | } | 29 | } |
30 | 30 | ||
31 | exports.mqttSubscribe = (client, topic) => { | 31 | exports.mqttSubscribe = (client, topic) => { |
32 | - client.subscribe(topic) | 32 | + client.subscribe(topic, () => { |
33 | + console.log('suscribe', topic); | ||
34 | + }); | ||
33 | } | 35 | } |
34 | 36 | ||
35 | exports.mqttPublishMessage = (client, { topic, message }) => { | 37 | exports.mqttPublishMessage = (client, { topic, message }) => { |
... | @@ -49,10 +51,10 @@ exports.mqttOff = (hosting) => { | ... | @@ -49,10 +51,10 @@ exports.mqttOff = (hosting) => { |
49 | return (client.options.clientId === hosting.clientId | 51 | return (client.options.clientId === hosting.clientId |
50 | && client.options.host === hosting.host | 52 | && client.options.host === hosting.host |
51 | && client.options.port === hosting.port) | 53 | && client.options.port === hosting.port) |
52 | - }) | 54 | + }); |
53 | 55 | ||
54 | if(filterIndex !== -1) { | 56 | if(filterIndex !== -1) { |
55 | - clientList[filterIndex].end() | 57 | + clientList[filterIndex].end(); |
56 | - clientList.splice(filterIndex, 1) | 58 | + clientList.splice(filterIndex, 1); |
57 | } | 59 | } |
58 | } | 60 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | -const Mqtt = require('../lib/MqttModule'); | 1 | +const Mqtt = require('./MqttModule'); |
2 | -const DataProcess = require('../lib/DataProcess'); | 2 | +const DataProcess = require('./DataProcess'); |
3 | const Hub = require('../models/hub'); | 3 | const Hub = require('../models/hub'); |
4 | const Bottle = require('../models/bottle'); | 4 | const Bottle = require('../models/bottle'); |
5 | 5 | ... | ... |
server/src/util/QrCodeUtil.js
0 → 100644
1 | +const QrCode = require('qrcode'); | ||
2 | +const moment = require('moment'); | ||
3 | + | ||
4 | + | ||
5 | +exports.generateQrCode_prescribe = async ({ medicine, dailyDosage, totalDosage, patientId, doctorId }) => { | ||
6 | + // eslint-disable-next-line no-undef | ||
7 | + const directory = process.env.QR_DIR; | ||
8 | + | ||
9 | + const now = moment().format('YYYY-MM-DD_HH:mm'); | ||
10 | + const qrCodeFileName = `${now}_${doctorId}_${patientId}_${medicine.medicineId}_${dailyDosage}_${totalDosage}.png`; | ||
11 | + | ||
12 | + try { | ||
13 | + await QrCode.toFile( | ||
14 | + directory + '/' + qrCodeFileName, | ||
15 | + `${medicine.medicineId}/${dailyDosage}/${totalDosage}/${doctorId}/${patientId}/${medicine.name}`, | ||
16 | + { | ||
17 | + color : { | ||
18 | + dark : '#337DFF', | ||
19 | + light : '#FFF' | ||
20 | + }, | ||
21 | + } | ||
22 | + ); | ||
23 | + | ||
24 | + return { | ||
25 | + directory, | ||
26 | + qrCodeFileName, | ||
27 | + }; | ||
28 | + | ||
29 | + } catch(e) { | ||
30 | + console.log(e); | ||
31 | + return null; | ||
32 | + } | ||
33 | + | ||
34 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
... | @@ -14,6 +14,7 @@ | ... | @@ -14,6 +14,7 @@ |
14 | "highcharts": "^9.2.0", | 14 | "highcharts": "^9.2.0", |
15 | "highcharts-react-official": "^3.0.0", | 15 | "highcharts-react-official": "^3.0.0", |
16 | "moment": "^2.29.1", | 16 | "moment": "^2.29.1", |
17 | + "qrcode": "^1.4.4", | ||
17 | "react": "^17.0.2", | 18 | "react": "^17.0.2", |
18 | "react-dom": "^17.0.2", | 19 | "react-dom": "^17.0.2", |
19 | "react-router-dom": "^5.2.0", | 20 | "react-router-dom": "^5.2.0", |
... | @@ -52,6 +53,7 @@ | ... | @@ -52,6 +53,7 @@ |
52 | ] | 53 | ] |
53 | }, | 54 | }, |
54 | "devDependencies": { | 55 | "devDependencies": { |
56 | + "@types/qrcode": "^1.4.1", | ||
55 | "@types/react-router-dom": "^5.1.8", | 57 | "@types/react-router-dom": "^5.1.8", |
56 | "@types/styled-components": "^5.1.12", | 58 | "@types/styled-components": "^5.1.12", |
57 | "@types/validator": "^13.6.3", | 59 | "@types/validator": "^13.6.3", | ... | ... |
web/public/static/img/apiLicense.png
0 → 100644
30.3 KB
web/public/static/img/next.png
0 → 100644
404 Bytes
web/public/static/img/prev.png
0 → 100644
392 Bytes
1 | import { client } from "./client"; | 1 | import { client } from "./client"; |
2 | 2 | ||
3 | export default { | 3 | export default { |
4 | - register : (Data : any) => { | 4 | + register : (Data : FormData) => { |
5 | return client.post('/auth/register', Data); | 5 | return client.post('/auth/register', Data); |
6 | }, | 6 | }, |
7 | 7 | ||
8 | + searchHospital : (hospitalNm : string, page : number) => { | ||
9 | + return client.get('/auth/hospital', { | ||
10 | + params : { | ||
11 | + hospitalNm, | ||
12 | + page, | ||
13 | + }, | ||
14 | + }); | ||
15 | + }, | ||
16 | + | ||
8 | registerDoctor : (Data : any) => { | 17 | registerDoctor : (Data : any) => { |
9 | return client.post('/auth/register/doctor', Data); | 18 | return client.post('/auth/register/doctor', Data); |
10 | }, | 19 | }, | ... | ... |
... | @@ -44,8 +44,8 @@ export default { | ... | @@ -44,8 +44,8 @@ export default { |
44 | }, | 44 | }, |
45 | }); | 45 | }); |
46 | }, | 46 | }, |
47 | - searchPatientById : (token : RecoilState<any>, patientId : string) => { | 47 | + searchPatientByContact : (token : RecoilState<any>, contact : string) => { |
48 | - return client.get(`/doctor/patient/search/${patientId}`, { | 48 | + return client.get(`/doctor/patient/search/${contact}`, { |
49 | headers : { | 49 | headers : { |
50 | Authorization : token, | 50 | Authorization : token, |
51 | }, | 51 | }, |
... | @@ -65,4 +65,11 @@ export default { | ... | @@ -65,4 +65,11 @@ export default { |
65 | }, | 65 | }, |
66 | }); | 66 | }); |
67 | }, | 67 | }, |
68 | + prescribeMedicine : (token : RecoilState<any>, Data : any) => { | ||
69 | + return client.post('/doctor/prescribe', Data, { | ||
70 | + headers : { | ||
71 | + Authorization : token, | ||
72 | + }, | ||
73 | + }); | ||
74 | + }, | ||
68 | }; | 75 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -9,6 +9,13 @@ export default { | ... | @@ -9,6 +9,13 @@ export default { |
9 | }, | 9 | }, |
10 | }); | 10 | }); |
11 | }, | 11 | }, |
12 | + getDoctorSecReqList : (token : RecoilState<any>) => { | ||
13 | + return client.get('/manage/doctor/secession', { | ||
14 | + headers : { | ||
15 | + Authorization : token, | ||
16 | + }, | ||
17 | + }); | ||
18 | + }, | ||
12 | getDoctorRegReqDetail : (token : RecoilState<any>, doctorId : string) => { | 19 | getDoctorRegReqDetail : (token : RecoilState<any>, doctorId : string) => { |
13 | return client.get(`/manage/doctor/${doctorId}`, { | 20 | return client.get(`/manage/doctor/${doctorId}`, { |
14 | headers : { | 21 | headers : { |
... | @@ -17,14 +24,21 @@ export default { | ... | @@ -17,14 +24,21 @@ export default { |
17 | }); | 24 | }); |
18 | }, | 25 | }, |
19 | acceptDoctorRegReq : (token : RecoilState<any>, Data : any) => { | 26 | acceptDoctorRegReq : (token : RecoilState<any>, Data : any) => { |
20 | - return client.post('/manage/doctor/accept', Data, { | 27 | + return client.patch('/manage/doctor/accept', Data, { |
21 | headers : { | 28 | headers : { |
22 | Authorization : token, | 29 | Authorization : token, |
23 | }, | 30 | }, |
24 | }); | 31 | }); |
25 | }, | 32 | }, |
26 | rejectDoctorRegReq : (token : RecoilState<any>, Data : any) => { | 33 | rejectDoctorRegReq : (token : RecoilState<any>, Data : any) => { |
27 | - return client.post('/manage/doctor/reject', Data, { | 34 | + return client.patch('/manage/doctor/reject', Data, { |
35 | + headers : { | ||
36 | + Authorization : token, | ||
37 | + }, | ||
38 | + }); | ||
39 | + }, | ||
40 | + acceptDoctorSecReq : (token : RecoilState<any>, Data : any) => { | ||
41 | + return client.patch('/manage/doctor/secession', Data, { | ||
28 | headers : { | 42 | headers : { |
29 | Authorization : token, | 43 | Authorization : token, |
30 | }, | 44 | }, | ... | ... |
web/src/components/Footer/FooterStyled.tsx
0 → 100644
1 | +import styled from 'styled-components'; | ||
2 | + | ||
3 | +export const Container = styled.div ` | ||
4 | + width : 100%; | ||
5 | + position : relative; | ||
6 | + | ||
7 | + margin : 5px 0; | ||
8 | + padding : 20px 5px; | ||
9 | + | ||
10 | + border-top : 1px solid #ddd; | ||
11 | + | ||
12 | + display : flex; | ||
13 | + flex-direction : column; | ||
14 | +`; | ||
15 | + | ||
16 | +export const TermsWrapper = styled.div ` | ||
17 | + width : 100%: | ||
18 | + | ||
19 | + display : flex; | ||
20 | + flex-direction : row; | ||
21 | + | ||
22 | + padding : 0 0 20px 0; | ||
23 | + margin : 0 0 10px 0; | ||
24 | + | ||
25 | + background-color : transparent; | ||
26 | + border : none; | ||
27 | + | ||
28 | + border-bottom : 1px solid #ddd; | ||
29 | +`; | ||
30 | + | ||
31 | +export const EachTerms = styled.button ` | ||
32 | + color : #000; | ||
33 | + background-color : transparent; | ||
34 | + | ||
35 | + margin : 0 10px 0 0; | ||
36 | + padding : 0 0 5px 0; | ||
37 | + | ||
38 | + cursor : pointer; | ||
39 | + | ||
40 | + font-size : 13px; | ||
41 | + | ||
42 | + font-weight : 400; | ||
43 | + border : none; | ||
44 | + border-bottom : 1px solid; | ||
45 | + | ||
46 | + transition : .25s all; | ||
47 | + | ||
48 | + &:hover { | ||
49 | + color : #337DFF; | ||
50 | + opacity : .5; | ||
51 | + } | ||
52 | +`; | ||
53 | + | ||
54 | +export const InfoWrapper = styled.div ` | ||
55 | + display : flex; | ||
56 | + flex-direction : row; | ||
57 | + | ||
58 | + border : none; | ||
59 | + background-color : transparent; | ||
60 | +`; | ||
61 | + | ||
62 | +export const LicenseWrapper = styled.div ` | ||
63 | + flex : 1; | ||
64 | + display : flex; | ||
65 | + flex-direction : column; | ||
66 | + | ||
67 | + border : none; | ||
68 | + background-color : transparent; | ||
69 | + justify-content : center; | ||
70 | + align-items : flex-start; | ||
71 | + | ||
72 | +`; | ||
73 | + | ||
74 | +export const LicenseExplain = styled.div ` | ||
75 | + color : #a0a0a0; | ||
76 | + font-size : 14px; | ||
77 | + | ||
78 | + font-weight : 400; | ||
79 | + padding : 0 5px; | ||
80 | + | ||
81 | + border : none; | ||
82 | +`; | ||
83 | + | ||
84 | +export const LicenseImg = styled.img ` | ||
85 | + height : 60px; | ||
86 | + width : 150px; | ||
87 | +`; | ||
88 | + | ||
89 | +export const ServiceInfoWrapper = styled.div ` | ||
90 | + flex : 3; | ||
91 | + display : flex; | ||
92 | + flex-direction : column; | ||
93 | + justify-content : center; | ||
94 | + align-items : center; | ||
95 | + | ||
96 | + border : none; | ||
97 | + background-color : transparent; | ||
98 | +`; | ||
99 | + | ||
100 | +export const ServiceInfoEach = styled.div ` | ||
101 | + color : #d0d0d0; | ||
102 | + font-size : 13px; | ||
103 | + | ||
104 | + font-weight : 400 | ||
105 | +`; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/Footer/index.tsx
0 → 100644
1 | +import React from 'react'; | ||
2 | +import { RouteComponentProps } from 'react-router'; | ||
3 | + | ||
4 | +import * as Alert from '../../util/alertMessage'; | ||
5 | +import * as styled from './FooterStyled'; | ||
6 | + | ||
7 | + | ||
8 | +const ApiLicense = '/static/img/apiLicense.png'; | ||
9 | + | ||
10 | +// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
11 | +interface FooterProps extends RouteComponentProps {} | ||
12 | + | ||
13 | + | ||
14 | +const Footer = (props : FooterProps) => { | ||
15 | + | ||
16 | + | ||
17 | + const onGoTerm = () => { | ||
18 | + Alert.onWarning('준비중입니다.', () => null); | ||
19 | + }; | ||
20 | + | ||
21 | + const onGoPrivateLicense = () => { | ||
22 | + Alert.onWarning('준비중입니다.', () => null); | ||
23 | + }; | ||
24 | + | ||
25 | + const onGoServiceCenter = () => { | ||
26 | + Alert.onWarning('준비중입니다.', () => null); | ||
27 | + }; | ||
28 | + | ||
29 | + return ( | ||
30 | + <styled.Container> | ||
31 | + <styled.TermsWrapper> | ||
32 | + <styled.EachTerms | ||
33 | + onClick = {onGoTerm} | ||
34 | + > | ||
35 | + 이용약관 | ||
36 | + </styled.EachTerms> | ||
37 | + <styled.EachTerms | ||
38 | + onClick = {onGoPrivateLicense} | ||
39 | + > | ||
40 | + 개인정보처리방침 | ||
41 | + </styled.EachTerms> | ||
42 | + <styled.EachTerms | ||
43 | + onClick = {onGoServiceCenter} | ||
44 | + > | ||
45 | + 고객센터 | ||
46 | + </styled.EachTerms> | ||
47 | + </styled.TermsWrapper> | ||
48 | + <styled.InfoWrapper> | ||
49 | + <styled.LicenseWrapper> | ||
50 | + <styled.LicenseExplain>저작권</styled.LicenseExplain> | ||
51 | + <styled.LicenseImg src = {ApiLicense}/> | ||
52 | + </styled.LicenseWrapper> | ||
53 | + <styled.ServiceInfoWrapper> | ||
54 | + <styled.ServiceInfoEach>서비스명 : Smart Medicine Box (SMB)</styled.ServiceInfoEach> | ||
55 | + <styled.ServiceInfoEach>서비스제공 : IoT 약병 제작회</styled.ServiceInfoEach> | ||
56 | + <styled.ServiceInfoEach>담당자 : 박권수</styled.ServiceInfoEach> | ||
57 | + <styled.ServiceInfoEach>주소 : OOO도 OOO시 OOO구 OOO OOO OOO</styled.ServiceInfoEach> | ||
58 | + <styled.ServiceInfoEach>연락처 : 010 - 0000 - 0000</styled.ServiceInfoEach> | ||
59 | + </styled.ServiceInfoWrapper> | ||
60 | + <styled.LicenseWrapper/> | ||
61 | + </styled.InfoWrapper> | ||
62 | + </styled.Container> | ||
63 | + ) | ||
64 | +}; | ||
65 | + | ||
66 | +export default Footer; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -10,7 +10,6 @@ import * as styled from './HeaderStyled'; | ... | @@ -10,7 +10,6 @@ import * as styled from './HeaderStyled'; |
10 | import { authApi } from '../../api'; | 10 | import { authApi } from '../../api'; |
11 | 11 | ||
12 | const headerImg = '/static/img/pharmacy.png'; | 12 | const headerImg = '/static/img/pharmacy.png'; |
13 | -const backButtonWhite = '/static/img/backButtonWhite.png'; | ||
14 | const backButtonBlue = '/static/img/backButtonBlue.png'; | 13 | const backButtonBlue = '/static/img/backButtonBlue.png'; |
15 | const logout = '/static/img/logout.png'; | 14 | const logout = '/static/img/logout.png'; |
16 | 15 | ... | ... |
web/src/components/Modal/ModalStyled.tsx
0 → 100644
1 | +import styled, { keyframes } from 'styled-components'; | ||
2 | + | ||
3 | + | ||
4 | +const ModalOn = keyframes ` | ||
5 | + 0% { | ||
6 | + background-color : rgba(52, 52, 52, .0); | ||
7 | + } | ||
8 | + 20% { | ||
9 | + background-color : rgba(52, 52, 52, .2); | ||
10 | + } | ||
11 | + 40% { | ||
12 | + background-color : rgba(52, 52, 52, .4); | ||
13 | + } | ||
14 | + 60% { | ||
15 | + background-color : rgba(52, 52, 52, .5); | ||
16 | + } | ||
17 | + 80% { | ||
18 | + background-color : rgba(52, 52, 52, .6); | ||
19 | + } | ||
20 | + 100% { | ||
21 | + background-color : rgba(52, 52, 52, .7); | ||
22 | + } | ||
23 | + | ||
24 | +`; | ||
25 | + | ||
26 | + | ||
27 | +export const ModalContainer = styled.div ` | ||
28 | + height : 100%; | ||
29 | + width : 100%; | ||
30 | + z-index : 99; | ||
31 | + position : absolute; | ||
32 | + | ||
33 | + display : flex; | ||
34 | + flex-direction : column; | ||
35 | + | ||
36 | + animation : ${ModalOn} .5s; | ||
37 | + | ||
38 | + background-color : rgba(52, 52, 52, .7); | ||
39 | + | ||
40 | +`; | ||
41 | + | ||
42 | +export const ModalClsButtonWrapper = styled.div ` | ||
43 | + flex : 1; | ||
44 | + | ||
45 | + display : flex; | ||
46 | + | ||
47 | + justify-content : flex-end; | ||
48 | + align-items : center; | ||
49 | + padding : 0 20px; | ||
50 | + | ||
51 | + border : none; | ||
52 | + background-color : transprent; | ||
53 | +`; | ||
54 | + | ||
55 | +export const ModalClsButton = styled.button ` | ||
56 | + border : none; | ||
57 | + background-color : transparent; | ||
58 | + | ||
59 | + cursor : pointer; | ||
60 | + | ||
61 | + color : #fff; | ||
62 | + | ||
63 | + display : flex; | ||
64 | + flex-direction : row; | ||
65 | + | ||
66 | + justify-content : center; | ||
67 | + align-items : center; | ||
68 | + | ||
69 | + transition : .25s all; | ||
70 | + &:hover { | ||
71 | + opacity : .5; | ||
72 | + } | ||
73 | +`; | ||
74 | + | ||
75 | +export const ModalClsButtonImg = styled.img ` | ||
76 | + height : 20px; | ||
77 | + width : 20px; | ||
78 | + | ||
79 | + margin : 0 10px 0 0; | ||
80 | +`; | ||
81 | + | ||
82 | +export const ModalClsButtonText = styled.div ` | ||
83 | + font-size : 18px; | ||
84 | + font-weight : 700; | ||
85 | +`; | ||
86 | + | ||
87 | +export const ModalContentWrapper = styled.div ` | ||
88 | + flex : 8; | ||
89 | + | ||
90 | + display : flex; | ||
91 | + flex-direction : column; | ||
92 | + | ||
93 | + justify-content : center; | ||
94 | + align-items : center; | ||
95 | + | ||
96 | + border : none; | ||
97 | +`; | ||
98 | + | ||
99 | +export const ModalContent = styled.div ` | ||
100 | + width : 600px; | ||
101 | + height : 400px; | ||
102 | + | ||
103 | + background-color : #fff; | ||
104 | + border : 1.2px solid #337DFF; | ||
105 | + border-radius : 5px; | ||
106 | + | ||
107 | + display : flex; | ||
108 | + flex-direction : column; | ||
109 | + | ||
110 | + justify-content : center; | ||
111 | + align-items : center; | ||
112 | +`; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/Modal/index.tsx
0 → 100644
1 | +import React from 'react'; | ||
2 | + | ||
3 | +import * as styled from './ModalStyled'; | ||
4 | + | ||
5 | +const closeButton = '/static/img/close.png'; | ||
6 | + | ||
7 | + | ||
8 | +interface ModalProps { | ||
9 | + children : JSX.Element, | ||
10 | + onModalClose : () => void; | ||
11 | +} | ||
12 | + | ||
13 | +const ModalContainer = (props : ModalProps) => { | ||
14 | + return ( | ||
15 | + <styled.ModalContainer> | ||
16 | + <styled.ModalClsButtonWrapper> | ||
17 | + <styled.ModalClsButton | ||
18 | + onClick = {props.onModalClose} | ||
19 | + > | ||
20 | + <styled.ModalClsButtonImg src = {closeButton}/> | ||
21 | + <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | ||
22 | + </styled.ModalClsButton> | ||
23 | + </styled.ModalClsButtonWrapper> | ||
24 | + <styled.ModalContentWrapper> | ||
25 | + <styled.ModalContent> | ||
26 | + {props.children} | ||
27 | + </styled.ModalContent> | ||
28 | + </styled.ModalContentWrapper> | ||
29 | + <styled.ModalClsButtonWrapper /> | ||
30 | + </styled.ModalContainer> | ||
31 | + ); | ||
32 | +}; | ||
33 | + | ||
34 | +export default ModalContainer; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | import moment from 'moment'; | 1 | import moment from 'moment'; |
2 | 2 | ||
3 | -export const make = (chartData : any[], numberOfRow : number) => { | 3 | +export const make = (takeMedicineHist : any[], numberOfRow : number) => { |
4 | const now = new Date(); | 4 | const now = new Date(); |
5 | const result : any = {}; | 5 | const result : any = {}; |
6 | - new Array(numberOfRow).fill(null).forEach((item : any, index : number) => { | 6 | + new Array(numberOfRow).fill(null).forEach(() => { |
7 | const key = moment(now).format('MM/DD'); | 7 | const key = moment(now).format('MM/DD'); |
8 | result[key] = 0; | 8 | result[key] = 0; |
9 | now.setDate(now.getDate() - 1); | 9 | now.setDate(now.getDate() - 1); |
10 | - }) | 10 | + }); |
11 | 11 | ||
12 | - chartData.forEach((data : any) => { | 12 | + takeMedicineHist.forEach((data : any) => { |
13 | const key : string = moment(data.takeDate).format('MM/DD'); | 13 | const key : string = moment(data.takeDate).format('MM/DD'); |
14 | - result[key] = result[key] + 1; | 14 | + !isNaN(result[key]) ? result[key] = result[key] + data.dosage : null; |
15 | }); | 15 | }); |
16 | 16 | ||
17 | const categories : any = []; | 17 | const categories : any = []; |
... | @@ -28,5 +28,5 @@ export const make = (chartData : any[], numberOfRow : number) => { | ... | @@ -28,5 +28,5 @@ export const make = (chartData : any[], numberOfRow : number) => { |
28 | return { | 28 | return { |
29 | categories, | 29 | categories, |
30 | data, | 30 | data, |
31 | - } | 31 | + }; |
32 | }; | 32 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -9,6 +9,12 @@ export const token = atom({ | ... | @@ -9,6 +9,12 @@ export const token = atom({ |
9 | effects_UNSTABLE : [persistAtom], | 9 | effects_UNSTABLE : [persistAtom], |
10 | }); | 10 | }); |
11 | 11 | ||
12 | +export const userId = atom({ | ||
13 | + key : 'userId', | ||
14 | + default : null, | ||
15 | + effects_UNSTABLE : [persistAtom], | ||
16 | +}); | ||
17 | + | ||
12 | export const userTypeCd = atom({ | 18 | export const userTypeCd = atom({ |
13 | key : 'userTypeCd', | 19 | key : 'userTypeCd', |
14 | default : 'NORMAL', | 20 | default : 'NORMAL', | ... | ... |
... | @@ -3,6 +3,7 @@ import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'; | ... | @@ -3,6 +3,7 @@ import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'; |
3 | 3 | ||
4 | import Error from '../components/error'; | 4 | import Error from '../components/error'; |
5 | import Loading from '../components/Loading'; | 5 | import Loading from '../components/Loading'; |
6 | +import Footer from '../components/Footer'; | ||
6 | import { LoginContainer } from "./login"; | 7 | import { LoginContainer } from "./login"; |
7 | import { RegisterContainer } from './register'; | 8 | import { RegisterContainer } from './register'; |
8 | import { MainContainer } from "./main"; | 9 | import { MainContainer } from "./main"; | ... | ... |
... | @@ -9,6 +9,7 @@ import * as Alert from '../../util/alertMessage'; | ... | @@ -9,6 +9,7 @@ import * as Alert from '../../util/alertMessage'; |
9 | import moment from 'moment'; | 9 | import moment from 'moment'; |
10 | 10 | ||
11 | import Header from '../../components/Header'; | 11 | import Header from '../../components/Header'; |
12 | +import Footer from '../../components/Footer'; | ||
12 | import BottleInfoPresenter from './BottleInfoPresenter'; | 13 | import BottleInfoPresenter from './BottleInfoPresenter'; |
13 | 14 | ||
14 | import { doctorApi } from '../../api'; | 15 | import { doctorApi } from '../../api'; |
... | @@ -35,6 +36,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -35,6 +36,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
35 | takeMedicineHist : [], | 36 | takeMedicineHist : [], |
36 | }); | 37 | }); |
37 | 38 | ||
39 | + //차트에 표시되는 행의 개수 | ||
38 | const numberOfChartItem = 7; | 40 | const numberOfChartItem = 7; |
39 | const [chartOption, setChartOption] = useState<any>({ | 41 | const [chartOption, setChartOption] = useState<any>({ |
40 | chart : { | 42 | chart : { |
... | @@ -51,19 +53,22 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -51,19 +53,22 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
51 | categories : [], | 53 | categories : [], |
52 | }, | 54 | }, |
53 | series : [{ | 55 | series : [{ |
54 | - name : '약 복용 횟수', | 56 | + name : '약 복용 회분', |
55 | color : '#337DFF', | 57 | color : '#337DFF', |
56 | data : [], | 58 | data : [], |
57 | }], | 59 | }], |
58 | }); | 60 | }); |
61 | + const [takeMedicineHist, setTakeMedicineHist] = useState<any[]>([]); | ||
59 | 62 | ||
60 | const [feedback, setFeedback] = useState<string>(''); | 63 | const [feedback, setFeedback] = useState<string>(''); |
61 | const [fdbType, setFdbType] = useState<string>('RECOMMEND'); | 64 | const [fdbType, setFdbType] = useState<string>('RECOMMEND'); |
62 | 65 | ||
63 | const [medicineInfoModal, setMedicineInfoModal] = useState<boolean>(false); | 66 | const [medicineInfoModal, setMedicineInfoModal] = useState<boolean>(false); |
67 | + const [modalType, setModalType] = useState<string>('hist'); //hist , info | ||
64 | 68 | ||
65 | 69 | ||
66 | const fetchData = async () => { | 70 | const fetchData = async () => { |
71 | + setModalType('hist'); | ||
67 | setFeedback(''); | 72 | setFeedback(''); |
68 | setFdbType('RECOMMEND'); | 73 | setFdbType('RECOMMEND'); |
69 | setMedicineInfoModal(false); | 74 | setMedicineInfoModal(false); |
... | @@ -71,6 +76,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -71,6 +76,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
71 | try { | 76 | try { |
72 | const result = await doctorApi.getPatientBottleDetail(token, bottleId); | 77 | const result = await doctorApi.getPatientBottleDetail(token, bottleId); |
73 | if (result.statusText === 'OK') { | 78 | if (result.statusText === 'OK') { |
79 | + setTakeMedicineHist(result.data.takeMedicineHist.map((takeMedicine : any) => { | ||
80 | + return ({ | ||
81 | + ...takeMedicine, | ||
82 | + takeDate : moment(takeMedicine.takeDate).format('YYYY년 MM월 DD일 hh시 mm분'), | ||
83 | + }); | ||
84 | + })); | ||
74 | const { categories, data } = makeChart.make(result.data.takeMedicineHist, numberOfChartItem); | 85 | const { categories, data } = makeChart.make(result.data.takeMedicineHist, numberOfChartItem); |
75 | setBottleInfo({ | 86 | setBottleInfo({ |
76 | ...result.data, | 87 | ...result.data, |
... | @@ -97,9 +108,8 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -97,9 +108,8 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
97 | Alert.onError('접근 권한이 없습니다.', () => props.history.push('/')); | 108 | Alert.onError('접근 권한이 없습니다.', () => props.history.push('/')); |
98 | } | 109 | } |
99 | } catch(e : any) { | 110 | } catch(e : any) { |
100 | - Alert.onError(e.response.data.error, () => props.history.push('/')); | ||
101 | - | ||
102 | console.log(e); | 111 | console.log(e); |
112 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => props.history.push('/')); | ||
103 | } | 113 | } |
104 | }; | 114 | }; |
105 | 115 | ||
... | @@ -123,7 +133,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -123,7 +133,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
123 | Alert.onError('피드백 등록에 실패했습니다.', () => null); | 133 | Alert.onError('피드백 등록에 실패했습니다.', () => null); |
124 | } | 134 | } |
125 | } catch(e : any) { | 135 | } catch(e : any) { |
126 | - Alert.onError(e.response.data.error, () => fetchData()); | 136 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => fetchData()); |
127 | } | 137 | } |
128 | } else { | 138 | } else { |
129 | Alert.onError('피드백 내용을 입력하세요.', () => null); | 139 | Alert.onError('피드백 내용을 입력하세요.', () => null); |
... | @@ -134,6 +144,14 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -134,6 +144,14 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
134 | 144 | ||
135 | }; | 145 | }; |
136 | 146 | ||
147 | + const onViewTakeHist = () => { | ||
148 | + if(modalType === 'info') setModalType('hist'); | ||
149 | + }; | ||
150 | + | ||
151 | + const onViewMedicineInfo = () => { | ||
152 | + if(modalType === 'hist') setModalType('info'); | ||
153 | + }; | ||
154 | + | ||
137 | 155 | ||
138 | useEffect(() => { | 156 | useEffect(() => { |
139 | if(userTypeCd !== 'DOCTOR') { | 157 | if(userTypeCd !== 'DOCTOR') { |
... | @@ -148,8 +166,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -148,8 +166,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
148 | <BottleInfoPresenter | 166 | <BottleInfoPresenter |
149 | bottleInfo = {bottleInfo} | 167 | bottleInfo = {bottleInfo} |
150 | chartOption = {chartOption} | 168 | chartOption = {chartOption} |
169 | + takeMedicineHist = {takeMedicineHist} | ||
151 | 170 | ||
152 | medicineInfoModal = {medicineInfoModal} | 171 | medicineInfoModal = {medicineInfoModal} |
172 | + modalType = {modalType} | ||
173 | + onViewTakeHist = {onViewTakeHist} | ||
174 | + onViewMedicineInfo = {onViewMedicineInfo} | ||
153 | setMedicineInfoModal = {setMedicineInfoModal} | 175 | setMedicineInfoModal = {setMedicineInfoModal} |
154 | 176 | ||
155 | feedback = {feedback} | 177 | feedback = {feedback} |
... | @@ -158,6 +180,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -158,6 +180,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
158 | setFdbType = {setFdbType} | 180 | setFdbType = {setFdbType} |
159 | onSubmitFeedback = {onSubmitFeedback} | 181 | onSubmitFeedback = {onSubmitFeedback} |
160 | /> | 182 | /> |
183 | + <Footer {...props}/> | ||
161 | </> | 184 | </> |
162 | ); | 185 | ); |
163 | }; | 186 | }; | ... | ... |
... | @@ -2,10 +2,10 @@ import React from 'react'; | ... | @@ -2,10 +2,10 @@ import React from 'react'; |
2 | import HighCharts from 'highcharts'; | 2 | import HighCharts from 'highcharts'; |
3 | import HighchartsReact from 'highcharts-react-official'; | 3 | import HighchartsReact from 'highcharts-react-official'; |
4 | 4 | ||
5 | +import Modal from '../../components/Modal'; | ||
5 | import * as styled from './BottleInfoStyled'; | 6 | import * as styled from './BottleInfoStyled'; |
6 | 7 | ||
7 | const plus = '/static/img/plus.png'; | 8 | const plus = '/static/img/plus.png'; |
8 | -const closeButton = '/static/img/close.png'; | ||
9 | 9 | ||
10 | 10 | ||
11 | interface BottleInfoProps { | 11 | interface BottleInfoProps { |
... | @@ -16,8 +16,12 @@ interface BottleInfoProps { | ... | @@ -16,8 +16,12 @@ interface BottleInfoProps { |
16 | takeMedicineHist : any[]; | 16 | takeMedicineHist : any[]; |
17 | }; | 17 | }; |
18 | chartOption : any; | 18 | chartOption : any; |
19 | + takeMedicineHist : any[]; | ||
19 | 20 | ||
20 | medicineInfoModal : boolean; | 21 | medicineInfoModal : boolean; |
22 | + modalType : string; | ||
23 | + onViewTakeHist : () => void; | ||
24 | + onViewMedicineInfo : () => void; | ||
21 | setMedicineInfoModal : (arg0 : boolean) => void; | 25 | setMedicineInfoModal : (arg0 : boolean) => void; |
22 | 26 | ||
23 | feedback : string; | 27 | feedback : string; |
... | @@ -32,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 | }; | ... | ... |
web/src/views/main/MainPresenter.tsx
deleted
100644 → 0
1 | -import React from 'react'; | ||
2 | - | ||
3 | -import * as styled from './MainStyled'; | ||
4 | - | ||
5 | - | ||
6 | -interface MainProps { | ||
7 | - userTypeCd : string; | ||
8 | -} | ||
9 | - | ||
10 | -const MainPresenter = (props : MainProps) => { | ||
11 | - return ( | ||
12 | - <styled.Container> | ||
13 | - <styled.InfoAndSearchWrapper> | ||
14 | - <styled.InfoWrapper> | ||
15 | - <styled.InfoSquare> | ||
16 | - | ||
17 | - </styled.InfoSquare> | ||
18 | - <styled.NewPatientButton>새 환자 등록</styled.NewPatientButton> | ||
19 | - </styled.InfoWrapper> | ||
20 | - <styled.SearchAndDetailWrapper> | ||
21 | - <styled.SearchBarWrapper> | ||
22 | - <styled.SearchBar | ||
23 | - placeholder = '환자 이름' | ||
24 | - /> | ||
25 | - <styled.SearchButton> | ||
26 | - 검색 | ||
27 | - </styled.SearchButton> | ||
28 | - </styled.SearchBarWrapper> | ||
29 | - <styled.SearchResultWrapper> | ||
30 | - | ||
31 | - </styled.SearchResultWrapper> | ||
32 | - </styled.SearchAndDetailWrapper> | ||
33 | - </styled.InfoAndSearchWrapper> | ||
34 | - <styled.BottleListWrapper> | ||
35 | - bottleListWrapper | ||
36 | - </styled.BottleListWrapper> | ||
37 | - </styled.Container> | ||
38 | - ) | ||
39 | -}; | ||
40 | - | ||
41 | -export default MainPresenter; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
1 | -import styled, { keyframes } from 'styled-components'; | 1 | +import styled from 'styled-components'; |
2 | - | ||
3 | - | ||
4 | -const ModalOn = keyframes ` | ||
5 | - 0% { | ||
6 | - background-color : rgba(52, 52, 52, .0); | ||
7 | - } | ||
8 | - 20% { | ||
9 | - background-color : rgba(52, 52, 52, .2); | ||
10 | - } | ||
11 | - 40% { | ||
12 | - background-color : rgba(52, 52, 52, .4); | ||
13 | - } | ||
14 | - 60% { | ||
15 | - background-color : rgba(52, 52, 52, .5); | ||
16 | - } | ||
17 | - 80% { | ||
18 | - background-color : rgba(52, 52, 52, .6); | ||
19 | - } | ||
20 | - 100% { | ||
21 | - background-color : rgba(52, 52, 52, .7); | ||
22 | - } | ||
23 | - | ||
24 | -`; | ||
25 | 2 | ||
26 | 3 | ||
27 | export const Container = styled.div ` | 4 | export const Container = styled.div ` |
... | @@ -32,93 +9,6 @@ export const Container = styled.div ` | ... | @@ -32,93 +9,6 @@ export const Container = styled.div ` |
32 | justify-content : center; | 9 | justify-content : center; |
33 | `; | 10 | `; |
34 | 11 | ||
35 | -export const ModalContainer = styled.div ` | ||
36 | - height : 100%; | ||
37 | - width : 100%; | ||
38 | - z-index : 99; | ||
39 | - position : absolute; | ||
40 | - | ||
41 | - display : flex; | ||
42 | - flex-direction : column; | ||
43 | - | ||
44 | - animation : ${ModalOn} .5s; | ||
45 | - | ||
46 | - background-color : rgba(52, 52, 52, .7); | ||
47 | - | ||
48 | -`; | ||
49 | - | ||
50 | -export const ModalClsButtonWrapper = styled.div ` | ||
51 | - flex : 1; | ||
52 | - | ||
53 | - display : flex; | ||
54 | - | ||
55 | - justify-content : flex-end; | ||
56 | - align-items : center; | ||
57 | - padding : 0 20px; | ||
58 | - | ||
59 | - border : none; | ||
60 | - background-color : transprent; | ||
61 | -`; | ||
62 | - | ||
63 | -export const ModalClsButton = styled.button ` | ||
64 | - border : none; | ||
65 | - background-color : transparent; | ||
66 | - | ||
67 | - cursor : pointer; | ||
68 | - | ||
69 | - color : #fff; | ||
70 | - | ||
71 | - display : flex; | ||
72 | - flex-direction : row; | ||
73 | - | ||
74 | - justify-content : center; | ||
75 | - align-items : center; | ||
76 | - | ||
77 | - transition : .25s all; | ||
78 | - &:hover { | ||
79 | - opacity : .5; | ||
80 | - } | ||
81 | -`; | ||
82 | - | ||
83 | -export const ModalClsButtonImg = styled.img ` | ||
84 | - height : 20px; | ||
85 | - width : 20px; | ||
86 | - | ||
87 | - margin : 0 10px 0 0; | ||
88 | -`; | ||
89 | - | ||
90 | -export const ModalClsButtonText = styled.div ` | ||
91 | - font-size : 18px; | ||
92 | - font-weight : 700; | ||
93 | -`; | ||
94 | - | ||
95 | -export const ModalContentWrapper = styled.div ` | ||
96 | - flex : 8; | ||
97 | - | ||
98 | - display : flex; | ||
99 | - flex-direction : column; | ||
100 | - | ||
101 | - justify-content : center; | ||
102 | - align-items : center; | ||
103 | - | ||
104 | - border : none; | ||
105 | -`; | ||
106 | - | ||
107 | -export const ModalContent = styled.div ` | ||
108 | - width : 600px; | ||
109 | - height : 400px; | ||
110 | - | ||
111 | - background-color : #fff; | ||
112 | - border : 1.2px solid #337DFF; | ||
113 | - border-radius : 5px; | ||
114 | - | ||
115 | - display : flex; | ||
116 | - flex-direction : column; | ||
117 | - | ||
118 | - justify-content : center; | ||
119 | - align-items : center; | ||
120 | -`; | ||
121 | - | ||
122 | export const NewPatientRegisterTitle = styled.div ` | 12 | export const NewPatientRegisterTitle = styled.div ` |
123 | font-size : 20px; | 13 | font-size : 20px; |
124 | font-weight : 700; | 14 | font-weight : 700; |
... | @@ -199,14 +89,18 @@ export const NewPatientSearchResult = styled.div ` | ... | @@ -199,14 +89,18 @@ export const NewPatientSearchResult = styled.div ` |
199 | 89 | ||
200 | export const NewPatientSearchResultInfoWrapper = styled.div ` | 90 | export const NewPatientSearchResultInfoWrapper = styled.div ` |
201 | display : flex; | 91 | display : flex; |
92 | + flex-direction : column; | ||
202 | `; | 93 | `; |
203 | 94 | ||
204 | export const NewPatientSearchResultInfo = styled.div ` | 95 | export const NewPatientSearchResultInfo = styled.div ` |
96 | + display : flex; | ||
97 | + flex-direction : row; | ||
98 | + | ||
205 | font-size : 13px; | 99 | font-size : 13px; |
206 | font-weight : 600; | 100 | font-weight : 600; |
207 | color : #a0a0a0; | 101 | color : #a0a0a0; |
208 | 102 | ||
209 | - margin : 0 5px 0 0; | 103 | + margin : 0 5px 0 5px; |
210 | `; | 104 | `; |
211 | 105 | ||
212 | export const NewPatientSearchResultInfoText = styled.div ` | 106 | export const NewPatientSearchResultInfoText = styled.div ` |
... | @@ -214,6 +108,8 @@ export const NewPatientSearchResultInfoText = styled.div ` | ... | @@ -214,6 +108,8 @@ export const NewPatientSearchResultInfoText = styled.div ` |
214 | color : #343434; | 108 | color : #343434; |
215 | font-weight : 600; | 109 | font-weight : 600; |
216 | letter-spacing : 1px; | 110 | letter-spacing : 1px; |
111 | + | ||
112 | + margin : 0 0 0 5px; | ||
217 | `; | 113 | `; |
218 | 114 | ||
219 | export const NewPatientRegisterButtonWrapper = styled.div ` | 115 | export const NewPatientRegisterButtonWrapper = styled.div ` |
... | @@ -436,6 +332,7 @@ export const MedicineSearchButtonImg = styled.img ` | ... | @@ -436,6 +332,7 @@ export const MedicineSearchButtonImg = styled.img ` |
436 | height : 15px; | 332 | height : 15px; |
437 | width : 15px; | 333 | width : 15px; |
438 | 334 | ||
335 | + transition : .25s all; | ||
439 | `; | 336 | `; |
440 | 337 | ||
441 | export const MedicineSearchResultWrapper = styled.div ` | 338 | export const MedicineSearchResultWrapper = styled.div ` |
... | @@ -499,6 +396,91 @@ export const MedicineSelectButtonImg = styled.img ` | ... | @@ -499,6 +396,91 @@ export const MedicineSelectButtonImg = styled.img ` |
499 | width : 15px; | 396 | width : 15px; |
500 | `; | 397 | `; |
501 | 398 | ||
399 | +export const MedicineDosageSetWrapper = styled.div ` | ||
400 | + width : 80%; | ||
401 | + | ||
402 | + display : flex; | ||
403 | + flex-direction : column; | ||
404 | + | ||
405 | + justify-content : center; | ||
406 | + align-items : center; | ||
407 | + | ||
408 | + border : none; | ||
409 | + | ||
410 | + margin : 20px 0; | ||
411 | + | ||
412 | + height : 200px; | ||
413 | +`; | ||
414 | + | ||
415 | +export const MedicineDosageInfo = styled.div ` | ||
416 | + font-size : 15px; | ||
417 | + font-weight : 500; | ||
418 | + | ||
419 | + color : #a0a0a0; | ||
420 | + | ||
421 | + width : 100%; | ||
422 | + margin : 10px 0 10px 0; | ||
423 | + | ||
424 | + border : none; | ||
425 | + background-color : transparent; | ||
426 | + | ||
427 | + text-align : center; | ||
428 | +`; | ||
429 | + | ||
430 | +export const MedicineDosageInput = styled.input.attrs({ | ||
431 | + type : 'number', | ||
432 | +}) ` | ||
433 | + margin : 0 0 10px 0; | ||
434 | + width : 40%; | ||
435 | + | ||
436 | + padding : 10px 20px; | ||
437 | + color : #337DFF; | ||
438 | + font-size : 20px; | ||
439 | + | ||
440 | + font-weight : 700; | ||
441 | + | ||
442 | + border : none; | ||
443 | + border-bottom : 1px solid #337DFF; | ||
444 | + | ||
445 | + display : flex; | ||
446 | + flex-direction : row; | ||
447 | + | ||
448 | + text-align : center; | ||
449 | + | ||
450 | + transition : .25s all; | ||
451 | +`; | ||
452 | + | ||
453 | +export const MedicineQRCodeWrapper = styled.div ` | ||
454 | + width : 80%; | ||
455 | + height : 200px; | ||
456 | + | ||
457 | + display : flex; | ||
458 | + flex-direction : column; | ||
459 | + | ||
460 | + justify-content : center; | ||
461 | + align-items : center; | ||
462 | + | ||
463 | + margin : 20px 0; | ||
464 | + | ||
465 | + border : none; | ||
466 | +`; | ||
467 | + | ||
468 | +export const MedicineQRCodeInfo = styled.div ` | ||
469 | + font-size : 15px; | ||
470 | + font-weight : 500; | ||
471 | + | ||
472 | + color : #a0a0a0; | ||
473 | + | ||
474 | + text-align : center; | ||
475 | +`; | ||
476 | + | ||
477 | +export const MedicineQRCode = styled.img ` | ||
478 | + margin : 10px 0 0 0; | ||
479 | + | ||
480 | + height : 170px; | ||
481 | + width : 170px; | ||
482 | +`; | ||
483 | + | ||
502 | export const MedicinePrescribeButtonWrapper = styled.div ` | 484 | export const MedicinePrescribeButtonWrapper = styled.div ` |
503 | margin : 20px 0 0 0; | 485 | margin : 20px 0 0 0; |
504 | 486 | ... | ... |
... | @@ -17,11 +17,14 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -17,11 +17,14 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
17 | 17 | ||
18 | const token = useRecoilValue(recoilUtil.token); | 18 | const token = useRecoilValue(recoilUtil.token); |
19 | 19 | ||
20 | - const [doctorRegReqList, setDoctorRegReqList] = useState<any>([]); | 20 | + const [doctorList, setDoctorList] = useState<any>([]); |
21 | + | ||
22 | + const [viewType, setViewType] = useState<string>('reg'); | ||
21 | 23 | ||
22 | const [doctorDetail, setDoctorDetail] = useState<any>({}); | 24 | const [doctorDetail, setDoctorDetail] = useState<any>({}); |
23 | const [modalUp, setModalUp] = useState<boolean>(false); | 25 | const [modalUp, setModalUp] = useState<boolean>(false); |
24 | const [validate, setValidate] = useState<string>('W'); | 26 | const [validate, setValidate] = useState<string>('W'); |
27 | + const [validateDoctorLicense, setValidateDoctorLicense] = useState<string>(''); | ||
25 | 28 | ||
26 | 29 | ||
27 | 30 | ||
... | @@ -30,20 +33,29 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -30,20 +33,29 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
30 | setValidate('W'); | 33 | setValidate('W'); |
31 | 34 | ||
32 | try { | 35 | try { |
33 | - await managerApi.getDoctorRegReqList(token) | 36 | + const res = viewType === 'reg' ? await managerApi.getDoctorRegReqList(token) : await managerApi.getDoctorSecReqList(token); |
34 | - .then((res : any) => { | ||
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.
-
Please register or login to post a comment