Showing
71 changed files
with
4126 additions
and
1140 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" | ... | ... |
1 | -/* eslint-disable no-undef */ | ||
2 | //회원가입, 로그인 및 로그아웃에 관한 api | 1 | //회원가입, 로그인 및 로그아웃에 관한 api |
3 | const User = require('../../models/user'); | 2 | const User = require('../../models/user'); |
4 | const Profile = require('../../models/profile'); | 3 | const Profile = require('../../models/profile'); |
5 | const DoctorInfo = require('../../models/doctorInfo'); | 4 | const DoctorInfo = require('../../models/doctorInfo'); |
5 | +const Hub = require('../../models/hub'); | ||
6 | +const Bottle = require('../../models/bottle'); | ||
7 | +const PatientInfo = require('../../models/patientInfo'); | ||
8 | +const { uploadDoctorLicense } = require('../../util/GoogleCloudStorage'); | ||
6 | const Joi = require('joi'); | 9 | const Joi = require('joi'); |
7 | const jwt = require('jsonwebtoken'); | 10 | const jwt = require('jsonwebtoken'); |
11 | +const axios = require('axios'); | ||
8 | 12 | ||
9 | 13 | ||
10 | exports.register = async(ctx) => { | 14 | exports.register = async(ctx) => { |
... | @@ -13,8 +17,9 @@ exports.register = async(ctx) => { | ... | @@ -13,8 +17,9 @@ exports.register = async(ctx) => { |
13 | password, | 17 | password, |
14 | passwordCheck, | 18 | passwordCheck, |
15 | userNm, | 19 | userNm, |
16 | - userAge, | 20 | + birth, |
17 | contact, | 21 | contact, |
22 | + deviceToken, | ||
18 | } = ctx.request.body; | 23 | } = ctx.request.body; |
19 | 24 | ||
20 | const schema = Joi.object().keys({ | 25 | const schema = Joi.object().keys({ |
... | @@ -22,8 +27,9 @@ exports.register = async(ctx) => { | ... | @@ -22,8 +27,9 @@ exports.register = async(ctx) => { |
22 | password : Joi.string().required(), | 27 | password : Joi.string().required(), |
23 | passwordCheck : Joi.string().required(), | 28 | passwordCheck : Joi.string().required(), |
24 | userNm : Joi.string().required(), | 29 | userNm : Joi.string().required(), |
25 | - userAge : Joi.number().required(), | 30 | + birth : Joi.string().required(), |
26 | contact : Joi.string().required(), | 31 | contact : Joi.string().required(), |
32 | + deviceToken : Joi.string().allow(null), | ||
27 | }); | 33 | }); |
28 | 34 | ||
29 | const result = schema.validate(ctx.request.body); | 35 | const result = schema.validate(ctx.request.body); |
... | @@ -44,6 +50,15 @@ exports.register = async(ctx) => { | ... | @@ -44,6 +50,15 @@ exports.register = async(ctx) => { |
44 | return; | 50 | return; |
45 | } | 51 | } |
46 | 52 | ||
53 | + const existContact = await Profile.findOne({ contact, useYn : 'Y' }); | ||
54 | + if(existContact) { | ||
55 | + ctx.status = 409; | ||
56 | + ctx.body = { | ||
57 | + error : '이미 가입된 번호입니다.', | ||
58 | + }; | ||
59 | + return; | ||
60 | + } | ||
61 | + | ||
47 | const user = new User({ | 62 | const user = new User({ |
48 | userId, | 63 | userId, |
49 | userTypeCd : 'NORMAL', | 64 | userTypeCd : 'NORMAL', |
... | @@ -55,8 +70,9 @@ exports.register = async(ctx) => { | ... | @@ -55,8 +70,9 @@ exports.register = async(ctx) => { |
55 | const profile = new Profile({ | 70 | const profile = new Profile({ |
56 | userId, | 71 | userId, |
57 | userNm, | 72 | userNm, |
58 | - userAge, | 73 | + birth, |
59 | - contact, | 74 | + contact, |
75 | + deviceToken, | ||
60 | }); | 76 | }); |
61 | 77 | ||
62 | await user.save(); | 78 | await user.save(); |
... | @@ -66,22 +82,59 @@ exports.register = async(ctx) => { | ... | @@ -66,22 +82,59 @@ exports.register = async(ctx) => { |
66 | 82 | ||
67 | }; | 83 | }; |
68 | 84 | ||
85 | +exports.searchHospital = async ctx => { | ||
86 | + const { | ||
87 | + hospitalNm, | ||
88 | + page, | ||
89 | + } = ctx.query; | ||
90 | + | ||
91 | + const pageSlice = 5; | ||
92 | + | ||
93 | + const url = 'http://apis.data.go.kr/B551182/hospInfoService1/getHospBasisList1'; | ||
94 | + // eslint-disable-next-line no-undef | ||
95 | + let queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY; | ||
96 | + queryParams += '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(page); | ||
97 | + queryParams += '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(pageSlice); | ||
98 | + queryParams += '&' + encodeURIComponent('yadmNm') + '=' + encodeURIComponent(hospitalNm); | ||
99 | + | ||
100 | + const result = await axios.get(url + queryParams); | ||
101 | + | ||
102 | + ctx.status = 200; | ||
103 | + ctx.body = { | ||
104 | + totalPage : Math.ceil(result.data.response.body.totalCount / pageSlice), | ||
105 | + hospitalList : result.data.response.body.items.item, | ||
106 | + }; | ||
107 | +}; | ||
108 | + | ||
109 | +//의사 회원가입 | ||
69 | exports.doctorRegister = async ctx => { | 110 | exports.doctorRegister = async ctx => { |
70 | const { | 111 | const { |
71 | userId, | 112 | userId, |
72 | password, | 113 | password, |
73 | passwordCheck, | 114 | passwordCheck, |
74 | - info, | 115 | + |
116 | + contact, | ||
117 | + hospitalNm, | ||
118 | + hospitalAddr, | ||
119 | + doctorType, | ||
120 | + doctorNm, | ||
75 | } = ctx.request.body; | 121 | } = ctx.request.body; |
76 | 122 | ||
123 | + const { doctorInfoFile } = ctx.request.files; | ||
124 | + | ||
77 | const schema = Joi.object().keys({ | 125 | const schema = Joi.object().keys({ |
78 | userId : Joi.string().email().max(50).required(), | 126 | userId : Joi.string().email().max(50).required(), |
79 | password : Joi.string().required(), | 127 | password : Joi.string().required(), |
80 | passwordCheck : Joi.string().required(), | 128 | passwordCheck : Joi.string().required(), |
81 | - info : Joi.object().required(), | 129 | + doctorInfoFile : Joi.object().required(), |
82 | - }) | 130 | + }); |
83 | - | 131 | + |
84 | - const result = schema.validate(ctx.request.body); | 132 | + const result = schema.validate({ |
133 | + userId, | ||
134 | + password, | ||
135 | + passwordCheck, | ||
136 | + doctorInfoFile, | ||
137 | + }); | ||
85 | if(result.error || password !== passwordCheck) { | 138 | if(result.error || password !== passwordCheck) { |
86 | ctx.status = 400; | 139 | ctx.status = 400; |
87 | ctx.body = { | 140 | ctx.body = { |
... | @@ -113,6 +166,24 @@ exports.doctorRegister = async ctx => { | ... | @@ -113,6 +166,24 @@ exports.doctorRegister = async ctx => { |
113 | }; | 166 | }; |
114 | return; | 167 | return; |
115 | } | 168 | } |
169 | + | ||
170 | + | ||
171 | + const [fileName, filePath] = [doctorInfoFile.name, doctorInfoFile.path]; | ||
172 | + const doctorLicense = await uploadDoctorLicense({ | ||
173 | + userId, | ||
174 | + fileName, | ||
175 | + filePath, | ||
176 | + }); | ||
177 | + | ||
178 | + const info = { | ||
179 | + contact, | ||
180 | + hospitalAddr, | ||
181 | + hospitalNm, | ||
182 | + doctorType, | ||
183 | + doctorNm, | ||
184 | + doctorLicense, | ||
185 | + }; | ||
186 | + | ||
116 | 187 | ||
117 | const doctor = new User({ | 188 | const doctor = new User({ |
118 | userId, | 189 | userId, |
... | @@ -128,21 +199,27 @@ exports.doctorRegister = async ctx => { | ... | @@ -128,21 +199,27 @@ exports.doctorRegister = async ctx => { |
128 | useYn : 'W', | 199 | useYn : 'W', |
129 | }); | 200 | }); |
130 | 201 | ||
131 | - | 202 | + await doctorInfo.save(); |
132 | - doctor.save(); | 203 | + await doctor.save(); |
133 | - doctorInfo.save(); | ||
134 | 204 | ||
135 | ctx.status = 201; | 205 | ctx.status = 201; |
136 | - | 206 | + |
137 | } | 207 | } |
138 | 208 | ||
209 | +/** | ||
210 | + * 로컬 로그인 | ||
211 | + * @param {*} ctx | ||
212 | + * @returns token | ||
213 | + * http methods : POST | ||
214 | + */ | ||
139 | exports.login = async(ctx) => { | 215 | exports.login = async(ctx) => { |
140 | - const { userId, password } = ctx.request.body; | 216 | + const { userId, password, deviceToken } = ctx.request.body; |
141 | 217 | ||
142 | const schema = Joi.object().keys({ | 218 | const schema = Joi.object().keys({ |
143 | userId : Joi.string().email().max(50).required(), | 219 | userId : Joi.string().email().max(50).required(), |
144 | - password : Joi.string().required() | 220 | + password : Joi.string().required(), |
145 | - }) | 221 | + deviceToken : Joi.string().allow(null), |
222 | + }); | ||
146 | 223 | ||
147 | const result = schema.validate(ctx.request.body); | 224 | const result = schema.validate(ctx.request.body); |
148 | if(result.error) { | 225 | if(result.error) { |
... | @@ -154,7 +231,7 @@ exports.login = async(ctx) => { | ... | @@ -154,7 +231,7 @@ exports.login = async(ctx) => { |
154 | } | 231 | } |
155 | 232 | ||
156 | const user = await User.findByUserId(userId); | 233 | const user = await User.findByUserId(userId); |
157 | - if(!user || !user.userTypeCd) { | 234 | + if(!user || !user.userTypeCd || user.authTypeCd !== 'NORMAL') { |
158 | ctx.status = 401; | 235 | ctx.status = 401; |
159 | ctx.body = { | 236 | ctx.body = { |
160 | error : '존재하지 않는 회원입니다.', | 237 | error : '존재하지 않는 회원입니다.', |
... | @@ -170,7 +247,6 @@ exports.login = async(ctx) => { | ... | @@ -170,7 +247,6 @@ exports.login = async(ctx) => { |
170 | }; | 247 | }; |
171 | return; | 248 | return; |
172 | } | 249 | } |
173 | - | ||
174 | if(user.useYn !== 'Y') { | 250 | if(user.useYn !== 'Y') { |
175 | ctx.status = 403; | 251 | ctx.status = 403; |
176 | ctx.body = { | 252 | ctx.body = { |
... | @@ -179,6 +255,16 @@ exports.login = async(ctx) => { | ... | @@ -179,6 +255,16 @@ exports.login = async(ctx) => { |
179 | return; | 255 | return; |
180 | } | 256 | } |
181 | 257 | ||
258 | + //일반 유저의 deviceToken값이 바뀌면 업데이트한다 = 기기가 바뀌면 | ||
259 | + if(user.userTypeCd === 'NORMAL') { | ||
260 | + const profile = await Profile.findByUserId(user.userId); | ||
261 | + if(deviceToken && profile.deviceToken !== deviceToken) { | ||
262 | + profile.updateDeviceToken(deviceToken); | ||
263 | + await profile.save(); | ||
264 | + } | ||
265 | + } | ||
266 | + | ||
267 | + | ||
182 | const token = await user.generateToken(); | 268 | const token = await user.generateToken(); |
183 | ctx.cookies.set('access_token', token, { | 269 | ctx.cookies.set('access_token', token, { |
184 | httpOnly : true, | 270 | httpOnly : true, |
... | @@ -188,11 +274,206 @@ exports.login = async(ctx) => { | ... | @@ -188,11 +274,206 @@ exports.login = async(ctx) => { |
188 | ctx.status = 200; | 274 | ctx.status = 200; |
189 | ctx.body = { | 275 | ctx.body = { |
190 | userTypeCd : user.userTypeCd, | 276 | userTypeCd : user.userTypeCd, |
191 | - token | 277 | + token, |
278 | + }; | ||
279 | + | ||
280 | +}; | ||
281 | + | ||
282 | +//social Register | ||
283 | +exports.socialRegister = async ctx => { | ||
284 | + const { socialType } = ctx.params; | ||
285 | + const { accessToken, deviceToken } = ctx.request.body; | ||
286 | + | ||
287 | + const verifyingToken = | ||
288 | + socialType.toUpperCase() === 'GOOGLE' ? async () => { | ||
289 | + //id_token | ||
290 | + const result = jwt.decode(accessToken); | ||
291 | + | ||
292 | + return { | ||
293 | + userId : result.email, | ||
294 | + userNm : result.name, | ||
295 | + contact : `${result.email}_등록되지않은 번호`, | ||
296 | + birth : '등록되지않음', | ||
297 | + }; | ||
298 | + } | ||
299 | + : socialType.toUpperCase() === 'NAVER' ? async () => { | ||
300 | + const url = 'https://openapi.naver.com/v1/nid/me'; | ||
301 | + const result = await axios.get(url, { | ||
302 | + headers : { | ||
303 | + Authorization : `Bearer ${accessToken}`, | ||
304 | + }, | ||
305 | + }); | ||
306 | + | ||
307 | + const { email, mobile, name, birthday, birthyear } = result.data.response; | ||
308 | + | ||
309 | + return { | ||
310 | + userId : email, | ||
311 | + userNm : name, | ||
312 | + contact : mobile, | ||
313 | + birth : `${birthyear}-${birthday}`, | ||
314 | + }; | ||
315 | + } | ||
316 | + : socialType.toUpperCase() === 'KAKAO' ? async () => { | ||
317 | + const url = 'https://kapi.kakao.com/v2/user/me'; | ||
318 | + const result = await axios.get(url, { | ||
319 | + headers : { | ||
320 | + Authorization : `Bearer ${accessToken}`, | ||
321 | + 'Content-type' : 'application/x-www-form-urlencoded;charset=utf-8', | ||
322 | + }, | ||
323 | + }); | ||
324 | + | ||
325 | + const { email, profile } = result.data.kakao_account; | ||
326 | + | ||
327 | + return { | ||
328 | + userId : email, | ||
329 | + userNm : profile.nickname, | ||
330 | + contact : `${email}_등록되지않은 번호`, | ||
331 | + birth : '등록되지않음', | ||
332 | + }; | ||
333 | + | ||
334 | + } : () => null; | ||
335 | + | ||
336 | + | ||
337 | + const verifyingInfo = await verifyingToken(); | ||
338 | + if(!verifyingInfo || !verifyingInfo.userId) { | ||
339 | + ctx.status = 403; | ||
340 | + ctx.body = { | ||
341 | + error : '잘못된 요청', | ||
342 | + }; | ||
343 | + | ||
344 | + return; | ||
345 | + } | ||
346 | + | ||
347 | + const { userId, userNm, birth, contact } = verifyingInfo; | ||
348 | + | ||
349 | + const existUser = await User.findByUserId(userId); | ||
350 | + if(existUser) { | ||
351 | + ctx.status = 409; | ||
352 | + ctx.body = { | ||
353 | + error : '이미 가입된 회원', | ||
354 | + }; | ||
355 | + | ||
356 | + return; | ||
357 | + } | ||
358 | + | ||
359 | + const existContact = await Profile.findOne({ contact, useYn : 'Y'}); | ||
360 | + if(existContact) { | ||
361 | + ctx.status = 409; | ||
362 | + ctx.body = { | ||
363 | + error : '이미 가입된 번호', | ||
364 | + }; | ||
365 | + | ||
366 | + return; | ||
367 | + } | ||
368 | + | ||
369 | + const user = new User({ | ||
370 | + userId, | ||
371 | + hashedPassword : 'unnecessary', | ||
372 | + authTypeCd : socialType.toUpperCase(), | ||
373 | + useYn : 'Y', | ||
374 | + }); | ||
375 | + | ||
376 | + const profile = new Profile({ | ||
377 | + userId, | ||
378 | + userNm, | ||
379 | + birth, | ||
380 | + contact, | ||
381 | + deviceToken, | ||
382 | + }); | ||
383 | + | ||
384 | + await profile.save(); | ||
385 | + await user.save(); | ||
386 | + | ||
387 | + ctx.status = 201; | ||
388 | + | ||
389 | +}; | ||
390 | + | ||
391 | +//social Login | ||
392 | +exports.socialLogin = async ctx => { | ||
393 | + const { socialType } = ctx.params; | ||
394 | + const { accessToken, deviceToken, } = ctx.request.body; | ||
395 | + | ||
396 | + const verifyingToken = | ||
397 | + socialType.toUpperCase() === 'GOOGLE' ? async () => { | ||
398 | + //id_token : google Login | ||
399 | + const result = jwt.decode(accessToken); | ||
400 | + | ||
401 | + return result.email; | ||
402 | + } | ||
403 | + : socialType.toUpperCase() === 'NAVER' ? async () => { | ||
404 | + //naver Login | ||
405 | + const url = 'https://openapi.naver.com/v1/nid/me'; | ||
406 | + const result = await axios.get(url, { | ||
407 | + headers : { | ||
408 | + Authorization : `Bearer ${accessToken}`, | ||
409 | + }, | ||
410 | + }); | ||
411 | + | ||
412 | + return result.data.response.email; | ||
413 | + } | ||
414 | + : socialType.toUpperCase() === 'KAKAO' ? async () => { | ||
415 | + //kakao Login | ||
416 | + const url = 'https://kapi.kakao.com/v2/user/me'; | ||
417 | + const result = await axios.get(url, { | ||
418 | + headers : { | ||
419 | + Authorization : `Bearer ${accessToken}`, | ||
420 | + 'Content-type' : 'application/x-www-form-urlencoded;charset=utf-8', | ||
421 | + }, | ||
422 | + }); | ||
423 | + | ||
424 | + return result.data.kakao_account.email; | ||
425 | + } : () => null; | ||
426 | + | ||
427 | + | ||
428 | + const userId = await verifyingToken(); | ||
429 | + if(!userId) { | ||
430 | + ctx.status = 403; | ||
431 | + ctx.body = { | ||
432 | + error : '잘못된 요청입니다', | ||
433 | + }; | ||
434 | + | ||
435 | + return; | ||
436 | + } | ||
437 | + | ||
438 | + const user = await User.findByUserId(userId); | ||
439 | + if(!user || user.useYn !== 'Y') { | ||
440 | + ctx.status = 404; | ||
441 | + ctx.body = { | ||
442 | + error : '존재하지 않는 회원입니다.', | ||
443 | + }; | ||
444 | + | ||
445 | + return; | ||
446 | + } else if (user.authTypeCd !== socialType.toUpperCase()) { | ||
447 | + ctx.status = 400; | ||
448 | + ctx.body = { | ||
449 | + error : '잘못된 소셜 로그인입니다.', | ||
450 | + }; | ||
451 | + | ||
452 | + return; | ||
453 | + } | ||
454 | + | ||
455 | + const profile = await Profile.findOne({ userId }); | ||
456 | + if(profile.deviceToken !== deviceToken) { | ||
457 | + profile.updateDeviceToken(deviceToken); | ||
458 | + await profile.save(); | ||
459 | + } | ||
460 | + | ||
461 | + | ||
462 | + const token = await user.generateToken(); | ||
463 | + | ||
464 | + ctx.status = 200; | ||
465 | + ctx.body = { | ||
466 | + userTypeCd : user.userTypeCd, | ||
467 | + token, | ||
192 | }; | 468 | }; |
193 | 469 | ||
194 | }; | 470 | }; |
195 | 471 | ||
472 | +/** | ||
473 | + * 로그아웃 | ||
474 | + * @param {*} ctx | ||
475 | + * httm methods : POST | ||
476 | + */ | ||
196 | exports.logout = async(ctx) => { | 477 | exports.logout = async(ctx) => { |
197 | ctx.cookies.set('access_token', null, { | 478 | ctx.cookies.set('access_token', null, { |
198 | httpOnly : true, | 479 | httpOnly : true, |
... | @@ -202,6 +483,70 @@ exports.logout = async(ctx) => { | ... | @@ -202,6 +483,70 @@ exports.logout = async(ctx) => { |
202 | ctx.status = 204; | 483 | ctx.status = 204; |
203 | }; | 484 | }; |
204 | 485 | ||
486 | + | ||
487 | +/** | ||
488 | + * 회원 탈퇴 | ||
489 | + * @param {*} ctx | ||
490 | + * http methods : delete | ||
491 | + */ | ||
492 | +exports.secession = async ctx => { | ||
493 | + const token = ctx.req.headers.authorization; | ||
494 | + if(!token || !token.length) { | ||
495 | + ctx.status = 401; | ||
496 | + return; | ||
497 | + } | ||
498 | + | ||
499 | + // eslint-disable-next-line no-undef | ||
500 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
501 | + const user = await User.findByUserId(userId); | ||
502 | + if(!user || user.useYn !== 'Y') { | ||
503 | + ctx.status = 403; | ||
504 | + return; | ||
505 | + } | ||
506 | + | ||
507 | + | ||
508 | + const { password } = ctx.query; | ||
509 | + const isPasswordTrue = await user.checkPassword(password); | ||
510 | + if(!isPasswordTrue) { | ||
511 | + ctx.status = 401; | ||
512 | + ctx.body = { | ||
513 | + error : '비밀번호가 틀렸습니다.', | ||
514 | + }; | ||
515 | + return; | ||
516 | + } | ||
517 | + | ||
518 | + if(user.userTypeCd === 'NORMAL') { | ||
519 | + //프로필 삭제 | ||
520 | + await Profile.updateOne({ userId }, { useYn : 'N' }); | ||
521 | + | ||
522 | + //유저에 등록된 허브, 약병, 약병정보 전부 삭제 | ||
523 | + const hubList = await Hub.find({ userId }); | ||
524 | + await Promise.all(hubList.map(async hub => { | ||
525 | + await Bottle.deleteMany({ hubId : hub.hubId }); | ||
526 | + })); | ||
527 | + | ||
528 | + await Hub.deleteMany({ userId }); | ||
529 | + | ||
530 | + //환자 정보 삭제 | ||
531 | + await PatientInfo.updateMany({ patientId : userId, useYn : 'Y'}, { useYn : 'N' }); | ||
532 | + | ||
533 | + //유저 삭제 | ||
534 | + await user.setUseYn('N'); | ||
535 | + await user.save(); | ||
536 | + | ||
537 | + } else if (user.userTypeCd === 'DOCTOR') { | ||
538 | + //의사 정보 및 환자 정보 삭제 | ||
539 | + await DoctorInfo.updateOne({ doctorId : userId }, { useYn : 'WS' }); | ||
540 | + await PatientInfo.updateMany({ doctorId : userId }, { useYn : 'WS' }); | ||
541 | + | ||
542 | + await user.setUseYn('WS'); | ||
543 | + await user.save(); | ||
544 | + } | ||
545 | + | ||
546 | + ctx.status = 200; | ||
547 | + | ||
548 | +}; | ||
549 | + | ||
205 | exports.verifyToken = async(ctx) => { | 550 | exports.verifyToken = async(ctx) => { |
206 | const token = ctx.req.headers.authorization; | 551 | const token = ctx.req.headers.authorization; |
207 | if(!token || !token.length) { | 552 | if(!token || !token.length) { |
... | @@ -214,6 +559,7 @@ exports.verifyToken = async(ctx) => { | ... | @@ -214,6 +559,7 @@ exports.verifyToken = async(ctx) => { |
214 | return; | 559 | return; |
215 | } | 560 | } |
216 | 561 | ||
562 | + // eslint-disable-next-line no-undef | ||
217 | jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => { | 563 | jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => { |
218 | if (err) { | 564 | if (err) { |
219 | ctx.status = 400; | 565 | ctx.status = 400; | ... | ... |
1 | const Router = require('koa-router') | 1 | const Router = require('koa-router') |
2 | +const KoaBody = require('koa-body')({multipart : true}); | ||
2 | const authCtrl = require('./auth.ctrl') | 3 | const authCtrl = require('./auth.ctrl') |
3 | 4 | ||
4 | const auth = new Router() | 5 | const auth = new Router() |
5 | 6 | ||
6 | /** | 7 | /** |
7 | - * 회원가입 (email type) : 환자 회원가입 | 8 | + * 로컬 회원가입 (email type) : 환자 회원가입 |
8 | * url : http://localhost:4000/api/auth/register | 9 | * url : http://localhost:4000/api/auth/register |
9 | * request parameter : userId, password, passwordCheck | 10 | * request parameter : userId, password, passwordCheck |
10 | * return : null | 11 | * return : null |
... | @@ -12,28 +13,60 @@ const auth = new Router() | ... | @@ -12,28 +13,60 @@ const auth = new Router() |
12 | auth.post('/register', authCtrl.register) | 13 | auth.post('/register', authCtrl.register) |
13 | 14 | ||
14 | /** | 15 | /** |
15 | - * 회원가입 (email type) : 의사 회원가입 | 16 | + * 병원 검색 |
17 | + * url : http://localhost:4000/api/auth/hospital | ||
18 | + * request parameter : hospitalNm | ||
19 | + * return : xml type data | ||
20 | + */ | ||
21 | +auth.get('/hospital', authCtrl.searchHospital); | ||
22 | + | ||
23 | +/** | ||
24 | + * 로컬 회원가입 (email type) : 의사 회원가입 | ||
16 | * url : http://localhost:4000/api/auth/register/doctor | 25 | * url : http://localhost:4000/api/auth/register/doctor |
17 | - * request parameter : userId, password, passwordCheck, doctorInfo | 26 | + * request parameter : userId, password, passwordCheck, doctorInfo(File) |
18 | * return : null | 27 | * return : null |
19 | */ | 28 | */ |
20 | - auth.post('/register/doctor', authCtrl.doctorRegister) | 29 | +auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister) |
21 | 30 | ||
22 | /** | 31 | /** |
23 | - * 로그인 (email type) | 32 | + * 로컬 로그인 (email type) |
24 | * url : http://localhost:4000/api/auth/login | 33 | * url : http://localhost:4000/api/auth/login |
25 | * request parameter : userId, password | 34 | * request parameter : userId, password |
26 | - * return : userId | 35 | + * return : token, userTypeCd |
27 | */ | 36 | */ |
28 | auth.post('/login', authCtrl.login) | 37 | auth.post('/login', authCtrl.login) |
29 | 38 | ||
30 | /** | 39 | /** |
40 | + * 소셜 회원가입(Google, Naver, Kakao) | ||
41 | + * url : http://localhost:4000/api/auth/register/${socialType} | ||
42 | + * request parameter : accessToken | ||
43 | + * return : status | ||
44 | + */ | ||
45 | +auth.post('/register/social/:socialType', authCtrl.socialRegister); | ||
46 | + | ||
47 | +/** | ||
48 | + * 소셜 로그인(Google, Naver, Kakao) | ||
49 | + * url : http://localhost:4000/api/auth/login/${socialType} | ||
50 | + * request parameter | ||
51 | + * return : token, userTypeCd | ||
52 | + */ | ||
53 | +auth.post('/login/social/:socialType', authCtrl.socialLogin); | ||
54 | + | ||
55 | +/** | ||
31 | * 로그아웃 | 56 | * 로그아웃 |
32 | * url : http://localhost:4000/api/auth/logout | 57 | * url : http://localhost:4000/api/auth/logout |
33 | * request parameter : null | 58 | * request parameter : null |
34 | * return : null | 59 | * return : null |
35 | */ | 60 | */ |
36 | -auth.post('/logout', authCtrl.logout) | 61 | +auth.post('/logout', authCtrl.logout); |
62 | + | ||
63 | +/** | ||
64 | + * 회원 탈퇴 | ||
65 | + * url : http://localhost:4000/api/auth | ||
66 | + * request parameter : password | ||
67 | + * return : null | ||
68 | + */ | ||
69 | +auth.delete('/', authCtrl.secession); | ||
37 | 70 | ||
38 | /** | 71 | /** |
39 | * 토큰이 유효한지 확인 | 72 | * 토큰이 유효한지 확인 | ... | ... |
1 | -/* eslint-disable no-undef */ | ||
2 | //어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다. | 1 | //어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다. |
3 | const Bottle = require('../../models/bottle'); | 2 | const Bottle = require('../../models/bottle'); |
4 | const Hub = require('../../models/hub'); | 3 | const Hub = require('../../models/hub'); |
5 | const Medicine = require('../../models/medicine'); | 4 | const Medicine = require('../../models/medicine'); |
6 | const User = require('../../models/user'); | 5 | const User = require('../../models/user'); |
6 | +const DoctorInfo = require('../../models/doctorInfo'); | ||
7 | const PatientInfo = require('../../models/patientInfo'); | 7 | const PatientInfo = require('../../models/patientInfo'); |
8 | const TakeMedicineHist = require('../../models/takeMedicineHistory'); | 8 | const TakeMedicineHist = require('../../models/takeMedicineHistory'); |
9 | const BottleMedicine = require('../../models/bottleMedicine'); | 9 | const BottleMedicine = require('../../models/bottleMedicine'); |
10 | const Feedback = require('../../models/feedback'); | 10 | const Feedback = require('../../models/feedback'); |
11 | -const Mqtt = require('../../lib/MqttModule'); | 11 | +const Mqtt = require('../../util/MqttModule'); |
12 | const jwt = require('jsonwebtoken'); | 12 | const jwt = require('jsonwebtoken'); |
13 | 13 | ||
14 | //약병 등록 | 14 | //약병 등록 |
... | @@ -19,6 +19,7 @@ exports.bottleConnect = async(ctx) => { | ... | @@ -19,6 +19,7 @@ exports.bottleConnect = async(ctx) => { |
19 | return; | 19 | return; |
20 | } | 20 | } |
21 | 21 | ||
22 | + // eslint-disable-next-line no-undef | ||
22 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 23 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
23 | const user = await User.findByUserId(userId); | 24 | const user = await User.findByUserId(userId); |
24 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 25 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
... | @@ -26,7 +27,7 @@ exports.bottleConnect = async(ctx) => { | ... | @@ -26,7 +27,7 @@ exports.bottleConnect = async(ctx) => { |
26 | return; | 27 | return; |
27 | } | 28 | } |
28 | 29 | ||
29 | - const { bottleId, hubId } = ctx.request.body; | 30 | + const { bottleId, hubId, bottleNm } = ctx.request.body; |
30 | 31 | ||
31 | const isExistBottle = await Bottle.findByBottleId(bottleId); | 32 | const isExistBottle = await Bottle.findByBottleId(bottleId); |
32 | if(isExistBottle) { | 33 | if(isExistBottle) { |
... | @@ -53,7 +54,8 @@ exports.bottleConnect = async(ctx) => { | ... | @@ -53,7 +54,8 @@ exports.bottleConnect = async(ctx) => { |
53 | 54 | ||
54 | const newBottle = new Bottle({ | 55 | const newBottle = new Bottle({ |
55 | bottleId, | 56 | bottleId, |
56 | - hubId | 57 | + hubId, |
58 | + bottleNm, | ||
57 | }); | 59 | }); |
58 | 60 | ||
59 | const client = await Mqtt.mqttOn(hosting); | 61 | const client = await Mqtt.mqttOn(hosting); |
... | @@ -73,6 +75,7 @@ exports.bottleDisconnect = async(ctx) => { | ... | @@ -73,6 +75,7 @@ exports.bottleDisconnect = async(ctx) => { |
73 | return; | 75 | return; |
74 | } | 76 | } |
75 | 77 | ||
78 | + // eslint-disable-next-line no-undef | ||
76 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 79 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
77 | const user = await User.findByUserId(userId); | 80 | const user = await User.findByUserId(userId); |
78 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 81 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
... | @@ -100,6 +103,7 @@ exports.bottleDisconnect = async(ctx) => { | ... | @@ -100,6 +103,7 @@ exports.bottleDisconnect = async(ctx) => { |
100 | const topic = 'bottle/' + bottleId + '/bts'; | 103 | const topic = 'bottle/' + bottleId + '/bts'; |
101 | Mqtt.mqttUnsubscribe(client, topic); | 104 | Mqtt.mqttUnsubscribe(client, topic); |
102 | 105 | ||
106 | + await BottleMedicine.updateMany({ bottleId }, { useYn : 'N' }); | ||
103 | await Bottle.deleteOne({ bottleId }); | 107 | await Bottle.deleteOne({ bottleId }); |
104 | 108 | ||
105 | ctx.status = 204; | 109 | ctx.status = 204; |
... | @@ -114,6 +118,7 @@ exports.getBottleInfo = async(ctx) => { | ... | @@ -114,6 +118,7 @@ exports.getBottleInfo = async(ctx) => { |
114 | return; | 118 | return; |
115 | } | 119 | } |
116 | 120 | ||
121 | + // eslint-disable-next-line no-undef | ||
117 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 122 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
118 | const user = await User.findByUserId(userId); | 123 | const user = await User.findByUserId(userId); |
119 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 124 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
... | @@ -146,21 +151,29 @@ exports.getBottleInfo = async(ctx) => { | ... | @@ -146,21 +151,29 @@ exports.getBottleInfo = async(ctx) => { |
146 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | 151 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); |
147 | 152 | ||
148 | if(bottleMedicine) { | 153 | if(bottleMedicine) { |
154 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
155 | + const doctorInfo = await DoctorInfo.findOne({ doctorId : bottleMedicine.doctorId }); | ||
156 | + | ||
149 | const takeMedicineHist = await TakeMedicineHist | 157 | const takeMedicineHist = await TakeMedicineHist |
150 | .find({ bmId : bottleMedicine._id }) | 158 | .find({ bmId : bottleMedicine._id }) |
151 | - .sort({ takeDate : 'desc' }) | 159 | + .sort({ takeDate : 'desc' }); |
152 | - .populate('bmId'); | ||
153 | 160 | ||
154 | ctx.status = 200; | 161 | ctx.status = 200; |
155 | ctx.body = { | 162 | ctx.body = { |
156 | - bottle, | 163 | + medicine, |
164 | + doctorInfo, | ||
165 | + dailyDosage : bottleMedicine.dailyDosage, | ||
166 | + totalDosage : bottleMedicine.totalDosage, | ||
157 | takeMedicineHist, | 167 | takeMedicineHist, |
158 | }; | 168 | }; |
159 | 169 | ||
160 | } else { | 170 | } else { |
161 | ctx.status = 200; | 171 | ctx.status = 200; |
162 | ctx.body = { | 172 | ctx.body = { |
163 | - bottle, | 173 | + medicine : null, |
174 | + doctorInfo : null, | ||
175 | + dailyDosage : null, | ||
176 | + totalDosage : null, | ||
164 | takeMedicineHist : [], | 177 | takeMedicineHist : [], |
165 | } | 178 | } |
166 | } | 179 | } |
... | @@ -175,6 +188,7 @@ exports.getBottleFeedback = async ctx => { | ... | @@ -175,6 +188,7 @@ exports.getBottleFeedback = async ctx => { |
175 | return; | 188 | return; |
176 | } | 189 | } |
177 | 190 | ||
191 | + // eslint-disable-next-line no-undef | ||
178 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 192 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
179 | const user = await User.findByUserId(userId); | 193 | const user = await User.findByUserId(userId); |
180 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 194 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
... | @@ -213,7 +227,9 @@ exports.getBottleFeedback = async ctx => { | ... | @@ -213,7 +227,9 @@ exports.getBottleFeedback = async ctx => { |
213 | .populate('bmId'); | 227 | .populate('bmId'); |
214 | 228 | ||
215 | ctx.status = 200; | 229 | ctx.status = 200; |
216 | - ctx.body = feedbackList; | 230 | + ctx.body = { |
231 | + feedbackList | ||
232 | + }; | ||
217 | } else { | 233 | } else { |
218 | ctx.status = 404; | 234 | ctx.status = 404; |
219 | ctx.body = { | 235 | ctx.body = { |
... | @@ -231,6 +247,7 @@ exports.setMedicine = async(ctx) => { | ... | @@ -231,6 +247,7 @@ exports.setMedicine = async(ctx) => { |
231 | return; | 247 | return; |
232 | } | 248 | } |
233 | 249 | ||
250 | + // eslint-disable-next-line no-undef | ||
234 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 251 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
235 | const user = await User.findByUserId(userId); | 252 | const user = await User.findByUserId(userId); |
236 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 253 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
... | @@ -239,7 +256,7 @@ exports.setMedicine = async(ctx) => { | ... | @@ -239,7 +256,7 @@ exports.setMedicine = async(ctx) => { |
239 | } | 256 | } |
240 | 257 | ||
241 | const { bottleId } = ctx.params; | 258 | const { bottleId } = ctx.params; |
242 | - const { medicineId, dosage, doctorId } = ctx.request.body; | 259 | + const { medicineId, doctorId, dailyDosage, totalDosage, } = ctx.request.body; |
243 | 260 | ||
244 | const bottle = await Bottle.findByBottleId(bottleId); | 261 | const bottle = await Bottle.findByBottleId(bottleId); |
245 | if(!bottle) { | 262 | if(!bottle) { |
... | @@ -273,7 +290,8 @@ exports.setMedicine = async(ctx) => { | ... | @@ -273,7 +290,8 @@ exports.setMedicine = async(ctx) => { |
273 | let bottleMedicine = new BottleMedicine({ | 290 | let bottleMedicine = new BottleMedicine({ |
274 | bottleId, | 291 | bottleId, |
275 | medicineId, | 292 | medicineId, |
276 | - dosage, | 293 | + dailyDosage, |
294 | + totalDosage, | ||
277 | }); | 295 | }); |
278 | 296 | ||
279 | if(doctorId !== undefined && doctorId !== null && doctorId !== '') { | 297 | if(doctorId !== undefined && doctorId !== null && doctorId !== '') { |
... | @@ -286,14 +304,109 @@ exports.setMedicine = async(ctx) => { | ... | @@ -286,14 +304,109 @@ exports.setMedicine = async(ctx) => { |
286 | return; | 304 | return; |
287 | } | 305 | } |
288 | 306 | ||
289 | - bottleMedicine.setDoctorId(doctorId); | 307 | + await bottleMedicine.setDoctorId(doctorId); |
290 | } | 308 | } |
291 | 309 | ||
292 | await BottleMedicine.updateMany({ bottleId }, { useYn : 'N '}); | 310 | await BottleMedicine.updateMany({ bottleId }, { useYn : 'N '}); |
293 | - | 311 | + await bottleMedicine.save(); |
294 | - bottleMedicine.save(); | 312 | + |
313 | + ctx.status = 200; | ||
314 | +}; | ||
315 | + | ||
316 | +//약 무게 세팅 | ||
317 | +exports.setMedicineWeight = async ctx => { | ||
318 | + const token = ctx.req.headers.authorization; | ||
319 | + if(!token || !token.length) { | ||
320 | + ctx.status = 401; | ||
321 | + return; | ||
322 | + } | ||
323 | + | ||
324 | + // eslint-disable-next-line no-undef | ||
325 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
326 | + const user = await User.findByUserId(userId); | ||
327 | + if(!user || !user.userTypeCd || user.useYn !== 'Y') { | ||
328 | + ctx.status = 403; | ||
329 | + return; | ||
330 | + } | ||
331 | + | ||
332 | + const { bottleId } = ctx.params; | ||
333 | + | ||
334 | + const bottle = await Bottle.findByBottleId(bottleId); | ||
335 | + if(!bottle) { | ||
336 | + ctx.status = 404; | ||
337 | + ctx.body = { | ||
338 | + error : '약병 찾을 수 없음.', | ||
339 | + } | ||
340 | + return; | ||
341 | + } | ||
342 | + | ||
343 | + const hub = await Hub.findByHubId(bottle.getHubId()); | ||
344 | + if(hub.getHub_UserId() !== userId) { | ||
345 | + ctx.status = 403; | ||
346 | + ctx.body = { | ||
347 | + error : '해당 허브 권한 없음', | ||
348 | + } | ||
349 | + return; | ||
350 | + } | ||
351 | + | ||
352 | + | ||
353 | + //toDo : 약병에서 가져온 무게 데이터를 이용하여, bottleMedicine값을 갱신. | ||
354 | + const client = await Mqtt.mqttOn(await hub.getHubHost()); | ||
355 | + const topic = 'bottle/' + bottleId + '/stb'; | ||
356 | + const message = 'weight'; | ||
357 | + await Mqtt.mqttPublishMessage(client, { topic, message }); | ||
358 | + | ||
359 | + | ||
360 | + // const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | ||
361 | + // const { totalWeight, totalDosage } = bottleMedicine; | ||
362 | + // if(totalDosage) bottleMedicine.setEachWeight(totalWeight / totalDosage); | ||
295 | 363 | ||
296 | ctx.status = 200; | 364 | ctx.status = 200; |
365 | + | ||
366 | +}; | ||
367 | + | ||
368 | +//약병 이름 변경 | ||
369 | +exports.setBottleName = async ctx => { | ||
370 | + const token = ctx.req.headers.authorization; | ||
371 | + if(!token || !token.length) { | ||
372 | + ctx.status = 401; | ||
373 | + return; | ||
374 | + } | ||
375 | + | ||
376 | + // eslint-disable-next-line no-undef | ||
377 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
378 | + const user = await User.findByUserId(userId); | ||
379 | + if(!user || !user.userTypeCd || user.useYn !== 'Y') { | ||
380 | + ctx.status = 403; | ||
381 | + return; | ||
382 | + } | ||
383 | + | ||
384 | + const { bottleId } = ctx.params; | ||
385 | + const { bottleNm } = ctx.request.body; | ||
386 | + | ||
387 | + const bottle = await Bottle.findByBottleId(bottleId); | ||
388 | + if(!bottle) { | ||
389 | + ctx.status = 404; | ||
390 | + ctx.body = { | ||
391 | + error : '약병 찾을 수 없음.', | ||
392 | + } | ||
393 | + return; | ||
394 | + } | ||
395 | + | ||
396 | + const hub = await Hub.findByHubId(bottle.getHubId()); | ||
397 | + if(hub.getHub_UserId() !== userId) { | ||
398 | + ctx.status = 403; | ||
399 | + ctx.body = { | ||
400 | + error : '해당 허브 권한 없음', | ||
401 | + } | ||
402 | + return; | ||
403 | + } | ||
404 | + | ||
405 | + await bottle.setBottleNm(bottleNm); | ||
406 | + await bottle.save(); | ||
407 | + | ||
408 | + ctx.status = 200; | ||
409 | + | ||
297 | }; | 410 | }; |
298 | 411 | ||
299 | // //비어있는 약병에 의사를 등록한다. | 412 | // //비어있는 약병에 의사를 등록한다. |
... | @@ -344,6 +457,7 @@ exports.getHubsBottleList = async(ctx) => { | ... | @@ -344,6 +457,7 @@ exports.getHubsBottleList = async(ctx) => { |
344 | return; | 457 | return; |
345 | } | 458 | } |
346 | 459 | ||
460 | + // eslint-disable-next-line no-undef | ||
347 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 461 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
348 | const user = await User.findByUserId(userId); | 462 | const user = await User.findByUserId(userId); |
349 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 463 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
... | @@ -385,6 +499,7 @@ exports.getAllBottleList = async ctx => { | ... | @@ -385,6 +499,7 @@ exports.getAllBottleList = async ctx => { |
385 | return; | 499 | return; |
386 | } | 500 | } |
387 | 501 | ||
502 | + // eslint-disable-next-line no-undef | ||
388 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); | 503 | const { userId } = jwt.verify(token, process.env.JWT_SECRET); |
389 | const user = await User.findByUserId(userId); | 504 | const user = await User.findByUserId(userId); |
390 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { | 505 | if(!user || !user.userTypeCd || user.useYn !== 'Y') { |
... | @@ -402,7 +517,7 @@ exports.getAllBottleList = async ctx => { | ... | @@ -402,7 +517,7 @@ exports.getAllBottleList = async ctx => { |
402 | 517 | ||
403 | ctx.status = 200; | 518 | ctx.status = 200; |
404 | ctx.body = { | 519 | ctx.body = { |
405 | - bottleList | 520 | + bottleList, |
406 | }; | 521 | }; |
407 | 522 | ||
408 | }; | 523 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -44,6 +44,22 @@ bottle.get('/feedback/:bottleId', bottleCtrl.getBottleFeedback); | ... | @@ -44,6 +44,22 @@ bottle.get('/feedback/:bottleId', bottleCtrl.getBottleFeedback); |
44 | bottle.patch('/:bottleId', bottleCtrl.setMedicine); | 44 | bottle.patch('/:bottleId', bottleCtrl.setMedicine); |
45 | 45 | ||
46 | /** | 46 | /** |
47 | + * 약병에 등록된 약의 무게 갱신 | ||
48 | + * request parameter : bottleid | ||
49 | + * url : http://localhost:4000/api/bottle/weight/:bottleId | ||
50 | + * return : null | ||
51 | + */ | ||
52 | +bottle.patch('/weight/:bottleId', bottleCtrl.setMedicineWeight); | ||
53 | + | ||
54 | +/** | ||
55 | + * 약병 이름 변경 | ||
56 | + * request parameter : bottleid, bottleNm | ||
57 | + * url : http://localhost:4000/api/bottle/name/:bottleId | ||
58 | + * return : null | ||
59 | + */ | ||
60 | + bottle.patch('/name/:bottleId', bottleCtrl.setBottleName); | ||
61 | + | ||
62 | +/** | ||
47 | * 비어있는 약병에 전담의 등록 | 63 | * 비어있는 약병에 전담의 등록 |
48 | * request parameter : bottleId, doctorId | 64 | * request parameter : bottleId, doctorId |
49 | * url : http://localhost:4000/api/bottle/doctor/:bottleId | 65 | * url : http://localhost:4000/api/bottle/doctor/:bottleId | ... | ... |
... | @@ -9,8 +9,14 @@ const Feedback = require('../../models/feedback'); | ... | @@ -9,8 +9,14 @@ const Feedback = require('../../models/feedback'); |
9 | const Hub = require('../../models/hub'); | 9 | const Hub = require('../../models/hub'); |
10 | const PatientInfo = require('../../models/patientInfo'); | 10 | const PatientInfo = require('../../models/patientInfo'); |
11 | const DoctorInfo = require('../../models/doctorInfo'); | 11 | const DoctorInfo = require('../../models/doctorInfo'); |
12 | +const PrescribeInfo = require('../../models/prescribeInfo'); | ||
13 | + | ||
12 | const jwt = require('jsonwebtoken'); | 14 | const jwt = require('jsonwebtoken'); |
13 | 15 | ||
16 | +const { uploadQrCode, getQrCodeUrl } = require('../../util/GoogleCloudStorage'); | ||
17 | +const QrCodeUtil = require('../../util/QrCodeUtil'); | ||
18 | +const { sendPushMessage } = require('../../util/FCM'); | ||
19 | + | ||
14 | 20 | ||
15 | /** | 21 | /** |
16 | * 현재 로그인한 유저의 의사 정보를 가져온다 | 22 | * 현재 로그인한 유저의 의사 정보를 가져온다 |
... | @@ -150,7 +156,8 @@ exports.getPatientDetail = async ctx => { | ... | @@ -150,7 +156,8 @@ exports.getPatientDetail = async ctx => { |
150 | bottleId : bottle.bottleId, | 156 | bottleId : bottle.bottleId, |
151 | useYn : 'Y', | 157 | useYn : 'Y', |
152 | }); | 158 | }); |
153 | - reqUserBmList.push(bm); | 159 | + |
160 | + if(bm) reqUserBmList.push(bm); | ||
154 | })); | 161 | })); |
155 | 162 | ||
156 | const bottleList = await Promise.all(reqUserBmList.map(async bottleMedicine => { | 163 | const bottleList = await Promise.all(reqUserBmList.map(async bottleMedicine => { |
... | @@ -280,7 +287,7 @@ exports.writeReqPatientReport = async ctx => { | ... | @@ -280,7 +287,7 @@ exports.writeReqPatientReport = async ctx => { |
280 | } | 287 | } |
281 | 288 | ||
282 | await patientInfo.updateInfo(info); | 289 | await patientInfo.updateInfo(info); |
283 | - patientInfo.save(); | 290 | + await patientInfo.save(); |
284 | 291 | ||
285 | ctx.status = 200; | 292 | ctx.status = 200; |
286 | 293 | ||
... | @@ -335,13 +342,31 @@ exports.writeReqBottleFeedback = async ctx => { | ... | @@ -335,13 +342,31 @@ exports.writeReqBottleFeedback = async ctx => { |
335 | doctorId : userId, | 342 | doctorId : userId, |
336 | feedback, | 343 | feedback, |
337 | }); | 344 | }); |
338 | - newFeedback.save(); | 345 | + await newFeedback.save(); |
346 | + | ||
347 | + | ||
348 | + //feedback 알람 보내기 | ||
349 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
350 | + const patientProfile = await Profile.findOne({ userId : hub.userId }); | ||
351 | + if(patientProfile) { | ||
352 | + sendPushMessage({ | ||
353 | + deviceToken : patientProfile.deviceToken, | ||
354 | + title : '의사에게 새로운 알람이 도착했습니다.', | ||
355 | + body : feedback, | ||
356 | + }); | ||
357 | + } | ||
339 | 358 | ||
340 | ctx.status = 200; | 359 | ctx.status = 200; |
341 | 360 | ||
361 | + | ||
342 | }; | 362 | }; |
343 | 363 | ||
344 | -exports.searchPatientById = async ctx => { | 364 | +/** |
365 | + * 이메일로 환자를 검색한다. | ||
366 | + * @param {*} ctx | ||
367 | + * @returns | ||
368 | + */ | ||
369 | +exports.searchPatientByContact = async ctx => { | ||
345 | const token = ctx.req.headers.authorization; | 370 | const token = ctx.req.headers.authorization; |
346 | if (!token || !token.length) { | 371 | if (!token || !token.length) { |
347 | ctx.status = 401; | 372 | ctx.status = 401; |
... | @@ -359,22 +384,19 @@ exports.searchPatientById = async ctx => { | ... | @@ -359,22 +384,19 @@ exports.searchPatientById = async ctx => { |
359 | return; | 384 | return; |
360 | } | 385 | } |
361 | 386 | ||
362 | - const { patientId } = ctx.params; | 387 | + const { contact } = ctx.params; |
363 | - const patient = await User.findByUserId(patientId); | 388 | + const patientProfile = await Profile.findOne({ contact, useYn : 'Y' }); |
364 | - if(!patient || patient.useYn !== 'Y') { | ||
365 | - ctx.status = 404; | ||
366 | - ctx.body = { | ||
367 | - error : '존재하지 않는 회원', | ||
368 | - }; | ||
369 | - return; | ||
370 | - } | ||
371 | 389 | ||
372 | - const patientProfile = await Profile.findOne({ userId : patientId }); | 390 | + const patientInfo = { |
391 | + userId : patientProfile.userId, | ||
392 | + userNm : patientProfile.userNm, | ||
393 | + birth : patientProfile.birth, | ||
394 | + contact: patientProfile.contact, | ||
395 | + }; | ||
373 | 396 | ||
374 | ctx.status = 200; | 397 | ctx.status = 200; |
375 | ctx.body = { | 398 | ctx.body = { |
376 | - patientNm : patientProfile.userNm, | 399 | + patientInfo, |
377 | - patientId, | ||
378 | }; | 400 | }; |
379 | }; | 401 | }; |
380 | 402 | ||
... | @@ -427,8 +449,17 @@ exports.registerNewPatient = async ctx => { | ... | @@ -427,8 +449,17 @@ exports.registerNewPatient = async ctx => { |
427 | useYn : 'W', | 449 | useYn : 'W', |
428 | }); | 450 | }); |
429 | 451 | ||
430 | - patientInfo.updateInfo('환자 등록 요청'); | 452 | + await patientInfo.updateInfo('환자 등록 요청'); |
431 | - patientInfo.save(); | 453 | + await patientInfo.save(); |
454 | + | ||
455 | + const profile = await Profile.findByUserId(patientId); | ||
456 | + const { deviceToken } = profile; | ||
457 | + | ||
458 | + sendPushMessage({ | ||
459 | + deviceToken, | ||
460 | + title : '새로운 의사 등록 요청이 왔습니다.', | ||
461 | + body : '어플리케이션을 실행해 확인하세요.', | ||
462 | + }); | ||
432 | 463 | ||
433 | ctx.status = 200; | 464 | ctx.status = 200; |
434 | 465 | ||
... | @@ -477,8 +508,113 @@ exports.removeReqPatient = async ctx => { | ... | @@ -477,8 +508,113 @@ exports.removeReqPatient = async ctx => { |
477 | } | 508 | } |
478 | 509 | ||
479 | await patientInfo.setUseYn('N') | 510 | await patientInfo.setUseYn('N') |
480 | - patientInfo.save(); | 511 | + await patientInfo.save(); |
481 | 512 | ||
482 | ctx.status = 200; | 513 | ctx.status = 200; |
483 | 514 | ||
515 | +}; | ||
516 | + | ||
517 | +/** | ||
518 | + * 특정 환자에게 특정 약을 처방한다 | ||
519 | + * @param {*} ctx | ||
520 | + * http methods : post | ||
521 | + */ | ||
522 | +exports.prescribeMedicine = async ctx => { | ||
523 | + const token = ctx.req.headers.authorization; | ||
524 | + if(!token || !token.length) { | ||
525 | + ctx.status = 401; | ||
526 | + return; | ||
527 | + } | ||
528 | + | ||
529 | + // eslint-disable-next-line no-undef | ||
530 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
531 | + const user = await User.findByUserId(userId); | ||
532 | + if(!user || user.userTypeCd !== 'DOCTOR') { | ||
533 | + ctx.status = 403; | ||
534 | + ctx.body = { | ||
535 | + error : '권한 없는 유저', | ||
536 | + }; | ||
537 | + return; | ||
538 | + } | ||
539 | + | ||
540 | + | ||
541 | + const { | ||
542 | + patientId, | ||
543 | + medicineId, | ||
544 | + dailyDosage, | ||
545 | + totalDosage, | ||
546 | + } = ctx.request.body; | ||
547 | + | ||
548 | + | ||
549 | + const patientInfo = await PatientInfo.findOne({ | ||
550 | + patientId, | ||
551 | + doctorId : userId, | ||
552 | + useYn : 'Y', | ||
553 | + }) | ||
554 | + if(!patientInfo) { | ||
555 | + ctx.status = 403; | ||
556 | + ctx.body = { | ||
557 | + error : '권한 없는 환자', | ||
558 | + }; | ||
559 | + return; | ||
560 | + } | ||
561 | + | ||
562 | + const medicine = await Medicine.findOne({ medicineId }); | ||
563 | + if(!medicine) { | ||
564 | + ctx.status = 404; | ||
565 | + ctx.body = { | ||
566 | + error : '존재하지 않는 약', | ||
567 | + }; | ||
568 | + return; | ||
569 | + } | ||
570 | + | ||
571 | + | ||
572 | + const qrCodeResult = await QrCodeUtil.generateQrCode_prescribe({ | ||
573 | + medicine, | ||
574 | + dailyDosage, | ||
575 | + totalDosage, | ||
576 | + patientId, | ||
577 | + doctorId : userId, | ||
578 | + }); | ||
579 | + if(!qrCodeResult) { | ||
580 | + ctx.status = 400; | ||
581 | + ctx.body = { | ||
582 | + error : 'QR 코드 생성 에러', | ||
583 | + }; | ||
584 | + return; | ||
585 | + } | ||
586 | + | ||
587 | + const qrCodeUrl = await uploadQrCode(qrCodeResult); | ||
588 | + if(!qrCodeUrl) { | ||
589 | + ctx.status = 400; | ||
590 | + ctx.body = { | ||
591 | + error : 'QR 코드 생성 후 서버 업로드 에러', | ||
592 | + }; | ||
593 | + return; | ||
594 | + } | ||
595 | + | ||
596 | + const prescribeInfo = new PrescribeInfo({ | ||
597 | + doctorId : userId, | ||
598 | + patientId, | ||
599 | + dailyDosage, | ||
600 | + totalDosage, | ||
601 | + medicineId, | ||
602 | + qrCodeUrl, | ||
603 | + }); | ||
604 | + await prescribeInfo.save(); | ||
605 | + | ||
606 | + //특이사항에 처방기록 저장 | ||
607 | + await patientInfo.updateInfo(`${medicine.name}, 하루 ${dailyDosage}회분 처방, 총 ${totalDosage}회분 처방`); | ||
608 | + await patientInfo.save(); | ||
609 | + | ||
610 | + | ||
611 | + const { qrCodeFileName } = qrCodeResult; | ||
612 | + const qrCode = await getQrCodeUrl({ qrCodeFileName }); | ||
613 | + | ||
614 | + ctx.status = 200; | ||
615 | + ctx.body = { | ||
616 | + qrCode, | ||
617 | + }; | ||
618 | + | ||
619 | + | ||
484 | }; | 620 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -7,7 +7,7 @@ const doctor = new Router(); | ... | @@ -7,7 +7,7 @@ const doctor = new Router(); |
7 | /** | 7 | /** |
8 | * 현재 로그인한 유저(의사)의 정보를 가져옴. | 8 | * 현재 로그인한 유저(의사)의 정보를 가져옴. |
9 | * request parameter : token | 9 | * request parameter : token |
10 | - * url : http://localhost:4000/doctor/ | 10 | + * url : http://localhost:4000/api/doctor/ |
11 | * return : doctor's Info | 11 | * return : doctor's Info |
12 | */ | 12 | */ |
13 | doctor.get('/', doctorCtrl.getDoctorsInfo); | 13 | doctor.get('/', doctorCtrl.getDoctorsInfo); |
... | @@ -15,7 +15,7 @@ doctor.get('/', doctorCtrl.getDoctorsInfo); | ... | @@ -15,7 +15,7 @@ doctor.get('/', doctorCtrl.getDoctorsInfo); |
15 | /** | 15 | /** |
16 | * 현재 로그인한 유저(의사)의 관리 환자 목록을 가져옴 | 16 | * 현재 로그인한 유저(의사)의 관리 환자 목록을 가져옴 |
17 | * request parameter | 17 | * request parameter |
18 | - * url : http://localhost:4000/doctor/patient | 18 | + * url : http://localhost:4000/api/doctor/patient |
19 | * return : patient List | 19 | * return : patient List |
20 | */ | 20 | */ |
21 | doctor.get('/patient', doctorCtrl.getPatientList); | 21 | doctor.get('/patient', doctorCtrl.getPatientList); |
... | @@ -23,7 +23,7 @@ doctor.get('/patient', doctorCtrl.getPatientList); | ... | @@ -23,7 +23,7 @@ doctor.get('/patient', doctorCtrl.getPatientList); |
23 | /** | 23 | /** |
24 | * 현재 로그인한 유저(의사)의 관리 환자 상세 정보를 가져옴 | 24 | * 현재 로그인한 유저(의사)의 관리 환자 상세 정보를 가져옴 |
25 | * request parameter : patient Id | 25 | * request parameter : patient Id |
26 | - * url : http://localhost:4000/doctor/patient/:patientId | 26 | + * url : http://localhost:4000/api/doctor/patient/:patientId |
27 | * return : patient Detail | 27 | * return : patient Detail |
28 | */ | 28 | */ |
29 | doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); | 29 | doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); |
... | @@ -31,7 +31,7 @@ doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); | ... | @@ -31,7 +31,7 @@ doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); |
31 | /** | 31 | /** |
32 | * 현재 로그인한 유저(의사)의 관리 약병 상세 정보를 가져옴 | 32 | * 현재 로그인한 유저(의사)의 관리 약병 상세 정보를 가져옴 |
33 | * request parameter : bottle Id | 33 | * request parameter : bottle Id |
34 | - * url : http://localhost:4000/doctor/bottle/:bottleId | 34 | + * url : http://localhost:4000/api/doctor/bottle/:bottleId |
35 | * return : bottle Detail | 35 | * return : bottle Detail |
36 | */ | 36 | */ |
37 | doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); | 37 | doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); |
... | @@ -39,7 +39,7 @@ doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); | ... | @@ -39,7 +39,7 @@ doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); |
39 | /** | 39 | /** |
40 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 특이사항을 기록함 | 40 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 특이사항을 기록함 |
41 | * request parameter : reqUserId, info | 41 | * request parameter : reqUserId, info |
42 | - * url : http://localhost:4000/doctor/patient | 42 | + * url : http://localhost:4000/api/doctor/patient |
43 | * return : null | 43 | * return : null |
44 | */ | 44 | */ |
45 | doctor.patch('/patient', doctorCtrl.writeReqPatientReport); | 45 | doctor.patch('/patient', doctorCtrl.writeReqPatientReport); |
... | @@ -47,7 +47,7 @@ doctor.patch('/patient', doctorCtrl.writeReqPatientReport); | ... | @@ -47,7 +47,7 @@ doctor.patch('/patient', doctorCtrl.writeReqPatientReport); |
47 | /** | 47 | /** |
48 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 약병의 피드백을 등록함. | 48 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 약병의 피드백을 등록함. |
49 | * request parameter : bottleId, fdbType, feedback | 49 | * request parameter : bottleId, fdbType, feedback |
50 | - * url : http://localhost:4000/doctor/bottle | 50 | + * url : http://localhost:4000/api/doctor/bottle |
51 | * return : null | 51 | * return : null |
52 | */ | 52 | */ |
53 | doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); | 53 | doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); |
... | @@ -55,15 +55,15 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); | ... | @@ -55,15 +55,15 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); |
55 | /** | 55 | /** |
56 | * 현재 로그인한 유저(의사)가 이메일로 유저를 검색함 | 56 | * 현재 로그인한 유저(의사)가 이메일로 유저를 검색함 |
57 | * request parameter : patientId | 57 | * request parameter : patientId |
58 | - * url : http://localhost:4000/api/doctor/patient/search/:patientId | 58 | + * url : http://localhost:4000/api/api/doctor/patient/search/:patientId |
59 | * return : patient Info(simple) | 59 | * return : patient Info(simple) |
60 | */ | 60 | */ |
61 | -doctor.get('/patient/search/:patientId', doctorCtrl.searchPatientById); | 61 | +doctor.get('/patient/search/:contact', doctorCtrl.searchPatientByContact); |
62 | 62 | ||
63 | /** | 63 | /** |
64 | * 현재 로그인한 유저(의사)의 관리 환자를 등록함. | 64 | * 현재 로그인한 유저(의사)의 관리 환자를 등록함. |
65 | * request parameter : patientId | 65 | * request parameter : patientId |
66 | - * url : http://localhost:4000/doctor/patient | 66 | + * url : http://localhost:4000/api/doctor/patient |
67 | * return : null | 67 | * return : null |
68 | */ | 68 | */ |
69 | doctor.post('/patient', doctorCtrl.registerNewPatient); | 69 | doctor.post('/patient', doctorCtrl.registerNewPatient); |
... | @@ -71,11 +71,18 @@ doctor.post('/patient', doctorCtrl.registerNewPatient); | ... | @@ -71,11 +71,18 @@ doctor.post('/patient', doctorCtrl.registerNewPatient); |
71 | /** | 71 | /** |
72 | * 현재 로그인한 유저(의사)의 특정 관리 환자를 삭제함. | 72 | * 현재 로그인한 유저(의사)의 특정 관리 환자를 삭제함. |
73 | * request parameter : patientId | 73 | * request parameter : patientId |
74 | - * url : http://localhost:4000/doctor/patient/:patientId | 74 | + * url : http://localhost:4000/api/doctor/patient/:patientId |
75 | * return : null | 75 | * return : null |
76 | */ | 76 | */ |
77 | doctor.delete('/patient/:patientId', doctorCtrl.removeReqPatient); | 77 | doctor.delete('/patient/:patientId', doctorCtrl.removeReqPatient); |
78 | 78 | ||
79 | +/** | ||
80 | + * 의사가 관리하는 환자에 대해 특정 약을 처방함 | ||
81 | + * request paramter : patientId, medicineId, dosage | ||
82 | + * url : http://localhost:4000/api/doctor/prescribe | ||
83 | + * return : null | ||
84 | + */ | ||
85 | +doctor.post('/prescribe', doctorCtrl.prescribeMedicine); | ||
86 | + | ||
79 | 87 | ||
80 | -module.exports = doctor; | ||
81 | - | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
88 | +module.exports = doctor; | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | //허브(Mqtt Broker)등록 및 삭제 | 1 | //허브(Mqtt Broker)등록 및 삭제 |
2 | const Hub = require('../../models/hub'); | 2 | const Hub = require('../../models/hub'); |
3 | +const Bottle = require('../../models/bottle'); | ||
3 | const User = require('../../models/user'); | 4 | const User = require('../../models/user'); |
4 | -const Mqtt = require('../../lib/MqttModule'); | 5 | +const Mqtt = require('../../util/MqttModule'); |
5 | -const DataProcess = require('../../lib/DataProcess'); | 6 | +const DataProcess = require('../../util/DataProcess'); |
6 | const jwt = require('jsonwebtoken'); | 7 | const jwt = require('jsonwebtoken'); |
8 | +const BottleMedicine = require('../../models/bottleMedicine'); | ||
7 | 9 | ||
10 | +//허브 연결 | ||
8 | exports.hubConnect = async (ctx) => { | 11 | exports.hubConnect = async (ctx) => { |
9 | const token = ctx.req.headers.authorization; | 12 | const token = ctx.req.headers.authorization; |
10 | if(!token || !token.length) { | 13 | if(!token || !token.length) { |
... | @@ -20,7 +23,7 @@ exports.hubConnect = async (ctx) => { | ... | @@ -20,7 +23,7 @@ exports.hubConnect = async (ctx) => { |
20 | return; | 23 | return; |
21 | } | 24 | } |
22 | 25 | ||
23 | - const { hubId, host, port } = ctx.request.body; | 26 | + const { hubId, host, hubNm, } = ctx.request.body; |
24 | 27 | ||
25 | const isExistHub = await Hub.findByHubId(hubId); | 28 | const isExistHub = await Hub.findByHubId(hubId); |
26 | if(isExistHub) { | 29 | if(isExistHub) { |
... | @@ -30,7 +33,7 @@ exports.hubConnect = async (ctx) => { | ... | @@ -30,7 +33,7 @@ exports.hubConnect = async (ctx) => { |
30 | 33 | ||
31 | const hosting = { | 34 | const hosting = { |
32 | host, | 35 | host, |
33 | - port | 36 | + port : "1883", |
34 | }; | 37 | }; |
35 | 38 | ||
36 | Mqtt.mqttOn(hosting, DataProcess.dataPublish); | 39 | Mqtt.mqttOn(hosting, DataProcess.dataPublish); |
... | @@ -38,7 +41,8 @@ exports.hubConnect = async (ctx) => { | ... | @@ -38,7 +41,8 @@ exports.hubConnect = async (ctx) => { |
38 | const hub = new Hub({ | 41 | const hub = new Hub({ |
39 | hubId, | 42 | hubId, |
40 | hosting, | 43 | hosting, |
41 | - userId | 44 | + userId, |
45 | + hubNm, | ||
42 | }); | 46 | }); |
43 | 47 | ||
44 | await hub.save(); | 48 | await hub.save(); |
... | @@ -47,6 +51,49 @@ exports.hubConnect = async (ctx) => { | ... | @@ -47,6 +51,49 @@ exports.hubConnect = async (ctx) => { |
47 | 51 | ||
48 | }; | 52 | }; |
49 | 53 | ||
54 | +//허브 연결 해제 | ||
55 | +exports.hubDisconnect = async(ctx) => { | ||
56 | + const token = ctx.req.headers.authorization; | ||
57 | + if(!token || !token.length) { | ||
58 | + ctx.status = 401; | ||
59 | + return; | ||
60 | + } | ||
61 | + | ||
62 | + // eslint-disable-next-line no-undef | ||
63 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
64 | + const user = await User.findByUserId(userId); | ||
65 | + if(!user || !user.userTypeCd || user.useYn !== 'Y') { | ||
66 | + ctx.status = 403; | ||
67 | + return; | ||
68 | + } | ||
69 | + | ||
70 | + const { hubId } = ctx.params; | ||
71 | + | ||
72 | + const hub = await Hub.findByHubId(hubId); | ||
73 | + if(!hub) { | ||
74 | + ctx.status = 404; | ||
75 | + return; | ||
76 | + } | ||
77 | + if(hub.getHub_UserId() !== userId) { | ||
78 | + ctx.status = 403; | ||
79 | + return; | ||
80 | + } | ||
81 | + | ||
82 | + const hosting = await hub.getHubHost(); | ||
83 | + Mqtt.mqttOff(hosting); | ||
84 | + | ||
85 | + const bottleList = await Bottle.find({ hubId }); | ||
86 | + await Promise.all(bottleList.map(async bottle => { | ||
87 | + await BottleMedicine.updateMany({ bottleId : bottle.bottleId }, { useYn : 'N' }); | ||
88 | + })); | ||
89 | + | ||
90 | + await Bottle.deleteMany({ hubId }); | ||
91 | + await Hub.deleteOne({ hubId }); | ||
92 | + | ||
93 | + ctx.status = 204; | ||
94 | +}; | ||
95 | + | ||
96 | +//허브 정보 조회 | ||
50 | exports.getHubList = async(ctx) => { | 97 | exports.getHubList = async(ctx) => { |
51 | const token = ctx.req.headers.authorization; | 98 | const token = ctx.req.headers.authorization; |
52 | if(!token || !token.length) { | 99 | if(!token || !token.length) { |
... | @@ -74,7 +121,8 @@ exports.getHubList = async(ctx) => { | ... | @@ -74,7 +121,8 @@ exports.getHubList = async(ctx) => { |
74 | }; | 121 | }; |
75 | }; | 122 | }; |
76 | 123 | ||
77 | -exports.hubDisconnect = async(ctx) => { | 124 | +//허브 이름 변경 |
125 | +exports.setHubName = async ctx => { | ||
78 | const token = ctx.req.headers.authorization; | 126 | const token = ctx.req.headers.authorization; |
79 | if(!token || !token.length) { | 127 | if(!token || !token.length) { |
80 | ctx.status = 401; | 128 | ctx.status = 401; |
... | @@ -90,21 +138,9 @@ exports.hubDisconnect = async(ctx) => { | ... | @@ -90,21 +138,9 @@ exports.hubDisconnect = async(ctx) => { |
90 | } | 138 | } |
91 | 139 | ||
92 | const { hubId } = ctx.params; | 140 | const { hubId } = ctx.params; |
141 | + const { hubNm } = ctx.request.body; | ||
93 | 142 | ||
94 | - const hub = await Hub.findByHubId(hubId); | 143 | + await Hub.updateOne({ hubId }, { hubNm }); |
95 | - if(!hub) { | ||
96 | - ctx.status = 404; | ||
97 | - return; | ||
98 | - } | ||
99 | - if(hub.getHub_UserId() !== userId) { | ||
100 | - ctx.status = 403; | ||
101 | - return; | ||
102 | - } | ||
103 | 144 | ||
104 | - const hosting = await hub.getHubHost(); | ||
105 | - Mqtt.mqttOff(hosting); | ||
106 | - | ||
107 | - await Hub.deleteOne({ hubId }); | ||
108 | - | ||
109 | - ctx.status = 204; | ||
110 | -}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
145 | + ctx.status = 200; | ||
146 | +}; | ... | ... |
... | @@ -12,6 +12,14 @@ const hub = new Router(); | ... | @@ -12,6 +12,14 @@ const hub = new Router(); |
12 | hub.post('/', hubCtrl.hubConnect); | 12 | hub.post('/', hubCtrl.hubConnect); |
13 | 13 | ||
14 | /** | 14 | /** |
15 | + * 허브 등록 해제 | ||
16 | + * request parameter : x | ||
17 | + * url : http://localhost:4000/api/hub/:hubId | ||
18 | + * return : null | ||
19 | + */ | ||
20 | + hub.delete('/:hubId', hubCtrl.hubDisconnect); | ||
21 | + | ||
22 | +/** | ||
15 | * 로그인한 유저의 허브 목록 가져오기 | 23 | * 로그인한 유저의 허브 목록 가져오기 |
16 | * request parameter : X | 24 | * request parameter : X |
17 | * url : http://localhost:4000/api/hub | 25 | * url : http://localhost:4000/api/hub |
... | @@ -20,11 +28,13 @@ hub.post('/', hubCtrl.hubConnect); | ... | @@ -20,11 +28,13 @@ hub.post('/', hubCtrl.hubConnect); |
20 | hub.get('/', hubCtrl.getHubList); | 28 | hub.get('/', hubCtrl.getHubList); |
21 | 29 | ||
22 | /** | 30 | /** |
23 | - * 허브 등록 해제 | 31 | + * 로그인한 유저의 특정 허브 이름 변경 |
24 | - * request parameter : x | 32 | + * request parameter : hubId, hubNm |
25 | * url : http://localhost:4000/api/hub/:hubId | 33 | * url : http://localhost:4000/api/hub/:hubId |
26 | * return : null | 34 | * return : null |
27 | */ | 35 | */ |
28 | -hub.delete('/:hubId', hubCtrl.hubDisconnect); | 36 | +hub.patch('/:hubId', hubCtrl.setHubName); |
37 | + | ||
38 | + | ||
29 | 39 | ||
30 | module.exports = hub; | 40 | module.exports = hub; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -7,6 +7,7 @@ const hub = require('./hub'); | ... | @@ -7,6 +7,7 @@ const hub = require('./hub'); |
7 | const medicine = require('./medicine'); | 7 | const medicine = require('./medicine'); |
8 | const doctor = require('./doctor'); | 8 | const doctor = require('./doctor'); |
9 | const manage = require('./manage'); | 9 | const manage = require('./manage'); |
10 | +const test = require('./test'); | ||
10 | 11 | ||
11 | const api = new Router(); | 12 | const api = new Router(); |
12 | 13 | ||
... | @@ -17,5 +18,6 @@ api.use('/hub', hub.routes()); | ... | @@ -17,5 +18,6 @@ api.use('/hub', hub.routes()); |
17 | api.use('/medicine', medicine.routes()); | 18 | api.use('/medicine', medicine.routes()); |
18 | api.use('/doctor', doctor.routes()); | 19 | api.use('/doctor', doctor.routes()); |
19 | api.use('/manage', manage.routes()); | 20 | api.use('/manage', manage.routes()); |
21 | +api.use('/test', test.routes()); | ||
20 | 22 | ||
21 | module.exports = api; | 23 | module.exports = api; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -12,6 +12,14 @@ const manage = new Router(); | ... | @@ -12,6 +12,14 @@ const manage = new Router(); |
12 | manage.get('/doctor', manageCtrl.getDoctorRegReqList); | 12 | manage.get('/doctor', manageCtrl.getDoctorRegReqList); |
13 | 13 | ||
14 | /** | 14 | /** |
15 | + * 의사 회원탈퇴 요청을 한 회원들의 목록을 리턴 | ||
16 | + * request parameter : null | ||
17 | + * url : http://localhost:4000/api/manage/doctor/sec | ||
18 | + * return : doctor request List | ||
19 | + */ | ||
20 | + manage.get('/doctor/secession', manageCtrl.getDoctorSecReqList); | ||
21 | + | ||
22 | +/** | ||
15 | * 의사 회원가입 요청을 한 특정 회원의 상세정보 확인 | 23 | * 의사 회원가입 요청을 한 특정 회원의 상세정보 확인 |
16 | * request parameter : doctor Id | 24 | * request parameter : doctor Id |
17 | * url : http://localhost:4000/api/manage/doctor/:doctorId | 25 | * url : http://localhost:4000/api/manage/doctor/:doctorId |
... | @@ -25,7 +33,7 @@ manage.get('/doctor/:doctorId', manageCtrl.getDoctorRegReqDetail); | ... | @@ -25,7 +33,7 @@ manage.get('/doctor/:doctorId', manageCtrl.getDoctorRegReqDetail); |
25 | * url : http://localhost:4000/api/manage/doctor/accept | 33 | * url : http://localhost:4000/api/manage/doctor/accept |
26 | * return : null | 34 | * return : null |
27 | */ | 35 | */ |
28 | -manage.post('/doctor/accept', manageCtrl.acceptDoctorRegReq); | 36 | +manage.patch('/doctor/accept', manageCtrl.acceptDoctorRegReq); |
29 | 37 | ||
30 | /** | 38 | /** |
31 | * 의사 요청을 한 회원을 거절한다. | 39 | * 의사 요청을 한 회원을 거절한다. |
... | @@ -33,7 +41,15 @@ manage.post('/doctor/accept', manageCtrl.acceptDoctorRegReq); | ... | @@ -33,7 +41,15 @@ manage.post('/doctor/accept', manageCtrl.acceptDoctorRegReq); |
33 | * url : http://localhost:4000/api/manange/doctor/reject | 41 | * url : http://localhost:4000/api/manange/doctor/reject |
34 | * return : null | 42 | * return : null |
35 | */ | 43 | */ |
36 | -manage.post('/doctor/reject', manageCtrl.rejectDoctorRegReq); | 44 | +manage.patch('/doctor/reject', manageCtrl.rejectDoctorRegReq); |
45 | + | ||
46 | +/** | ||
47 | + * 의사 탈퇴 요청을 수락한다. | ||
48 | + * request parameter : doctor Id | ||
49 | + * url : http://localhost:4000/api/manange/doctor/secession | ||
50 | + * return : null | ||
51 | + */ | ||
52 | + manage.patch('/doctor/secession', manageCtrl.acceptDoctorSecReq); | ||
37 | 53 | ||
38 | /** | 54 | /** |
39 | * 의사 요청을 한 회원의 자격 번호가 유효한지 검증한다 | 55 | * 의사 요청을 한 회원의 자격 번호가 유효한지 검증한다 | ... | ... |
1 | const User = require('../../models/user'); | 1 | const User = require('../../models/user'); |
2 | const DoctorInfo = require('../../models/doctorInfo'); | 2 | const DoctorInfo = require('../../models/doctorInfo'); |
3 | -const Profile = require('../../models/profile'); | 3 | +const PatientInfo = require('../../models/patientInfo'); |
4 | const jwt = require('jsonwebtoken'); | 4 | const jwt = require('jsonwebtoken'); |
5 | +const { viewDoctorLicense } = require('../../util/GoogleCloudStorage'); | ||
6 | + | ||
5 | 7 | ||
6 | /** | 8 | /** |
7 | * 의사 회원가입을 요청한 회원 리스트를 확인한다. | 9 | * 의사 회원가입을 요청한 회원 리스트를 확인한다. |
... | @@ -25,13 +27,54 @@ exports.getDoctorRegReqList = async ctx => { | ... | @@ -25,13 +27,54 @@ exports.getDoctorRegReqList = async ctx => { |
25 | } | 27 | } |
26 | 28 | ||
27 | try { | 29 | try { |
28 | - const doctorRegReqList = await DoctorInfo.find({ | 30 | + const doctorList = await DoctorInfo.find({ |
29 | useYn : 'W', | 31 | useYn : 'W', |
30 | }); | 32 | }); |
31 | 33 | ||
32 | ctx.status = 200; | 34 | ctx.status = 200; |
33 | ctx.body = { | 35 | ctx.body = { |
34 | - doctorRegReqList | 36 | + doctorList |
37 | + }; | ||
38 | + | ||
39 | + } catch(e) { | ||
40 | + ctx.status = 500; | ||
41 | + ctx.body = { | ||
42 | + error : '알 수 없는 에러가 발생했습니다.', | ||
43 | + }; | ||
44 | + console.log(e); | ||
45 | + return; | ||
46 | + } | ||
47 | +}; | ||
48 | + | ||
49 | +/** | ||
50 | + * 의사 회원탈퇴를 요청한 회원 리스트를 확인한다. | ||
51 | + * http methods : get | ||
52 | + * @param {*} ctx | ||
53 | + * @returns | ||
54 | + */ | ||
55 | + exports.getDoctorSecReqList = async ctx => { | ||
56 | + const token = ctx.req.headers.authorization; | ||
57 | + if (!token || !token.length) { | ||
58 | + ctx.status = 401; | ||
59 | + return; | ||
60 | + } | ||
61 | + | ||
62 | + // eslint-disable-next-line no-undef | ||
63 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
64 | + const user = await User.findByUserId(userId); | ||
65 | + if(!user || user.userTypeCd !== 'MANAGER' || user.useYn !== 'Y') { | ||
66 | + ctx.status = 403; | ||
67 | + return; | ||
68 | + } | ||
69 | + | ||
70 | + try { | ||
71 | + const doctorList = await DoctorInfo.find({ | ||
72 | + useYn : 'WS', | ||
73 | + }); | ||
74 | + | ||
75 | + ctx.status = 200; | ||
76 | + ctx.body = { | ||
77 | + doctorList | ||
35 | }; | 78 | }; |
36 | 79 | ||
37 | } catch(e) { | 80 | } catch(e) { |
... | @@ -40,6 +83,7 @@ exports.getDoctorRegReqList = async ctx => { | ... | @@ -40,6 +83,7 @@ exports.getDoctorRegReqList = async ctx => { |
40 | error : '알 수 없는 에러가 발생했습니다.', | 83 | error : '알 수 없는 에러가 발생했습니다.', |
41 | }; | 84 | }; |
42 | console.log(e); | 85 | console.log(e); |
86 | + return; | ||
43 | } | 87 | } |
44 | }; | 88 | }; |
45 | 89 | ||
... | @@ -108,9 +152,20 @@ exports.getDoctorRegReqDetail = async ctx => { | ... | @@ -108,9 +152,20 @@ exports.getDoctorRegReqDetail = async ctx => { |
108 | return; | 152 | return; |
109 | } | 153 | } |
110 | 154 | ||
155 | + | ||
156 | + const doctorLicense = await viewDoctorLicense({ | ||
157 | + doctorInfo | ||
158 | + }); | ||
159 | + | ||
111 | ctx.status = 200; | 160 | ctx.status = 200; |
112 | ctx.body = { | 161 | ctx.body = { |
113 | - doctorInfo, | 162 | + doctorInfo : { |
163 | + ...doctorInfo._doc, | ||
164 | + info : { | ||
165 | + ...doctorInfo.info, | ||
166 | + doctorLicense, | ||
167 | + }, | ||
168 | + }, | ||
114 | }; | 169 | }; |
115 | 170 | ||
116 | } catch (e) { | 171 | } catch (e) { |
... | @@ -118,12 +173,14 @@ exports.getDoctorRegReqDetail = async ctx => { | ... | @@ -118,12 +173,14 @@ exports.getDoctorRegReqDetail = async ctx => { |
118 | ctx.body = { | 173 | ctx.body = { |
119 | error : '알 수 없는 에러가 발생했습니다.', | 174 | error : '알 수 없는 에러가 발생했습니다.', |
120 | }; | 175 | }; |
176 | + console.log(e); | ||
177 | + return; | ||
121 | } | 178 | } |
122 | }; | 179 | }; |
123 | 180 | ||
124 | /** | 181 | /** |
125 | * 의사 요청이 온 회원을 수락한다. | 182 | * 의사 요청이 온 회원을 수락한다. |
126 | - * http methods : post | 183 | + * http methods : patch |
127 | * @param {*} ctx | 184 | * @param {*} ctx |
128 | * @returns | 185 | * @returns |
129 | */ | 186 | */ |
... | @@ -143,7 +200,7 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -143,7 +200,7 @@ exports.acceptDoctorRegReq = async ctx => { |
143 | } | 200 | } |
144 | 201 | ||
145 | try { | 202 | try { |
146 | - const { doctorId } = ctx.request.body; | 203 | + const { doctorId, validateDoctorLicense } = ctx.request.body; |
147 | const doctor = await User.findOne({ userId : doctorId }); | 204 | const doctor = await User.findOne({ userId : doctorId }); |
148 | if(!doctor) { | 205 | if(!doctor) { |
149 | ctx.status = 404; | 206 | ctx.status = 404; |
... | @@ -169,17 +226,37 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -169,17 +226,37 @@ exports.acceptDoctorRegReq = async ctx => { |
169 | error : '의사로 가입된 회원이 아닙니다.', | 226 | error : '의사로 가입된 회원이 아닙니다.', |
170 | }; | 227 | }; |
171 | return; | 228 | return; |
229 | + } else if(!validateDoctorLicense) { | ||
230 | + ctx.status = 400; | ||
231 | + ctx.body = { | ||
232 | + error : '유효한 자격 번호가 아닙니다.', | ||
233 | + }; | ||
234 | + return; | ||
235 | + } | ||
236 | + | ||
237 | + const existDoctorInfo = await DoctorInfo.findOne({ | ||
238 | + 'info.validateDoctorLicense' : validateDoctorLicense | ||
239 | + }); | ||
240 | + if(existDoctorInfo) { | ||
241 | + ctx.status = 403; | ||
242 | + ctx.body = { | ||
243 | + error : '중복된 자격번호입니다.', | ||
244 | + }; | ||
245 | + return; | ||
172 | } | 246 | } |
173 | 247 | ||
248 | + | ||
174 | const doctorInfo = await DoctorInfo.findOne({ | 249 | const doctorInfo = await DoctorInfo.findOne({ |
175 | doctorId, | 250 | doctorId, |
176 | useYn : 'W', | 251 | useYn : 'W', |
177 | }); | 252 | }); |
178 | 253 | ||
179 | - doctor.setUseYn('Y'); | 254 | + await doctor.setUseYn('Y'); |
180 | - doctor.save(); | 255 | + await doctor.save(); |
181 | - doctorInfo.setUseYn('Y'); | 256 | + |
182 | - doctorInfo.save(); | 257 | + await doctorInfo.setUseYn('Y'); |
258 | + await doctorInfo.setValidateDoctorLicense(validateDoctorLicense); | ||
259 | + await doctorInfo.save(); | ||
183 | 260 | ||
184 | ctx.status = 200; | 261 | ctx.status = 200; |
185 | 262 | ||
... | @@ -189,12 +266,13 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -189,12 +266,13 @@ exports.acceptDoctorRegReq = async ctx => { |
189 | error : '알 수 없는 에러가 발생했습니다.', | 266 | error : '알 수 없는 에러가 발생했습니다.', |
190 | }; | 267 | }; |
191 | console.log(e); | 268 | console.log(e); |
269 | + return; | ||
192 | } | 270 | } |
193 | }; | 271 | }; |
194 | 272 | ||
195 | /** | 273 | /** |
196 | * 의사 요청이 온 회원을 거절한다. | 274 | * 의사 요청이 온 회원을 거절한다. |
197 | - * http methods : post | 275 | + * http methods : patch |
198 | * @param {*} ctx | 276 | * @param {*} ctx |
199 | * @returns | 277 | * @returns |
200 | */ | 278 | */ |
... | @@ -242,16 +320,79 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -242,16 +320,79 @@ exports.acceptDoctorRegReq = async ctx => { |
242 | return; | 320 | return; |
243 | } | 321 | } |
244 | 322 | ||
323 | + await DoctorInfo.updateOne({ doctorId, useYn : 'W' }, { useYn : 'N' }); | ||
245 | 324 | ||
246 | - const doctorInfo = await DoctorInfo.findOne({ | 325 | + await doctor.setUseYn('N'); |
247 | - doctorId, | 326 | + await doctor.save(); |
248 | - useYn : 'W', | ||
249 | - }); | ||
250 | 327 | ||
251 | - doctor.setUseYn('N'); | 328 | + ctx.status = 200; |
252 | - doctor.save(); | 329 | + |
253 | - doctorInfo.setUseYn('N'); | 330 | + } catch(e) { |
254 | - doctorInfo.save(); | 331 | + ctx.status = 500; |
332 | + ctx.body = { | ||
333 | + error : '알 수 없는 에러가 발생했습니다.', | ||
334 | + }; | ||
335 | + console.log(e); | ||
336 | + return; | ||
337 | + } | ||
338 | +}; | ||
339 | + | ||
340 | +/** | ||
341 | + * 의사 회원탈퇴 요청을 수락한다. | ||
342 | + * http methods : patch | ||
343 | + * @param {*} ctx | ||
344 | + * @returns | ||
345 | + */ | ||
346 | + exports.acceptDoctorSecReq = async ctx => { | ||
347 | + const token = ctx.req.headers.authorization; | ||
348 | + if (!token || !token.length) { | ||
349 | + ctx.status = 401; | ||
350 | + return; | ||
351 | + } | ||
352 | + | ||
353 | + // eslint-disable-next-line no-undef | ||
354 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
355 | + const user = await User.findByUserId(userId); | ||
356 | + if(!user || user.userTypeCd !== 'MANAGER' || user.useYn !== 'Y') { | ||
357 | + ctx.status = 403; | ||
358 | + return; | ||
359 | + } | ||
360 | + | ||
361 | + try { | ||
362 | + const { doctorId } = ctx.request.body; | ||
363 | + const doctor = await User.findOne({ userId : doctorId }); | ||
364 | + if(!doctor) { | ||
365 | + ctx.status = 404; | ||
366 | + ctx.body = { | ||
367 | + error : '존재하지 않는 회원입니다.', | ||
368 | + }; | ||
369 | + return; | ||
370 | + } else if(doctor.useYn === 'N') { | ||
371 | + ctx.status = 400; | ||
372 | + ctx.body = { | ||
373 | + error : '탈퇴된 회원입니다.', | ||
374 | + }; | ||
375 | + return; | ||
376 | + } else if(doctor.useYn === 'Y') { | ||
377 | + ctx.status = 400; | ||
378 | + ctx.body = { | ||
379 | + error : '이미 가입이 완료된 의사입니다.', | ||
380 | + }; | ||
381 | + return; | ||
382 | + } else if(doctor.userTypeCd !== 'DOCTOR') { | ||
383 | + ctx.status = 400; | ||
384 | + ctx.body = { | ||
385 | + error : '의사로 가입된 회원이 아닙니다.', | ||
386 | + }; | ||
387 | + return; | ||
388 | + } | ||
389 | + | ||
390 | + | ||
391 | + await DoctorInfo.updateOne({ doctorId, useYn : 'WS' }, { useYn : 'N' }); | ||
392 | + await PatientInfo.updateMany({ doctorId : userId, useYn : 'WS' }, { useYn : 'N' }); | ||
393 | + | ||
394 | + await doctor.setUseYn('N'); | ||
395 | + await doctor.save(); | ||
255 | 396 | ||
256 | ctx.status = 200; | 397 | ctx.status = 200; |
257 | 398 | ||
... | @@ -261,9 +402,11 @@ exports.acceptDoctorRegReq = async ctx => { | ... | @@ -261,9 +402,11 @@ exports.acceptDoctorRegReq = async ctx => { |
261 | error : '알 수 없는 에러가 발생했습니다.', | 402 | error : '알 수 없는 에러가 발생했습니다.', |
262 | }; | 403 | }; |
263 | console.log(e); | 404 | console.log(e); |
405 | + return; | ||
264 | } | 406 | } |
265 | }; | 407 | }; |
266 | 408 | ||
409 | + | ||
267 | /** | 410 | /** |
268 | * 회원가입을 요청한 의사의 유효한 자격 번호인지를 검증한다. | 411 | * 회원가입을 요청한 의사의 유효한 자격 번호인지를 검증한다. |
269 | * @param {*} ctx | 412 | * @param {*} ctx |
... | @@ -284,12 +427,20 @@ exports.validateDoctorLicense = async ctx => { | ... | @@ -284,12 +427,20 @@ exports.validateDoctorLicense = async ctx => { |
284 | return; | 427 | return; |
285 | } | 428 | } |
286 | 429 | ||
287 | - const { doctorLicense } = ctx.request.body; | 430 | + const { validateDoctorLicense } = ctx.request.body; |
288 | - const doctorInfo = await DoctorInfo.find({ 'info.doctorLicense' : doctorLicense }); | 431 | + if(!validateDoctorLicense) { |
432 | + ctx.status = 404; | ||
433 | + ctx.body = { | ||
434 | + error : '유효한 자격번호를 입력해주세요', | ||
435 | + }; | ||
436 | + return; | ||
437 | + } | ||
438 | + | ||
439 | + const doctorInfo = await DoctorInfo.findOne({ 'info.validateDoctorLicense' : validateDoctorLicense }); | ||
289 | 440 | ||
290 | ctx.status = 200; | 441 | ctx.status = 200; |
291 | ctx.body = { | 442 | ctx.body = { |
292 | - result : doctorInfo.length > 1 ? false : true, | 443 | + result : doctorInfo ? false : true, |
293 | }; | 444 | }; |
294 | 445 | ||
295 | }; | 446 | }; | ... | ... |
server/src/api/test/index.js
0 → 100644
server/src/api/test/test.ctrl.js
0 → 100644
1 | +//테스트용 api service | ||
2 | +const { sendPushMessage } = require('../../util/FCM'); | ||
3 | + | ||
4 | + | ||
5 | +exports.sendFcmMessage = async ctx => { | ||
6 | + const { deviceToken, title, body } = ctx.request.body; | ||
7 | + | ||
8 | + try { | ||
9 | + await sendPushMessage(ctx.request.body); | ||
10 | + } catch(e) { | ||
11 | + console.log('Error at FCM Sending : ', e); | ||
12 | + } | ||
13 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -11,9 +11,18 @@ const user = new Router(); | ... | @@ -11,9 +11,18 @@ const user = new Router(); |
11 | */ | 11 | */ |
12 | user.get('/', userCtrl.getMyDetail); | 12 | user.get('/', userCtrl.getMyDetail); |
13 | 13 | ||
14 | + | ||
14 | /** | 15 | /** |
15 | - * 현재 로그인한 유저에 등록된 의사 목록 가져옴 | 16 | + * 현재 유저 정보 수정 |
16 | * request parameter : token | 17 | * request parameter : token |
18 | + * url : http://localhost:4000/api/user | ||
19 | + * return : Object User | ||
20 | + */ | ||
21 | +user.patch('/', userCtrl.updateMyDetail); | ||
22 | + | ||
23 | +/** | ||
24 | + * 현재 로그인한 유저에 등록된 의사 목록 가져옴 | ||
25 | + * request parameter : userNm, birth, contact, password, passwordCheck | ||
17 | * url : http://localhost:4000/api/user/doctor | 26 | * url : http://localhost:4000/api/user/doctor |
18 | * return : Doctor List | 27 | * return : Doctor List |
19 | */ | 28 | */ | ... | ... |
... | @@ -38,10 +38,66 @@ exports.getMyDetail = async ctx => { | ... | @@ -38,10 +38,66 @@ exports.getMyDetail = async ctx => { |
38 | /** | 38 | /** |
39 | * 내 정보를 업데이트한다. | 39 | * 내 정보를 업데이트한다. |
40 | * @param {*} ctx | 40 | * @param {*} ctx |
41 | - * http methods : post | 41 | + * http methods : patch |
42 | */ | 42 | */ |
43 | -exports.updateMyInfo = async ctx => { | 43 | +exports.updateMyDetail = async ctx => { |
44 | + const token = ctx.req.headers.authorization; | ||
45 | + if(!token || !token.length) { | ||
46 | + ctx.status = 401; | ||
47 | + return; | ||
48 | + } | ||
49 | + | ||
50 | + // eslint-disable-next-line no-undef | ||
51 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
52 | + const user = await User.findByUserId(userId); | ||
53 | + if(!user || user.useYn !== 'Y' || user.userTypeCd !== 'NORMAL') { | ||
54 | + ctx.status = 403; | ||
55 | + return; | ||
56 | + } | ||
44 | 57 | ||
58 | + const profile = await Profile.findByUserId(userId); | ||
59 | + if(!profile || profile.useYn !== 'Y') { | ||
60 | + ctx.status = 403; | ||
61 | + return; | ||
62 | + } | ||
63 | + | ||
64 | + const { userNm, birth, contact, password, passwordCheck, } = ctx.request.body; | ||
65 | + | ||
66 | + const existContact = await Profile.findOne({ contact, useYn : 'Y' }); | ||
67 | + if(existContact) { | ||
68 | + ctx.status = 409; | ||
69 | + ctx.body = { | ||
70 | + error : '이미 가입된 번호', | ||
71 | + }; | ||
72 | + | ||
73 | + return; | ||
74 | + } | ||
75 | + | ||
76 | + //passwordCheck가 있고 로컬 회원이라면 비밀번호 변경함 | ||
77 | + if(passwordCheck && user.authTypeCd === 'NORMAL') { | ||
78 | + //passwordCheck와 password가 같아야함 | ||
79 | + if(passwordCheck !== password) { | ||
80 | + ctx.status = 401; | ||
81 | + ctx.body = { | ||
82 | + error : '비밀번호가 일치하지 않습니다.', | ||
83 | + }; | ||
84 | + return; | ||
85 | + } | ||
86 | + | ||
87 | + await user.setPassword(password); | ||
88 | + await user.save(); | ||
89 | + } | ||
90 | + | ||
91 | + await profile.updateProfileInfo({ | ||
92 | + userNm, | ||
93 | + birth, | ||
94 | + contact, | ||
95 | + }); | ||
96 | + | ||
97 | + await profile.save(); | ||
98 | + | ||
99 | + ctx.status = 200; | ||
100 | + | ||
45 | }; | 101 | }; |
46 | 102 | ||
47 | /** | 103 | /** |
... | @@ -76,7 +132,12 @@ exports.getMyDoctorList = async ctx => { | ... | @@ -76,7 +132,12 @@ exports.getMyDoctorList = async ctx => { |
76 | useYn : 'Y', | 132 | useYn : 'Y', |
77 | }); | 133 | }); |
78 | 134 | ||
79 | - return doctorInfo.info; | 135 | + if (doctorInfo) { |
136 | + return ({ | ||
137 | + ...doctorInfo.info, | ||
138 | + doctorId : doctorInfo.doctorId, | ||
139 | + }) | ||
140 | + } | ||
80 | })); | 141 | })); |
81 | 142 | ||
82 | ctx.status = 200; | 143 | ctx.status = 200; |
... | @@ -112,7 +173,10 @@ exports.viewAllDoctorRegisterReq = async ctx => { | ... | @@ -112,7 +173,10 @@ exports.viewAllDoctorRegisterReq = async ctx => { |
112 | }) | 173 | }) |
113 | 174 | ||
114 | const doctorReqList = await Promise.all(patientInfoList.map(async patientInfo => { | 175 | const doctorReqList = await Promise.all(patientInfoList.map(async patientInfo => { |
115 | - const doctor = await DoctorInfo.findOne({ doctorId : patientInfo.doctorId }); | 176 | + const doctor = await DoctorInfo.findOne({ |
177 | + doctorId : patientInfo.doctorId, | ||
178 | + useYn : 'Y', | ||
179 | + }); | ||
116 | return { | 180 | return { |
117 | patientId : patientInfo.patientId, | 181 | patientId : patientInfo.patientId, |
118 | doctorId : patientInfo.doctorId, | 182 | doctorId : patientInfo.doctorId, |
... | @@ -156,9 +220,9 @@ exports.acceptDoctorRegister = async ctx => { | ... | @@ -156,9 +220,9 @@ exports.acceptDoctorRegister = async ctx => { |
156 | return; | 220 | return; |
157 | } | 221 | } |
158 | 222 | ||
159 | - patientInfo.updateInfo('환자 등록 요청 수락'); | 223 | + await patientInfo.updateInfo('환자 등록 요청 수락'); |
160 | - patientInfo.setUseYn('Y'); | 224 | + await patientInfo.setUseYn('Y'); |
161 | - patientInfo.save(); | 225 | + await patientInfo.save(); |
162 | 226 | ||
163 | ctx.status = 200; | 227 | ctx.status = 200; |
164 | 228 | ... | ... |
... | @@ -11,6 +11,7 @@ exports.updateMedicineInfo = async() => { | ... | @@ -11,6 +11,7 @@ exports.updateMedicineInfo = async() => { |
11 | //queryUrl을 return하는 함수 : 한 페이지에 100개의 item씩 요청할 수 있다. | 11 | //queryUrl을 return하는 함수 : 한 페이지에 100개의 item씩 요청할 수 있다. |
12 | const getQueryURL = (i) => { | 12 | const getQueryURL = (i) => { |
13 | const url = 'http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList'; | 13 | const url = 'http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList'; |
14 | + // eslint-disable-next-line no-undef | ||
14 | const queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY; | 15 | const queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY; |
15 | const pageNum = '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(i); | 16 | const pageNum = '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(i); |
16 | const numOfItem = '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(100); | 17 | const numOfItem = '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(100); |
... | @@ -24,6 +25,7 @@ const getItemsList = async(queryUrl) => { | ... | @@ -24,6 +25,7 @@ const getItemsList = async(queryUrl) => { |
24 | let i = 1, getItem = null, items = null; | 25 | let i = 1, getItem = null, items = null; |
25 | const result = []; | 26 | const result = []; |
26 | 27 | ||
28 | + // eslint-disable-next-line no-constant-condition | ||
27 | while(true) { | 29 | while(true) { |
28 | getItem = await axios.get(queryUrl(i)); | 30 | getItem = await axios.get(queryUrl(i)); |
29 | items = getItem.data.body.items; | 31 | items = getItem.data.body.items; | ... | ... |
... | @@ -8,6 +8,7 @@ const jwtMiddleware = async (ctx, next) => { | ... | @@ -8,6 +8,7 @@ const jwtMiddleware = async (ctx, next) => { |
8 | } | 8 | } |
9 | 9 | ||
10 | try { | 10 | try { |
11 | + // eslint-disable-next-line no-undef | ||
11 | const decoded = jwt.verify(token, process.env.JWT_SECRET); | 12 | const decoded = jwt.verify(token, process.env.JWT_SECRET); |
12 | ctx.state.user = { | 13 | ctx.state.user = { |
13 | _id : decoded._id, | 14 | _id : decoded._id, | ... | ... |
... | @@ -5,6 +5,7 @@ const Schema = mongoose.Schema; | ... | @@ -5,6 +5,7 @@ const Schema = mongoose.Schema; |
5 | const BottleSchema = new Schema ({ | 5 | const BottleSchema = new Schema ({ |
6 | bottleId : { type : Number, required : true, unique : true }, | 6 | bottleId : { type : Number, required : true, unique : true }, |
7 | hubId : { type : Number, required : true, ref : 'Hub', }, | 7 | hubId : { type : Number, required : true, ref : 'Hub', }, |
8 | + bottleNm : { type : String, required : true, maxlength : 10, }, | ||
8 | }); | 9 | }); |
9 | 10 | ||
10 | BottleSchema.statics.findByBottleId = function(bottleId) { | 11 | BottleSchema.statics.findByBottleId = function(bottleId) { |
... | @@ -23,5 +24,9 @@ BottleSchema.methods.getHubId = function() { | ... | @@ -23,5 +24,9 @@ BottleSchema.methods.getHubId = function() { |
23 | return this.hubId; | 24 | return this.hubId; |
24 | }; | 25 | }; |
25 | 26 | ||
27 | +BottleSchema.methods.setBottleNm = function(bottleNm) { | ||
28 | + this.bottleNm = bottleNm; | ||
29 | +} | ||
30 | + | ||
26 | 31 | ||
27 | module.exports = mongoose.model('Bottle', BottleSchema); | 32 | module.exports = mongoose.model('Bottle', BottleSchema); |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -16,16 +16,26 @@ const BottleMedicineSchema = new Schema({ | ... | @@ -16,16 +16,26 @@ const BottleMedicineSchema = new Schema({ |
16 | doctorId : { | 16 | doctorId : { |
17 | type : String, | 17 | type : String, |
18 | ref : 'User', | 18 | ref : 'User', |
19 | - required : true, | 19 | + lowercase : true, |
20 | }, | 20 | }, |
21 | - dosage : { | 21 | + dailyDosage : { |
22 | + type : Number, | ||
23 | + default : 1, | ||
24 | + }, | ||
25 | + totalDosage : { | ||
26 | + type : Number, | ||
27 | + default : 1, | ||
28 | + }, | ||
29 | + eachWeight : { | ||
30 | + type : Number, | ||
31 | + default : 0, | ||
32 | + }, | ||
33 | + totalWeight : { | ||
22 | type : Number, | 34 | type : Number, |
23 | - required : true, | ||
24 | default : 0, | 35 | default : 0, |
25 | }, | 36 | }, |
26 | regDtm : { | 37 | regDtm : { |
27 | type : Date, | 38 | type : Date, |
28 | - required : true, | ||
29 | default : Date.now, | 39 | default : Date.now, |
30 | }, | 40 | }, |
31 | useYn : { | 41 | useYn : { |
... | @@ -39,6 +49,14 @@ BottleMedicineSchema.methods.setDoctorId = function(doctorId) { | ... | @@ -39,6 +49,14 @@ BottleMedicineSchema.methods.setDoctorId = function(doctorId) { |
39 | this.doctorId = doctorId; | 49 | this.doctorId = doctorId; |
40 | }; | 50 | }; |
41 | 51 | ||
52 | +BottleMedicineSchema.methods.setEachWeight = function(eachWeight) { | ||
53 | + this.eachWeight = eachWeight; | ||
54 | +}; | ||
55 | + | ||
56 | +BottleMedicineSchema.methods.setTotalWeight = function(totalWeight) { | ||
57 | + this.totalWeight = totalWeight; | ||
58 | +}; | ||
59 | + | ||
42 | BottleMedicineSchema.methods.setUseYn = function(useYn) { | 60 | BottleMedicineSchema.methods.setUseYn = function(useYn) { |
43 | this.useYn = useYn; | 61 | this.useYn = useYn; |
44 | }; | 62 | }; | ... | ... |
... | @@ -3,9 +3,10 @@ const mongoose = require('mongoose'); | ... | @@ -3,9 +3,10 @@ const mongoose = require('mongoose'); |
3 | const Schema = mongoose.Schema; | 3 | const Schema = mongoose.Schema; |
4 | 4 | ||
5 | const DoctorInfoSchema = new Schema({ | 5 | const DoctorInfoSchema = new Schema({ |
6 | - doctorId : { type : String, required : true, }, | 6 | + doctorId : { type : String, required : true, lowercase : true, }, |
7 | info : { | 7 | info : { |
8 | doctorLicense : { type : String, required : true, }, | 8 | doctorLicense : { type : String, required : true, }, |
9 | + validateDoctorLicense : { type : String, default : null }, | ||
9 | hospitalNm : { type : String, default : null, }, | 10 | hospitalNm : { type : String, default : null, }, |
10 | hospitalAddr : { type : String, default : null, }, | 11 | hospitalAddr : { type : String, default : null, }, |
11 | contact : { type : String, required : true, }, | 12 | contact : { type : String, required : true, }, |
... | @@ -23,5 +24,9 @@ DoctorInfoSchema.methods.setUseYn = function(useYn) { | ... | @@ -23,5 +24,9 @@ DoctorInfoSchema.methods.setUseYn = function(useYn) { |
23 | this.useYn = useYn; | 24 | this.useYn = useYn; |
24 | }; | 25 | }; |
25 | 26 | ||
27 | +DoctorInfoSchema.methods.setValidateDoctorLicense = function(validateDoctorLicense) { | ||
28 | + this.info.validateDoctorLicense = validateDoctorLicense; | ||
29 | +}; | ||
30 | + | ||
26 | 31 | ||
27 | module.exports = mongoose.model('DoctorInfo', DoctorInfoSchema); | 32 | module.exports = mongoose.model('DoctorInfo', DoctorInfoSchema); | ... | ... |
... | @@ -10,7 +10,7 @@ const FeedbackSchema = new Schema({ | ... | @@ -10,7 +10,7 @@ const FeedbackSchema = new Schema({ |
10 | required : true, | 10 | required : true, |
11 | ref : 'BottleMedicine', | 11 | ref : 'BottleMedicine', |
12 | }, | 12 | }, |
13 | - doctorId : { type : String, required : true, ref : 'User', }, | 13 | + doctorId : { type : String, required : true, ref : 'User', lowercase : true, }, |
14 | feedback : { type : String, required : true, }, | 14 | feedback : { type : String, required : true, }, |
15 | }); | 15 | }); |
16 | 16 | ... | ... |
... | @@ -5,7 +5,8 @@ const Schema = mongoose.Schema; | ... | @@ -5,7 +5,8 @@ const Schema = mongoose.Schema; |
5 | const HubSchema = new Schema ({ | 5 | const HubSchema = new Schema ({ |
6 | hubId : { type : Number, required : true, unique : true }, | 6 | hubId : { type : Number, required : true, unique : true }, |
7 | hosting : { type : Object, default : null }, | 7 | hosting : { type : Object, default : null }, |
8 | - userId : { type : String, default : null, ref : 'User' }, | 8 | + userId : { type : String, default : null, ref : 'User', lowercase : true, }, |
9 | + hubNm : { type : String, required : true, maxlength : 10, }, | ||
9 | }); | 10 | }); |
10 | 11 | ||
11 | HubSchema.statics.findByHubId = function(hubId) { | 12 | HubSchema.statics.findByHubId = function(hubId) { | ... | ... |
1 | const mongoose = require('mongoose'); | 1 | const mongoose = require('mongoose'); |
2 | const moment = require('moment'); | 2 | const moment = require('moment'); |
3 | +require('moment-timezone'); | ||
3 | 4 | ||
4 | const Schema = mongoose.Schema; | 5 | const Schema = mongoose.Schema; |
5 | 6 | ||
6 | const PatientInfoSchema = new Schema({ | 7 | const PatientInfoSchema = new Schema({ |
7 | - patientId : { type : String, required : true, ref : 'User', }, | 8 | + patientId : { type : String, required : true, ref : 'User', lowercase : true, }, |
8 | - doctorId : { type : String, required : true, ref : 'User', }, | 9 | + doctorId : { type : String, required : true, ref : 'User', lowercase : true, }, |
9 | info : { type : String, required : true, }, | 10 | info : { type : String, required : true, }, |
10 | useYn : { type : String, required : true, default : 'W', }, | 11 | useYn : { type : String, required : true, default : 'W', }, |
11 | }); | 12 | }); |
... | @@ -35,9 +36,9 @@ PatientInfoSchema.methods.setUseYn = function(useYn) { | ... | @@ -35,9 +36,9 @@ PatientInfoSchema.methods.setUseYn = function(useYn) { |
35 | }; | 36 | }; |
36 | 37 | ||
37 | PatientInfoSchema.methods.updateInfo = function(info) { | 38 | PatientInfoSchema.methods.updateInfo = function(info) { |
38 | - const date = moment(new Date()).format('YYYY-MM-DD hh:mm'); | 39 | + const date = moment.tz('Asia/Seoul').format('YYYY-MM-DD HH:mm'); |
39 | if(this.info.length) | 40 | if(this.info.length) |
40 | - this.info = this.info.concat('\n\n', `${date} ➡ ${info}`); | 41 | + this.info = this.info.concat('\n\n', `${date} -> ${info}`); |
41 | else | 42 | else |
42 | this.info = `${date} ➡ ${info}`; | 43 | this.info = `${date} ➡ ${info}`; |
43 | }; | 44 | }; | ... | ... |
server/src/models/prescribeInfo.js
0 → 100644
1 | +const mongoose = require('mongoose'); | ||
2 | + | ||
3 | +const Schema = mongoose.Schema; | ||
4 | + | ||
5 | +const PrescribeInfoSchema = new Schema({ | ||
6 | + doctorId : { type : String, require : true, }, | ||
7 | + patientId : { type : String, require : true, }, | ||
8 | + medicineId : { type : Number, require : true, }, | ||
9 | + dailyDosage : { type : Number, require : true, }, | ||
10 | + totalDosage : { type : Number, require : true, }, | ||
11 | + qrCodeUrl : { type : String, require : true, }, | ||
12 | +}); | ||
13 | + | ||
14 | + | ||
15 | +module.exports = mongoose.model('PrescribeInfo', PrescribeInfoSchema); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -3,10 +3,11 @@ const mongoose = require('mongoose'); | ... | @@ -3,10 +3,11 @@ const mongoose = require('mongoose'); |
3 | const Schema = mongoose.Schema; | 3 | const Schema = mongoose.Schema; |
4 | 4 | ||
5 | const ProfileSchema = new Schema({ | 5 | const ProfileSchema = new Schema({ |
6 | - userId : { type : String, required : true, ref : 'User', }, | 6 | + userId : { type : String, required : true, ref : 'User', lowercase : true, }, |
7 | userNm : { type : String, required : true, }, | 7 | userNm : { type : String, required : true, }, |
8 | - userAge : { type : Number, required : true, }, | 8 | + birth : { type : String, required : true, }, |
9 | contact : { type : String, required : true, }, | 9 | contact : { type : String, required : true, }, |
10 | + useYn : { type : String, default : 'Y', }, | ||
10 | deviceToken : { type : String, default : null, }, | 11 | deviceToken : { type : String, default : null, }, |
11 | }); | 12 | }); |
12 | 13 | ||
... | @@ -14,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) { | ... | @@ -14,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) { |
14 | return this.findOne({ userId }); | 15 | return this.findOne({ userId }); |
15 | }; | 16 | }; |
16 | 17 | ||
17 | -ProfileSchema.methods.updateUserContact = function(contact) { | 18 | +ProfileSchema.methods.setUseYn = function(useYn) { |
18 | - this.contact = contact; | 19 | + this.useYn = useYn; |
19 | }; | 20 | }; |
20 | 21 | ||
21 | -ProfileSchema.methods.updateUserAge = function() { | 22 | +ProfileSchema.methods.updateProfileInfo = function({ userNm, birth, contact }) { |
22 | - this.userAge = this.userAge + 1; | 23 | + if(userNm) { this.userNm = userNm } |
24 | + if(birth) { this.birth = birth } | ||
25 | + if(contact) { this.contact = contact } | ||
23 | }; | 26 | }; |
24 | 27 | ||
25 | ProfileSchema.methods.updateDeviceToken = function(deviceToken) { | 28 | ProfileSchema.methods.updateDeviceToken = function(deviceToken) { | ... | ... |
... | @@ -5,7 +5,6 @@ const Schema = mongoose.Schema; | ... | @@ -5,7 +5,6 @@ const Schema = mongoose.Schema; |
5 | const TakeMedicineHistorySchema = new Schema ({ | 5 | const TakeMedicineHistorySchema = new Schema ({ |
6 | takeDate : { | 6 | takeDate : { |
7 | type : Date, | 7 | type : Date, |
8 | - required : true, | ||
9 | default : Date.now, | 8 | default : Date.now, |
10 | }, | 9 | }, |
11 | bmId : { | 10 | bmId : { |
... | @@ -13,9 +12,10 @@ const TakeMedicineHistorySchema = new Schema ({ | ... | @@ -13,9 +12,10 @@ const TakeMedicineHistorySchema = new Schema ({ |
13 | ref : 'BottleMedicine', | 12 | ref : 'BottleMedicine', |
14 | required : true, | 13 | required : true, |
15 | }, | 14 | }, |
16 | - temperature : { type : Number, default : 0 }, | 15 | + temperature : { type : Number, default : 0, }, |
17 | - humidity : { type : Number, default : 0 }, | 16 | + humidity : { type : Number, default : 0, }, |
18 | - balance : { type : Number, default : 0 }, | 17 | + dosage : { type : Number, default : 0, }, |
18 | + balance : { type : Number, default : 0, }, | ||
19 | }); | 19 | }); |
20 | 20 | ||
21 | 21 | ... | ... |
... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); | ... | @@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); |
5 | const Schema = mongoose.Schema; | 5 | const Schema = mongoose.Schema; |
6 | 6 | ||
7 | const UserSchema = new Schema ({ | 7 | const UserSchema = new Schema ({ |
8 | - userId : { type: String, required : true, unique : true, lowercase : true, }, | 8 | + userId : { type: String, required : true, unique : true, lowercase : true, trim : true }, |
9 | - hashedPassword : { type : String, required : true }, | 9 | + hashedPassword : { type : String, required : true, }, |
10 | - userTypeCd : { type : String, required : true, default : 'NORMAL' }, | 10 | + userTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, |
11 | + authTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, }, | ||
11 | useYn : { type : String, default : 'W', required : true, }, | 12 | useYn : { type : String, default : 'W', required : true, }, |
12 | }); | 13 | }); |
13 | 14 | ... | ... |
... | @@ -3,11 +3,136 @@ | ... | @@ -3,11 +3,136 @@ |
3 | * 21/09/14 | 3 | * 21/09/14 |
4 | * Author : 박권수 | 4 | * Author : 박권수 |
5 | * 배치 시스템 | 5 | * 배치 시스템 |
6 | - * 1) 매년 지나면 프로필의 Age를 +1 | 6 | + * 1) Dosage에 따라, Push Notification 발송 |
7 | - * 2) Dosage에 따라, Push Notification 발송 | ||
8 | */ | 7 | */ |
9 | 8 | ||
10 | const cron = require('node-cron'); | 9 | const cron = require('node-cron'); |
10 | +const fs = require('fs'); | ||
11 | 11 | ||
12 | const Profile = require('../models/profile'); | 12 | const Profile = require('../models/profile'); |
13 | -const BottleMedicine = require('../models/bottleMedicine'); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
13 | +const User = require('../models/user'); | ||
14 | +const Hub = require('../models/hub'); | ||
15 | +const Bottle = require('../models/bottle'); | ||
16 | +const BottleMedicine = require('../models/bottleMedicine'); | ||
17 | +const Medicine = require('../models/medicine'); | ||
18 | + | ||
19 | +const updateMedicineInfo = require('../lib/UpdatingMedicineInfo'); | ||
20 | +const { sendPushMessage } = require('./FCM'); | ||
21 | + | ||
22 | + | ||
23 | +//매월 1일 0시 0분에 약 정보 업데이트 | ||
24 | +exports.updateMedicineData = async () => { | ||
25 | + cron.schedule('0 0 0 1 * *', () => { | ||
26 | + updateMedicineInfo.updateMedicineInfo(); | ||
27 | + }, { | ||
28 | + timezone : 'Asia/Tokyo', | ||
29 | + }); | ||
30 | +}; | ||
31 | + | ||
32 | +//매주 일요일마다 불필요한 qrcode 제거 | ||
33 | +exports.removeQrCode = () => { | ||
34 | + cron.schedule('0 0 0 * * 0', () => { | ||
35 | + // eslint-disable-next-line no-undef | ||
36 | + const qrDir = process.env.QR_DIR; | ||
37 | + fs.rm(qrDir, { recursive : true, force : true, }, () => { | ||
38 | + fs.mkdir(qrDir, (err) => { if(err) console.log(err) }); | ||
39 | + }); | ||
40 | + }, { | ||
41 | + timezone : 'Asia/Tokyo', | ||
42 | + }); | ||
43 | +}; | ||
44 | + | ||
45 | +//dosage에 따라, Push Notification을 발송한다. | ||
46 | +//아침 8시, 점심 12시, 저녁 6시에 한번씩 발송 | ||
47 | +exports.pushNotifyByDosage = async() => { | ||
48 | + | ||
49 | + //매일 아침 8시 : 복용량 상관 없이 보냄 | ||
50 | + cron.schedule('0 0 8 * * *', async () => { | ||
51 | + const bottleMedicineList = await BottleMedicine.find({ useYn : 'Y', dosage : { $gte : 1 } }); | ||
52 | + bottleMedicineList.forEach(async bottleMedicine => { | ||
53 | + const bottle = await Bottle.findOne({ bottleId : bottleMedicine.bottleId }); | ||
54 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
55 | + const user = await User.findOne({ userId : hub.userId, useYn : 'Y' }); | ||
56 | + | ||
57 | + if(user) { | ||
58 | + const profile = await Profile.findOne({ userId : user.userId }); | ||
59 | + const { deviceToken } = profile; | ||
60 | + | ||
61 | + if(deviceToken) { | ||
62 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
63 | + pushNotify({ | ||
64 | + deviceToken, | ||
65 | + title : '약 복용 시간입니다', | ||
66 | + body : medicine.name + '을 복용하셔야 합니다.', | ||
67 | + }); | ||
68 | + } | ||
69 | + } | ||
70 | + }); | ||
71 | + }, { | ||
72 | + timezone : 'Asia/Tokyo', | ||
73 | + }); | ||
74 | + | ||
75 | + | ||
76 | + //매일 점심 12시 : 복용량이 3인 환자들만 | ||
77 | + cron.schedule('0 0 12 * * *', async () => { | ||
78 | + const bottleMedicineList = await BottleMedicine.find({ useYn : 'Y', dosage : { $gte : 3 } }); | ||
79 | + bottleMedicineList.forEach(async bottleMedicine => { | ||
80 | + const bottle = await Bottle.findOne({ bottleId : bottleMedicine.bottleId }); | ||
81 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
82 | + const user = await User.findOne({ userId : hub.userId, useYn : 'Y' }); | ||
83 | + | ||
84 | + if(user) { | ||
85 | + const profile = await Profile.findOne({ userId : user.userId }); | ||
86 | + const { deviceToken } = profile; | ||
87 | + | ||
88 | + if(deviceToken) { | ||
89 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
90 | + pushNotify({ | ||
91 | + deviceToken, | ||
92 | + title : '약 복용 시간입니다', | ||
93 | + body : medicine.name + '을 복용하셔야 합니다.', | ||
94 | + }); | ||
95 | + } | ||
96 | + } | ||
97 | + }); | ||
98 | + }, { | ||
99 | + timezone : 'Asia/Tokyo', | ||
100 | + }); | ||
101 | + | ||
102 | + | ||
103 | + //매일 저녁 6시 | ||
104 | + cron.schedule('0 0 18 * * *', async () => { | ||
105 | + const bottleMedicineList = await BottleMedicine.find({ useYn : 'Y', dosage : { $gte : 2 } }); | ||
106 | + bottleMedicineList.forEach(async bottleMedicine => { | ||
107 | + const bottle = await Bottle.findOne({ bottleId : bottleMedicine.bottleId }); | ||
108 | + const hub = await Hub.findOne({ hubId : bottle.hubId }); | ||
109 | + const user = await User.findOne({ userId : hub.userId, useYn : 'Y' }); | ||
110 | + | ||
111 | + if(user) { | ||
112 | + const profile = await Profile.findOne({ userId : user.userId }); | ||
113 | + const { deviceToken } = profile; | ||
114 | + | ||
115 | + if(deviceToken) { | ||
116 | + const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); | ||
117 | + pushNotify({ | ||
118 | + deviceToken, | ||
119 | + title : '약 복용 시간입니다', | ||
120 | + body : medicine.name + '을 복용하셔야 합니다.', | ||
121 | + }); | ||
122 | + } | ||
123 | + } | ||
124 | + }); | ||
125 | + }, { | ||
126 | + timezone : 'Asia/Tokyo', | ||
127 | + }); | ||
128 | + | ||
129 | +}; | ||
130 | + | ||
131 | +const pushNotify = ({ deviceToken, title, body }) => { | ||
132 | + //toDo : deviceToken을 받아서 push notification을 발송하는 함수 | ||
133 | + sendPushMessage({ | ||
134 | + deviceToken, | ||
135 | + title, | ||
136 | + body, | ||
137 | + }); | ||
138 | +}; | ... | ... |
1 | -const Bottle = require('../models/bottle'); | ||
2 | const BottleMedicine = require('../models/bottleMedicine'); | 1 | const BottleMedicine = require('../models/bottleMedicine'); |
3 | const TakeMedicineHist = require('../models/takeMedicineHistory'); | 2 | const TakeMedicineHist = require('../models/takeMedicineHistory'); |
4 | 3 | ||
4 | + | ||
5 | //message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수 | 5 | //message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수 |
6 | exports.dataPublish = async (topic, message) => { | 6 | exports.dataPublish = async (topic, message) => { |
7 | - //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴 | 7 | + if(message.includes('weight')) { |
8 | - const data = await factoring(topic, message); | 8 | + //무게 갱신 |
9 | - //가공된 데이터를 bottleId의 약병에 업데이트 | 9 | + const result = await updateBottleMedicineWeight(topic, message); |
10 | - await bottleInfoUpdate(data); | 10 | + |
11 | - //가공된 데이터를 메시지로 만들어 topic과 message 리턴 | 11 | + return result; |
12 | - const result = await transPublishingTopicAndMessage(data.bottleId); | 12 | + |
13 | + } else { | ||
14 | + //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴 | ||
15 | + const data = await factoring(topic, message); | ||
16 | + //가공된 데이터를 bottleId의 약병에 업데이트 | ||
17 | + await bottleInfoUpdate(data); | ||
18 | + //가공된 데이터를 메시지로 만들어 topic과 message 리턴 | ||
19 | + const result = await transPublishingTopicAndMessage(data.bottleId); | ||
13 | 20 | ||
14 | - return result; | 21 | + return result; |
15 | 22 | ||
23 | + } | ||
16 | }; | 24 | }; |
17 | 25 | ||
18 | //Hub topic : bottle/bottleId | 26 | //Hub topic : bottle/bottleId |
19 | -//Hub로부터 받은 message : 개폐여부/온도/습도/초음파센서 | 27 | +//Hub로부터 받은 message : 개폐여부/온도/습도/무게센서 |
20 | const factoring = async (topic, message) => { | 28 | const factoring = async (topic, message) => { |
21 | const bottleId = parseInt(topic.split('/')[1]); | 29 | const bottleId = parseInt(topic.split('/')[1]); |
22 | const data = message.split('/'); | 30 | const data = message.split('/'); |
23 | - let [isOpen, temperature, humidity, balance] = data; | 31 | + const [isOpen, temperature, totalWeight, humidity] = data; |
24 | 32 | ||
25 | - if(isOpen === '0') | ||
26 | - balance = await balanceFactoring(balance); | ||
27 | - else balance = '-1'; | ||
28 | - | ||
29 | return { | 33 | return { |
30 | bottleId, | 34 | bottleId, |
31 | isOpen, | 35 | isOpen, |
32 | - openDate : new Date(), | 36 | + temperature, |
33 | - temperature, | ||
34 | humidity, | 37 | humidity, |
35 | - balance | 38 | + totalWeight, |
36 | }; | 39 | }; |
37 | 40 | ||
38 | } | 41 | } |
39 | 42 | ||
40 | -const balanceFactoring = (balance) => { | ||
41 | - const max = 10; //Digital Lead Sensor Maximum Value | ||
42 | - const slicingBalance = max / 5; | ||
43 | - | ||
44 | - if(parseInt(balance) < slicingBalance || parseInt(balance) > max * 2) | ||
45 | - return '80'; | ||
46 | - else if(parseInt(balance) < slicingBalance * 2) | ||
47 | - return '60'; | ||
48 | - else if(parseInt(balance) < slicingBalance * 3) | ||
49 | - return '40'; | ||
50 | - else if(parseInt(balance) < slicingBalance * 4) | ||
51 | - return '20'; | ||
52 | - else return '0'; | ||
53 | - | ||
54 | -} | ||
55 | - | ||
56 | //bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다. | 43 | //bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다. |
57 | const bottleInfoUpdate = async(data) => { | 44 | const bottleInfoUpdate = async(data) => { |
58 | - let { bottleId, isOpen, openDate, temperature, humidity, balance } = data; | 45 | + let { bottleId, isOpen, temperature, humidity, totalWeight } = data; |
59 | - | ||
60 | - bottleId = parseInt(bottleId); | ||
61 | - isOpen = parseInt(isOpen); | ||
62 | - temperature = parseFloat(temperature); | ||
63 | - humidity = parseFloat(humidity); | ||
64 | - balance = parseInt(balance); | ||
65 | - | ||
66 | - const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | ||
67 | 46 | ||
68 | - if(bottleMedicine) { | 47 | + if(!parseInt(isOpen)) { |
69 | - if(isOpen) { | 48 | + bottleId = parseInt(bottleId); |
70 | - const takeMedicineHist = new TakeMedicineHist({ | 49 | + temperature = parseFloat(temperature); |
71 | - takeDate : openDate, | 50 | + humidity = parseFloat(humidity); |
72 | - bmId : bottleMedicine._id, | 51 | + totalWeight = parseFloat(totalWeight); |
73 | - temperature, | 52 | + |
74 | - humidity, | 53 | + const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); |
75 | - balance, | 54 | + |
76 | - }); | 55 | + if(bottleMedicine) { |
77 | - takeMedicineHist.save(); | 56 | + const lastTotalWeight = parseFloat(bottleMedicine.totalWeight); |
57 | + const { eachWeight } = bottleMedicine; | ||
58 | + | ||
59 | + const dosage = Math.round((lastTotalWeight - totalWeight) / parseFloat(eachWeight)); | ||
60 | + | ||
61 | + if(dosage > 0) { | ||
62 | + const balance = Math.round(totalWeight / parseFloat(eachWeight)); | ||
63 | + | ||
64 | + const takeMedicineHist = new TakeMedicineHist({ | ||
65 | + bmId : bottleMedicine._id, | ||
66 | + temperature, | ||
67 | + humidity, | ||
68 | + dosage, | ||
69 | + balance, | ||
70 | + }); | ||
71 | + await takeMedicineHist.save(); | ||
72 | + } | ||
73 | + | ||
74 | + await bottleMedicine.setTotalWeight(totalWeight); | ||
75 | + await bottleMedicine.save(); | ||
78 | } | 76 | } |
79 | } | 77 | } |
80 | } | 78 | } |
... | @@ -84,11 +82,13 @@ const transPublishingTopicAndMessage = async(bottleId) => { | ... | @@ -84,11 +82,13 @@ const transPublishingTopicAndMessage = async(bottleId) => { |
84 | const topic = 'bottle/' + bottleId + '/stb'; | 82 | const topic = 'bottle/' + bottleId + '/stb'; |
85 | 83 | ||
86 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | 84 | const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); |
87 | - const takeMedicineHist = await TakeMedicineHist.find({ | 85 | + const takeMedicineHistList = await TakeMedicineHist.find({ |
88 | bmId : bottleMedicine._id | 86 | bmId : bottleMedicine._id |
89 | - }).sort((a, b) => a.takeDate < b.takeDate)[0]; | 87 | + }).sort({ takeDate : 'desc' }).limit(1); |
90 | 88 | ||
91 | - const message = 'res/' + await transDate(takeMedicineHist.takeDate) + '/' + bottleMedicine.dosage; | 89 | + const message = takeMedicineHistList && takeMedicineHistList[0] ? |
90 | + 'res/' + await transDate(takeMedicineHistList[0].takeDate) + '/' + takeMedicineHistList[0].dosage : | ||
91 | + 'res/' + await transDate(new Date()) + '/' + 0; | ||
92 | 92 | ||
93 | return { | 93 | return { |
94 | topic, | 94 | topic, |
... | @@ -100,4 +100,23 @@ const transPublishingTopicAndMessage = async(bottleId) => { | ... | @@ -100,4 +100,23 @@ const transPublishingTopicAndMessage = async(bottleId) => { |
100 | const transDate = (date) => { | 100 | const transDate = (date) => { |
101 | return (date.getMonth() + 1 < 10 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1)) | 101 | return (date.getMonth() + 1 < 10 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1)) |
102 | + (date.getDate() < 10 ? '0' + String(date.getDate()) : String(date.getDate())); | 102 | + (date.getDate() < 10 ? '0' + String(date.getDate()) : String(date.getDate())); |
103 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
103 | +}; | ||
104 | + | ||
105 | + | ||
106 | +//무게센서를 이용하여 데이터값을 갱신하는 함수 | ||
107 | +const updateBottleMedicineWeight = async (topic, message) => { | ||
108 | + const bottleId = parseInt(topic.split('/')[1]); | ||
109 | + //message = weight/무게 | ||
110 | + const totalWeight = parseFloat(message.split('/')[1]); | ||
111 | + | ||
112 | + const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); | ||
113 | + const totalDosage = parseInt(bottleMedicine.totalDosage); | ||
114 | + //받은 값으로 총 무게를 설정한 이후, 총 무게 / 총 복용량으로 개별 무게를 설정한다. | ||
115 | + await bottleMedicine.setTotalWeight(totalWeight); | ||
116 | + await bottleMedicine.setEachWeight(totalWeight / totalDosage); | ||
117 | + | ||
118 | + await bottleMedicine.save(); | ||
119 | + | ||
120 | + return null; | ||
121 | + | ||
122 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
server/src/util/FCM.js
0 → 100644
1 | +// const fcm = require('firebase-admin'); | ||
2 | +const axios = require('axios'); | ||
3 | + | ||
4 | +// exports.initializeFCM = () => { | ||
5 | +// fcm.initializeApp({ | ||
6 | +// credential: fcm.credential.applicationDefault(), | ||
7 | +// }); | ||
8 | +// }; | ||
9 | + | ||
10 | +exports.sendPushMessage = async ({ deviceToken, title, body }) => { | ||
11 | + // const notifyMessage = { | ||
12 | + // notification : { | ||
13 | + // title, | ||
14 | + // body, | ||
15 | + // }, | ||
16 | + // token : deviceToken, | ||
17 | + // }; | ||
18 | + // fcm.messaging().send(notifyMessage).then(res => { | ||
19 | + // console.log(res); | ||
20 | + // }).catch(e => { | ||
21 | + // console.log(e); | ||
22 | + // }); | ||
23 | + | ||
24 | + const message = { | ||
25 | + to : deviceToken, | ||
26 | + notification : { | ||
27 | + title, | ||
28 | + body, | ||
29 | + priority : 'high', | ||
30 | + }, | ||
31 | + data : null, | ||
32 | + } | ||
33 | + | ||
34 | + const url = 'https://fcm.googleapis.com/fcm/send'; | ||
35 | + const result = await axios.post(url, message, { | ||
36 | + headers : { | ||
37 | + 'Content-Type' : 'application/json', | ||
38 | + // eslint-disable-next-line no-undef | ||
39 | + 'Authorization' : `key=${process.env.FCM_KEY}`, | ||
40 | + }, | ||
41 | + }); | ||
42 | + | ||
43 | + console.log(result.data); | ||
44 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
server/src/util/GoogleCloudStorage.js
0 → 100644
1 | +const { Storage } = require('@google-cloud/storage'); | ||
2 | +const fs = require('fs'); | ||
3 | + | ||
4 | +const storage = new Storage(); | ||
5 | +const GoogleStorageUrl = 'https://storage.googleapis.com/'; | ||
6 | + | ||
7 | + | ||
8 | +//의사 아이디, 업로드할 파일명, 업로드할 파일 경로를 인자로 받아, File을 GCS에 업로드 후 GCS 주소를 반환 | ||
9 | +exports.uploadDoctorLicense = async ({ userId, fileName, filePath }) => { | ||
10 | + const destination = userId + '_' + fileName; | ||
11 | + try { | ||
12 | + const result = await storage.bucket('doctor-info').upload(filePath, { | ||
13 | + destination, | ||
14 | + }); | ||
15 | + | ||
16 | + const doctorLicenseUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | ||
17 | + | ||
18 | + return doctorLicenseUrl; | ||
19 | + } catch(e) { | ||
20 | + console.log(e); | ||
21 | + return null; | ||
22 | + } | ||
23 | +}; | ||
24 | + | ||
25 | +//의사 정보를 인자로 받아 해당 Doctor License의 Signed URL을 반환 | ||
26 | +exports.viewDoctorLicense = async ({ doctorInfo }) => { | ||
27 | + try { | ||
28 | + const fileName = doctorInfo.info.doctorLicense.split('/').pop(); | ||
29 | + const file = storage.bucket('doctor-info').file(fileName); | ||
30 | + const option = { | ||
31 | + version : 'v4', | ||
32 | + expires : Date.now() + 1000 * 60 * 15, | ||
33 | + action : 'read', | ||
34 | + }; | ||
35 | + | ||
36 | + const [signedUrl] = file ? await file.getSignedUrl(option) : [null]; | ||
37 | + | ||
38 | + return signedUrl; | ||
39 | + } catch(e) { | ||
40 | + console.log(e); | ||
41 | + return null; | ||
42 | + } | ||
43 | +}; | ||
44 | + | ||
45 | +//의사 ID, 약 ID, 복용량을 인자로 받아, QR Code를 생성 | ||
46 | +exports.uploadQrCode = async ({ directory, qrCodeFileName }) => { | ||
47 | + const destination = qrCodeFileName; | ||
48 | + try { | ||
49 | + //파일을 GCS에 업로드 | ||
50 | + const result = await storage.bucket('prescribe-medicine-qrcode').upload(directory + '/' + qrCodeFileName, { | ||
51 | + destination | ||
52 | + }); | ||
53 | + | ||
54 | + //업로드 후 파일 삭제 | ||
55 | + fs.rm(directory + '/' + qrCodeFileName, () => {}); | ||
56 | + | ||
57 | + const qrCodeUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | ||
58 | + | ||
59 | + return qrCodeUrl; | ||
60 | + } catch(e) { | ||
61 | + console.log(e); | ||
62 | + return null; | ||
63 | + } | ||
64 | +}; | ||
65 | + | ||
66 | +//생성된 QR코드의 signedUrl을 가져옴 | ||
67 | +exports.getQrCodeUrl = async ({ qrCodeFileName }) => { | ||
68 | + try { | ||
69 | + const fileName = qrCodeFileName; | ||
70 | + const file = storage.bucket('prescribe-medicine-qrcode').file(fileName); | ||
71 | + const option = { | ||
72 | + version : 'v4', | ||
73 | + expires : Date.now() + 1000 * 60 * 15, | ||
74 | + action : 'read', | ||
75 | + }; | ||
76 | + | ||
77 | + const [signedUrl] = file ? await file.getSignedUrl(option) : [null]; | ||
78 | + | ||
79 | + return signedUrl; | ||
80 | + } catch(e) { | ||
81 | + console.log(e); | ||
82 | + return null; | ||
83 | + } | ||
84 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -const mqtt = require('mqtt') | 1 | +const mqtt = require('mqtt'); |
2 | -const clientList = [] | 2 | +const clientList = []; |
3 | 3 | ||
4 | exports.mqttOn = async (hosting, foo) => { | 4 | exports.mqttOn = async (hosting, foo) => { |
5 | const filterIndex = clientList.findIndex(client => { | 5 | const filterIndex = clientList.findIndex(client => { |
... | @@ -9,27 +9,29 @@ exports.mqttOn = async (hosting, foo) => { | ... | @@ -9,27 +9,29 @@ exports.mqttOn = async (hosting, foo) => { |
9 | }) | 9 | }) |
10 | 10 | ||
11 | if(filterIndex === -1) { | 11 | if(filterIndex === -1) { |
12 | - const client = mqtt.connect(hosting) | 12 | + const client = mqtt.connect(hosting); |
13 | - clientList.push(client) | 13 | + clientList.push(client); |
14 | 14 | ||
15 | client.on('connect', () => { | 15 | client.on('connect', () => { |
16 | console.log('Hub connected: ', client.connected) | 16 | console.log('Hub connected: ', client.connected) |
17 | - }) | 17 | + }); |
18 | 18 | ||
19 | client.on('message', async (topic, message) => { | 19 | client.on('message', async (topic, message) => { |
20 | - const result = await foo(topic, message.toString()) | 20 | + const result = await foo(topic, message.toString()); |
21 | console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m') | 21 | console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m') |
22 | - this.mqttPublishMessage(client, result) | 22 | + if(result) this.mqttPublishMessage(client, result); |
23 | - }) | 23 | + }); |
24 | 24 | ||
25 | - return client | 25 | + return client; |
26 | } | 26 | } |
27 | 27 | ||
28 | - return clientList[filterIndex] | 28 | + return clientList[filterIndex]; |
29 | } | 29 | } |
30 | 30 | ||
31 | exports.mqttSubscribe = (client, topic) => { | 31 | exports.mqttSubscribe = (client, topic) => { |
32 | - client.subscribe(topic) | 32 | + client.subscribe(topic, () => { |
33 | + console.log('suscribe', topic); | ||
34 | + }); | ||
33 | } | 35 | } |
34 | 36 | ||
35 | exports.mqttPublishMessage = (client, { topic, message }) => { | 37 | exports.mqttPublishMessage = (client, { topic, message }) => { |
... | @@ -49,10 +51,10 @@ exports.mqttOff = (hosting) => { | ... | @@ -49,10 +51,10 @@ exports.mqttOff = (hosting) => { |
49 | return (client.options.clientId === hosting.clientId | 51 | return (client.options.clientId === hosting.clientId |
50 | && client.options.host === hosting.host | 52 | && client.options.host === hosting.host |
51 | && client.options.port === hosting.port) | 53 | && client.options.port === hosting.port) |
52 | - }) | 54 | + }); |
53 | 55 | ||
54 | if(filterIndex !== -1) { | 56 | if(filterIndex !== -1) { |
55 | - clientList[filterIndex].end() | 57 | + clientList[filterIndex].end(); |
56 | - clientList.splice(filterIndex, 1) | 58 | + clientList.splice(filterIndex, 1); |
57 | } | 59 | } |
58 | } | 60 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | -const Mqtt = require('../lib/MqttModule'); | 1 | +const Mqtt = require('./MqttModule'); |
2 | -const DataProcess = require('../lib/DataProcess'); | 2 | +const DataProcess = require('./DataProcess'); |
3 | const Hub = require('../models/hub'); | 3 | const Hub = require('../models/hub'); |
4 | const Bottle = require('../models/bottle'); | 4 | const Bottle = require('../models/bottle'); |
5 | 5 | ... | ... |
server/src/util/QrCodeUtil.js
0 → 100644
1 | +const QrCode = require('qrcode'); | ||
2 | +const moment = require('moment'); | ||
3 | + | ||
4 | + | ||
5 | +exports.generateQrCode_prescribe = async ({ medicine, dailyDosage, totalDosage, patientId, doctorId }) => { | ||
6 | + // eslint-disable-next-line no-undef | ||
7 | + const directory = process.env.QR_DIR; | ||
8 | + | ||
9 | + const now = moment().format('YYYY-MM-DD_HH:mm'); | ||
10 | + const qrCodeFileName = `${now}_${doctorId}_${patientId}_${medicine.medicineId}_${dailyDosage}_${totalDosage}.png`; | ||
11 | + | ||
12 | + try { | ||
13 | + await QrCode.toFile( | ||
14 | + directory + '/' + qrCodeFileName, | ||
15 | + `${medicine.medicineId}/${dailyDosage}/${totalDosage}/${doctorId}/${patientId}/${medicine.name}`, | ||
16 | + { | ||
17 | + color : { | ||
18 | + dark : '#337DFF', | ||
19 | + light : '#FFF' | ||
20 | + }, | ||
21 | + } | ||
22 | + ); | ||
23 | + | ||
24 | + return { | ||
25 | + directory, | ||
26 | + qrCodeFileName, | ||
27 | + }; | ||
28 | + | ||
29 | + } catch(e) { | ||
30 | + console.log(e); | ||
31 | + return null; | ||
32 | + } | ||
33 | + | ||
34 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
This diff could not be displayed because it is too large.
... | @@ -5,6 +5,7 @@ | ... | @@ -5,6 +5,7 @@ |
5 | "requires": true, | 5 | "requires": true, |
6 | "packages": { | 6 | "packages": { |
7 | "": { | 7 | "": { |
8 | + "name": "web", | ||
8 | "version": "0.1.0", | 9 | "version": "0.1.0", |
9 | "dependencies": { | 10 | "dependencies": { |
10 | "@testing-library/jest-dom": "^5.11.4", | 11 | "@testing-library/jest-dom": "^5.11.4", |
... | @@ -18,10 +19,12 @@ | ... | @@ -18,10 +19,12 @@ |
18 | "highcharts": "^9.2.0", | 19 | "highcharts": "^9.2.0", |
19 | "highcharts-react-official": "^3.0.0", | 20 | "highcharts-react-official": "^3.0.0", |
20 | "moment": "^2.29.1", | 21 | "moment": "^2.29.1", |
22 | + "qrcode": "^1.4.4", | ||
21 | "react": "^17.0.2", | 23 | "react": "^17.0.2", |
22 | "react-dom": "^17.0.2", | 24 | "react-dom": "^17.0.2", |
23 | "react-router-dom": "^5.2.0", | 25 | "react-router-dom": "^5.2.0", |
24 | "react-scripts": "4.0.3", | 26 | "react-scripts": "4.0.3", |
27 | + "react-spinners": "^0.11.0", | ||
25 | "recoil": "^0.4.0", | 28 | "recoil": "^0.4.0", |
26 | "recoil-persist": "^3.0.0", | 29 | "recoil-persist": "^3.0.0", |
27 | "styled-components": "^5.3.0", | 30 | "styled-components": "^5.3.0", |
... | @@ -31,6 +34,7 @@ | ... | @@ -31,6 +34,7 @@ |
31 | "web-vitals": "^1.0.1" | 34 | "web-vitals": "^1.0.1" |
32 | }, | 35 | }, |
33 | "devDependencies": { | 36 | "devDependencies": { |
37 | + "@types/qrcode": "^1.4.1", | ||
34 | "@types/react-router-dom": "^5.1.8", | 38 | "@types/react-router-dom": "^5.1.8", |
35 | "@types/styled-components": "^5.1.12", | 39 | "@types/styled-components": "^5.1.12", |
36 | "@types/validator": "^13.6.3", | 40 | "@types/validator": "^13.6.3", |
... | @@ -1247,11 +1251,14 @@ | ... | @@ -1247,11 +1251,14 @@ |
1247 | } | 1251 | } |
1248 | }, | 1252 | }, |
1249 | "node_modules/@babel/runtime": { | 1253 | "node_modules/@babel/runtime": { |
1250 | - "version": "7.12.18", | 1254 | + "version": "7.15.4", |
1251 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.18.tgz", | 1255 | + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", |
1252 | - "integrity": "sha512-BogPQ7ciE6SYAUPtlm9tWbgI9+2AgqSam6QivMgXgAT+fKbgppaj4ZX15MHeLC1PVF5sNk70huBu20XxWOs8Cg==", | 1256 | + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", |
1253 | "dependencies": { | 1257 | "dependencies": { |
1254 | "regenerator-runtime": "^0.13.4" | 1258 | "regenerator-runtime": "^0.13.4" |
1259 | + }, | ||
1260 | + "engines": { | ||
1261 | + "node": ">=6.9.0" | ||
1255 | } | 1262 | } |
1256 | }, | 1263 | }, |
1257 | "node_modules/@babel/runtime-corejs3": { | 1264 | "node_modules/@babel/runtime-corejs3": { |
... | @@ -1332,6 +1339,23 @@ | ... | @@ -1332,6 +1339,23 @@ |
1332 | "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", | 1339 | "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", |
1333 | "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" | 1340 | "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" |
1334 | }, | 1341 | }, |
1342 | + "node_modules/@emotion/cache": { | ||
1343 | + "version": "11.4.0", | ||
1344 | + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz", | ||
1345 | + "integrity": "sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==", | ||
1346 | + "dependencies": { | ||
1347 | + "@emotion/memoize": "^0.7.4", | ||
1348 | + "@emotion/sheet": "^1.0.0", | ||
1349 | + "@emotion/utils": "^1.0.0", | ||
1350 | + "@emotion/weak-memoize": "^0.2.5", | ||
1351 | + "stylis": "^4.0.3" | ||
1352 | + } | ||
1353 | + }, | ||
1354 | + "node_modules/@emotion/hash": { | ||
1355 | + "version": "0.8.0", | ||
1356 | + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", | ||
1357 | + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" | ||
1358 | + }, | ||
1335 | "node_modules/@emotion/is-prop-valid": { | 1359 | "node_modules/@emotion/is-prop-valid": { |
1336 | "version": "0.8.8", | 1360 | "version": "0.8.8", |
1337 | "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", | 1361 | "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", |
... | @@ -1345,6 +1369,49 @@ | ... | @@ -1345,6 +1369,49 @@ |
1345 | "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", | 1369 | "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", |
1346 | "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" | 1370 | "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" |
1347 | }, | 1371 | }, |
1372 | + "node_modules/@emotion/react": { | ||
1373 | + "version": "11.4.1", | ||
1374 | + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.4.1.tgz", | ||
1375 | + "integrity": "sha512-pRegcsuGYj4FCdZN6j5vqCALkNytdrKw3TZMekTzNXixRg4wkLsU5QEaBG5LC6l01Vppxlp7FE3aTHpIG5phLg==", | ||
1376 | + "dependencies": { | ||
1377 | + "@babel/runtime": "^7.13.10", | ||
1378 | + "@emotion/cache": "^11.4.0", | ||
1379 | + "@emotion/serialize": "^1.0.2", | ||
1380 | + "@emotion/sheet": "^1.0.2", | ||
1381 | + "@emotion/utils": "^1.0.0", | ||
1382 | + "@emotion/weak-memoize": "^0.2.5", | ||
1383 | + "hoist-non-react-statics": "^3.3.1" | ||
1384 | + }, | ||
1385 | + "peerDependencies": { | ||
1386 | + "@babel/core": "^7.0.0", | ||
1387 | + "react": ">=16.8.0" | ||
1388 | + }, | ||
1389 | + "peerDependenciesMeta": { | ||
1390 | + "@babel/core": { | ||
1391 | + "optional": true | ||
1392 | + }, | ||
1393 | + "@types/react": { | ||
1394 | + "optional": true | ||
1395 | + } | ||
1396 | + } | ||
1397 | + }, | ||
1398 | + "node_modules/@emotion/serialize": { | ||
1399 | + "version": "1.0.2", | ||
1400 | + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", | ||
1401 | + "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", | ||
1402 | + "dependencies": { | ||
1403 | + "@emotion/hash": "^0.8.0", | ||
1404 | + "@emotion/memoize": "^0.7.4", | ||
1405 | + "@emotion/unitless": "^0.7.5", | ||
1406 | + "@emotion/utils": "^1.0.0", | ||
1407 | + "csstype": "^3.0.2" | ||
1408 | + } | ||
1409 | + }, | ||
1410 | + "node_modules/@emotion/sheet": { | ||
1411 | + "version": "1.0.2", | ||
1412 | + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.2.tgz", | ||
1413 | + "integrity": "sha512-QQPB1B70JEVUHuNtzjHftMGv6eC3Y9wqavyarj4x4lg47RACkeSfNo5pxIOKizwS9AEFLohsqoaxGQj4p0vSIw==" | ||
1414 | + }, | ||
1348 | "node_modules/@emotion/stylis": { | 1415 | "node_modules/@emotion/stylis": { |
1349 | "version": "0.8.5", | 1416 | "version": "0.8.5", |
1350 | "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", | 1417 | "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", |
... | @@ -1355,6 +1422,16 @@ | ... | @@ -1355,6 +1422,16 @@ |
1355 | "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", | 1422 | "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", |
1356 | "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" | 1423 | "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" |
1357 | }, | 1424 | }, |
1425 | + "node_modules/@emotion/utils": { | ||
1426 | + "version": "1.0.0", | ||
1427 | + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", | ||
1428 | + "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==" | ||
1429 | + }, | ||
1430 | + "node_modules/@emotion/weak-memoize": { | ||
1431 | + "version": "0.2.5", | ||
1432 | + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", | ||
1433 | + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" | ||
1434 | + }, | ||
1358 | "node_modules/@eslint/eslintrc": { | 1435 | "node_modules/@eslint/eslintrc": { |
1359 | "version": "0.4.3", | 1436 | "version": "0.4.3", |
1360 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", | 1437 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", |
... | @@ -2229,17 +2306,6 @@ | ... | @@ -2229,17 +2306,6 @@ |
2229 | "node": ">=10" | 2306 | "node": ">=10" |
2230 | } | 2307 | } |
2231 | }, | 2308 | }, |
2232 | - "node_modules/@testing-library/dom/node_modules/@babel/runtime": { | ||
2233 | - "version": "7.14.8", | ||
2234 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
2235 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
2236 | - "dependencies": { | ||
2237 | - "regenerator-runtime": "^0.13.4" | ||
2238 | - }, | ||
2239 | - "engines": { | ||
2240 | - "node": ">=6.9.0" | ||
2241 | - } | ||
2242 | - }, | ||
2243 | "node_modules/@testing-library/dom/node_modules/chalk": { | 2309 | "node_modules/@testing-library/dom/node_modules/chalk": { |
2244 | "version": "4.1.2", | 2310 | "version": "4.1.2", |
2245 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", | 2311 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", |
... | @@ -2276,17 +2342,6 @@ | ... | @@ -2276,17 +2342,6 @@ |
2276 | "yarn": ">=1" | 2342 | "yarn": ">=1" |
2277 | } | 2343 | } |
2278 | }, | 2344 | }, |
2279 | - "node_modules/@testing-library/jest-dom/node_modules/@babel/runtime": { | ||
2280 | - "version": "7.14.8", | ||
2281 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
2282 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
2283 | - "dependencies": { | ||
2284 | - "regenerator-runtime": "^0.13.4" | ||
2285 | - }, | ||
2286 | - "engines": { | ||
2287 | - "node": ">=6.9.0" | ||
2288 | - } | ||
2289 | - }, | ||
2290 | "node_modules/@testing-library/jest-dom/node_modules/chalk": { | 2345 | "node_modules/@testing-library/jest-dom/node_modules/chalk": { |
2291 | "version": "3.0.0", | 2346 | "version": "3.0.0", |
2292 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", | 2347 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", |
... | @@ -2315,17 +2370,6 @@ | ... | @@ -2315,17 +2370,6 @@ |
2315 | "react-dom": "*" | 2370 | "react-dom": "*" |
2316 | } | 2371 | } |
2317 | }, | 2372 | }, |
2318 | - "node_modules/@testing-library/react/node_modules/@babel/runtime": { | ||
2319 | - "version": "7.14.8", | ||
2320 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
2321 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
2322 | - "dependencies": { | ||
2323 | - "regenerator-runtime": "^0.13.4" | ||
2324 | - }, | ||
2325 | - "engines": { | ||
2326 | - "node": ">=6.9.0" | ||
2327 | - } | ||
2328 | - }, | ||
2329 | "node_modules/@testing-library/user-event": { | 2373 | "node_modules/@testing-library/user-event": { |
2330 | "version": "12.8.3", | 2374 | "version": "12.8.3", |
2331 | "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz", | 2375 | "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz", |
... | @@ -2341,17 +2385,6 @@ | ... | @@ -2341,17 +2385,6 @@ |
2341 | "@testing-library/dom": ">=7.21.4" | 2385 | "@testing-library/dom": ">=7.21.4" |
2342 | } | 2386 | } |
2343 | }, | 2387 | }, |
2344 | - "node_modules/@testing-library/user-event/node_modules/@babel/runtime": { | ||
2345 | - "version": "7.14.8", | ||
2346 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
2347 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
2348 | - "dependencies": { | ||
2349 | - "regenerator-runtime": "^0.13.4" | ||
2350 | - }, | ||
2351 | - "engines": { | ||
2352 | - "node": ">=6.9.0" | ||
2353 | - } | ||
2354 | - }, | ||
2355 | "node_modules/@types/anymatch": { | 2388 | "node_modules/@types/anymatch": { |
2356 | "version": "1.3.1", | 2389 | "version": "1.3.1", |
2357 | "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", | 2390 | "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", |
... | @@ -2536,6 +2569,15 @@ | ... | @@ -2536,6 +2569,15 @@ |
2536 | "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", | 2569 | "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", |
2537 | "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" | 2570 | "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" |
2538 | }, | 2571 | }, |
2572 | + "node_modules/@types/qrcode": { | ||
2573 | + "version": "1.4.1", | ||
2574 | + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.4.1.tgz", | ||
2575 | + "integrity": "sha512-vxMyr7JM7tYPxu8vUE83NiosWX5DZieCyYeJRoOIg0pAkyofCBzknJ2ycUZkPGDFis2RS8GN/BeJLnRnAPxeCA==", | ||
2576 | + "dev": true, | ||
2577 | + "dependencies": { | ||
2578 | + "@types/node": "*" | ||
2579 | + } | ||
2580 | + }, | ||
2539 | "node_modules/@types/react": { | 2581 | "node_modules/@types/react": { |
2540 | "version": "17.0.16", | 2582 | "version": "17.0.16", |
2541 | "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.16.tgz", | 2583 | "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.16.tgz", |
... | @@ -4813,6 +4855,25 @@ | ... | @@ -4813,6 +4855,25 @@ |
4813 | "isarray": "^1.0.0" | 4855 | "isarray": "^1.0.0" |
4814 | } | 4856 | } |
4815 | }, | 4857 | }, |
4858 | + "node_modules/buffer-alloc": { | ||
4859 | + "version": "1.2.0", | ||
4860 | + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", | ||
4861 | + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", | ||
4862 | + "dependencies": { | ||
4863 | + "buffer-alloc-unsafe": "^1.1.0", | ||
4864 | + "buffer-fill": "^1.0.0" | ||
4865 | + } | ||
4866 | + }, | ||
4867 | + "node_modules/buffer-alloc-unsafe": { | ||
4868 | + "version": "1.1.0", | ||
4869 | + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", | ||
4870 | + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" | ||
4871 | + }, | ||
4872 | + "node_modules/buffer-fill": { | ||
4873 | + "version": "1.0.0", | ||
4874 | + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", | ||
4875 | + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" | ||
4876 | + }, | ||
4816 | "node_modules/buffer-from": { | 4877 | "node_modules/buffer-from": { |
4817 | "version": "1.1.1", | 4878 | "version": "1.1.1", |
4818 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", | 4879 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", |
... | @@ -6483,6 +6544,11 @@ | ... | @@ -6483,6 +6544,11 @@ |
6483 | "randombytes": "^2.0.0" | 6544 | "randombytes": "^2.0.0" |
6484 | } | 6545 | } |
6485 | }, | 6546 | }, |
6547 | + "node_modules/dijkstrajs": { | ||
6548 | + "version": "1.0.2", | ||
6549 | + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", | ||
6550 | + "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" | ||
6551 | + }, | ||
6486 | "node_modules/dir-glob": { | 6552 | "node_modules/dir-glob": { |
6487 | "version": "3.0.1", | 6553 | "version": "3.0.1", |
6488 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", | 6554 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", |
... | @@ -12762,6 +12828,14 @@ | ... | @@ -12762,6 +12828,14 @@ |
12762 | "node": ">=6" | 12828 | "node": ">=6" |
12763 | } | 12829 | } |
12764 | }, | 12830 | }, |
12831 | + "node_modules/pngjs": { | ||
12832 | + "version": "3.4.0", | ||
12833 | + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", | ||
12834 | + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", | ||
12835 | + "engines": { | ||
12836 | + "node": ">=4.0.0" | ||
12837 | + } | ||
12838 | + }, | ||
12765 | "node_modules/pnp-webpack-plugin": { | 12839 | "node_modules/pnp-webpack-plugin": { |
12766 | "version": "1.6.4", | 12840 | "version": "1.6.4", |
12767 | "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", | 12841 | "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", |
... | @@ -14095,6 +14169,193 @@ | ... | @@ -14095,6 +14169,193 @@ |
14095 | "teleport": ">=0.2.0" | 14169 | "teleport": ">=0.2.0" |
14096 | } | 14170 | } |
14097 | }, | 14171 | }, |
14172 | + "node_modules/qrcode": { | ||
14173 | + "version": "1.4.4", | ||
14174 | + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", | ||
14175 | + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", | ||
14176 | + "dependencies": { | ||
14177 | + "buffer": "^5.4.3", | ||
14178 | + "buffer-alloc": "^1.2.0", | ||
14179 | + "buffer-from": "^1.1.1", | ||
14180 | + "dijkstrajs": "^1.0.1", | ||
14181 | + "isarray": "^2.0.1", | ||
14182 | + "pngjs": "^3.3.0", | ||
14183 | + "yargs": "^13.2.4" | ||
14184 | + }, | ||
14185 | + "bin": { | ||
14186 | + "qrcode": "bin/qrcode" | ||
14187 | + }, | ||
14188 | + "engines": { | ||
14189 | + "node": ">=4" | ||
14190 | + } | ||
14191 | + }, | ||
14192 | + "node_modules/qrcode/node_modules/ansi-regex": { | ||
14193 | + "version": "4.1.0", | ||
14194 | + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", | ||
14195 | + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", | ||
14196 | + "engines": { | ||
14197 | + "node": ">=6" | ||
14198 | + } | ||
14199 | + }, | ||
14200 | + "node_modules/qrcode/node_modules/ansi-styles": { | ||
14201 | + "version": "3.2.1", | ||
14202 | + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", | ||
14203 | + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", | ||
14204 | + "dependencies": { | ||
14205 | + "color-convert": "^1.9.0" | ||
14206 | + }, | ||
14207 | + "engines": { | ||
14208 | + "node": ">=4" | ||
14209 | + } | ||
14210 | + }, | ||
14211 | + "node_modules/qrcode/node_modules/buffer": { | ||
14212 | + "version": "5.7.1", | ||
14213 | + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", | ||
14214 | + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", | ||
14215 | + "funding": [ | ||
14216 | + { | ||
14217 | + "type": "github", | ||
14218 | + "url": "https://github.com/sponsors/feross" | ||
14219 | + }, | ||
14220 | + { | ||
14221 | + "type": "patreon", | ||
14222 | + "url": "https://www.patreon.com/feross" | ||
14223 | + }, | ||
14224 | + { | ||
14225 | + "type": "consulting", | ||
14226 | + "url": "https://feross.org/support" | ||
14227 | + } | ||
14228 | + ], | ||
14229 | + "dependencies": { | ||
14230 | + "base64-js": "^1.3.1", | ||
14231 | + "ieee754": "^1.1.13" | ||
14232 | + } | ||
14233 | + }, | ||
14234 | + "node_modules/qrcode/node_modules/cliui": { | ||
14235 | + "version": "5.0.0", | ||
14236 | + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", | ||
14237 | + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", | ||
14238 | + "dependencies": { | ||
14239 | + "string-width": "^3.1.0", | ||
14240 | + "strip-ansi": "^5.2.0", | ||
14241 | + "wrap-ansi": "^5.1.0" | ||
14242 | + } | ||
14243 | + }, | ||
14244 | + "node_modules/qrcode/node_modules/emoji-regex": { | ||
14245 | + "version": "7.0.3", | ||
14246 | + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", | ||
14247 | + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" | ||
14248 | + }, | ||
14249 | + "node_modules/qrcode/node_modules/find-up": { | ||
14250 | + "version": "3.0.0", | ||
14251 | + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", | ||
14252 | + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", | ||
14253 | + "dependencies": { | ||
14254 | + "locate-path": "^3.0.0" | ||
14255 | + }, | ||
14256 | + "engines": { | ||
14257 | + "node": ">=6" | ||
14258 | + } | ||
14259 | + }, | ||
14260 | + "node_modules/qrcode/node_modules/is-fullwidth-code-point": { | ||
14261 | + "version": "2.0.0", | ||
14262 | + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", | ||
14263 | + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", | ||
14264 | + "engines": { | ||
14265 | + "node": ">=4" | ||
14266 | + } | ||
14267 | + }, | ||
14268 | + "node_modules/qrcode/node_modules/isarray": { | ||
14269 | + "version": "2.0.5", | ||
14270 | + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", | ||
14271 | + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" | ||
14272 | + }, | ||
14273 | + "node_modules/qrcode/node_modules/locate-path": { | ||
14274 | + "version": "3.0.0", | ||
14275 | + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", | ||
14276 | + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", | ||
14277 | + "dependencies": { | ||
14278 | + "p-locate": "^3.0.0", | ||
14279 | + "path-exists": "^3.0.0" | ||
14280 | + }, | ||
14281 | + "engines": { | ||
14282 | + "node": ">=6" | ||
14283 | + } | ||
14284 | + }, | ||
14285 | + "node_modules/qrcode/node_modules/p-locate": { | ||
14286 | + "version": "3.0.0", | ||
14287 | + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", | ||
14288 | + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", | ||
14289 | + "dependencies": { | ||
14290 | + "p-limit": "^2.0.0" | ||
14291 | + }, | ||
14292 | + "engines": { | ||
14293 | + "node": ">=6" | ||
14294 | + } | ||
14295 | + }, | ||
14296 | + "node_modules/qrcode/node_modules/string-width": { | ||
14297 | + "version": "3.1.0", | ||
14298 | + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", | ||
14299 | + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", | ||
14300 | + "dependencies": { | ||
14301 | + "emoji-regex": "^7.0.1", | ||
14302 | + "is-fullwidth-code-point": "^2.0.0", | ||
14303 | + "strip-ansi": "^5.1.0" | ||
14304 | + }, | ||
14305 | + "engines": { | ||
14306 | + "node": ">=6" | ||
14307 | + } | ||
14308 | + }, | ||
14309 | + "node_modules/qrcode/node_modules/strip-ansi": { | ||
14310 | + "version": "5.2.0", | ||
14311 | + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", | ||
14312 | + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", | ||
14313 | + "dependencies": { | ||
14314 | + "ansi-regex": "^4.1.0" | ||
14315 | + }, | ||
14316 | + "engines": { | ||
14317 | + "node": ">=6" | ||
14318 | + } | ||
14319 | + }, | ||
14320 | + "node_modules/qrcode/node_modules/wrap-ansi": { | ||
14321 | + "version": "5.1.0", | ||
14322 | + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", | ||
14323 | + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", | ||
14324 | + "dependencies": { | ||
14325 | + "ansi-styles": "^3.2.0", | ||
14326 | + "string-width": "^3.0.0", | ||
14327 | + "strip-ansi": "^5.0.0" | ||
14328 | + }, | ||
14329 | + "engines": { | ||
14330 | + "node": ">=6" | ||
14331 | + } | ||
14332 | + }, | ||
14333 | + "node_modules/qrcode/node_modules/yargs": { | ||
14334 | + "version": "13.3.2", | ||
14335 | + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", | ||
14336 | + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", | ||
14337 | + "dependencies": { | ||
14338 | + "cliui": "^5.0.0", | ||
14339 | + "find-up": "^3.0.0", | ||
14340 | + "get-caller-file": "^2.0.1", | ||
14341 | + "require-directory": "^2.1.1", | ||
14342 | + "require-main-filename": "^2.0.0", | ||
14343 | + "set-blocking": "^2.0.0", | ||
14344 | + "string-width": "^3.0.0", | ||
14345 | + "which-module": "^2.0.0", | ||
14346 | + "y18n": "^4.0.0", | ||
14347 | + "yargs-parser": "^13.1.2" | ||
14348 | + } | ||
14349 | + }, | ||
14350 | + "node_modules/qrcode/node_modules/yargs-parser": { | ||
14351 | + "version": "13.1.2", | ||
14352 | + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", | ||
14353 | + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", | ||
14354 | + "dependencies": { | ||
14355 | + "camelcase": "^5.0.0", | ||
14356 | + "decamelize": "^1.2.0" | ||
14357 | + } | ||
14358 | + }, | ||
14098 | "node_modules/qs": { | 14359 | "node_modules/qs": { |
14099 | "version": "6.7.0", | 14360 | "version": "6.7.0", |
14100 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", | 14361 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", |
... | @@ -14737,6 +14998,18 @@ | ... | @@ -14737,6 +14998,18 @@ |
14737 | "node": ">=0.10.0" | 14998 | "node": ">=0.10.0" |
14738 | } | 14999 | } |
14739 | }, | 15000 | }, |
15001 | + "node_modules/react-spinners": { | ||
15002 | + "version": "0.11.0", | ||
15003 | + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.11.0.tgz", | ||
15004 | + "integrity": "sha512-rDZc0ABWn/M1OryboGsWVmIPg8uYWl0L35jPUhr40+Yg+syVPjeHwvnB7XWaRpaKus3M0cG9BiJA+ZB0dAwWyw==", | ||
15005 | + "dependencies": { | ||
15006 | + "@emotion/react": "^11.1.4" | ||
15007 | + }, | ||
15008 | + "peerDependencies": { | ||
15009 | + "react": "^16.0.0 || ^17.0.0", | ||
15010 | + "react-dom": "^16.0.0 || ^17.0.0" | ||
15011 | + } | ||
15012 | + }, | ||
14740 | "node_modules/read-pkg": { | 15013 | "node_modules/read-pkg": { |
14741 | "version": "5.2.0", | 15014 | "version": "5.2.0", |
14742 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", | 15015 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", |
... | @@ -16776,6 +17049,11 @@ | ... | @@ -16776,6 +17049,11 @@ |
16776 | "node": ">=8" | 17049 | "node": ">=8" |
16777 | } | 17050 | } |
16778 | }, | 17051 | }, |
17052 | + "node_modules/stylis": { | ||
17053 | + "version": "4.0.10", | ||
17054 | + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz", | ||
17055 | + "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==" | ||
17056 | + }, | ||
16779 | "node_modules/supports-color": { | 17057 | "node_modules/supports-color": { |
16780 | "version": "7.2.0", | 17058 | "version": "7.2.0", |
16781 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | 17059 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", |
... | @@ -20048,9 +20326,9 @@ | ... | @@ -20048,9 +20326,9 @@ |
20048 | } | 20326 | } |
20049 | }, | 20327 | }, |
20050 | "@babel/runtime": { | 20328 | "@babel/runtime": { |
20051 | - "version": "7.12.18", | 20329 | + "version": "7.15.4", |
20052 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.18.tgz", | 20330 | + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", |
20053 | - "integrity": "sha512-BogPQ7ciE6SYAUPtlm9tWbgI9+2AgqSam6QivMgXgAT+fKbgppaj4ZX15MHeLC1PVF5sNk70huBu20XxWOs8Cg==", | 20331 | + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", |
20054 | "requires": { | 20332 | "requires": { |
20055 | "regenerator-runtime": "^0.13.4" | 20333 | "regenerator-runtime": "^0.13.4" |
20056 | } | 20334 | } |
... | @@ -20124,6 +20402,23 @@ | ... | @@ -20124,6 +20402,23 @@ |
20124 | "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", | 20402 | "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", |
20125 | "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" | 20403 | "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" |
20126 | }, | 20404 | }, |
20405 | + "@emotion/cache": { | ||
20406 | + "version": "11.4.0", | ||
20407 | + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz", | ||
20408 | + "integrity": "sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==", | ||
20409 | + "requires": { | ||
20410 | + "@emotion/memoize": "^0.7.4", | ||
20411 | + "@emotion/sheet": "^1.0.0", | ||
20412 | + "@emotion/utils": "^1.0.0", | ||
20413 | + "@emotion/weak-memoize": "^0.2.5", | ||
20414 | + "stylis": "^4.0.3" | ||
20415 | + } | ||
20416 | + }, | ||
20417 | + "@emotion/hash": { | ||
20418 | + "version": "0.8.0", | ||
20419 | + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", | ||
20420 | + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" | ||
20421 | + }, | ||
20127 | "@emotion/is-prop-valid": { | 20422 | "@emotion/is-prop-valid": { |
20128 | "version": "0.8.8", | 20423 | "version": "0.8.8", |
20129 | "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", | 20424 | "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", |
... | @@ -20137,6 +20432,37 @@ | ... | @@ -20137,6 +20432,37 @@ |
20137 | "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", | 20432 | "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", |
20138 | "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" | 20433 | "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" |
20139 | }, | 20434 | }, |
20435 | + "@emotion/react": { | ||
20436 | + "version": "11.4.1", | ||
20437 | + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.4.1.tgz", | ||
20438 | + "integrity": "sha512-pRegcsuGYj4FCdZN6j5vqCALkNytdrKw3TZMekTzNXixRg4wkLsU5QEaBG5LC6l01Vppxlp7FE3aTHpIG5phLg==", | ||
20439 | + "requires": { | ||
20440 | + "@babel/runtime": "^7.13.10", | ||
20441 | + "@emotion/cache": "^11.4.0", | ||
20442 | + "@emotion/serialize": "^1.0.2", | ||
20443 | + "@emotion/sheet": "^1.0.2", | ||
20444 | + "@emotion/utils": "^1.0.0", | ||
20445 | + "@emotion/weak-memoize": "^0.2.5", | ||
20446 | + "hoist-non-react-statics": "^3.3.1" | ||
20447 | + } | ||
20448 | + }, | ||
20449 | + "@emotion/serialize": { | ||
20450 | + "version": "1.0.2", | ||
20451 | + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", | ||
20452 | + "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", | ||
20453 | + "requires": { | ||
20454 | + "@emotion/hash": "^0.8.0", | ||
20455 | + "@emotion/memoize": "^0.7.4", | ||
20456 | + "@emotion/unitless": "^0.7.5", | ||
20457 | + "@emotion/utils": "^1.0.0", | ||
20458 | + "csstype": "^3.0.2" | ||
20459 | + } | ||
20460 | + }, | ||
20461 | + "@emotion/sheet": { | ||
20462 | + "version": "1.0.2", | ||
20463 | + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.2.tgz", | ||
20464 | + "integrity": "sha512-QQPB1B70JEVUHuNtzjHftMGv6eC3Y9wqavyarj4x4lg47RACkeSfNo5pxIOKizwS9AEFLohsqoaxGQj4p0vSIw==" | ||
20465 | + }, | ||
20140 | "@emotion/stylis": { | 20466 | "@emotion/stylis": { |
20141 | "version": "0.8.5", | 20467 | "version": "0.8.5", |
20142 | "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", | 20468 | "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", |
... | @@ -20147,6 +20473,16 @@ | ... | @@ -20147,6 +20473,16 @@ |
20147 | "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", | 20473 | "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", |
20148 | "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" | 20474 | "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" |
20149 | }, | 20475 | }, |
20476 | + "@emotion/utils": { | ||
20477 | + "version": "1.0.0", | ||
20478 | + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", | ||
20479 | + "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==" | ||
20480 | + }, | ||
20481 | + "@emotion/weak-memoize": { | ||
20482 | + "version": "0.2.5", | ||
20483 | + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", | ||
20484 | + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" | ||
20485 | + }, | ||
20150 | "@eslint/eslintrc": { | 20486 | "@eslint/eslintrc": { |
20151 | "version": "0.4.3", | 20487 | "version": "0.4.3", |
20152 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", | 20488 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", |
... | @@ -20777,14 +21113,6 @@ | ... | @@ -20777,14 +21113,6 @@ |
20777 | "pretty-format": "^26.6.2" | 21113 | "pretty-format": "^26.6.2" |
20778 | }, | 21114 | }, |
20779 | "dependencies": { | 21115 | "dependencies": { |
20780 | - "@babel/runtime": { | ||
20781 | - "version": "7.14.8", | ||
20782 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
20783 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
20784 | - "requires": { | ||
20785 | - "regenerator-runtime": "^0.13.4" | ||
20786 | - } | ||
20787 | - }, | ||
20788 | "chalk": { | 21116 | "chalk": { |
20789 | "version": "4.1.2", | 21117 | "version": "4.1.2", |
20790 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", | 21118 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", |
... | @@ -20812,14 +21140,6 @@ | ... | @@ -20812,14 +21140,6 @@ |
20812 | "redent": "^3.0.0" | 21140 | "redent": "^3.0.0" |
20813 | }, | 21141 | }, |
20814 | "dependencies": { | 21142 | "dependencies": { |
20815 | - "@babel/runtime": { | ||
20816 | - "version": "7.14.8", | ||
20817 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
20818 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
20819 | - "requires": { | ||
20820 | - "regenerator-runtime": "^0.13.4" | ||
20821 | - } | ||
20822 | - }, | ||
20823 | "chalk": { | 21143 | "chalk": { |
20824 | "version": "3.0.0", | 21144 | "version": "3.0.0", |
20825 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", | 21145 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", |
... | @@ -20838,16 +21158,6 @@ | ... | @@ -20838,16 +21158,6 @@ |
20838 | "requires": { | 21158 | "requires": { |
20839 | "@babel/runtime": "^7.12.5", | 21159 | "@babel/runtime": "^7.12.5", |
20840 | "@testing-library/dom": "^7.28.1" | 21160 | "@testing-library/dom": "^7.28.1" |
20841 | - }, | ||
20842 | - "dependencies": { | ||
20843 | - "@babel/runtime": { | ||
20844 | - "version": "7.14.8", | ||
20845 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
20846 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
20847 | - "requires": { | ||
20848 | - "regenerator-runtime": "^0.13.4" | ||
20849 | - } | ||
20850 | - } | ||
20851 | } | 21161 | } |
20852 | }, | 21162 | }, |
20853 | "@testing-library/user-event": { | 21163 | "@testing-library/user-event": { |
... | @@ -20856,16 +21166,6 @@ | ... | @@ -20856,16 +21166,6 @@ |
20856 | "integrity": "sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==", | 21166 | "integrity": "sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==", |
20857 | "requires": { | 21167 | "requires": { |
20858 | "@babel/runtime": "^7.12.5" | 21168 | "@babel/runtime": "^7.12.5" |
20859 | - }, | ||
20860 | - "dependencies": { | ||
20861 | - "@babel/runtime": { | ||
20862 | - "version": "7.14.8", | ||
20863 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", | ||
20864 | - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", | ||
20865 | - "requires": { | ||
20866 | - "regenerator-runtime": "^0.13.4" | ||
20867 | - } | ||
20868 | - } | ||
20869 | } | 21169 | } |
20870 | }, | 21170 | }, |
20871 | "@types/anymatch": { | 21171 | "@types/anymatch": { |
... | @@ -21056,6 +21356,15 @@ | ... | @@ -21056,6 +21356,15 @@ |
21056 | "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", | 21356 | "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", |
21057 | "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" | 21357 | "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" |
21058 | }, | 21358 | }, |
21359 | + "@types/qrcode": { | ||
21360 | + "version": "1.4.1", | ||
21361 | + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.4.1.tgz", | ||
21362 | + "integrity": "sha512-vxMyr7JM7tYPxu8vUE83NiosWX5DZieCyYeJRoOIg0pAkyofCBzknJ2ycUZkPGDFis2RS8GN/BeJLnRnAPxeCA==", | ||
21363 | + "dev": true, | ||
21364 | + "requires": { | ||
21365 | + "@types/node": "*" | ||
21366 | + } | ||
21367 | + }, | ||
21059 | "@types/react": { | 21368 | "@types/react": { |
21060 | "version": "17.0.16", | 21369 | "version": "17.0.16", |
21061 | "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.16.tgz", | 21370 | "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.16.tgz", |
... | @@ -22917,6 +23226,25 @@ | ... | @@ -22917,6 +23226,25 @@ |
22917 | "isarray": "^1.0.0" | 23226 | "isarray": "^1.0.0" |
22918 | } | 23227 | } |
22919 | }, | 23228 | }, |
23229 | + "buffer-alloc": { | ||
23230 | + "version": "1.2.0", | ||
23231 | + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", | ||
23232 | + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", | ||
23233 | + "requires": { | ||
23234 | + "buffer-alloc-unsafe": "^1.1.0", | ||
23235 | + "buffer-fill": "^1.0.0" | ||
23236 | + } | ||
23237 | + }, | ||
23238 | + "buffer-alloc-unsafe": { | ||
23239 | + "version": "1.1.0", | ||
23240 | + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", | ||
23241 | + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" | ||
23242 | + }, | ||
23243 | + "buffer-fill": { | ||
23244 | + "version": "1.0.0", | ||
23245 | + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", | ||
23246 | + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" | ||
23247 | + }, | ||
22920 | "buffer-from": { | 23248 | "buffer-from": { |
22921 | "version": "1.1.1", | 23249 | "version": "1.1.1", |
22922 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", | 23250 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", |
... | @@ -24256,6 +24584,11 @@ | ... | @@ -24256,6 +24584,11 @@ |
24256 | "randombytes": "^2.0.0" | 24584 | "randombytes": "^2.0.0" |
24257 | } | 24585 | } |
24258 | }, | 24586 | }, |
24587 | + "dijkstrajs": { | ||
24588 | + "version": "1.0.2", | ||
24589 | + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", | ||
24590 | + "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" | ||
24591 | + }, | ||
24259 | "dir-glob": { | 24592 | "dir-glob": { |
24260 | "version": "3.0.1", | 24593 | "version": "3.0.1", |
24261 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", | 24594 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", |
... | @@ -29110,6 +29443,11 @@ | ... | @@ -29110,6 +29443,11 @@ |
29110 | } | 29443 | } |
29111 | } | 29444 | } |
29112 | }, | 29445 | }, |
29446 | + "pngjs": { | ||
29447 | + "version": "3.4.0", | ||
29448 | + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", | ||
29449 | + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" | ||
29450 | + }, | ||
29113 | "pnp-webpack-plugin": { | 29451 | "pnp-webpack-plugin": { |
29114 | "version": "1.6.4", | 29452 | "version": "1.6.4", |
29115 | "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", | 29453 | "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", |
... | @@ -30180,6 +30518,148 @@ | ... | @@ -30180,6 +30518,148 @@ |
30180 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", | 30518 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", |
30181 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" | 30519 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" |
30182 | }, | 30520 | }, |
30521 | + "qrcode": { | ||
30522 | + "version": "1.4.4", | ||
30523 | + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", | ||
30524 | + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", | ||
30525 | + "requires": { | ||
30526 | + "buffer": "^5.4.3", | ||
30527 | + "buffer-alloc": "^1.2.0", | ||
30528 | + "buffer-from": "^1.1.1", | ||
30529 | + "dijkstrajs": "^1.0.1", | ||
30530 | + "isarray": "^2.0.1", | ||
30531 | + "pngjs": "^3.3.0", | ||
30532 | + "yargs": "^13.2.4" | ||
30533 | + }, | ||
30534 | + "dependencies": { | ||
30535 | + "ansi-regex": { | ||
30536 | + "version": "4.1.0", | ||
30537 | + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", | ||
30538 | + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" | ||
30539 | + }, | ||
30540 | + "ansi-styles": { | ||
30541 | + "version": "3.2.1", | ||
30542 | + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", | ||
30543 | + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", | ||
30544 | + "requires": { | ||
30545 | + "color-convert": "^1.9.0" | ||
30546 | + } | ||
30547 | + }, | ||
30548 | + "buffer": { | ||
30549 | + "version": "5.7.1", | ||
30550 | + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", | ||
30551 | + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", | ||
30552 | + "requires": { | ||
30553 | + "base64-js": "^1.3.1", | ||
30554 | + "ieee754": "^1.1.13" | ||
30555 | + } | ||
30556 | + }, | ||
30557 | + "cliui": { | ||
30558 | + "version": "5.0.0", | ||
30559 | + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", | ||
30560 | + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", | ||
30561 | + "requires": { | ||
30562 | + "string-width": "^3.1.0", | ||
30563 | + "strip-ansi": "^5.2.0", | ||
30564 | + "wrap-ansi": "^5.1.0" | ||
30565 | + } | ||
30566 | + }, | ||
30567 | + "emoji-regex": { | ||
30568 | + "version": "7.0.3", | ||
30569 | + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", | ||
30570 | + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" | ||
30571 | + }, | ||
30572 | + "find-up": { | ||
30573 | + "version": "3.0.0", | ||
30574 | + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", | ||
30575 | + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", | ||
30576 | + "requires": { | ||
30577 | + "locate-path": "^3.0.0" | ||
30578 | + } | ||
30579 | + }, | ||
30580 | + "is-fullwidth-code-point": { | ||
30581 | + "version": "2.0.0", | ||
30582 | + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", | ||
30583 | + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" | ||
30584 | + }, | ||
30585 | + "isarray": { | ||
30586 | + "version": "2.0.5", | ||
30587 | + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", | ||
30588 | + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" | ||
30589 | + }, | ||
30590 | + "locate-path": { | ||
30591 | + "version": "3.0.0", | ||
30592 | + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", | ||
30593 | + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", | ||
30594 | + "requires": { | ||
30595 | + "p-locate": "^3.0.0", | ||
30596 | + "path-exists": "^3.0.0" | ||
30597 | + } | ||
30598 | + }, | ||
30599 | + "p-locate": { | ||
30600 | + "version": "3.0.0", | ||
30601 | + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", | ||
30602 | + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", | ||
30603 | + "requires": { | ||
30604 | + "p-limit": "^2.0.0" | ||
30605 | + } | ||
30606 | + }, | ||
30607 | + "string-width": { | ||
30608 | + "version": "3.1.0", | ||
30609 | + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", | ||
30610 | + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", | ||
30611 | + "requires": { | ||
30612 | + "emoji-regex": "^7.0.1", | ||
30613 | + "is-fullwidth-code-point": "^2.0.0", | ||
30614 | + "strip-ansi": "^5.1.0" | ||
30615 | + } | ||
30616 | + }, | ||
30617 | + "strip-ansi": { | ||
30618 | + "version": "5.2.0", | ||
30619 | + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", | ||
30620 | + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", | ||
30621 | + "requires": { | ||
30622 | + "ansi-regex": "^4.1.0" | ||
30623 | + } | ||
30624 | + }, | ||
30625 | + "wrap-ansi": { | ||
30626 | + "version": "5.1.0", | ||
30627 | + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", | ||
30628 | + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", | ||
30629 | + "requires": { | ||
30630 | + "ansi-styles": "^3.2.0", | ||
30631 | + "string-width": "^3.0.0", | ||
30632 | + "strip-ansi": "^5.0.0" | ||
30633 | + } | ||
30634 | + }, | ||
30635 | + "yargs": { | ||
30636 | + "version": "13.3.2", | ||
30637 | + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", | ||
30638 | + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", | ||
30639 | + "requires": { | ||
30640 | + "cliui": "^5.0.0", | ||
30641 | + "find-up": "^3.0.0", | ||
30642 | + "get-caller-file": "^2.0.1", | ||
30643 | + "require-directory": "^2.1.1", | ||
30644 | + "require-main-filename": "^2.0.0", | ||
30645 | + "set-blocking": "^2.0.0", | ||
30646 | + "string-width": "^3.0.0", | ||
30647 | + "which-module": "^2.0.0", | ||
30648 | + "y18n": "^4.0.0", | ||
30649 | + "yargs-parser": "^13.1.2" | ||
30650 | + } | ||
30651 | + }, | ||
30652 | + "yargs-parser": { | ||
30653 | + "version": "13.1.2", | ||
30654 | + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", | ||
30655 | + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", | ||
30656 | + "requires": { | ||
30657 | + "camelcase": "^5.0.0", | ||
30658 | + "decamelize": "^1.2.0" | ||
30659 | + } | ||
30660 | + } | ||
30661 | + } | ||
30662 | + }, | ||
30183 | "qs": { | 30663 | "qs": { |
30184 | "version": "6.7.0", | 30664 | "version": "6.7.0", |
30185 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", | 30665 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", |
... | @@ -30669,6 +31149,14 @@ | ... | @@ -30669,6 +31149,14 @@ |
30669 | } | 31149 | } |
30670 | } | 31150 | } |
30671 | }, | 31151 | }, |
31152 | + "react-spinners": { | ||
31153 | + "version": "0.11.0", | ||
31154 | + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.11.0.tgz", | ||
31155 | + "integrity": "sha512-rDZc0ABWn/M1OryboGsWVmIPg8uYWl0L35jPUhr40+Yg+syVPjeHwvnB7XWaRpaKus3M0cG9BiJA+ZB0dAwWyw==", | ||
31156 | + "requires": { | ||
31157 | + "@emotion/react": "^11.1.4" | ||
31158 | + } | ||
31159 | + }, | ||
30672 | "read-pkg": { | 31160 | "read-pkg": { |
30673 | "version": "5.2.0", | 31161 | "version": "5.2.0", |
30674 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", | 31162 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", |
... | @@ -32283,6 +32771,11 @@ | ... | @@ -32283,6 +32771,11 @@ |
32283 | } | 32771 | } |
32284 | } | 32772 | } |
32285 | }, | 32773 | }, |
32774 | + "stylis": { | ||
32775 | + "version": "4.0.10", | ||
32776 | + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz", | ||
32777 | + "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==" | ||
32778 | + }, | ||
32286 | "supports-color": { | 32779 | "supports-color": { |
32287 | "version": "7.2.0", | 32780 | "version": "7.2.0", |
32288 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | 32781 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | ... | ... |
... | @@ -14,6 +14,7 @@ | ... | @@ -14,6 +14,7 @@ |
14 | "highcharts": "^9.2.0", | 14 | "highcharts": "^9.2.0", |
15 | "highcharts-react-official": "^3.0.0", | 15 | "highcharts-react-official": "^3.0.0", |
16 | "moment": "^2.29.1", | 16 | "moment": "^2.29.1", |
17 | + "qrcode": "^1.4.4", | ||
17 | "react": "^17.0.2", | 18 | "react": "^17.0.2", |
18 | "react-dom": "^17.0.2", | 19 | "react-dom": "^17.0.2", |
19 | "react-router-dom": "^5.2.0", | 20 | "react-router-dom": "^5.2.0", |
... | @@ -52,6 +53,7 @@ | ... | @@ -52,6 +53,7 @@ |
52 | ] | 53 | ] |
53 | }, | 54 | }, |
54 | "devDependencies": { | 55 | "devDependencies": { |
56 | + "@types/qrcode": "^1.4.1", | ||
55 | "@types/react-router-dom": "^5.1.8", | 57 | "@types/react-router-dom": "^5.1.8", |
56 | "@types/styled-components": "^5.1.12", | 58 | "@types/styled-components": "^5.1.12", |
57 | "@types/validator": "^13.6.3", | 59 | "@types/validator": "^13.6.3", | ... | ... |
web/public/static/img/apiLicense.png
0 → 100644
30.3 KB
web/public/static/img/next.png
0 → 100644
404 Bytes
web/public/static/img/prev.png
0 → 100644
392 Bytes
1 | import { client } from "./client"; | 1 | import { client } from "./client"; |
2 | 2 | ||
3 | export default { | 3 | export default { |
4 | - register : (Data : any) => { | 4 | + register : (Data : FormData) => { |
5 | return client.post('/auth/register', Data); | 5 | return client.post('/auth/register', Data); |
6 | }, | 6 | }, |
7 | 7 | ||
8 | + searchHospital : (hospitalNm : string, page : number) => { | ||
9 | + return client.get('/auth/hospital', { | ||
10 | + params : { | ||
11 | + hospitalNm, | ||
12 | + page, | ||
13 | + }, | ||
14 | + }); | ||
15 | + }, | ||
16 | + | ||
8 | registerDoctor : (Data : any) => { | 17 | registerDoctor : (Data : any) => { |
9 | return client.post('/auth/register/doctor', Data); | 18 | return client.post('/auth/register/doctor', Data); |
10 | }, | 19 | }, | ... | ... |
... | @@ -44,8 +44,8 @@ export default { | ... | @@ -44,8 +44,8 @@ export default { |
44 | }, | 44 | }, |
45 | }); | 45 | }); |
46 | }, | 46 | }, |
47 | - searchPatientById : (token : RecoilState<any>, patientId : string) => { | 47 | + searchPatientByContact : (token : RecoilState<any>, contact : string) => { |
48 | - return client.get(`/doctor/patient/search/${patientId}`, { | 48 | + return client.get(`/doctor/patient/search/${contact}`, { |
49 | headers : { | 49 | headers : { |
50 | Authorization : token, | 50 | Authorization : token, |
51 | }, | 51 | }, |
... | @@ -65,4 +65,11 @@ export default { | ... | @@ -65,4 +65,11 @@ export default { |
65 | }, | 65 | }, |
66 | }); | 66 | }); |
67 | }, | 67 | }, |
68 | + prescribeMedicine : (token : RecoilState<any>, Data : any) => { | ||
69 | + return client.post('/doctor/prescribe', Data, { | ||
70 | + headers : { | ||
71 | + Authorization : token, | ||
72 | + }, | ||
73 | + }); | ||
74 | + }, | ||
68 | }; | 75 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -9,6 +9,13 @@ export default { | ... | @@ -9,6 +9,13 @@ export default { |
9 | }, | 9 | }, |
10 | }); | 10 | }); |
11 | }, | 11 | }, |
12 | + getDoctorSecReqList : (token : RecoilState<any>) => { | ||
13 | + return client.get('/manage/doctor/secession', { | ||
14 | + headers : { | ||
15 | + Authorization : token, | ||
16 | + }, | ||
17 | + }); | ||
18 | + }, | ||
12 | getDoctorRegReqDetail : (token : RecoilState<any>, doctorId : string) => { | 19 | getDoctorRegReqDetail : (token : RecoilState<any>, doctorId : string) => { |
13 | return client.get(`/manage/doctor/${doctorId}`, { | 20 | return client.get(`/manage/doctor/${doctorId}`, { |
14 | headers : { | 21 | headers : { |
... | @@ -17,14 +24,21 @@ export default { | ... | @@ -17,14 +24,21 @@ export default { |
17 | }); | 24 | }); |
18 | }, | 25 | }, |
19 | acceptDoctorRegReq : (token : RecoilState<any>, Data : any) => { | 26 | acceptDoctorRegReq : (token : RecoilState<any>, Data : any) => { |
20 | - return client.post('/manage/doctor/accept', Data, { | 27 | + return client.patch('/manage/doctor/accept', Data, { |
21 | headers : { | 28 | headers : { |
22 | Authorization : token, | 29 | Authorization : token, |
23 | }, | 30 | }, |
24 | }); | 31 | }); |
25 | }, | 32 | }, |
26 | rejectDoctorRegReq : (token : RecoilState<any>, Data : any) => { | 33 | rejectDoctorRegReq : (token : RecoilState<any>, Data : any) => { |
27 | - return client.post('/manage/doctor/reject', Data, { | 34 | + return client.patch('/manage/doctor/reject', Data, { |
35 | + headers : { | ||
36 | + Authorization : token, | ||
37 | + }, | ||
38 | + }); | ||
39 | + }, | ||
40 | + acceptDoctorSecReq : (token : RecoilState<any>, Data : any) => { | ||
41 | + return client.patch('/manage/doctor/secession', Data, { | ||
28 | headers : { | 42 | headers : { |
29 | Authorization : token, | 43 | Authorization : token, |
30 | }, | 44 | }, | ... | ... |
web/src/components/Footer/FooterStyled.tsx
0 → 100644
1 | +import styled from 'styled-components'; | ||
2 | + | ||
3 | +export const Container = styled.div ` | ||
4 | + width : 100%; | ||
5 | + position : relative; | ||
6 | + | ||
7 | + margin : 5px 0; | ||
8 | + padding : 20px 5px; | ||
9 | + | ||
10 | + border-top : 1px solid #ddd; | ||
11 | + | ||
12 | + display : flex; | ||
13 | + flex-direction : column; | ||
14 | +`; | ||
15 | + | ||
16 | +export const TermsWrapper = styled.div ` | ||
17 | + width : 100%: | ||
18 | + | ||
19 | + display : flex; | ||
20 | + flex-direction : row; | ||
21 | + | ||
22 | + padding : 0 0 20px 0; | ||
23 | + margin : 0 0 10px 0; | ||
24 | + | ||
25 | + background-color : transparent; | ||
26 | + border : none; | ||
27 | + | ||
28 | + border-bottom : 1px solid #ddd; | ||
29 | +`; | ||
30 | + | ||
31 | +export const EachTerms = styled.button ` | ||
32 | + color : #000; | ||
33 | + background-color : transparent; | ||
34 | + | ||
35 | + margin : 0 10px 0 0; | ||
36 | + padding : 0 0 5px 0; | ||
37 | + | ||
38 | + cursor : pointer; | ||
39 | + | ||
40 | + font-size : 13px; | ||
41 | + | ||
42 | + font-weight : 400; | ||
43 | + border : none; | ||
44 | + border-bottom : 1px solid; | ||
45 | + | ||
46 | + transition : .25s all; | ||
47 | + | ||
48 | + &:hover { | ||
49 | + color : #337DFF; | ||
50 | + opacity : .5; | ||
51 | + } | ||
52 | +`; | ||
53 | + | ||
54 | +export const InfoWrapper = styled.div ` | ||
55 | + display : flex; | ||
56 | + flex-direction : row; | ||
57 | + | ||
58 | + border : none; | ||
59 | + background-color : transparent; | ||
60 | +`; | ||
61 | + | ||
62 | +export const LicenseWrapper = styled.div ` | ||
63 | + flex : 1; | ||
64 | + display : flex; | ||
65 | + flex-direction : column; | ||
66 | + | ||
67 | + border : none; | ||
68 | + background-color : transparent; | ||
69 | + justify-content : center; | ||
70 | + align-items : flex-start; | ||
71 | + | ||
72 | +`; | ||
73 | + | ||
74 | +export const LicenseExplain = styled.div ` | ||
75 | + color : #a0a0a0; | ||
76 | + font-size : 14px; | ||
77 | + | ||
78 | + font-weight : 400; | ||
79 | + padding : 0 5px; | ||
80 | + | ||
81 | + border : none; | ||
82 | +`; | ||
83 | + | ||
84 | +export const LicenseImg = styled.img ` | ||
85 | + height : 60px; | ||
86 | + width : 150px; | ||
87 | +`; | ||
88 | + | ||
89 | +export const ServiceInfoWrapper = styled.div ` | ||
90 | + flex : 3; | ||
91 | + display : flex; | ||
92 | + flex-direction : column; | ||
93 | + justify-content : center; | ||
94 | + align-items : center; | ||
95 | + | ||
96 | + border : none; | ||
97 | + background-color : transparent; | ||
98 | +`; | ||
99 | + | ||
100 | +export const ServiceInfoEach = styled.div ` | ||
101 | + color : #d0d0d0; | ||
102 | + font-size : 13px; | ||
103 | + | ||
104 | + font-weight : 400 | ||
105 | +`; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/Footer/index.tsx
0 → 100644
1 | +import React from 'react'; | ||
2 | +import { RouteComponentProps } from 'react-router'; | ||
3 | + | ||
4 | +import * as Alert from '../../util/alertMessage'; | ||
5 | +import * as styled from './FooterStyled'; | ||
6 | + | ||
7 | + | ||
8 | +const ApiLicense = '/static/img/apiLicense.png'; | ||
9 | + | ||
10 | +// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
11 | +interface FooterProps extends RouteComponentProps {} | ||
12 | + | ||
13 | + | ||
14 | +const Footer = (props : FooterProps) => { | ||
15 | + | ||
16 | + | ||
17 | + const onGoTerm = () => { | ||
18 | + Alert.onWarning('준비중입니다.', () => null); | ||
19 | + }; | ||
20 | + | ||
21 | + const onGoPrivateLicense = () => { | ||
22 | + Alert.onWarning('준비중입니다.', () => null); | ||
23 | + }; | ||
24 | + | ||
25 | + const onGoServiceCenter = () => { | ||
26 | + Alert.onWarning('준비중입니다.', () => null); | ||
27 | + }; | ||
28 | + | ||
29 | + return ( | ||
30 | + <styled.Container> | ||
31 | + <styled.TermsWrapper> | ||
32 | + <styled.EachTerms | ||
33 | + onClick = {onGoTerm} | ||
34 | + > | ||
35 | + 이용약관 | ||
36 | + </styled.EachTerms> | ||
37 | + <styled.EachTerms | ||
38 | + onClick = {onGoPrivateLicense} | ||
39 | + > | ||
40 | + 개인정보처리방침 | ||
41 | + </styled.EachTerms> | ||
42 | + <styled.EachTerms | ||
43 | + onClick = {onGoServiceCenter} | ||
44 | + > | ||
45 | + 고객센터 | ||
46 | + </styled.EachTerms> | ||
47 | + </styled.TermsWrapper> | ||
48 | + <styled.InfoWrapper> | ||
49 | + <styled.LicenseWrapper> | ||
50 | + <styled.LicenseExplain>저작권</styled.LicenseExplain> | ||
51 | + <styled.LicenseImg src = {ApiLicense}/> | ||
52 | + </styled.LicenseWrapper> | ||
53 | + <styled.ServiceInfoWrapper> | ||
54 | + <styled.ServiceInfoEach>서비스명 : Smart Medicine Box (SMB)</styled.ServiceInfoEach> | ||
55 | + <styled.ServiceInfoEach>서비스제공 : IoT 약병 제작회</styled.ServiceInfoEach> | ||
56 | + <styled.ServiceInfoEach>담당자 : 박권수</styled.ServiceInfoEach> | ||
57 | + <styled.ServiceInfoEach>주소 : OOO도 OOO시 OOO구 OOO OOO OOO</styled.ServiceInfoEach> | ||
58 | + <styled.ServiceInfoEach>연락처 : 010 - 0000 - 0000</styled.ServiceInfoEach> | ||
59 | + </styled.ServiceInfoWrapper> | ||
60 | + <styled.LicenseWrapper/> | ||
61 | + </styled.InfoWrapper> | ||
62 | + </styled.Container> | ||
63 | + ) | ||
64 | +}; | ||
65 | + | ||
66 | +export default Footer; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -10,7 +10,6 @@ import * as styled from './HeaderStyled'; | ... | @@ -10,7 +10,6 @@ import * as styled from './HeaderStyled'; |
10 | import { authApi } from '../../api'; | 10 | import { authApi } from '../../api'; |
11 | 11 | ||
12 | const headerImg = '/static/img/pharmacy.png'; | 12 | const headerImg = '/static/img/pharmacy.png'; |
13 | -const backButtonWhite = '/static/img/backButtonWhite.png'; | ||
14 | const backButtonBlue = '/static/img/backButtonBlue.png'; | 13 | const backButtonBlue = '/static/img/backButtonBlue.png'; |
15 | const logout = '/static/img/logout.png'; | 14 | const logout = '/static/img/logout.png'; |
16 | 15 | ... | ... |
web/src/components/Modal/ModalStyled.tsx
0 → 100644
1 | +import styled, { keyframes } from 'styled-components'; | ||
2 | + | ||
3 | + | ||
4 | +const ModalOn = keyframes ` | ||
5 | + 0% { | ||
6 | + background-color : rgba(52, 52, 52, .0); | ||
7 | + } | ||
8 | + 20% { | ||
9 | + background-color : rgba(52, 52, 52, .2); | ||
10 | + } | ||
11 | + 40% { | ||
12 | + background-color : rgba(52, 52, 52, .4); | ||
13 | + } | ||
14 | + 60% { | ||
15 | + background-color : rgba(52, 52, 52, .5); | ||
16 | + } | ||
17 | + 80% { | ||
18 | + background-color : rgba(52, 52, 52, .6); | ||
19 | + } | ||
20 | + 100% { | ||
21 | + background-color : rgba(52, 52, 52, .7); | ||
22 | + } | ||
23 | + | ||
24 | +`; | ||
25 | + | ||
26 | + | ||
27 | +export const ModalContainer = styled.div ` | ||
28 | + height : 100%; | ||
29 | + width : 100%; | ||
30 | + z-index : 99; | ||
31 | + position : absolute; | ||
32 | + | ||
33 | + display : flex; | ||
34 | + flex-direction : column; | ||
35 | + | ||
36 | + animation : ${ModalOn} .5s; | ||
37 | + | ||
38 | + background-color : rgba(52, 52, 52, .7); | ||
39 | + | ||
40 | +`; | ||
41 | + | ||
42 | +export const ModalClsButtonWrapper = styled.div ` | ||
43 | + flex : 1; | ||
44 | + | ||
45 | + display : flex; | ||
46 | + | ||
47 | + justify-content : flex-end; | ||
48 | + align-items : center; | ||
49 | + padding : 0 20px; | ||
50 | + | ||
51 | + border : none; | ||
52 | + background-color : transprent; | ||
53 | +`; | ||
54 | + | ||
55 | +export const ModalClsButton = styled.button ` | ||
56 | + border : none; | ||
57 | + background-color : transparent; | ||
58 | + | ||
59 | + cursor : pointer; | ||
60 | + | ||
61 | + color : #fff; | ||
62 | + | ||
63 | + display : flex; | ||
64 | + flex-direction : row; | ||
65 | + | ||
66 | + justify-content : center; | ||
67 | + align-items : center; | ||
68 | + | ||
69 | + transition : .25s all; | ||
70 | + &:hover { | ||
71 | + opacity : .5; | ||
72 | + } | ||
73 | +`; | ||
74 | + | ||
75 | +export const ModalClsButtonImg = styled.img ` | ||
76 | + height : 20px; | ||
77 | + width : 20px; | ||
78 | + | ||
79 | + margin : 0 10px 0 0; | ||
80 | +`; | ||
81 | + | ||
82 | +export const ModalClsButtonText = styled.div ` | ||
83 | + font-size : 18px; | ||
84 | + font-weight : 700; | ||
85 | +`; | ||
86 | + | ||
87 | +export const ModalContentWrapper = styled.div ` | ||
88 | + flex : 8; | ||
89 | + | ||
90 | + display : flex; | ||
91 | + flex-direction : column; | ||
92 | + | ||
93 | + justify-content : center; | ||
94 | + align-items : center; | ||
95 | + | ||
96 | + border : none; | ||
97 | +`; | ||
98 | + | ||
99 | +export const ModalContent = styled.div ` | ||
100 | + width : 600px; | ||
101 | + height : 400px; | ||
102 | + | ||
103 | + background-color : #fff; | ||
104 | + border : 1.2px solid #337DFF; | ||
105 | + border-radius : 5px; | ||
106 | + | ||
107 | + display : flex; | ||
108 | + flex-direction : column; | ||
109 | + | ||
110 | + justify-content : center; | ||
111 | + align-items : center; | ||
112 | +`; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/Modal/index.tsx
0 → 100644
1 | +import React from 'react'; | ||
2 | + | ||
3 | +import * as styled from './ModalStyled'; | ||
4 | + | ||
5 | +const closeButton = '/static/img/close.png'; | ||
6 | + | ||
7 | + | ||
8 | +interface ModalProps { | ||
9 | + children : JSX.Element, | ||
10 | + onModalClose : () => void; | ||
11 | +} | ||
12 | + | ||
13 | +const ModalContainer = (props : ModalProps) => { | ||
14 | + return ( | ||
15 | + <styled.ModalContainer> | ||
16 | + <styled.ModalClsButtonWrapper> | ||
17 | + <styled.ModalClsButton | ||
18 | + onClick = {props.onModalClose} | ||
19 | + > | ||
20 | + <styled.ModalClsButtonImg src = {closeButton}/> | ||
21 | + <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | ||
22 | + </styled.ModalClsButton> | ||
23 | + </styled.ModalClsButtonWrapper> | ||
24 | + <styled.ModalContentWrapper> | ||
25 | + <styled.ModalContent> | ||
26 | + {props.children} | ||
27 | + </styled.ModalContent> | ||
28 | + </styled.ModalContentWrapper> | ||
29 | + <styled.ModalClsButtonWrapper /> | ||
30 | + </styled.ModalContainer> | ||
31 | + ); | ||
32 | +}; | ||
33 | + | ||
34 | +export default ModalContainer; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | import moment from 'moment'; | 1 | import moment from 'moment'; |
2 | 2 | ||
3 | -export const make = (chartData : any[], numberOfRow : number) => { | 3 | +export const make = (takeMedicineHist : any[], numberOfRow : number) => { |
4 | const now = new Date(); | 4 | const now = new Date(); |
5 | const result : any = {}; | 5 | const result : any = {}; |
6 | - new Array(numberOfRow).fill(null).forEach((item : any, index : number) => { | 6 | + new Array(numberOfRow).fill(null).forEach(() => { |
7 | const key = moment(now).format('MM/DD'); | 7 | const key = moment(now).format('MM/DD'); |
8 | result[key] = 0; | 8 | result[key] = 0; |
9 | now.setDate(now.getDate() - 1); | 9 | now.setDate(now.getDate() - 1); |
10 | - }) | 10 | + }); |
11 | 11 | ||
12 | - chartData.forEach((data : any) => { | 12 | + takeMedicineHist.forEach((data : any) => { |
13 | const key : string = moment(data.takeDate).format('MM/DD'); | 13 | const key : string = moment(data.takeDate).format('MM/DD'); |
14 | - result[key] = result[key] + 1; | 14 | + !isNaN(result[key]) ? result[key] = result[key] + data.dosage : null; |
15 | }); | 15 | }); |
16 | 16 | ||
17 | const categories : any = []; | 17 | const categories : any = []; |
... | @@ -28,5 +28,5 @@ export const make = (chartData : any[], numberOfRow : number) => { | ... | @@ -28,5 +28,5 @@ export const make = (chartData : any[], numberOfRow : number) => { |
28 | return { | 28 | return { |
29 | categories, | 29 | categories, |
30 | data, | 30 | data, |
31 | - } | 31 | + }; |
32 | }; | 32 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -9,6 +9,12 @@ export const token = atom({ | ... | @@ -9,6 +9,12 @@ export const token = atom({ |
9 | effects_UNSTABLE : [persistAtom], | 9 | effects_UNSTABLE : [persistAtom], |
10 | }); | 10 | }); |
11 | 11 | ||
12 | +export const userId = atom({ | ||
13 | + key : 'userId', | ||
14 | + default : null, | ||
15 | + effects_UNSTABLE : [persistAtom], | ||
16 | +}); | ||
17 | + | ||
12 | export const userTypeCd = atom({ | 18 | export const userTypeCd = atom({ |
13 | key : 'userTypeCd', | 19 | key : 'userTypeCd', |
14 | default : 'NORMAL', | 20 | default : 'NORMAL', | ... | ... |
... | @@ -3,6 +3,7 @@ import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'; | ... | @@ -3,6 +3,7 @@ import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'; |
3 | 3 | ||
4 | import Error from '../components/error'; | 4 | import Error from '../components/error'; |
5 | import Loading from '../components/Loading'; | 5 | import Loading from '../components/Loading'; |
6 | +import Footer from '../components/Footer'; | ||
6 | import { LoginContainer } from "./login"; | 7 | import { LoginContainer } from "./login"; |
7 | import { RegisterContainer } from './register'; | 8 | import { RegisterContainer } from './register'; |
8 | import { MainContainer } from "./main"; | 9 | import { MainContainer } from "./main"; | ... | ... |
... | @@ -9,6 +9,7 @@ import * as Alert from '../../util/alertMessage'; | ... | @@ -9,6 +9,7 @@ import * as Alert from '../../util/alertMessage'; |
9 | import moment from 'moment'; | 9 | import moment from 'moment'; |
10 | 10 | ||
11 | import Header from '../../components/Header'; | 11 | import Header from '../../components/Header'; |
12 | +import Footer from '../../components/Footer'; | ||
12 | import BottleInfoPresenter from './BottleInfoPresenter'; | 13 | import BottleInfoPresenter from './BottleInfoPresenter'; |
13 | 14 | ||
14 | import { doctorApi } from '../../api'; | 15 | import { doctorApi } from '../../api'; |
... | @@ -35,6 +36,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -35,6 +36,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
35 | takeMedicineHist : [], | 36 | takeMedicineHist : [], |
36 | }); | 37 | }); |
37 | 38 | ||
39 | + //차트에 표시되는 행의 개수 | ||
38 | const numberOfChartItem = 7; | 40 | const numberOfChartItem = 7; |
39 | const [chartOption, setChartOption] = useState<any>({ | 41 | const [chartOption, setChartOption] = useState<any>({ |
40 | chart : { | 42 | chart : { |
... | @@ -51,19 +53,22 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -51,19 +53,22 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
51 | categories : [], | 53 | categories : [], |
52 | }, | 54 | }, |
53 | series : [{ | 55 | series : [{ |
54 | - name : '약 복용 횟수', | 56 | + name : '약 복용 회분', |
55 | color : '#337DFF', | 57 | color : '#337DFF', |
56 | data : [], | 58 | data : [], |
57 | }], | 59 | }], |
58 | }); | 60 | }); |
61 | + const [takeMedicineHist, setTakeMedicineHist] = useState<any[]>([]); | ||
59 | 62 | ||
60 | const [feedback, setFeedback] = useState<string>(''); | 63 | const [feedback, setFeedback] = useState<string>(''); |
61 | const [fdbType, setFdbType] = useState<string>('RECOMMEND'); | 64 | const [fdbType, setFdbType] = useState<string>('RECOMMEND'); |
62 | 65 | ||
63 | const [medicineInfoModal, setMedicineInfoModal] = useState<boolean>(false); | 66 | const [medicineInfoModal, setMedicineInfoModal] = useState<boolean>(false); |
67 | + const [modalType, setModalType] = useState<string>('hist'); //hist , info | ||
64 | 68 | ||
65 | 69 | ||
66 | const fetchData = async () => { | 70 | const fetchData = async () => { |
71 | + setModalType('hist'); | ||
67 | setFeedback(''); | 72 | setFeedback(''); |
68 | setFdbType('RECOMMEND'); | 73 | setFdbType('RECOMMEND'); |
69 | setMedicineInfoModal(false); | 74 | setMedicineInfoModal(false); |
... | @@ -71,6 +76,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -71,6 +76,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
71 | try { | 76 | try { |
72 | const result = await doctorApi.getPatientBottleDetail(token, bottleId); | 77 | const result = await doctorApi.getPatientBottleDetail(token, bottleId); |
73 | if (result.statusText === 'OK') { | 78 | if (result.statusText === 'OK') { |
79 | + setTakeMedicineHist(result.data.takeMedicineHist.map((takeMedicine : any) => { | ||
80 | + return ({ | ||
81 | + ...takeMedicine, | ||
82 | + takeDate : moment(takeMedicine.takeDate).format('YYYY년 MM월 DD일 hh시 mm분'), | ||
83 | + }); | ||
84 | + })); | ||
74 | const { categories, data } = makeChart.make(result.data.takeMedicineHist, numberOfChartItem); | 85 | const { categories, data } = makeChart.make(result.data.takeMedicineHist, numberOfChartItem); |
75 | setBottleInfo({ | 86 | setBottleInfo({ |
76 | ...result.data, | 87 | ...result.data, |
... | @@ -97,9 +108,8 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -97,9 +108,8 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
97 | Alert.onError('접근 권한이 없습니다.', () => props.history.push('/')); | 108 | Alert.onError('접근 권한이 없습니다.', () => props.history.push('/')); |
98 | } | 109 | } |
99 | } catch(e : any) { | 110 | } catch(e : any) { |
100 | - Alert.onError(e.response.data.error, () => props.history.push('/')); | ||
101 | - | ||
102 | console.log(e); | 111 | console.log(e); |
112 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => props.history.push('/')); | ||
103 | } | 113 | } |
104 | }; | 114 | }; |
105 | 115 | ||
... | @@ -123,7 +133,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -123,7 +133,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
123 | Alert.onError('피드백 등록에 실패했습니다.', () => null); | 133 | Alert.onError('피드백 등록에 실패했습니다.', () => null); |
124 | } | 134 | } |
125 | } catch(e : any) { | 135 | } catch(e : any) { |
126 | - Alert.onError(e.response.data.error, () => fetchData()); | 136 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => fetchData()); |
127 | } | 137 | } |
128 | } else { | 138 | } else { |
129 | Alert.onError('피드백 내용을 입력하세요.', () => null); | 139 | Alert.onError('피드백 내용을 입력하세요.', () => null); |
... | @@ -134,6 +144,14 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -134,6 +144,14 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
134 | 144 | ||
135 | }; | 145 | }; |
136 | 146 | ||
147 | + const onViewTakeHist = () => { | ||
148 | + if(modalType === 'info') setModalType('hist'); | ||
149 | + }; | ||
150 | + | ||
151 | + const onViewMedicineInfo = () => { | ||
152 | + if(modalType === 'hist') setModalType('info'); | ||
153 | + }; | ||
154 | + | ||
137 | 155 | ||
138 | useEffect(() => { | 156 | useEffect(() => { |
139 | if(userTypeCd !== 'DOCTOR') { | 157 | if(userTypeCd !== 'DOCTOR') { |
... | @@ -148,8 +166,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -148,8 +166,12 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
148 | <BottleInfoPresenter | 166 | <BottleInfoPresenter |
149 | bottleInfo = {bottleInfo} | 167 | bottleInfo = {bottleInfo} |
150 | chartOption = {chartOption} | 168 | chartOption = {chartOption} |
169 | + takeMedicineHist = {takeMedicineHist} | ||
151 | 170 | ||
152 | medicineInfoModal = {medicineInfoModal} | 171 | medicineInfoModal = {medicineInfoModal} |
172 | + modalType = {modalType} | ||
173 | + onViewTakeHist = {onViewTakeHist} | ||
174 | + onViewMedicineInfo = {onViewMedicineInfo} | ||
153 | setMedicineInfoModal = {setMedicineInfoModal} | 175 | setMedicineInfoModal = {setMedicineInfoModal} |
154 | 176 | ||
155 | feedback = {feedback} | 177 | feedback = {feedback} |
... | @@ -158,6 +180,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { | ... | @@ -158,6 +180,7 @@ const BottleInfoContainer = (props : BottleInfoProps) => { |
158 | setFdbType = {setFdbType} | 180 | setFdbType = {setFdbType} |
159 | onSubmitFeedback = {onSubmitFeedback} | 181 | onSubmitFeedback = {onSubmitFeedback} |
160 | /> | 182 | /> |
183 | + <Footer {...props}/> | ||
161 | </> | 184 | </> |
162 | ); | 185 | ); |
163 | }; | 186 | }; | ... | ... |
... | @@ -2,10 +2,10 @@ import React from 'react'; | ... | @@ -2,10 +2,10 @@ import React from 'react'; |
2 | import HighCharts from 'highcharts'; | 2 | import HighCharts from 'highcharts'; |
3 | import HighchartsReact from 'highcharts-react-official'; | 3 | import HighchartsReact from 'highcharts-react-official'; |
4 | 4 | ||
5 | +import Modal from '../../components/Modal'; | ||
5 | import * as styled from './BottleInfoStyled'; | 6 | import * as styled from './BottleInfoStyled'; |
6 | 7 | ||
7 | const plus = '/static/img/plus.png'; | 8 | const plus = '/static/img/plus.png'; |
8 | -const closeButton = '/static/img/close.png'; | ||
9 | 9 | ||
10 | 10 | ||
11 | interface BottleInfoProps { | 11 | interface BottleInfoProps { |
... | @@ -16,8 +16,12 @@ interface BottleInfoProps { | ... | @@ -16,8 +16,12 @@ interface BottleInfoProps { |
16 | takeMedicineHist : any[]; | 16 | takeMedicineHist : any[]; |
17 | }; | 17 | }; |
18 | chartOption : any; | 18 | chartOption : any; |
19 | + takeMedicineHist : any[]; | ||
19 | 20 | ||
20 | medicineInfoModal : boolean; | 21 | medicineInfoModal : boolean; |
22 | + modalType : string; | ||
23 | + onViewTakeHist : () => void; | ||
24 | + onViewMedicineInfo : () => void; | ||
21 | setMedicineInfoModal : (arg0 : boolean) => void; | 25 | setMedicineInfoModal : (arg0 : boolean) => void; |
22 | 26 | ||
23 | feedback : string; | 27 | feedback : string; |
... | @@ -32,42 +36,87 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { | ... | @@ -32,42 +36,87 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { |
32 | <styled.Container> | 36 | <styled.Container> |
33 | { | 37 | { |
34 | props.medicineInfoModal ? | 38 | props.medicineInfoModal ? |
35 | - <styled.ModalContainer> | 39 | + <Modal onModalClose = {() => props.setMedicineInfoModal(false)}> |
36 | - <styled.ModalClsButtonWrapper> | 40 | + <> |
37 | - <styled.ModalClsButton | 41 | + <styled.ModalTypeButtonWrapper> |
38 | - onClick = {() => props.setMedicineInfoModal(false)} | 42 | + <styled.ModalTypeButton |
43 | + isSelect = {props.modalType === 'hist'} | ||
44 | + onClick = {props.onViewTakeHist} | ||
39 | > | 45 | > |
40 | - <styled.ModalClsButtonImg src = {closeButton}/> | 46 | + 복용 기록 |
41 | - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | 47 | + </styled.ModalTypeButton> |
42 | - </styled.ModalClsButton> | 48 | + <styled.ModalTypeButton |
43 | - </styled.ModalClsButtonWrapper> | 49 | + isSelect = {props.modalType === 'info'} |
44 | - <styled.ModalContentWrapper> | 50 | + onClick = {props.onViewMedicineInfo} |
45 | - <styled.ModalContent> | 51 | + > |
46 | - <styled.MedicineNameWrapper> | 52 | + 약 정보 |
47 | - <styled.MedicineName>{props.bottleInfo.medicine.name}</styled.MedicineName> | 53 | + </styled.ModalTypeButton> |
48 | - <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{props.bottleInfo.medicine.company}</styled.MedicineName> | 54 | + </styled.ModalTypeButtonWrapper> |
49 | - </styled.MedicineNameWrapper> | 55 | + { |
50 | - <styled.MedicineInfoWrapper> | 56 | + props.modalType === 'hist' ? |
51 | - <styled.MedicineEachInfoWrapper> | 57 | + <> |
52 | - <styled.MedicineEachInfoTitle>효능</styled.MedicineEachInfoTitle> | 58 | + <styled.MedicineNameWrapper> |
53 | - <styled.MedicineEachInfo>{props.bottleInfo.medicine.target}</styled.MedicineEachInfo> | 59 | + <styled.MedicineName>{`복용 기록`}</styled.MedicineName> |
54 | - </styled.MedicineEachInfoWrapper> | 60 | + <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{`전체 : ${props.takeMedicineHist.length}건`}</styled.MedicineName> |
55 | - <styled.MedicineEachInfoWrapper> | 61 | + </styled.MedicineNameWrapper> |
56 | - <styled.MedicineEachInfoTitle>복용 정보</styled.MedicineEachInfoTitle> | 62 | + <styled.MedicineInfoWrapper> |
57 | - <styled.MedicineEachInfo>{props.bottleInfo.medicine.dosage}</styled.MedicineEachInfo> | 63 | + { |
58 | - </styled.MedicineEachInfoWrapper> | 64 | + props.takeMedicineHist.map((hist : any) => { |
59 | - <styled.MedicineEachInfoWrapper> | 65 | + return ( |
60 | - <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>주의 사항</styled.MedicineEachInfoTitle> | 66 | + <styled.HistWrapper |
61 | - <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.warn}</styled.MedicineEachInfo> | 67 | + key = {hist._id} |
62 | - </styled.MedicineEachInfoWrapper> | 68 | + > |
63 | - <styled.MedicineEachInfoWrapper> | 69 | + <styled.HistDtmWrapper> |
64 | - <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>부작용</styled.MedicineEachInfoTitle> | 70 | + <styled.HistInfoEachWrapper style = {{fontSize : 11}}>복용 날짜</styled.HistInfoEachWrapper> |
65 | - <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.antiEffect}</styled.MedicineEachInfo> | 71 | + <styled.HistDtm>{hist.takeDate}</styled.HistDtm> |
66 | - </styled.MedicineEachInfoWrapper> | 72 | + </styled.HistDtmWrapper> |
67 | - </styled.MedicineInfoWrapper> | 73 | + <styled.HistInfoWrapper> |
68 | - </styled.ModalContent> | 74 | + <styled.HistInfoEachWrapper> |
69 | - </styled.ModalContentWrapper> | 75 | + 복용량 |
70 | - </styled.ModalContainer> : null | 76 | + <styled.HistInfoEach>{hist.dosage}회분</styled.HistInfoEach> |
77 | + </styled.HistInfoEachWrapper> | ||
78 | + <styled.HistInfoEachWrapper> | ||
79 | + 약병 내 온도 | ||
80 | + <styled.HistInfoEach>{hist.temperature}℃</styled.HistInfoEach> | ||
81 | + </styled.HistInfoEachWrapper> | ||
82 | + <styled.HistInfoEachWrapper> | ||
83 | + 약병 내 습도 | ||
84 | + <styled.HistInfoEach>{hist.humidity}%</styled.HistInfoEach> | ||
85 | + </styled.HistInfoEachWrapper> | ||
86 | + </styled.HistInfoWrapper> | ||
87 | + </styled.HistWrapper> | ||
88 | + ) | ||
89 | + }) | ||
90 | + } | ||
91 | + </styled.MedicineInfoWrapper> | ||
92 | + </> : | ||
93 | + <> | ||
94 | + <styled.MedicineNameWrapper> | ||
95 | + <styled.MedicineName>{props.bottleInfo.medicine.name}</styled.MedicineName> | ||
96 | + <styled.MedicineName style = {{color : '#343434', fontSize : 15, marginTop : 4,}}>{props.bottleInfo.medicine.company}</styled.MedicineName> | ||
97 | + </styled.MedicineNameWrapper> | ||
98 | + <styled.MedicineInfoWrapper> | ||
99 | + <styled.MedicineEachInfoWrapper> | ||
100 | + <styled.MedicineEachInfoTitle>효능</styled.MedicineEachInfoTitle> | ||
101 | + <styled.MedicineEachInfo>{props.bottleInfo.medicine.target}</styled.MedicineEachInfo> | ||
102 | + </styled.MedicineEachInfoWrapper> | ||
103 | + <styled.MedicineEachInfoWrapper> | ||
104 | + <styled.MedicineEachInfoTitle>복용 정보</styled.MedicineEachInfoTitle> | ||
105 | + <styled.MedicineEachInfo>{props.bottleInfo.medicine.dosage}</styled.MedicineEachInfo> | ||
106 | + </styled.MedicineEachInfoWrapper> | ||
107 | + <styled.MedicineEachInfoWrapper> | ||
108 | + <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>주의 사항</styled.MedicineEachInfoTitle> | ||
109 | + <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.warn}</styled.MedicineEachInfo> | ||
110 | + </styled.MedicineEachInfoWrapper> | ||
111 | + <styled.MedicineEachInfoWrapper> | ||
112 | + <styled.MedicineEachInfoTitle style = {{color : '#FF3F3F', fontWeight : 'bold'}}>부작용</styled.MedicineEachInfoTitle> | ||
113 | + <styled.MedicineEachInfo style = {{color : '#9B0000'}}>{props.bottleInfo.medicine.antiEffect}</styled.MedicineEachInfo> | ||
114 | + </styled.MedicineEachInfoWrapper> | ||
115 | + </styled.MedicineInfoWrapper> | ||
116 | + </> | ||
117 | + } | ||
118 | + </> | ||
119 | + </Modal> : null | ||
71 | } | 120 | } |
72 | <styled.ChartAndFeedbackWrapper> | 121 | <styled.ChartAndFeedbackWrapper> |
73 | <styled.ChartWrapper> | 122 | <styled.ChartWrapper> |
... | @@ -107,7 +156,7 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { | ... | @@ -107,7 +156,7 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { |
107 | /> | 156 | /> |
108 | <styled.NewFeedbackButtonWrapper> | 157 | <styled.NewFeedbackButtonWrapper> |
109 | <styled.NewFeedbackTypeButtonWrapper> | 158 | <styled.NewFeedbackTypeButtonWrapper> |
110 | - <styled.NewFeedbackTypeButtonEachWrapper> | 159 | + <styled.NewFeedbackTypeButtonEachWrapper> |
111 | <styled.NewFeedbackTypeButton | 160 | <styled.NewFeedbackTypeButton |
112 | valueType = 'RECOMMEND' | 161 | valueType = 'RECOMMEND' |
113 | selected = {props.fdbType === 'RECOMMEND'} | 162 | selected = {props.fdbType === 'RECOMMEND'} |
... | @@ -143,7 +192,6 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { | ... | @@ -143,7 +192,6 @@ const BottleInfoPresenter = (props : BottleInfoProps) => { |
143 | <styled.NewFeedbackRegButton | 192 | <styled.NewFeedbackRegButton |
144 | onClick = {props.onSubmitFeedback} | 193 | onClick = {props.onSubmitFeedback} |
145 | > | 194 | > |
146 | - {/* <styled.NewFeedbackRegButtonImg /> */} | ||
147 | <styled.NewFeedbackRegButtonText>피드백<br/>등록</styled.NewFeedbackRegButtonText> | 195 | <styled.NewFeedbackRegButtonText>피드백<br/>등록</styled.NewFeedbackRegButtonText> |
148 | </styled.NewFeedbackRegButton> | 196 | </styled.NewFeedbackRegButton> |
149 | </styled.NewFeedbackButtonWrapper> | 197 | </styled.NewFeedbackButtonWrapper> | ... | ... |
1 | -import styled, { keyframes } from 'styled-components'; | 1 | +import styled from 'styled-components'; |
2 | - | ||
3 | - | ||
4 | -const ModalOn = keyframes ` | ||
5 | - 0% { | ||
6 | - background-color : rgba(52, 52, 52, .0); | ||
7 | - } | ||
8 | - 20% { | ||
9 | - background-color : rgba(52, 52, 52, .2); | ||
10 | - } | ||
11 | - 40% { | ||
12 | - background-color : rgba(52, 52, 52, .4); | ||
13 | - } | ||
14 | - 60% { | ||
15 | - background-color : rgba(52, 52, 52, .5); | ||
16 | - } | ||
17 | - 80% { | ||
18 | - background-color : rgba(52, 52, 52, .6); | ||
19 | - } | ||
20 | - 100% { | ||
21 | - background-color : rgba(52, 52, 52, .7); | ||
22 | - } | ||
23 | - | ||
24 | -`; | ||
25 | 2 | ||
26 | 3 | ||
27 | export const Container = styled.div ` | 4 | export const Container = styled.div ` |
... | @@ -32,91 +9,106 @@ export const Container = styled.div ` | ... | @@ -32,91 +9,106 @@ export const Container = styled.div ` |
32 | justify-content : center; | 9 | justify-content : center; |
33 | `; | 10 | `; |
34 | 11 | ||
35 | -export const ModalContainer = styled.div ` | 12 | +export const ModalTypeButtonWrapper = styled.div ` |
36 | - height : 100%; | 13 | + border : none; |
37 | - width : 100%; | ||
38 | - z-index : 99; | ||
39 | - position : absolute; | ||
40 | - | ||
41 | display : flex; | 14 | display : flex; |
42 | - flex-direction : column; | 15 | + flex-direction : row; |
43 | - | ||
44 | - animation : ${ModalOn} .5s; | ||
45 | - | ||
46 | - background-color : rgba(52, 52, 52, .7); | ||
47 | - | ||
48 | -`; | ||
49 | - | ||
50 | -export const ModalClsButtonWrapper = styled.div ` | ||
51 | - flex : 1; | ||
52 | 16 | ||
53 | - display : flex; | 17 | + width : 100%; |
54 | 18 | ||
55 | - justify-content : flex-end; | 19 | + justify-content : center; |
56 | align-items : center; | 20 | align-items : center; |
57 | - padding : 0 20px; | ||
58 | 21 | ||
59 | - border : none; | ||
60 | - background-color : transprent; | ||
61 | -`; | ||
62 | - | ||
63 | -export const ModalClsButton = styled.button ` | ||
64 | - border : none; | ||
65 | background-color : transparent; | 22 | background-color : transparent; |
23 | + | ||
24 | + gap : 5%; | ||
66 | 25 | ||
67 | - cursor : pointer; | 26 | + padding : 3% 0 0 0; |
27 | +`; | ||
68 | 28 | ||
69 | - color : #fff; | 29 | +export const ModalTypeButton = styled.button<{isSelect : boolean}> ` |
30 | + border : 1px solid #337DFF; | ||
31 | + border-radius : 3px; | ||
32 | + color : ${props => props.isSelect ? '#fff' : '#337DFF'}; | ||
33 | + background-color : ${props => props.isSelect ? '#337DFF' : '#fff'}; | ||
70 | 34 | ||
71 | - display : flex; | 35 | + padding : 1% 3%; |
72 | - flex-direction : row; | ||
73 | 36 | ||
74 | - justify-content : center; | 37 | + cursor : pointer; |
75 | - align-items : center; | 38 | + |
39 | + font-size : 16px; | ||
40 | + font-weight : ${props => props.isSelect ? '600' : '500'}; | ||
76 | 41 | ||
77 | transition : .25s all; | 42 | transition : .25s all; |
43 | + | ||
78 | &:hover { | 44 | &:hover { |
79 | opacity : .5; | 45 | opacity : .5; |
80 | } | 46 | } |
47 | + | ||
81 | `; | 48 | `; |
82 | 49 | ||
83 | -export const ModalClsButtonImg = styled.img ` | 50 | +export const HistWrapper = styled.div ` |
84 | - height : 20px; | 51 | + display : flex; |
85 | - width : 20px; | 52 | + flex-direction : column; |
86 | 53 | ||
87 | - margin : 0 10px 0 0; | 54 | + height : 65px; |
88 | -`; | 55 | + width : 100%; |
89 | 56 | ||
90 | -export const ModalClsButtonText = styled.div ` | 57 | + border : none; |
91 | - font-size : 18px; | 58 | + border-bottom : 1px solid #ddd; |
92 | - font-weight : 700; | ||
93 | `; | 59 | `; |
94 | 60 | ||
95 | -export const ModalContentWrapper = styled.div ` | 61 | +export const HistDtmWrapper = styled.div ` |
96 | - flex : 8; | 62 | + flex : 2; |
63 | + padding : 0 3%; | ||
64 | + | ||
65 | + border : none; | ||
97 | 66 | ||
98 | display : flex; | 67 | display : flex; |
99 | flex-direction : column; | 68 | flex-direction : column; |
100 | 69 | ||
101 | justify-content : center; | 70 | justify-content : center; |
102 | - align-items : center; | ||
103 | 71 | ||
104 | - border : none; | ||
105 | `; | 72 | `; |
106 | 73 | ||
107 | -export const ModalContent = styled.div ` | 74 | +export const HistDtm = styled.div ` |
108 | - width : 700px; | 75 | + font-size : 16px; |
109 | - height : 500px; | 76 | + color : #000; |
77 | +`; | ||
78 | + | ||
79 | +export const HistInfoWrapper = styled.div ` | ||
80 | + flex : 1; | ||
81 | + padding : 0 3%; | ||
82 | + | ||
83 | + border : none; | ||
110 | 84 | ||
111 | - background-color : #fff; | ||
112 | - border : 1.2px solid #337DFF; | ||
113 | - border-radius : 5px; | ||
114 | - | ||
115 | display : flex; | 85 | display : flex; |
116 | - flex-direction : column; | 86 | + flex-direction : row; |
87 | + align-items : flex-start; | ||
88 | + | ||
89 | +`; | ||
90 | + | ||
91 | +export const HistInfoEachWrapper = styled.div ` | ||
92 | + flex : 1; | ||
93 | + border : none; | ||
117 | 94 | ||
118 | - // justify-content : center; | 95 | + display : flex; |
96 | + flex-direction : row; | ||
119 | align-items : center; | 97 | align-items : center; |
98 | + | ||
99 | + gap : 5%; | ||
100 | + | ||
101 | + font-size : 14px; | ||
102 | + font-weight : 500; | ||
103 | + | ||
104 | + color : #a0a0a0; | ||
105 | +`; | ||
106 | + | ||
107 | +export const HistInfoEach = styled.div ` | ||
108 | + font-size : 15px; | ||
109 | + font-weight : 600; | ||
110 | + | ||
111 | + color : #337DFF; | ||
120 | `; | 112 | `; |
121 | 113 | ||
122 | export const MedicineNameWrapper = styled.div ` | 114 | export const MedicineNameWrapper = styled.div ` |
... | @@ -169,6 +161,7 @@ export const MedicineEachInfoWrapper = styled.div ` | ... | @@ -169,6 +161,7 @@ export const MedicineEachInfoWrapper = styled.div ` |
169 | display : flex; | 161 | display : flex; |
170 | flex-direction : column; | 162 | flex-direction : column; |
171 | 163 | ||
164 | + | ||
172 | width : 80%; | 165 | width : 80%; |
173 | padding : 20px 10%; | 166 | padding : 20px 10%; |
174 | border : none; | 167 | border : none; | ... | ... |
... | @@ -23,6 +23,7 @@ const LoginContainer = (props : LoginProps) => { | ... | @@ -23,6 +23,7 @@ const LoginContainer = (props : LoginProps) => { |
23 | }); | 23 | }); |
24 | 24 | ||
25 | const [token, setToken] = useRecoilState(recoilUtil.token); | 25 | const [token, setToken] = useRecoilState(recoilUtil.token); |
26 | + const [userId, setUserId] = useRecoilState(recoilUtil.userId); | ||
26 | const [userTypeCd, setUserTypeCd] = useRecoilState(recoilUtil.userTypeCd); | 27 | const [userTypeCd, setUserTypeCd] = useRecoilState(recoilUtil.userTypeCd); |
27 | 28 | ||
28 | 29 | ||
... | @@ -58,13 +59,14 @@ const LoginContainer = (props : LoginProps) => { | ... | @@ -58,13 +59,14 @@ const LoginContainer = (props : LoginProps) => { |
58 | const result : any = await authApi.login(loginForm); | 59 | const result : any = await authApi.login(loginForm); |
59 | if(result.statusText === 'OK' && result.data.userTypeCd !== 'NORMAL') { | 60 | if(result.statusText === 'OK' && result.data.userTypeCd !== 'NORMAL') { |
60 | setToken(result.data.token); | 61 | setToken(result.data.token); |
62 | + setUserId(loginForm.userId); | ||
61 | setUserTypeCd(result.data.userTypeCd); | 63 | setUserTypeCd(result.data.userTypeCd); |
62 | Alert.onSuccess('로그인 성공, 메인 화면으로 이동합니다.', () => props.history.push('/')); | 64 | Alert.onSuccess('로그인 성공, 메인 화면으로 이동합니다.', () => props.history.push('/')); |
63 | } else if(result.data.userTypeCd === 'NORMAL') { | 65 | } else if(result.data.userTypeCd === 'NORMAL') { |
64 | Alert.onError('권한이 없는 유저입니다.', () => props.history.push('/')); | 66 | Alert.onError('권한이 없는 유저입니다.', () => props.history.push('/')); |
65 | } | 67 | } |
66 | - } catch(e) { | 68 | + } catch(e : any) { |
67 | - Alert.onError(e.response.data.error, () => null); | 69 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
68 | } | 70 | } |
69 | 71 | ||
70 | }; | 72 | }; | ... | ... |
... | @@ -6,6 +6,7 @@ import * as recoilUtil from '../../util/recoilUtil'; | ... | @@ -6,6 +6,7 @@ import * as recoilUtil from '../../util/recoilUtil'; |
6 | 6 | ||
7 | 7 | ||
8 | import Header from '../../components/Header'; | 8 | import Header from '../../components/Header'; |
9 | +import Footer from '../../components/Footer'; | ||
9 | import DoctorMenuContainer from './doctor'; | 10 | import DoctorMenuContainer from './doctor'; |
10 | import ManagerMenuContainer from './manager'; | 11 | import ManagerMenuContainer from './manager'; |
11 | 12 | ||
... | @@ -32,6 +33,7 @@ const MainContainer = (props : MainProps) => { | ... | @@ -32,6 +33,7 @@ const MainContainer = (props : MainProps) => { |
32 | userTypeCd === 'MANAGER' ? | 33 | userTypeCd === 'MANAGER' ? |
33 | <ManagerMenuContainer {...props}/> : null | 34 | <ManagerMenuContainer {...props}/> : null |
34 | } | 35 | } |
36 | + <Footer {...props}/> | ||
35 | </> | 37 | </> |
36 | ); | 38 | ); |
37 | }; | 39 | }; | ... | ... |
web/src/views/main/MainPresenter.tsx
deleted
100644 → 0
1 | -import React from 'react'; | ||
2 | - | ||
3 | -import * as styled from './MainStyled'; | ||
4 | - | ||
5 | - | ||
6 | -interface MainProps { | ||
7 | - userTypeCd : string; | ||
8 | -} | ||
9 | - | ||
10 | -const MainPresenter = (props : MainProps) => { | ||
11 | - return ( | ||
12 | - <styled.Container> | ||
13 | - <styled.InfoAndSearchWrapper> | ||
14 | - <styled.InfoWrapper> | ||
15 | - <styled.InfoSquare> | ||
16 | - | ||
17 | - </styled.InfoSquare> | ||
18 | - <styled.NewPatientButton>새 환자 등록</styled.NewPatientButton> | ||
19 | - </styled.InfoWrapper> | ||
20 | - <styled.SearchAndDetailWrapper> | ||
21 | - <styled.SearchBarWrapper> | ||
22 | - <styled.SearchBar | ||
23 | - placeholder = '환자 이름' | ||
24 | - /> | ||
25 | - <styled.SearchButton> | ||
26 | - 검색 | ||
27 | - </styled.SearchButton> | ||
28 | - </styled.SearchBarWrapper> | ||
29 | - <styled.SearchResultWrapper> | ||
30 | - | ||
31 | - </styled.SearchResultWrapper> | ||
32 | - </styled.SearchAndDetailWrapper> | ||
33 | - </styled.InfoAndSearchWrapper> | ||
34 | - <styled.BottleListWrapper> | ||
35 | - bottleListWrapper | ||
36 | - </styled.BottleListWrapper> | ||
37 | - </styled.Container> | ||
38 | - ) | ||
39 | -}; | ||
40 | - | ||
41 | -export default MainPresenter; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -11,13 +11,13 @@ import * as Alert from '../../../util/alertMessage'; | ... | @@ -11,13 +11,13 @@ import * as Alert from '../../../util/alertMessage'; |
11 | import { doctorApi, medicineApi } from '../../../api'; | 11 | import { doctorApi, medicineApi } from '../../../api'; |
12 | 12 | ||
13 | 13 | ||
14 | -//toDo : Generate QR Code By Medicine Id | ||
15 | 14 | ||
16 | type DoctorMenuProps = RouteComponentProps | 15 | type DoctorMenuProps = RouteComponentProps |
17 | 16 | ||
18 | const DoctorMenuContainer = (props : DoctorMenuProps) => { | 17 | const DoctorMenuContainer = (props : DoctorMenuProps) => { |
19 | 18 | ||
20 | const token = useRecoilValue(recoilUtil.token); | 19 | const token = useRecoilValue(recoilUtil.token); |
20 | + const userId = useRecoilValue(recoilUtil.userId); | ||
21 | const [loading, setLoading] = useRecoilState(recoilUtil.loading); | 21 | const [loading, setLoading] = useRecoilState(recoilUtil.loading); |
22 | 22 | ||
23 | const [doctorInfo, setDoctorInfo] = useState<any>({ | 23 | const [doctorInfo, setDoctorInfo] = useState<any>({ |
... | @@ -33,7 +33,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -33,7 +33,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
33 | const [info, setInfo] = useState<any>({ | 33 | const [info, setInfo] = useState<any>({ |
34 | infoType : 'DOCTOR', | 34 | infoType : 'DOCTOR', |
35 | userNm : '', | 35 | userNm : '', |
36 | - userAge : 0, | 36 | + birth : '', |
37 | contact : '', | 37 | contact : '', |
38 | doctorType : '', | 38 | doctorType : '', |
39 | patientInfo : '', | 39 | patientInfo : '', |
... | @@ -48,13 +48,18 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -48,13 +48,18 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
48 | const [editPatientInfo, setEditPatientInfo] = useState<string>(''); | 48 | const [editPatientInfo, setEditPatientInfo] = useState<string>(''); |
49 | 49 | ||
50 | const [newPatientRegisterModal, setNewPatientRegisterModal] = useState<boolean>(false); | 50 | const [newPatientRegisterModal, setNewPatientRegisterModal] = useState<boolean>(false); |
51 | - const [newPatientSearchId, setNewPatientSearchId] = useState<string>(''); | 51 | + const [newPatientSearchContact, setNewPatientSearchContact] = useState<string>(''); |
52 | const [newPatientSearchResult, setNewPatientSearchResult] = useState<any | null>(null); | 52 | const [newPatientSearchResult, setNewPatientSearchResult] = useState<any | null>(null); |
53 | 53 | ||
54 | const [prescribeModal, setPrescribeModal] = useState<boolean>(false); | 54 | const [prescribeModal, setPrescribeModal] = useState<boolean>(false); |
55 | + const [prescribeModalStep, setPrescribeModalStep] = useState<number>(1); | ||
55 | const [searchMedicineKeyword, setSearchMedicineKeyword] = useState<string>(''); | 56 | const [searchMedicineKeyword, setSearchMedicineKeyword] = useState<string>(''); |
56 | const [medicineList, setMedicineList] = useState<any>([]); | 57 | const [medicineList, setMedicineList] = useState<any>([]); |
57 | const [prescribeMedicine, setPrescribeMedicine] = useState<any>(null); | 58 | const [prescribeMedicine, setPrescribeMedicine] = useState<any>(null); |
59 | + const [dailyDosage, setDailyDosage] = useState<string>('1'); | ||
60 | + const [totalDay, setTotalDay] = useState<string>('1'); | ||
61 | + | ||
62 | + const [qrcodeUrl, setQrcodeUrl] = useState<string | null>(null); | ||
58 | 63 | ||
59 | 64 | ||
60 | const fetchData = async() => { | 65 | const fetchData = async() => { |
... | @@ -69,7 +74,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -69,7 +74,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
69 | userNm : doctorInfo.doctorNm, | 74 | userNm : doctorInfo.doctorNm, |
70 | doctorType : doctorInfo.doctorType, | 75 | doctorType : doctorInfo.doctorType, |
71 | contact : doctorInfo.contact, | 76 | contact : doctorInfo.contact, |
72 | - userAge : null, | 77 | + birth : null, |
73 | patientInfo : '', | 78 | patientInfo : '', |
74 | }); | 79 | }); |
75 | 80 | ||
... | @@ -79,7 +84,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -79,7 +84,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
79 | }).catch(error => console.log(error)); | 84 | }).catch(error => console.log(error)); |
80 | } | 85 | } |
81 | setLoading(false); | 86 | setLoading(false); |
82 | - } catch(e) { | 87 | + } catch(e : any) { |
83 | console.log(e); | 88 | console.log(e); |
84 | setLoading(false); | 89 | setLoading(false); |
85 | } | 90 | } |
... | @@ -94,10 +99,12 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -94,10 +99,12 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
94 | setLoading(true); | 99 | setLoading(true); |
95 | await doctorApi.getPatientDetail(token, patientId).then(res => { | 100 | await doctorApi.getPatientDetail(token, patientId).then(res => { |
96 | setPatientDetail(res.data); | 101 | setPatientDetail(res.data); |
102 | + | ||
103 | + const birth = res.data.profile.birth.split('-'); | ||
97 | setInfo({ | 104 | setInfo({ |
98 | infoType : 'PATIENT', | 105 | infoType : 'PATIENT', |
99 | userNm : res.data.profile.userNm, | 106 | userNm : res.data.profile.userNm, |
100 | - userAge : res.data.profile.userAge, | 107 | + birth : `${birth[0]}년 ${birth[1]}월 ${birth[2]}일`, |
101 | contact : res.data.profile.contact, | 108 | contact : res.data.profile.contact, |
102 | doctorType : null, | 109 | doctorType : null, |
103 | patientInfo : res.data.info, | 110 | patientInfo : res.data.info, |
... | @@ -118,7 +125,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -118,7 +125,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
118 | userNm : doctorInfo.doctorNm, | 125 | userNm : doctorInfo.doctorNm, |
119 | doctorType : doctorInfo.doctorType, | 126 | doctorType : doctorInfo.doctorType, |
120 | contact : doctorInfo.contact, | 127 | contact : doctorInfo.contact, |
121 | - userAge : null, | 128 | + birth : null, |
122 | patientInfo : '', | 129 | patientInfo : '', |
123 | }); | 130 | }); |
124 | setFilteringPatientList([]); | 131 | setFilteringPatientList([]); |
... | @@ -145,7 +152,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -145,7 +152,7 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
145 | } | 152 | } |
146 | 153 | ||
147 | } catch(e : any) { | 154 | } catch(e : any) { |
148 | - Alert.onError(e.response.data.error, () => null); | 155 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
149 | } | 156 | } |
150 | }; | 157 | }; |
151 | 158 | ||
... | @@ -158,15 +165,15 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -158,15 +165,15 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
158 | }; | 165 | }; |
159 | 166 | ||
160 | 167 | ||
161 | - const onSetNewPatientSearchId = (e : React.ChangeEvent<HTMLInputElement>) => { | 168 | + const onSetNewPatientSearchContact = (e : React.ChangeEvent<HTMLInputElement>) => { |
162 | - setNewPatientSearchId(e.target.value); | 169 | + setNewPatientSearchContact(e.target.value); |
163 | }; | 170 | }; |
164 | 171 | ||
165 | - const onSearchNewPatientByEmail = async () => { | 172 | + const onSearchNewPatientByContact = async () => { |
166 | try { | 173 | try { |
167 | setLoading(true); | 174 | setLoading(true); |
168 | - await doctorApi.searchPatientById(token, newPatientSearchId).then(res => { | 175 | + await doctorApi.searchPatientByContact(token, newPatientSearchContact).then(res => { |
169 | - setNewPatientSearchResult(res.data); | 176 | + setNewPatientSearchResult(res.data.patientInfo); |
170 | setLoading(false); | 177 | setLoading(false); |
171 | }).catch(err => { | 178 | }).catch(err => { |
172 | console.log(err); | 179 | console.log(err); |
... | @@ -176,29 +183,30 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -176,29 +183,30 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
176 | }); | 183 | }); |
177 | } catch(e : any) { | 184 | } catch(e : any) { |
178 | setLoading(false); | 185 | setLoading(false); |
179 | - Alert.onError(e.response.data.error, () => null); | 186 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
180 | } | 187 | } |
181 | }; | 188 | }; |
182 | 189 | ||
183 | const onRegisterNewPatient = () => { | 190 | const onRegisterNewPatient = () => { |
184 | if(newPatientSearchResult) { | 191 | if(newPatientSearchResult) { |
185 | - const { patientId, patientNm } = newPatientSearchResult; | 192 | + const { userId, userNm } = newPatientSearchResult; |
186 | const onRegisterReq = async () => { | 193 | const onRegisterReq = async () => { |
187 | try { | 194 | try { |
188 | const result = await doctorApi.registerPatient(token, { | 195 | const result = await doctorApi.registerPatient(token, { |
189 | - patientId, | 196 | + patientId : userId, |
190 | }); | 197 | }); |
191 | if(result.statusText === 'OK') { | 198 | if(result.statusText === 'OK') { |
192 | Alert.onSuccess('환자에게 담당의 등록 요청을 전송했습니다.', () => null); | 199 | Alert.onSuccess('환자에게 담당의 등록 요청을 전송했습니다.', () => null); |
200 | + setNewPatientRegisterModal(false); | ||
193 | } else { | 201 | } else { |
194 | Alert.onError('환자에게 담당의 등록 요청을 실패했습니다.', () => null); | 202 | Alert.onError('환자에게 담당의 등록 요청을 실패했습니다.', () => null); |
195 | } | 203 | } |
196 | } catch(e : any) { | 204 | } catch(e : any) { |
197 | - Alert.onError(e.response.data.error, () => null); | 205 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
198 | } | 206 | } |
199 | }; | 207 | }; |
200 | 208 | ||
201 | - Alert.onCheck(`${patientNm} 환자에게 담당의 등록 요청을 전송하시겠습니까?`, onRegisterReq, () => null); | 209 | + Alert.onCheck(`${userNm} 환자에게 담당의 등록 요청을 전송하시겠습니까?`, onRegisterReq, () => null); |
202 | } else { | 210 | } else { |
203 | Alert.onError('환자를 먼저 검색해주세요.', () => null); | 211 | Alert.onError('환자를 먼저 검색해주세요.', () => null); |
204 | } | 212 | } |
... | @@ -206,14 +214,17 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -206,14 +214,17 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
206 | 214 | ||
207 | const onCloseModal = async () => { | 215 | const onCloseModal = async () => { |
208 | setNewPatientRegisterModal(false); | 216 | setNewPatientRegisterModal(false); |
209 | - setNewPatientSearchId(''); | 217 | + setNewPatientSearchContact(''); |
210 | setNewPatientSearchResult(null); | 218 | setNewPatientSearchResult(null); |
211 | setEditModal(false); | 219 | setEditModal(false); |
212 | setEditPatientInfo(''); | 220 | setEditPatientInfo(''); |
213 | setPrescribeModal(false); | 221 | setPrescribeModal(false); |
222 | + setPrescribeModalStep(1); | ||
214 | setSearchMedicineKeyword(''); | 223 | setSearchMedicineKeyword(''); |
215 | setMedicineList([]); | 224 | setMedicineList([]); |
216 | setPrescribeMedicine(null); | 225 | setPrescribeMedicine(null); |
226 | + setDailyDosage('1'); | ||
227 | + setTotalDay('1'); | ||
217 | }; | 228 | }; |
218 | 229 | ||
219 | const onGoBottleDetail = (bottleId : number) => { | 230 | const onGoBottleDetail = (bottleId : number) => { |
... | @@ -231,18 +242,68 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -231,18 +242,68 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
231 | setLoading(true); | 242 | setLoading(true); |
232 | const res = await medicineApi.searchMedicine(token, searchMedicineKeyword); | 243 | const res = await medicineApi.searchMedicine(token, searchMedicineKeyword); |
233 | if(res.statusText === 'OK') { | 244 | if(res.statusText === 'OK') { |
234 | - console.log(res.data.medicineList) | ||
235 | setMedicineList(res.data.medicineList); | 245 | setMedicineList(res.data.medicineList); |
236 | } | 246 | } |
237 | setLoading(false); | 247 | setLoading(false); |
238 | } catch(e : any) { | 248 | } catch(e : any) { |
239 | - Alert.onError(e.response.data.error, () => null); | 249 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
240 | } | 250 | } |
241 | }; | 251 | }; |
242 | 252 | ||
253 | + const onSetDailyDosage = (e : React.ChangeEvent<HTMLInputElement>) => { | ||
254 | + setDailyDosage(e.target.value); | ||
255 | + }; | ||
256 | + | ||
257 | + const onSetTotalDay = (e : React.ChangeEvent<HTMLInputElement>) => { | ||
258 | + setTotalDay(e.target.value); | ||
259 | + }; | ||
260 | + | ||
261 | + const onSetNextStepPrescribe = () => { | ||
262 | + if(prescribeMedicine) setPrescribeModalStep(prescribeModalStep + 1); | ||
263 | + else Alert.onWarning('먼저 처방할 약을 선택해야 합니다.', () => null); | ||
264 | + }; | ||
265 | + | ||
266 | + const onSetPrevStepPrescribe = () => { | ||
267 | + if(prescribeModalStep > 1) setPrescribeModalStep(prescribeModalStep - 1); | ||
268 | + }; | ||
269 | + | ||
243 | const onPrescribeSubmit = async() => { | 270 | const onPrescribeSubmit = async() => { |
244 | - //toDo : 처방해서, QR코드 생성 | 271 | + const onPrescribeMedicine = async () => { |
245 | - Alert.onWarning('작업 중입니다.', () => null); | 272 | + setLoading(true); |
273 | + try { | ||
274 | + const res = await doctorApi.prescribeMedicine(token, { | ||
275 | + patientId : patientDetail.profile.userId, | ||
276 | + medicineId : prescribeMedicine.medicineId, | ||
277 | + dailyDosage, | ||
278 | + totalDosage : String(parseInt(totalDay) * parseInt(dailyDosage)), | ||
279 | + }); | ||
280 | + | ||
281 | + if(res.statusText === 'OK') { | ||
282 | + setQrcodeUrl(res.data.qrCode); | ||
283 | + setLoading(false); | ||
284 | + Alert.onSuccess('처방 정보가 생성 되었습니다.', () => onSetNextStepPrescribe()); | ||
285 | + } | ||
286 | + } catch(e : any) { | ||
287 | + setLoading(false); | ||
288 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); | ||
289 | + } | ||
290 | + }; | ||
291 | + | ||
292 | + Alert.onCheck(`${prescribeMedicine.name}(일 복용량:${dailyDosage})\n을 ${totalDay}일동안 처방하시겠습니까?`, async () => { | ||
293 | + await onPrescribeMedicine(); | ||
294 | + }, () => null); | ||
295 | + }; | ||
296 | + | ||
297 | + const onPrintQrcode = async(divId : string) => { | ||
298 | + const printContent : any = document.getElementById(divId); | ||
299 | + const windowOpen : any = window.open('', '_blank'); | ||
300 | + | ||
301 | + //toDo : 현재 인증되지 않은 사용자(=http)이기 때문에, GCS에서 signed url을 불러와도 만료되어, 이미지가 정상 표시 안됨 : 해결 필요 | ||
302 | + windowOpen.document.writeln(printContent.innerHTML); | ||
303 | + windowOpen.document.close(); | ||
304 | + windowOpen.focus(); | ||
305 | + windowOpen.print(); | ||
306 | + windowOpen.close(); | ||
246 | }; | 307 | }; |
247 | 308 | ||
248 | const onPrescribeCancel = () => { | 309 | const onPrescribeCancel = () => { |
... | @@ -289,21 +350,30 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { | ... | @@ -289,21 +350,30 @@ const DoctorMenuContainer = (props : DoctorMenuProps) => { |
289 | 350 | ||
290 | newPatientRegisterModal = {newPatientRegisterModal} | 351 | newPatientRegisterModal = {newPatientRegisterModal} |
291 | setNewPatientRegisterModal = {setNewPatientRegisterModal} | 352 | setNewPatientRegisterModal = {setNewPatientRegisterModal} |
292 | - newPatientSearchId = {newPatientSearchId} | 353 | + newPatientSearchContact = {newPatientSearchContact} |
293 | - onSetNewPatientSearchId = {onSetNewPatientSearchId} | 354 | + onSetNewPatientSearchContact = {onSetNewPatientSearchContact} |
294 | - onSearchNewPatientByEmail = {onSearchNewPatientByEmail} | 355 | + onSearchNewPatientByContact = {onSearchNewPatientByContact} |
295 | onRegisterNewPatient = {onRegisterNewPatient} | 356 | onRegisterNewPatient = {onRegisterNewPatient} |
296 | onCloseModal = {onCloseModal} | 357 | onCloseModal = {onCloseModal} |
297 | 358 | ||
298 | prescribeModal = {prescribeModal} | 359 | prescribeModal = {prescribeModal} |
360 | + prescribeModalStep = {prescribeModalStep} | ||
361 | + onSetNextStepPrescribe = {onSetNextStepPrescribe} | ||
362 | + onSetPrevStepPrescribe = {onSetPrevStepPrescribe} | ||
299 | setPrescribeModal = {setPrescribeModal} | 363 | setPrescribeModal = {setPrescribeModal} |
300 | searchMedicineKeyword = {searchMedicineKeyword} | 364 | searchMedicineKeyword = {searchMedicineKeyword} |
301 | onSetSearchMedicineKeyword = {onSetSearchMedicineKeyword} | 365 | onSetSearchMedicineKeyword = {onSetSearchMedicineKeyword} |
302 | medicineList = {medicineList} | 366 | medicineList = {medicineList} |
303 | searchMedicine = {searchMedicine} | 367 | searchMedicine = {searchMedicine} |
304 | prescribeMedicine = {prescribeMedicine} | 368 | prescribeMedicine = {prescribeMedicine} |
369 | + dailyDosage = {dailyDosage} | ||
370 | + onSetDailyDosage = {onSetDailyDosage} | ||
371 | + totalDay = {totalDay} | ||
372 | + onSetTotalDay = {onSetTotalDay} | ||
373 | + qrcodeUrl = {qrcodeUrl} | ||
305 | setPrescribeMedicine = {setPrescribeMedicine} | 374 | setPrescribeMedicine = {setPrescribeMedicine} |
306 | onPrescribeSubmit = {onPrescribeSubmit} | 375 | onPrescribeSubmit = {onPrescribeSubmit} |
376 | + onPrintQrcode = {onPrintQrcode} | ||
307 | onPrescribeCancel = {onPrescribeCancel} | 377 | onPrescribeCancel = {onPrescribeCancel} |
308 | 378 | ||
309 | newPatientSearchResult = {newPatientSearchResult} | 379 | newPatientSearchResult = {newPatientSearchResult} | ... | ... |
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | 2 | ||
3 | +import Modal from '../../../components/Modal'; | ||
4 | + | ||
3 | import * as styled from './DoctorMenuStyled'; | 5 | import * as styled from './DoctorMenuStyled'; |
4 | 6 | ||
5 | const medicineImg = '/static/img/medicine.png'; | 7 | const medicineImg = '/static/img/medicine.png'; |
6 | const addButton = '/static/img/plus.png'; | 8 | const addButton = '/static/img/plus.png'; |
7 | const lensImg = '/static/img/lens.png'; | 9 | const lensImg = '/static/img/lens.png'; |
8 | -const closeButton = '/static/img/close.png'; | ||
9 | const edit = '/static/img/edit.png'; | 10 | const edit = '/static/img/edit.png'; |
10 | const refreshing = '/static/img/refreshing.png'; | 11 | const refreshing = '/static/img/refreshing.png'; |
11 | const check = '/static/img/check.png'; | 12 | const check = '/static/img/check.png'; |
... | @@ -18,7 +19,7 @@ interface DoctorMenuProps { | ... | @@ -18,7 +19,7 @@ interface DoctorMenuProps { |
18 | userNm : string; | 19 | userNm : string; |
19 | doctorType : string | null; | 20 | doctorType : string | null; |
20 | contact : string; | 21 | contact : string; |
21 | - userAge : number | null; | 22 | + birth : number | null; |
22 | patientInfo : string; | 23 | patientInfo : string; |
23 | }; | 24 | }; |
24 | searchPatientKeyword : string; | 25 | searchPatientKeyword : string; |
... | @@ -38,9 +39,9 @@ interface DoctorMenuProps { | ... | @@ -38,9 +39,9 @@ interface DoctorMenuProps { |
38 | 39 | ||
39 | newPatientRegisterModal : boolean; | 40 | newPatientRegisterModal : boolean; |
40 | setNewPatientRegisterModal : any; | 41 | setNewPatientRegisterModal : any; |
41 | - newPatientSearchId: string; | 42 | + newPatientSearchContact: string; |
42 | - onSetNewPatientSearchId : React.ChangeEventHandler<HTMLInputElement>; | 43 | + onSetNewPatientSearchContact : React.ChangeEventHandler<HTMLInputElement>; |
43 | - onSearchNewPatientByEmail : () => void; | 44 | + onSearchNewPatientByContact : () => void; |
44 | onRegisterNewPatient : () => void; | 45 | onRegisterNewPatient : () => void; |
45 | onCloseModal : () => void; | 46 | onCloseModal : () => void; |
46 | 47 | ||
... | @@ -48,6 +49,10 @@ interface DoctorMenuProps { | ... | @@ -48,6 +49,10 @@ interface DoctorMenuProps { |
48 | 49 | ||
49 | prescribeModal : boolean; | 50 | prescribeModal : boolean; |
50 | setPrescribeModal : any; | 51 | setPrescribeModal : any; |
52 | + prescribeModalStep : number; | ||
53 | + onSetNextStepPrescribe : () => void; | ||
54 | + onSetPrevStepPrescribe : () => void; | ||
55 | + | ||
51 | searchMedicineKeyword : string; | 56 | searchMedicineKeyword : string; |
52 | onSetSearchMedicineKeyword : React.ChangeEventHandler<HTMLInputElement>; | 57 | onSetSearchMedicineKeyword : React.ChangeEventHandler<HTMLInputElement>; |
53 | 58 | ||
... | @@ -57,7 +62,15 @@ interface DoctorMenuProps { | ... | @@ -57,7 +62,15 @@ interface DoctorMenuProps { |
57 | prescribeMedicine : any; | 62 | prescribeMedicine : any; |
58 | setPrescribeMedicine : (arg0 : any) => void; | 63 | setPrescribeMedicine : (arg0 : any) => void; |
59 | 64 | ||
65 | + dailyDosage : string; | ||
66 | + onSetDailyDosage : React.ChangeEventHandler<HTMLInputElement>; | ||
67 | + totalDay : string; | ||
68 | + onSetTotalDay : React.ChangeEventHandler<HTMLInputElement>; | ||
69 | + | ||
70 | + qrcodeUrl : string | null; | ||
71 | + | ||
60 | onPrescribeSubmit : () => void; | 72 | onPrescribeSubmit : () => void; |
73 | + onPrintQrcode : (arg0 : string) => void; | ||
61 | onPrescribeCancel : () => void; | 74 | onPrescribeCancel : () => void; |
62 | } | 75 | } |
63 | 76 | ||
... | @@ -65,188 +78,227 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { | ... | @@ -65,188 +78,227 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { |
65 | return ( | 78 | return ( |
66 | <styled.Container> | 79 | <styled.Container> |
67 | { | 80 | { |
68 | - props.newPatientRegisterModal ? | 81 | + props.newPatientRegisterModal ? |
69 | - <styled.ModalContainer> | 82 | + <Modal onModalClose = {() => props.setNewPatientRegisterModal(false)}> |
70 | - <styled.ModalClsButtonWrapper> | 83 | + <> |
71 | - <styled.ModalClsButton | 84 | + <styled.NewPatientRegisterTitle>새 환자 등록</styled.NewPatientRegisterTitle> |
72 | - onClick = {() => props.setNewPatientRegisterModal(false)} | 85 | + <styled.NewPatientSearchWrapper> |
86 | + <styled.NewPatientSearchInput | ||
87 | + placeholder = '환자의 연락처를 입력하세요.' | ||
88 | + value = {props.newPatientSearchContact} | ||
89 | + onChange = {props.onSetNewPatientSearchContact} | ||
90 | + /> | ||
91 | + <styled.NewPatientSearchButton | ||
92 | + onClick = {props.onSearchNewPatientByContact} | ||
73 | > | 93 | > |
74 | - <styled.ModalClsButtonImg src = {closeButton}/> | 94 | + <styled.NewPatientSearchButtonImg src = {lensImg}/> |
75 | - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | 95 | + </styled.NewPatientSearchButton> |
76 | - </styled.ModalClsButton> | 96 | + </styled.NewPatientSearchWrapper> |
77 | - </styled.ModalClsButtonWrapper> | 97 | + <styled.NewPatientSearchResultWrapper> |
78 | - <styled.ModalContentWrapper> | 98 | + { |
79 | - <styled.ModalContent> | 99 | + props.newPatientSearchResult ? |
80 | - <styled.NewPatientRegisterTitle>새 환자 등록</styled.NewPatientRegisterTitle> | 100 | + <styled.NewPatientSearchResult> |
81 | - <styled.NewPatientSearchWrapper> | 101 | + <styled.NewPatientSearchResultInfoWrapper> |
82 | - <styled.NewPatientSearchInput | 102 | + <styled.NewPatientSearchResultInfo> |
83 | - placeholder = '환자 이메일을 입력하세요.' | 103 | + 이름 : |
84 | - value = {props.newPatientSearchId} | 104 | + <styled.NewPatientSearchResultInfoText> |
85 | - onChange = {props.onSetNewPatientSearchId} | 105 | + {props.newPatientSearchResult.userNm} |
86 | - /> | 106 | + </styled.NewPatientSearchResultInfoText> |
87 | - <styled.NewPatientSearchButton | 107 | + </styled.NewPatientSearchResultInfo> |
88 | - onClick = {props.onSearchNewPatientByEmail} | 108 | + <styled.NewPatientSearchResultInfo> |
89 | - > | 109 | + 생년월일 : |
90 | - <styled.NewPatientSearchButtonImg src = {lensImg}/> | 110 | + <styled.NewPatientSearchResultInfoText> |
91 | - </styled.NewPatientSearchButton> | 111 | + {props.newPatientSearchResult.birth} |
92 | - </styled.NewPatientSearchWrapper> | 112 | + </styled.NewPatientSearchResultInfoText> |
93 | - <styled.NewPatientSearchResultWrapper> | 113 | + </styled.NewPatientSearchResultInfo> |
94 | - { | 114 | + </styled.NewPatientSearchResultInfoWrapper> |
95 | - props.newPatientSearchResult ? | 115 | + </styled.NewPatientSearchResult> : |
96 | - <styled.NewPatientSearchResult> | 116 | + '🤔검색 결과가 없습니다.' |
97 | - <styled.NewPatientSearchResultInfoWrapper> | 117 | + } |
98 | - <styled.NewPatientSearchResultInfo>이름 : </styled.NewPatientSearchResultInfo> | 118 | + </styled.NewPatientSearchResultWrapper> |
99 | - <styled.NewPatientSearchResultInfoText> | 119 | + <styled.NewPatientRegisterButtonWrapper> |
100 | - {props.newPatientSearchResult.patientNm} | 120 | + <styled.NewPatientRegisterButton |
101 | - </styled.NewPatientSearchResultInfoText> | 121 | + onClick = {props.onRegisterNewPatient} |
102 | - </styled.NewPatientSearchResultInfoWrapper> | 122 | + > |
103 | - </styled.NewPatientSearchResult> : | 123 | + 확인 |
104 | - '🤔검색 결과가 없습니다.' | 124 | + </styled.NewPatientRegisterButton> |
105 | - } | 125 | + <styled.NewPatientRegisterButton |
106 | - </styled.NewPatientSearchResultWrapper> | 126 | + onClick = {props.onCloseModal} |
107 | - <styled.NewPatientRegisterButtonWrapper> | 127 | + > |
108 | - <styled.NewPatientRegisterButton | 128 | + 취소 |
109 | - onClick = {props.onRegisterNewPatient} | 129 | + </styled.NewPatientRegisterButton> |
110 | - > | 130 | + </styled.NewPatientRegisterButtonWrapper> |
111 | - 확인 | 131 | + </> |
112 | - </styled.NewPatientRegisterButton> | 132 | + </Modal> : null |
113 | - <styled.NewPatientRegisterButton | ||
114 | - onClick = {props.onCloseModal} | ||
115 | - > | ||
116 | - 취소 | ||
117 | - </styled.NewPatientRegisterButton> | ||
118 | - </styled.NewPatientRegisterButtonWrapper> | ||
119 | - </styled.ModalContent> | ||
120 | - </styled.ModalContentWrapper> | ||
121 | - <styled.ModalClsButtonWrapper/> | ||
122 | - </styled.ModalContainer> : null | ||
123 | } | 133 | } |
124 | { | 134 | { |
125 | props.editModal ? | 135 | props.editModal ? |
126 | - <styled.ModalContainer> | 136 | + <Modal onModalClose = {() => props.setEditModal(false)}> |
127 | - <styled.ModalClsButtonWrapper> | 137 | + <> |
128 | - <styled.ModalClsButton | 138 | + <styled.PatientInfoViewContainer> |
129 | - onClick = {() => props.setEditModal(false)} | 139 | + <styled.PatientInfoPatientNmWrapper> |
140 | + <styled.PatientInfoPatientNmInfo>이름 : </styled.PatientInfoPatientNmInfo> | ||
141 | + <styled.PatientInfoPatientNm>{props.info.userNm}</styled.PatientInfoPatientNm> | ||
142 | + </styled.PatientInfoPatientNmWrapper> | ||
143 | + <styled.PatientInfoView> | ||
144 | + | ||
145 | + { | ||
146 | + props.info.patientInfo.split('\n\n').map((patientInfoText : string) => { | ||
147 | + return ( | ||
148 | + <div key = {patientInfoText}> | ||
149 | + {patientInfoText}<br/><br/> | ||
150 | + </div> | ||
151 | + ) | ||
152 | + }) | ||
153 | + } | ||
154 | + </styled.PatientInfoView> | ||
155 | + </styled.PatientInfoViewContainer> | ||
156 | + <styled.PatientInfoEditWrapper> | ||
157 | + <styled.PatientInfoEditInput | ||
158 | + value = {props.editPatientInfo} | ||
159 | + onChange = {props.onEditPatientInfo} | ||
160 | + /> | ||
161 | + </styled.PatientInfoEditWrapper> | ||
162 | + <styled.PatientInfoEditButtonWrapper> | ||
163 | + <styled.PatientInfoEditButton | ||
164 | + onClick = {props.onSubmitPatientInfo} | ||
130 | > | 165 | > |
131 | - <styled.ModalClsButtonImg src = {closeButton}/> | 166 | + 확인 |
132 | - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | 167 | + </styled.PatientInfoEditButton> |
133 | - </styled.ModalClsButton> | 168 | + <styled.PatientInfoEditButton |
134 | - </styled.ModalClsButtonWrapper> | 169 | + onClick = {props.onCloseModal} |
135 | - <styled.ModalContentWrapper> | 170 | + > |
136 | - <styled.ModalContent> | 171 | + 취소 |
137 | - <styled.PatientInfoViewContainer> | 172 | + </styled.PatientInfoEditButton> |
138 | - <styled.PatientInfoPatientNmWrapper> | 173 | + </styled.PatientInfoEditButtonWrapper> |
139 | - <styled.PatientInfoPatientNmInfo>이름 : </styled.PatientInfoPatientNmInfo> | 174 | + </> |
140 | - <styled.PatientInfoPatientNm>{props.info.userNm}</styled.PatientInfoPatientNm> | 175 | + </Modal> : null |
141 | - </styled.PatientInfoPatientNmWrapper> | ||
142 | - <styled.PatientInfoView> | ||
143 | - | ||
144 | - { | ||
145 | - props.info.patientInfo.split('\n\n').map((patientInfoText : string) => { | ||
146 | - return ( | ||
147 | - <div key = {patientInfoText}> | ||
148 | - {patientInfoText}<br/><br/> | ||
149 | - </div> | ||
150 | - ) | ||
151 | - }) | ||
152 | - } | ||
153 | - </styled.PatientInfoView> | ||
154 | - </styled.PatientInfoViewContainer> | ||
155 | - <styled.PatientInfoEditWrapper> | ||
156 | - <styled.PatientInfoEditInput | ||
157 | - value = {props.editPatientInfo} | ||
158 | - onChange = {props.onEditPatientInfo} | ||
159 | - /> | ||
160 | - </styled.PatientInfoEditWrapper> | ||
161 | - <styled.PatientInfoEditButtonWrapper> | ||
162 | - <styled.PatientInfoEditButton | ||
163 | - onClick = {props.onSubmitPatientInfo} | ||
164 | - > | ||
165 | - 확인 | ||
166 | - </styled.PatientInfoEditButton> | ||
167 | - <styled.PatientInfoEditButton | ||
168 | - onClick = {props.onCloseModal} | ||
169 | - > | ||
170 | - 취소 | ||
171 | - </styled.PatientInfoEditButton> | ||
172 | - </styled.PatientInfoEditButtonWrapper> | ||
173 | - </styled.ModalContent> | ||
174 | - </styled.ModalContentWrapper> | ||
175 | - <styled.ModalClsButtonWrapper/> | ||
176 | - </styled.ModalContainer> : null | ||
177 | } | 176 | } |
178 | { | 177 | { |
179 | props.prescribeModal ? | 178 | props.prescribeModal ? |
180 | - <styled.ModalContainer> | 179 | + <Modal onModalClose = {props.onCloseModal}> |
181 | - <styled.ModalClsButtonWrapper> | 180 | + <> |
182 | - <styled.ModalClsButton | 181 | + <styled.MedicineSearchTitle> |
183 | - onClick = {props.onCloseModal} | 182 | + { |
183 | + props.prescribeModalStep === 1 ? | ||
184 | + '약 검색' : | ||
185 | + props.prescribeModalStep === 2 ? | ||
186 | + '복용량 입력' : | ||
187 | + '처방 정보 QR코드' | ||
188 | + } | ||
189 | + </styled.MedicineSearchTitle> | ||
190 | + { | ||
191 | + props.prescribeModalStep === 1 ? | ||
192 | + <> | ||
193 | + <styled.MedicineSearchInputWrapper> | ||
194 | + <styled.MedicineSearchInput | ||
195 | + placeholder = '증상, 또는 약 이름을 검색하세요.' | ||
196 | + onChange = {props.onSetSearchMedicineKeyword} | ||
197 | + value = {props.searchMedicineKeyword} | ||
198 | + /> | ||
199 | + <styled.MedicineSearchButton | ||
200 | + onClick = {props.searchMedicine} | ||
201 | + > | ||
202 | + <styled.MedicineSearchButtonImg src = {lensImg}/> | ||
203 | + </styled.MedicineSearchButton> | ||
204 | + </styled.MedicineSearchInputWrapper> | ||
205 | + <styled.MedicineSearchResultWrapper> | ||
206 | + { | ||
207 | + props.medicineList.length ? | ||
208 | + props.medicineList.map((medicine : any) => { | ||
209 | + return ( | ||
210 | + <styled.MedicineSearchResultEach | ||
211 | + key = {medicine.medicineId} | ||
212 | + onClick = {() => props.setPrescribeMedicine( | ||
213 | + props.prescribeMedicine && props.prescribeMedicine.medicineId === medicine.medicineId ? | ||
214 | + null : medicine | ||
215 | + )} | ||
216 | + > | ||
217 | + <styled.MedicineSearchResultEachInfo> | ||
218 | + {medicine.name} | ||
219 | + </styled.MedicineSearchResultEachInfo> | ||
220 | + <styled.MedicineSearchButtonImg | ||
221 | + src = { | ||
222 | + props.prescribeMedicine && props.prescribeMedicine.medicineId === medicine.medicineId ? | ||
223 | + check : uncheck | ||
224 | + } | ||
225 | + /> | ||
226 | + </styled.MedicineSearchResultEach> | ||
227 | + ) | ||
228 | + }) : | ||
229 | + <styled.NothingWrapper style = {{fontSize : 13,}}> | ||
230 | + 🤔검색 결과가 없습니다. | ||
231 | + </styled.NothingWrapper> | ||
232 | + } | ||
233 | + </styled.MedicineSearchResultWrapper> | ||
234 | + </> | ||
235 | + : | ||
236 | + props.prescribeModalStep === 2 ? | ||
237 | + <styled.MedicineDosageSetWrapper> | ||
238 | + <styled.MedicineDosageInfo> | ||
239 | + *하루 복용량을 입력하세요. | ||
240 | + </styled.MedicineDosageInfo> | ||
241 | + <styled.MedicineDosageInput | ||
242 | + value = {props.dailyDosage} | ||
243 | + onChange = {props.onSetDailyDosage} | ||
244 | + min = {1} | ||
245 | + max = {3} | ||
246 | + /> | ||
247 | + <styled.MedicineDosageInfo> | ||
248 | + *총 며칠 분량인지 입력하세요. | ||
249 | + </styled.MedicineDosageInfo> | ||
250 | + <styled.MedicineDosageInput | ||
251 | + value = {props.totalDay} | ||
252 | + onChange = {props.onSetTotalDay} | ||
253 | + /> | ||
254 | + </styled.MedicineDosageSetWrapper> | ||
255 | + : | ||
256 | + <styled.MedicineQRCodeWrapper | ||
257 | + id = 'qrCodePrint' | ||
184 | > | 258 | > |
185 | - <styled.ModalClsButtonImg src = {closeButton}/> | 259 | + <styled.MedicineQRCodeInfo> |
186 | - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | 260 | + *어플리케이션에서 QR코드를 스캔하면 약병에 약이 등록됩니다. |
187 | - </styled.ModalClsButton> | 261 | + </styled.MedicineQRCodeInfo> |
188 | - </styled.ModalClsButtonWrapper> | ||
189 | - <styled.ModalContentWrapper> | ||
190 | - <styled.ModalContent> | ||
191 | - <styled.MedicineSearchTitle> | ||
192 | - 약 검색 | ||
193 | - </styled.MedicineSearchTitle> | ||
194 | - <styled.MedicineSearchInputWrapper> | ||
195 | - <styled.MedicineSearchInput | ||
196 | - placeholder = '증상, 또는 약 이름을 검색하세요.' | ||
197 | - onChange = {props.onSetSearchMedicineKeyword} | ||
198 | - value = {props.searchMedicineKeyword} | ||
199 | - /> | ||
200 | - <styled.MedicineSearchButton | ||
201 | - onClick = {props.searchMedicine} | ||
202 | - > | ||
203 | - <styled.MedicineSearchButtonImg src = {lensImg}/> | ||
204 | - </styled.MedicineSearchButton> | ||
205 | - </styled.MedicineSearchInputWrapper> | ||
206 | - <styled.MedicineSearchResultWrapper> | ||
207 | { | 262 | { |
208 | - props.medicineList.length ? | 263 | + props.qrcodeUrl ? |
209 | - props.medicineList.map((medicine : any) => { | 264 | + <styled.MedicineQRCode |
210 | - return ( | 265 | + src = {props.qrcodeUrl}/> : null |
211 | - <styled.MedicineSearchResultEach | ||
212 | - key = {medicine.medicineId} | ||
213 | - onClick = {() => props.setPrescribeMedicine(medicine)} | ||
214 | - > | ||
215 | - <styled.MedicineSearchResultEachInfo> | ||
216 | - {medicine.name} | ||
217 | - </styled.MedicineSearchResultEachInfo> | ||
218 | - <styled.MedicineSearchButtonImg | ||
219 | - src = { | ||
220 | - props.prescribeMedicine && props.prescribeMedicine.medicineId === medicine.medicineId ? | ||
221 | - check : uncheck | ||
222 | - } | ||
223 | - /> | ||
224 | - </styled.MedicineSearchResultEach> | ||
225 | - ) | ||
226 | - }) : | ||
227 | - <styled.NothingWrapper style = {{fontSize : 13,}}> | ||
228 | - 🤔검색 결과가 없습니다. | ||
229 | - </styled.NothingWrapper> | ||
230 | } | 266 | } |
231 | - </styled.MedicineSearchResultWrapper> | 267 | + </styled.MedicineQRCodeWrapper> |
232 | - <styled.MedicinePrescribeButtonWrapper> | 268 | + } |
233 | - <styled.MedicinePrescribeButton | 269 | + <styled.MedicinePrescribeButtonWrapper> |
234 | - isClose = {false} | 270 | + { |
235 | - onClick = {props.onPrescribeSubmit} | 271 | + props.prescribeModalStep === 1 ? |
236 | - > | 272 | + <styled.MedicinePrescribeButton |
237 | - 처방 | 273 | + isClose = {false} |
238 | - </styled.MedicinePrescribeButton> | 274 | + onClick = {props.onSetNextStepPrescribe} |
239 | - <styled.MedicinePrescribeButton | 275 | + > |
240 | - isClose = {true} | 276 | + 다음 단계 |
241 | - onClick = {props.onPrescribeCancel} | 277 | + </styled.MedicinePrescribeButton> : |
242 | - > | 278 | + props.prescribeModalStep === 2 ? |
243 | - 취소 | 279 | + <styled.MedicinePrescribeButton |
244 | - </styled.MedicinePrescribeButton> | 280 | + isClose = {false} |
245 | - </styled.MedicinePrescribeButtonWrapper> | 281 | + onClick = {props.onPrescribeSubmit} |
246 | - </styled.ModalContent> | 282 | + > |
247 | - </styled.ModalContentWrapper> | 283 | + 처방 |
248 | - <styled.ModalClsButtonWrapper/> | 284 | + </styled.MedicinePrescribeButton> |
249 | - </styled.ModalContainer> : null | 285 | + : |
286 | + <styled.MedicinePrescribeButton | ||
287 | + isClose = {false} | ||
288 | + onClick = {() => props.onPrintQrcode('qrCodePrint')} | ||
289 | + > | ||
290 | + 출력 | ||
291 | + </styled.MedicinePrescribeButton> | ||
292 | + } | ||
293 | + <styled.MedicinePrescribeButton | ||
294 | + isClose = {true} | ||
295 | + onClick = {props.onPrescribeCancel} | ||
296 | + > | ||
297 | + 취소 | ||
298 | + </styled.MedicinePrescribeButton> | ||
299 | + </styled.MedicinePrescribeButtonWrapper> | ||
300 | + </> | ||
301 | + </Modal> : null | ||
250 | } | 302 | } |
251 | <styled.InfoAndSearchWrapper> | 303 | <styled.InfoAndSearchWrapper> |
252 | <styled.InfoWrapper> | 304 | <styled.InfoWrapper> |
... | @@ -287,7 +339,7 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { | ... | @@ -287,7 +339,7 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { |
287 | </styled.InfoEachWrapper> | 339 | </styled.InfoEachWrapper> |
288 | <styled.InfoEachWrapper> | 340 | <styled.InfoEachWrapper> |
289 | <styled.InfoEachTopic>생년월일</styled.InfoEachTopic> | 341 | <styled.InfoEachTopic>생년월일</styled.InfoEachTopic> |
290 | - <styled.InfoEachText>{props.info.userAge}세</styled.InfoEachText> | 342 | + <styled.InfoEachText>{props.info.birth}</styled.InfoEachText> |
291 | </styled.InfoEachWrapper> | 343 | </styled.InfoEachWrapper> |
292 | <styled.InfoEachWrapper> | 344 | <styled.InfoEachWrapper> |
293 | <styled.InfoEachTopic>연락처</styled.InfoEachTopic> | 345 | <styled.InfoEachTopic>연락처</styled.InfoEachTopic> |
... | @@ -351,7 +403,7 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { | ... | @@ -351,7 +403,7 @@ const DoctorMenuPresenter = (props : DoctorMenuProps) => { |
351 | onClick = {() => props.onFetchPatientDetail(patient.userId)} | 403 | onClick = {() => props.onFetchPatientDetail(patient.userId)} |
352 | > | 404 | > |
353 | <styled.SearchResultEachText isLast = {false}>{patient.userNm}</styled.SearchResultEachText> | 405 | <styled.SearchResultEachText isLast = {false}>{patient.userNm}</styled.SearchResultEachText> |
354 | - <styled.SearchResultEachText isLast = {false}>{patient.userAge}세</styled.SearchResultEachText> | 406 | + <styled.SearchResultEachText isLast = {false}>{patient.birth}</styled.SearchResultEachText> |
355 | <styled.SearchResultEachText isLast = {true}>{patient.contact}</styled.SearchResultEachText> | 407 | <styled.SearchResultEachText isLast = {true}>{patient.contact}</styled.SearchResultEachText> |
356 | </styled.SearchResultEach> | 408 | </styled.SearchResultEach> |
357 | ) | 409 | ) | ... | ... |
1 | -import styled, { keyframes } from 'styled-components'; | 1 | +import styled from 'styled-components'; |
2 | - | ||
3 | - | ||
4 | -const ModalOn = keyframes ` | ||
5 | - 0% { | ||
6 | - background-color : rgba(52, 52, 52, .0); | ||
7 | - } | ||
8 | - 20% { | ||
9 | - background-color : rgba(52, 52, 52, .2); | ||
10 | - } | ||
11 | - 40% { | ||
12 | - background-color : rgba(52, 52, 52, .4); | ||
13 | - } | ||
14 | - 60% { | ||
15 | - background-color : rgba(52, 52, 52, .5); | ||
16 | - } | ||
17 | - 80% { | ||
18 | - background-color : rgba(52, 52, 52, .6); | ||
19 | - } | ||
20 | - 100% { | ||
21 | - background-color : rgba(52, 52, 52, .7); | ||
22 | - } | ||
23 | - | ||
24 | -`; | ||
25 | 2 | ||
26 | 3 | ||
27 | export const Container = styled.div ` | 4 | export const Container = styled.div ` |
... | @@ -32,93 +9,6 @@ export const Container = styled.div ` | ... | @@ -32,93 +9,6 @@ export const Container = styled.div ` |
32 | justify-content : center; | 9 | justify-content : center; |
33 | `; | 10 | `; |
34 | 11 | ||
35 | -export const ModalContainer = styled.div ` | ||
36 | - height : 100%; | ||
37 | - width : 100%; | ||
38 | - z-index : 99; | ||
39 | - position : absolute; | ||
40 | - | ||
41 | - display : flex; | ||
42 | - flex-direction : column; | ||
43 | - | ||
44 | - animation : ${ModalOn} .5s; | ||
45 | - | ||
46 | - background-color : rgba(52, 52, 52, .7); | ||
47 | - | ||
48 | -`; | ||
49 | - | ||
50 | -export const ModalClsButtonWrapper = styled.div ` | ||
51 | - flex : 1; | ||
52 | - | ||
53 | - display : flex; | ||
54 | - | ||
55 | - justify-content : flex-end; | ||
56 | - align-items : center; | ||
57 | - padding : 0 20px; | ||
58 | - | ||
59 | - border : none; | ||
60 | - background-color : transprent; | ||
61 | -`; | ||
62 | - | ||
63 | -export const ModalClsButton = styled.button ` | ||
64 | - border : none; | ||
65 | - background-color : transparent; | ||
66 | - | ||
67 | - cursor : pointer; | ||
68 | - | ||
69 | - color : #fff; | ||
70 | - | ||
71 | - display : flex; | ||
72 | - flex-direction : row; | ||
73 | - | ||
74 | - justify-content : center; | ||
75 | - align-items : center; | ||
76 | - | ||
77 | - transition : .25s all; | ||
78 | - &:hover { | ||
79 | - opacity : .5; | ||
80 | - } | ||
81 | -`; | ||
82 | - | ||
83 | -export const ModalClsButtonImg = styled.img ` | ||
84 | - height : 20px; | ||
85 | - width : 20px; | ||
86 | - | ||
87 | - margin : 0 10px 0 0; | ||
88 | -`; | ||
89 | - | ||
90 | -export const ModalClsButtonText = styled.div ` | ||
91 | - font-size : 18px; | ||
92 | - font-weight : 700; | ||
93 | -`; | ||
94 | - | ||
95 | -export const ModalContentWrapper = styled.div ` | ||
96 | - flex : 8; | ||
97 | - | ||
98 | - display : flex; | ||
99 | - flex-direction : column; | ||
100 | - | ||
101 | - justify-content : center; | ||
102 | - align-items : center; | ||
103 | - | ||
104 | - border : none; | ||
105 | -`; | ||
106 | - | ||
107 | -export const ModalContent = styled.div ` | ||
108 | - width : 600px; | ||
109 | - height : 400px; | ||
110 | - | ||
111 | - background-color : #fff; | ||
112 | - border : 1.2px solid #337DFF; | ||
113 | - border-radius : 5px; | ||
114 | - | ||
115 | - display : flex; | ||
116 | - flex-direction : column; | ||
117 | - | ||
118 | - justify-content : center; | ||
119 | - align-items : center; | ||
120 | -`; | ||
121 | - | ||
122 | export const NewPatientRegisterTitle = styled.div ` | 12 | export const NewPatientRegisterTitle = styled.div ` |
123 | font-size : 20px; | 13 | font-size : 20px; |
124 | font-weight : 700; | 14 | font-weight : 700; |
... | @@ -199,14 +89,18 @@ export const NewPatientSearchResult = styled.div ` | ... | @@ -199,14 +89,18 @@ export const NewPatientSearchResult = styled.div ` |
199 | 89 | ||
200 | export const NewPatientSearchResultInfoWrapper = styled.div ` | 90 | export const NewPatientSearchResultInfoWrapper = styled.div ` |
201 | display : flex; | 91 | display : flex; |
92 | + flex-direction : column; | ||
202 | `; | 93 | `; |
203 | 94 | ||
204 | export const NewPatientSearchResultInfo = styled.div ` | 95 | export const NewPatientSearchResultInfo = styled.div ` |
96 | + display : flex; | ||
97 | + flex-direction : row; | ||
98 | + | ||
205 | font-size : 13px; | 99 | font-size : 13px; |
206 | font-weight : 600; | 100 | font-weight : 600; |
207 | color : #a0a0a0; | 101 | color : #a0a0a0; |
208 | 102 | ||
209 | - margin : 0 5px 0 0; | 103 | + margin : 0 5px 0 5px; |
210 | `; | 104 | `; |
211 | 105 | ||
212 | export const NewPatientSearchResultInfoText = styled.div ` | 106 | export const NewPatientSearchResultInfoText = styled.div ` |
... | @@ -214,6 +108,8 @@ export const NewPatientSearchResultInfoText = styled.div ` | ... | @@ -214,6 +108,8 @@ export const NewPatientSearchResultInfoText = styled.div ` |
214 | color : #343434; | 108 | color : #343434; |
215 | font-weight : 600; | 109 | font-weight : 600; |
216 | letter-spacing : 1px; | 110 | letter-spacing : 1px; |
111 | + | ||
112 | + margin : 0 0 0 5px; | ||
217 | `; | 113 | `; |
218 | 114 | ||
219 | export const NewPatientRegisterButtonWrapper = styled.div ` | 115 | export const NewPatientRegisterButtonWrapper = styled.div ` |
... | @@ -436,6 +332,7 @@ export const MedicineSearchButtonImg = styled.img ` | ... | @@ -436,6 +332,7 @@ export const MedicineSearchButtonImg = styled.img ` |
436 | height : 15px; | 332 | height : 15px; |
437 | width : 15px; | 333 | width : 15px; |
438 | 334 | ||
335 | + transition : .25s all; | ||
439 | `; | 336 | `; |
440 | 337 | ||
441 | export const MedicineSearchResultWrapper = styled.div ` | 338 | export const MedicineSearchResultWrapper = styled.div ` |
... | @@ -499,6 +396,91 @@ export const MedicineSelectButtonImg = styled.img ` | ... | @@ -499,6 +396,91 @@ export const MedicineSelectButtonImg = styled.img ` |
499 | width : 15px; | 396 | width : 15px; |
500 | `; | 397 | `; |
501 | 398 | ||
399 | +export const MedicineDosageSetWrapper = styled.div ` | ||
400 | + width : 80%; | ||
401 | + | ||
402 | + display : flex; | ||
403 | + flex-direction : column; | ||
404 | + | ||
405 | + justify-content : center; | ||
406 | + align-items : center; | ||
407 | + | ||
408 | + border : none; | ||
409 | + | ||
410 | + margin : 20px 0; | ||
411 | + | ||
412 | + height : 200px; | ||
413 | +`; | ||
414 | + | ||
415 | +export const MedicineDosageInfo = styled.div ` | ||
416 | + font-size : 15px; | ||
417 | + font-weight : 500; | ||
418 | + | ||
419 | + color : #a0a0a0; | ||
420 | + | ||
421 | + width : 100%; | ||
422 | + margin : 10px 0 10px 0; | ||
423 | + | ||
424 | + border : none; | ||
425 | + background-color : transparent; | ||
426 | + | ||
427 | + text-align : center; | ||
428 | +`; | ||
429 | + | ||
430 | +export const MedicineDosageInput = styled.input.attrs({ | ||
431 | + type : 'number', | ||
432 | +}) ` | ||
433 | + margin : 0 0 10px 0; | ||
434 | + width : 40%; | ||
435 | + | ||
436 | + padding : 10px 20px; | ||
437 | + color : #337DFF; | ||
438 | + font-size : 20px; | ||
439 | + | ||
440 | + font-weight : 700; | ||
441 | + | ||
442 | + border : none; | ||
443 | + border-bottom : 1px solid #337DFF; | ||
444 | + | ||
445 | + display : flex; | ||
446 | + flex-direction : row; | ||
447 | + | ||
448 | + text-align : center; | ||
449 | + | ||
450 | + transition : .25s all; | ||
451 | +`; | ||
452 | + | ||
453 | +export const MedicineQRCodeWrapper = styled.div ` | ||
454 | + width : 80%; | ||
455 | + height : 200px; | ||
456 | + | ||
457 | + display : flex; | ||
458 | + flex-direction : column; | ||
459 | + | ||
460 | + justify-content : center; | ||
461 | + align-items : center; | ||
462 | + | ||
463 | + margin : 20px 0; | ||
464 | + | ||
465 | + border : none; | ||
466 | +`; | ||
467 | + | ||
468 | +export const MedicineQRCodeInfo = styled.div ` | ||
469 | + font-size : 15px; | ||
470 | + font-weight : 500; | ||
471 | + | ||
472 | + color : #a0a0a0; | ||
473 | + | ||
474 | + text-align : center; | ||
475 | +`; | ||
476 | + | ||
477 | +export const MedicineQRCode = styled.img ` | ||
478 | + margin : 10px 0 0 0; | ||
479 | + | ||
480 | + height : 170px; | ||
481 | + width : 170px; | ||
482 | +`; | ||
483 | + | ||
502 | export const MedicinePrescribeButtonWrapper = styled.div ` | 484 | export const MedicinePrescribeButtonWrapper = styled.div ` |
503 | margin : 20px 0 0 0; | 485 | margin : 20px 0 0 0; |
504 | 486 | ... | ... |
... | @@ -17,11 +17,14 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -17,11 +17,14 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
17 | 17 | ||
18 | const token = useRecoilValue(recoilUtil.token); | 18 | const token = useRecoilValue(recoilUtil.token); |
19 | 19 | ||
20 | - const [doctorRegReqList, setDoctorRegReqList] = useState<any>([]); | 20 | + const [doctorList, setDoctorList] = useState<any>([]); |
21 | + | ||
22 | + const [viewType, setViewType] = useState<string>('reg'); | ||
21 | 23 | ||
22 | const [doctorDetail, setDoctorDetail] = useState<any>({}); | 24 | const [doctorDetail, setDoctorDetail] = useState<any>({}); |
23 | const [modalUp, setModalUp] = useState<boolean>(false); | 25 | const [modalUp, setModalUp] = useState<boolean>(false); |
24 | const [validate, setValidate] = useState<string>('W'); | 26 | const [validate, setValidate] = useState<string>('W'); |
27 | + const [validateDoctorLicense, setValidateDoctorLicense] = useState<string>(''); | ||
25 | 28 | ||
26 | 29 | ||
27 | 30 | ||
... | @@ -30,20 +33,29 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -30,20 +33,29 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
30 | setValidate('W'); | 33 | setValidate('W'); |
31 | 34 | ||
32 | try { | 35 | try { |
33 | - await managerApi.getDoctorRegReqList(token) | 36 | + const res = viewType === 'reg' ? await managerApi.getDoctorRegReqList(token) : await managerApi.getDoctorSecReqList(token); |
34 | - .then((res : any) => { | 37 | + if(res.statusText === 'OK') { |
35 | - if(res.statusText === 'OK') { | 38 | + setDoctorList(res.data.doctorList); |
36 | - setDoctorRegReqList(res.data.doctorRegReqList); | 39 | + } else { |
37 | - } | 40 | + Alert.onError(res.data.error, () => null); |
38 | - }).catch(err => { | 41 | + } |
39 | - Alert.onError(err.response.data.error, () => null); | 42 | + } catch(e : any) { |
40 | - }) | 43 | + console.log(e); |
41 | - } catch(e) { | 44 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); |
42 | - Alert.onError(e.response.data.error, () => null); | ||
43 | } | 45 | } |
44 | }; | 46 | }; |
45 | 47 | ||
48 | + //가입요청 보기 | ||
49 | + const onViewRegList = () => { | ||
50 | + setViewType('reg'); | ||
51 | + }; | ||
52 | + | ||
53 | + //탈퇴요청 보기 | ||
54 | + const onViewSecList = () => { | ||
55 | + setViewType('sec'); | ||
56 | + }; | ||
46 | 57 | ||
58 | + //가입요청 상세보기 | ||
47 | const onViewDetailReq = async (doctorId : string) => { | 59 | const onViewDetailReq = async (doctorId : string) => { |
48 | setValidate('W'); | 60 | setValidate('W'); |
49 | 61 | ||
... | @@ -55,13 +67,23 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -55,13 +67,23 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
55 | setModalUp(true); | 67 | setModalUp(true); |
56 | } | 68 | } |
57 | }) | 69 | }) |
58 | - } catch(e) { | 70 | + } catch(e : any) { |
59 | - Alert.onError(e.response.data.error, () => setModalUp(false)); | 71 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => setModalUp(false)); |
60 | } | 72 | } |
61 | }; | 73 | }; |
62 | 74 | ||
75 | + const onViewLicenseDetail = async (url : string) => { | ||
76 | + const licensePage : any = window.open(url); | ||
77 | + licensePage.focus(); | ||
78 | + }; | ||
79 | + | ||
80 | + //자격 확인 문서를 보고, 면허 번호를 입력하는 함수 | ||
81 | + const onSetValidateDoctorLicense = (e : React.ChangeEvent<HTMLInputElement>) => { | ||
82 | + setValidateDoctorLicense(e.target.value); | ||
83 | + }; | ||
84 | + | ||
63 | //회원 가입 수락 | 85 | //회원 가입 수락 |
64 | - const onAcceptRequest = () => { | 86 | + const onAcceptRegReq = () => { |
65 | if(validate === 'W') { | 87 | if(validate === 'W') { |
66 | Alert.onError('먼저 의사의 자격번호가 유효한지 검증해주세요.', () => null); | 88 | Alert.onError('먼저 의사의 자격번호가 유효한지 검증해주세요.', () => null); |
67 | return; | 89 | return; |
... | @@ -72,14 +94,16 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -72,14 +94,16 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
72 | 94 | ||
73 | const onAccept = async() => { | 95 | const onAccept = async() => { |
74 | try { | 96 | try { |
75 | - await managerApi.acceptDoctorRegReq(token, doctorDetail) | 97 | + await managerApi.acceptDoctorRegReq(token, { |
76 | - .then((res : any) => { | 98 | + doctorId : doctorDetail.doctorId, |
77 | - if(res.statusText === 'OK') { | 99 | + validateDoctorLicense, |
78 | - Alert.onSuccess('회원 등록이 완료되었습니다.', fetchData); | 100 | + }).then((res : any) => { |
79 | - } | 101 | + if(res.statusText === 'OK') { |
80 | - }) | 102 | + Alert.onSuccess('회원 등록이 완료되었습니다.', fetchData); |
81 | - } catch(e) { | 103 | + } |
82 | - Alert.onError(e.response.data.error, () => setModalUp(false)); | 104 | + }); |
105 | + } catch(e : any) { | ||
106 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => setModalUp(false)); | ||
83 | } | 107 | } |
84 | }; | 108 | }; |
85 | 109 | ||
... | @@ -90,58 +114,95 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { | ... | @@ -90,58 +114,95 @@ const ManagerMenuContainer = (props : ManagerMenuProps) => { |
90 | const onRejectRequest = () => { | 114 | const onRejectRequest = () => { |
91 | const onReject = async() => { | 115 | const onReject = async() => { |
92 | try { | 116 | try { |
93 | - await managerApi.rejectDoctorRegReq(token, doctorDetail) | 117 | + await managerApi.rejectDoctorRegReq(token, { |
94 | - .then((res : any) => { | 118 | + doctorId : doctorDetail.doctorId, |
95 | - if(res.statusText === 'OK') { | 119 | + }).then((res : any) => { |
96 | - Alert.onSuccess('회원 등록이 취소되었습니다.', fetchData); | 120 | + if(res.statusText === 'OK') { |
97 | - } | 121 | + Alert.onSuccess('회원 등록이 취소되었습니다.', fetchData); |
98 | - }) | 122 | + } |
99 | - } catch(e) { | 123 | + }); |
100 | - Alert.onError(e.response.data.error, () => setModalUp(false)); | 124 | + } catch(e : any) { |
125 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => setModalUp(false)); | ||
101 | } | 126 | } |
102 | }; | 127 | }; |
103 | 128 | ||
104 | Alert.onCheck('회원 가입 요청을 취소하시겠습니까?', onReject, () => null); | 129 | Alert.onCheck('회원 가입 요청을 취소하시겠습니까?', onReject, () => null); |
105 | }; | 130 | }; |
106 | 131 | ||
132 | + //회원 자격번호 유효 검증 api | ||
107 | const onValidate = async () => { | 133 | const onValidate = async () => { |
108 | try { | 134 | try { |
109 | await managerApi.validateDoctorLicense(token, { | 135 | await managerApi.validateDoctorLicense(token, { |
110 | - doctorLicense : doctorDetail.info.doctorLicense, | 136 | + validateDoctorLicense, |
111 | }).then(res => { | 137 | }).then(res => { |
112 | if(res.statusText === 'OK') { | 138 | if(res.statusText === 'OK') { |
113 | setValidate(res.data.result ? 'Y' : 'N'); | 139 | setValidate(res.data.result ? 'Y' : 'N'); |
114 | } | 140 | } |
115 | }).catch(err => { | 141 | }).catch(err => { |
116 | - Alert.onError(err.response.data, () => { | 142 | + console.log(err); |
143 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => { | ||
117 | setModalUp(false); | 144 | setModalUp(false); |
118 | setValidate('W'); | 145 | setValidate('W'); |
119 | }); | 146 | }); |
120 | }) | 147 | }) |
121 | - } catch(e) { | 148 | + } catch(e : any) { |
122 | - Alert.onError(e.response.data, () => { | 149 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => { |
123 | setModalUp(false); | 150 | setModalUp(false); |
124 | setValidate('W'); | 151 | setValidate('W'); |
125 | }); | 152 | }); |
126 | } | 153 | } |
127 | }; | 154 | }; |
128 | 155 | ||
156 | + const onAcceptSecReq = (doctorId : string) => { | ||
157 | + const onAccept = async () => { | ||
158 | + try { | ||
159 | + const res = await managerApi.acceptDoctorSecReq(token, { | ||
160 | + doctorId, | ||
161 | + }); | ||
162 | + if(res.statusText === 'OK') { | ||
163 | + Alert.onSuccess('탈퇴를 승인했습니다.', fetchData); | ||
164 | + } | ||
165 | + } catch (e : any) { | ||
166 | + Alert.onError('알 수 없는 에러가 발생했습니다.', () => null); | ||
167 | + } | ||
168 | + }; | ||
169 | + | ||
170 | + Alert.onCheck('회원 탈퇴를 승인하시겠습니까?\n이 작업은 되돌릴 수 없습니다.', onAccept, () => null); | ||
171 | + }; | ||
172 | + | ||
173 | + | ||
174 | + | ||
175 | + useEffect(() => { | ||
176 | + setValidate('W'); | ||
177 | + setValidateDoctorLicense(''); | ||
178 | + }, [modalUp]); | ||
179 | + | ||
129 | useEffect(() => { | 180 | useEffect(() => { |
130 | fetchData(); | 181 | fetchData(); |
131 | - }, []); | 182 | + }, [viewType]); |
132 | 183 | ||
133 | return ( | 184 | return ( |
134 | <ManagerMenuPresenter | 185 | <ManagerMenuPresenter |
135 | - doctorRegReqList = {doctorRegReqList} | 186 | + viewType = {viewType} |
187 | + onViewRegList = {onViewRegList} | ||
188 | + onViewSecList = {onViewSecList} | ||
189 | + | ||
190 | + doctorList = {doctorList} | ||
136 | 191 | ||
137 | doctorDetail = {doctorDetail} | 192 | doctorDetail = {doctorDetail} |
138 | modalUp = {modalUp} | 193 | modalUp = {modalUp} |
139 | setModalUp = {setModalUp} | 194 | setModalUp = {setModalUp} |
140 | onViewDetailReq = {onViewDetailReq} | 195 | onViewDetailReq = {onViewDetailReq} |
196 | + onViewLicenseDetail = {onViewLicenseDetail} | ||
197 | + | ||
141 | validate = {validate} | 198 | validate = {validate} |
142 | onValidate = {onValidate} | 199 | onValidate = {onValidate} |
143 | 200 | ||
144 | - onAcceptRequest = {onAcceptRequest} | 201 | + validateDoctorLicense = {validateDoctorLicense} |
202 | + onSetValidateDoctorLicense = {onSetValidateDoctorLicense} | ||
203 | + | ||
204 | + onAcceptRegReq = {onAcceptRegReq} | ||
205 | + onAcceptSecReq = {onAcceptSecReq} | ||
145 | onRejectRequest = {onRejectRequest} | 206 | onRejectRequest = {onRejectRequest} |
146 | /> | 207 | /> |
147 | ); | 208 | ); | ... | ... |
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,117 +34,156 @@ const ManagerMenuPresenter = (props : ManagerMenuProps) => { | ... | @@ -26,117 +34,156 @@ 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 | 39 | + <styled.ModalTitleWrapper> |
32 | - onClick = {() => props.setModalUp(false)} | 40 | + <styled.ModalTitle>가입 요청 정보</styled.ModalTitle> |
41 | + </styled.ModalTitleWrapper> | ||
42 | + <styled.ModalBodyWrapper> | ||
43 | + <styled.ModalBodyLeftAndRight> | ||
44 | + <styled.ModalInfoWrapper> | ||
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> | ||
58 | + <styled.ModalInfo> | ||
59 | + <styled.DoctorLicenseViewInput | ||
60 | + placeholder = '의사 면허 번호' | ||
61 | + value = {props.validateDoctorLicense} | ||
62 | + onChange = {props.onSetValidateDoctorLicense} | ||
63 | + /> | ||
64 | + <styled.ValidateButton | ||
65 | + onClick = {props.onValidate} | ||
66 | + disabled = {props.validate !== 'W'} | ||
67 | + validate = {props.validate} | ||
68 | + > | ||
69 | + { | ||
70 | + props.validate === 'Y' ? | ||
71 | + '검증 완료' : | ||
72 | + props.validate === 'W' ? | ||
73 | + '검증' : | ||
74 | + props.validate === 'N' ? | ||
75 | + '검증 실패' : null | ||
76 | + } | ||
77 | + </styled.ValidateButton> | ||
78 | + </styled.ModalInfo> | ||
79 | + </styled.ModalInfoWrapper> | ||
80 | + <styled.ModalInfoWrapper> | ||
81 | + <styled.ModalInfoExplain>이름</styled.ModalInfoExplain> | ||
82 | + <styled.ModalInfo>{props.doctorDetail.info.doctorNm}</styled.ModalInfo> | ||
83 | + </styled.ModalInfoWrapper> | ||
84 | + <styled.ModalInfoWrapper> | ||
85 | + <styled.ModalInfoExplain>연락처</styled.ModalInfoExplain> | ||
86 | + <styled.ModalInfo>{props.doctorDetail.info.contact}</styled.ModalInfo> | ||
87 | + </styled.ModalInfoWrapper> | ||
88 | + </styled.ModalBodyLeftAndRight> | ||
89 | + <styled.ModalBodyLeftAndRight> | ||
90 | + <styled.ModalInfoWrapper> | ||
91 | + <styled.ModalInfoExplain>전문 분야</styled.ModalInfoExplain> | ||
92 | + <styled.ModalInfo>{props.doctorDetail.info.doctorType}</styled.ModalInfo> | ||
93 | + </styled.ModalInfoWrapper> | ||
94 | + <styled.ModalInfoWrapper> | ||
95 | + <styled.ModalInfoExplain>병원명</styled.ModalInfoExplain> | ||
96 | + <styled.ModalInfo>{props.doctorDetail.info.hospitalNm}</styled.ModalInfo> | ||
97 | + </styled.ModalInfoWrapper> | ||
98 | + <styled.ModalInfoWrapper> | ||
99 | + <styled.ModalInfoExplain>병원 주소</styled.ModalInfoExplain> | ||
100 | + <styled.ModalInfo>{props.doctorDetail.info.hospitalAddr}</styled.ModalInfo> | ||
101 | + </styled.ModalInfoWrapper> | ||
102 | + </styled.ModalBodyLeftAndRight> | ||
103 | + </styled.ModalBodyWrapper> | ||
104 | + <styled.ModalButtonWrapper> | ||
105 | + <styled.ModalButton | ||
106 | + onClick = {props.onAcceptRegReq} | ||
107 | + isAccept = {true} | ||
33 | > | 108 | > |
34 | - <styled.ModalClsButtonImg src = {closeButton}/> | 109 | + 수락 |
35 | - <styled.ModalClsButtonText>닫기</styled.ModalClsButtonText> | 110 | + </styled.ModalButton> |
36 | - </styled.ModalClsButton> | 111 | + <styled.ModalButton |
37 | - </styled.ModalClsButtonWrapper> | 112 | + onClick = {props.onRejectRequest} |
38 | - <styled.ModalContentWrapper> | 113 | + isAccept = {false} |
39 | - <styled.ModalContent> | 114 | + > |
40 | - <styled.ModalTitleWrapper> | 115 | + 거절 |
41 | - <styled.ModalTitle>가입 요청 정보</styled.ModalTitle> | 116 | + </styled.ModalButton> |
42 | - </styled.ModalTitleWrapper> | 117 | + </styled.ModalButtonWrapper> |
43 | - <styled.ModalBodyWrapper> | 118 | + </> |
44 | - <styled.ModalBodyLeftAndRight> | 119 | + </Modal> : null |
45 | - <styled.ModalInfoWrapper> | ||
46 | - <styled.ModalInfoExplain>의사 자격 번호</styled.ModalInfoExplain> | ||
47 | - <styled.ModalInfo> | ||
48 | - {props.doctorDetail.info.doctorLicense} | ||
49 | - <styled.ValidateButton | ||
50 | - onClick = {props.onValidate} | ||
51 | - disabled = {props.validate !== 'W'} | ||
52 | - validate = {props.validate} | ||
53 | - > | ||
54 | - { | ||
55 | - props.validate === 'Y' ? | ||
56 | - '검증 완료' : | ||
57 | - props.validate === 'W' ? | ||
58 | - '검증' : | ||
59 | - props.validate === 'N' ? | ||
60 | - '검증 실패' : null | ||
61 | - } | ||
62 | - </styled.ValidateButton> | ||
63 | - </styled.ModalInfo> | ||
64 | - </styled.ModalInfoWrapper> | ||
65 | - <styled.ModalInfoWrapper> | ||
66 | - <styled.ModalInfoExplain>이름</styled.ModalInfoExplain> | ||
67 | - <styled.ModalInfo>{props.doctorDetail.info.doctorNm}</styled.ModalInfo> | ||
68 | - </styled.ModalInfoWrapper> | ||
69 | - <styled.ModalInfoWrapper> | ||
70 | - <styled.ModalInfoExplain>연락처</styled.ModalInfoExplain> | ||
71 | - <styled.ModalInfo>{props.doctorDetail.info.contact}</styled.ModalInfo> | ||
72 | - </styled.ModalInfoWrapper> | ||
73 | - </styled.ModalBodyLeftAndRight> | ||
74 | - <styled.ModalBodyLeftAndRight> | ||
75 | - <styled.ModalInfoWrapper> | ||
76 | - <styled.ModalInfoExplain>전문 분야</styled.ModalInfoExplain> | ||
77 | - <styled.ModalInfo>{props.doctorDetail.info.doctorType}</styled.ModalInfo> | ||
78 | - </styled.ModalInfoWrapper> | ||
79 | - <styled.ModalInfoWrapper> | ||
80 | - <styled.ModalInfoExplain>병원명</styled.ModalInfoExplain> | ||
81 | - <styled.ModalInfo>{props.doctorDetail.info.hospitalNm}</styled.ModalInfo> | ||
82 | - </styled.ModalInfoWrapper> | ||
83 | - <styled.ModalInfoWrapper> | ||
84 | - <styled.ModalInfoExplain>병원 주소</styled.ModalInfoExplain> | ||
85 | - <styled.ModalInfo>{props.doctorDetail.info.hospitalAddr}</styled.ModalInfo> | ||
86 | - </styled.ModalInfoWrapper> | ||
87 | - </styled.ModalBodyLeftAndRight> | ||
88 | - </styled.ModalBodyWrapper> | ||
89 | - <styled.ModalButtonWrapper> | ||
90 | - <styled.ModalButton | ||
91 | - onClick = {props.onAcceptRequest} | ||
92 | - isAccept = {true} | ||
93 | - > | ||
94 | - 수락 | ||
95 | - </styled.ModalButton> | ||
96 | - <styled.ModalButton | ||
97 | - onClick = {props.onRejectRequest} | ||
98 | - isAccept = {false} | ||
99 | - > | ||
100 | - 거절 | ||
101 | - </styled.ModalButton> | ||
102 | - </styled.ModalButtonWrapper> | ||
103 | - </styled.ModalContent> | ||
104 | - </styled.ModalContentWrapper> | ||
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> |
110 | - 가입 대기 중 의사 회원 | 137 | + { |
111 | - <styled.ContentExplain> | 138 | + props.viewType === 'sec' ? |
112 | - *클릭하면 상세정보를 확인할 수 있습니다. | 139 | + <> |
113 | - </styled.ContentExplain> | 140 | + 탈퇴 대기 중 의사 회원 |
141 | + <styled.ContentExplain> | ||
142 | + *승인을 누르면 탈퇴를 승인합니다. | ||
143 | + </styled.ContentExplain> | ||
144 | + </> : | ||
145 | + <> | ||
146 | + 가입 대기 중 의사 회원 | ||
147 | + <styled.ContentExplain> | ||
148 | + *클릭하면 상세정보를 확인할 수 있습니다. | ||
149 | + </styled.ContentExplain> | ||
150 | + </> | ||
151 | + } | ||
114 | </styled.ContentTitle> | 152 | </styled.ContentTitle> |
115 | - <styled.ContentBody> | 153 | + <styled.ContentInfoWrapper> |
116 | - <styled.ContentInfoWrapper> | 154 | + <styled.ContentInfo |
117 | - <styled.ContentInfo | 155 | + isLast = {false} |
118 | - isLast = {false} | 156 | + > |
119 | - > | 157 | + 분야 |
120 | - 분야 | 158 | + </styled.ContentInfo> |
121 | - </styled.ContentInfo> | 159 | + <styled.ContentInfo |
122 | - <styled.ContentInfo | 160 | + isLast = {false} |
123 | - isLast = {false} | 161 | + > |
124 | - > | 162 | + 이름 |
125 | - 이름 | 163 | + </styled.ContentInfo> |
126 | - </styled.ContentInfo> | 164 | + <styled.ContentInfo |
127 | - <styled.ContentInfo | 165 | + isLast = {props.viewType !== 'sec'} |
166 | + > | ||
167 | + 이메일 | ||
168 | + </styled.ContentInfo> | ||
169 | + { | ||
170 | + props.viewType === 'sec' ? | ||
171 | + <styled.ContentInfo | ||
128 | isLast = {true} | 172 | isLast = {true} |
129 | > | 173 | > |
130 | - 이메일 | 174 | + 탈퇴 수락 |
131 | - </styled.ContentInfo> | 175 | + </styled.ContentInfo> : null |
132 | - </styled.ContentInfoWrapper> | 176 | + } |
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; | ||
96 | + | ||
97 | + display : flex; | ||
98 | + flex-direction : row; | ||
99 | + align-items : center; | ||
100 | +`; | ||
101 | + | ||
102 | +export const ModalInfoNotice = styled.div ` | ||
103 | + font-size : 11px; | ||
104 | + color : #bbb; | ||
105 | + | ||
106 | + font-weight : 400; | ||
107 | + | ||
108 | + letter-spacing : 0px; | ||
109 | + | ||
110 | + margin : 5px 0 0px 0; | ||
111 | +`; | ||
112 | + | ||
113 | +export const DoctorLicenseViewWrapper = styled.div ` | ||
114 | + display : flex; | ||
115 | + flex-direction : row; | ||
116 | + | ||
117 | + justify-content : center; | ||
118 | + align-items : center; | ||
119 | + | ||
120 | + border : none; | ||
121 | + background-color : transparent; | ||
122 | +`; | ||
123 | + | ||
124 | +export const DoctorLicenseViewButton = styled.button ` | ||
125 | + margin : 5px 0 0 7px; | ||
126 | + | ||
127 | + border : 1px solid #343434; | ||
128 | + border-radius : 3px; | ||
129 | + background-color : #EAF2FF; | ||
130 | + padding : 2px 5px; | ||
197 | 131 | ||
198 | display : flex; | 132 | display : flex; |
199 | flex-direction : row; | 133 | flex-direction : row; |
134 | + justify-content : center; | ||
200 | align-items : center; | 135 | align-items : center; |
136 | + | ||
137 | + cursor : pointer; | ||
138 | + transition : .25s all; | ||
139 | + | ||
140 | + &:hover { | ||
141 | + border : 1px solid #337DFF; | ||
142 | + background-color : #337DFF; | ||
143 | + color : #fff; | ||
144 | + } | ||
145 | + | ||
146 | + font-size : 11px; | ||
147 | +`; | ||
148 | + | ||
149 | +export const DoctorLicenseViewInput = styled.input ` | ||
150 | + padding : 2px 1px; | ||
151 | + | ||
152 | + font-size : 11px; | ||
153 | + letter-spacing : 1px; | ||
154 | + color : #343434; | ||
155 | + | ||
156 | + border : none; | ||
157 | + border-bottom : 1px solid #343434; | ||
158 | + | ||
159 | + &::placeholder { | ||
160 | + color : #ccc; | ||
161 | + } | ||
201 | `; | 162 | `; |
202 | 163 | ||
203 | export const ValidateButton = styled.button<{validate : string}> ` | 164 | export const ValidateButton = styled.button<{validate : string}> ` |
204 | - margin : 0 0 0 15px; | 165 | + font-size : 11px; |
166 | + | ||
167 | + margin : 0 0 0 5px; | ||
205 | padding : 2px 5px; | 168 | padding : 2px 5px; |
206 | 169 | ||
207 | border-radius : 3px; | 170 | border-radius : 3px; |
... | @@ -288,8 +251,45 @@ export const ContentWrapper = styled.div ` | ... | @@ -288,8 +251,45 @@ export const ContentWrapper = styled.div ` |
288 | 251 | ||
289 | `; | 252 | `; |
290 | 253 | ||
254 | +export const ContentButtonWrapper = styled.div ` | ||
255 | + width : 100%; | ||
256 | + height : 10%; | ||
257 | + border : none; | ||
258 | + | ||
259 | + display : flex; | ||
260 | + flex-direction : row; | ||
261 | + justify-content : center; | ||
262 | + align-items : flex-end; | ||
263 | + | ||
264 | + gap : 10%; | ||
265 | + | ||
266 | + background-color : transparent; | ||
267 | +`; | ||
268 | + | ||
269 | +export const ContentButton = styled.button<{isSelect : boolean}> ` | ||
270 | + background-color : ${props => props.isSelect ? '#337DFF' : 'transparent'}; | ||
271 | + color : ${props => props.isSelect ? '#fff' : '#337DFF'}; | ||
272 | + border : 1px solid #337DFF; | ||
273 | + border-radius : 4px; | ||
274 | + | ||
275 | + padding : 4px 10px; | ||
276 | + | ||
277 | + cursor : pointer; | ||
278 | + | ||
279 | + display : flex; | ||
280 | + justify-content : center; | ||
281 | + align-items : center; | ||
282 | + | ||
283 | + transition : .25s all; | ||
284 | + | ||
285 | + &:hover { | ||
286 | + opacity : .5; | ||
287 | + } | ||
288 | +`; | ||
289 | + | ||
291 | export const ContentTitle = styled.div ` | 290 | export const ContentTitle = styled.div ` |
292 | width : 100%; | 291 | width : 100%; |
292 | + height : 20%; | ||
293 | border : none; | 293 | border : none; |
294 | border-bottom : 1px solid #ddd; | 294 | border-bottom : 1px solid #ddd; |
295 | 295 | ||
... | @@ -298,7 +298,6 @@ export const ContentTitle = styled.div ` | ... | @@ -298,7 +298,6 @@ export const ContentTitle = styled.div ` |
298 | justify-content : center; | 298 | justify-content : center; |
299 | align-items : center; | 299 | align-items : center; |
300 | 300 | ||
301 | - padding : 4% 0; | ||
302 | font-size : 22px; | 301 | font-size : 22px; |
303 | font-weight : 600; | 302 | font-weight : 600; |
304 | letter-spacing : 1px; | 303 | letter-spacing : 1px; |
... | @@ -318,16 +317,17 @@ export const ContentExplain = styled.div ` | ... | @@ -318,16 +317,17 @@ export const ContentExplain = styled.div ` |
318 | export const ContentBody = styled.div ` | 317 | export const ContentBody = styled.div ` |
319 | overflow : scroll; | 318 | overflow : scroll; |
320 | 319 | ||
321 | - height : 79%; | 320 | + min-height : 60%; |
321 | + max-height : 60%; | ||
322 | 322 | ||
323 | border : none; | 323 | border : none; |
324 | 324 | ||
325 | - padding : 0 0 0 3px; | ||
326 | - | ||
327 | display : flex; | 325 | display : flex; |
328 | flex-direction : column; | 326 | flex-direction : column; |
329 | align-items : center; | 327 | align-items : center; |
330 | 328 | ||
329 | + padding : 0 0 0 3px; | ||
330 | + | ||
331 | &::-webkit-scrollbar { | 331 | &::-webkit-scrollbar { |
332 | width : 3px; | 332 | width : 3px; |
333 | background-color : transparent; | 333 | background-color : transparent; |
... | @@ -341,6 +341,7 @@ export const ContentBody = styled.div ` | ... | @@ -341,6 +341,7 @@ export const ContentBody = styled.div ` |
341 | 341 | ||
342 | export const ContentInfoWrapper = styled.div ` | 342 | export const ContentInfoWrapper = styled.div ` |
343 | width : 100%; | 343 | width : 100%; |
344 | + height : 10%; | ||
344 | border : none; | 345 | border : none; |
345 | border-bottom : 1px solid #a0a0a0; | 346 | border-bottom : 1px solid #a0a0a0; |
346 | 347 | ||
... | @@ -350,7 +351,6 @@ export const ContentInfoWrapper = styled.div ` | ... | @@ -350,7 +351,6 @@ export const ContentInfoWrapper = styled.div ` |
350 | justify-content : center; | 351 | justify-content : center; |
351 | align-items : center; | 352 | align-items : center; |
352 | 353 | ||
353 | - padding : 12px 0px; | ||
354 | `; | 354 | `; |
355 | 355 | ||
356 | export const ContentInfo = styled.div<{isLast : boolean}> ` | 356 | export const ContentInfo = styled.div<{isLast : boolean}> ` |
... | @@ -388,13 +388,15 @@ export const EachContentWrapper = styled.button ` | ... | @@ -388,13 +388,15 @@ export const EachContentWrapper = styled.button ` |
388 | 388 | ||
389 | padding : 10px 0px; | 389 | padding : 10px 0px; |
390 | 390 | ||
391 | - cursor : pointer; | 391 | + :not(:disabled) { |
392 | + cursor : pointer; | ||
392 | 393 | ||
393 | - transition : .1s all; | 394 | + transition : .1s all; |
394 | 395 | ||
395 | - &:hover { | 396 | + &:hover { |
396 | - background-color : #337DFF; | 397 | + background-color : #337DFF; |
397 | - color : #fff; | 398 | + color : #fff; |
399 | + } | ||
398 | } | 400 | } |
399 | 401 | ||
400 | `; | 402 | `; |
... | @@ -417,10 +419,33 @@ export const EachContentNm = styled.div<{isLast : boolean}> ` | ... | @@ -417,10 +419,33 @@ export const EachContentNm = styled.div<{isLast : boolean}> ` |
417 | 419 | ||
418 | `; | 420 | `; |
419 | 421 | ||
422 | +export const AcceptButton = styled.button ` | ||
423 | + background-color : transparent; | ||
424 | + color : #337DFF; | ||
425 | + border : 1px solid #337DFF; | ||
426 | + border-radius : 3px; | ||
427 | + padding : 2px 10px; | ||
428 | + | ||
429 | + display : flex; | ||
430 | + justify-content : center; | ||
431 | + align-items : center; | ||
432 | + | ||
433 | + cursor : pointer; | ||
434 | + | ||
435 | + transition : .25s all; | ||
436 | + | ||
437 | + &:hover { | ||
438 | + background-color : #337DFF; | ||
439 | + color : #fff; | ||
440 | + } | ||
441 | +`; | ||
442 | + | ||
420 | export const NothingWrapper = styled.div ` | 443 | export const NothingWrapper = styled.div ` |
421 | height : 100%; | 444 | height : 100%; |
422 | width : 100%; | 445 | width : 100%; |
423 | 446 | ||
447 | + border : none; | ||
448 | + | ||
424 | display : flex; | 449 | display : flex; |
425 | justify-content : center; | 450 | justify-content : center; |
426 | align-items : center; | 451 | align-items : center; | ... | ... |
1 | -import React, { useState, useEffect } from "react"; | 1 | +import React, { useState, useEffect, useRef } from "react"; |
2 | import { RouteComponentProps } from 'react-router-dom'; | 2 | import { RouteComponentProps } from 'react-router-dom'; |
3 | 3 | ||
4 | -import { useRecoilValue } from "recoil"; | 4 | +import { useRecoilValue, useRecoilState } from "recoil"; |
5 | import * as recoilUtil from '../../util/recoilUtil'; | 5 | import * as recoilUtil from '../../util/recoilUtil'; |
6 | 6 | ||
7 | import validator from 'validator'; | 7 | import validator from 'validator'; |
... | @@ -11,7 +11,6 @@ import Header from '../../components/Header'; | ... | @@ -11,7 +11,6 @@ import Header from '../../components/Header'; |
11 | import RegisterPresenter from "./RegisterPresenter"; | 11 | import RegisterPresenter from "./RegisterPresenter"; |
12 | 12 | ||
13 | import { authApi } from '../../api'; | 13 | import { authApi } from '../../api'; |
14 | -import { resourceLimits } from "worker_threads"; | ||
15 | 14 | ||
16 | 15 | ||
17 | // eslint-disable-next-line @typescript-eslint/no-empty-interface | 16 | // eslint-disable-next-line @typescript-eslint/no-empty-interface |
... | @@ -20,13 +19,13 @@ interface RegisterProps extends RouteComponentProps {} | ... | @@ -20,13 +19,13 @@ interface RegisterProps extends RouteComponentProps {} |
20 | const RegisterContainer = (props : RegisterProps) => { | 19 | const RegisterContainer = (props : RegisterProps) => { |
21 | 20 | ||
22 | const token = useRecoilValue(recoilUtil.token); | 21 | const token = useRecoilValue(recoilUtil.token); |
22 | + const [loading, setLoading] = useRecoilState(recoilUtil.loading); | ||
23 | 23 | ||
24 | const [registerForm, setRegisterForm] = useState<any>({ | 24 | const [registerForm, setRegisterForm] = useState<any>({ |
25 | userId : '', | 25 | userId : '', |
26 | password : '', | 26 | password : '', |
27 | passwordCheck : '', | 27 | passwordCheck : '', |
28 | info : { | 28 | info : { |
29 | - doctorLicense : '', | ||
30 | hospitalNm : '', | 29 | hospitalNm : '', |
31 | hospitalAddr : '', | 30 | hospitalAddr : '', |
32 | contact : '', | 31 | contact : '', |
... | @@ -34,10 +33,18 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -34,10 +33,18 @@ const RegisterContainer = (props : RegisterProps) => { |
34 | doctorNm : '', | 33 | doctorNm : '', |
35 | }, | 34 | }, |
36 | }); | 35 | }); |
36 | + const [doctorInfoFile, setDoctorInfoFile] = useState<FileList | null>(null); | ||
37 | + const doctorInfoFile_Select = useRef(null); | ||
37 | 38 | ||
38 | const [page, setPage] = useState<number>(1); | 39 | const [page, setPage] = useState<number>(1); |
39 | const [error, setError] = useState<string | null>(null); | 40 | const [error, setError] = useState<string | null>(null); |
40 | 41 | ||
42 | + const [searchHospital, setSearchHospital] = useState<boolean>(false); | ||
43 | + const [hospitalNm, setHospitalNm] = useState<string>(''); | ||
44 | + const [hospitalSearchPage, setHospitalSearchPage] = useState<number>(1); | ||
45 | + const [hospitalSearchPageList, setHospitalSearchPageList] = useState<number[]>([1]); | ||
46 | + const [hospitalList, setHospitalList] = useState<any[]>([]); | ||
47 | + const [selectHospital, setSelectHospital] = useState<any>(null); | ||
41 | 48 | ||
42 | 49 | ||
43 | const fetchData = async() => { | 50 | const fetchData = async() => { |
... | @@ -46,7 +53,7 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -46,7 +53,7 @@ const RegisterContainer = (props : RegisterProps) => { |
46 | if (result.statusText === 'OK') { | 53 | if (result.statusText === 'OK') { |
47 | props.history.push('/'); | 54 | props.history.push('/'); |
48 | } | 55 | } |
49 | - } | 56 | + } |
50 | }; | 57 | }; |
51 | 58 | ||
52 | const onCancleRegister = () => { | 59 | const onCancleRegister = () => { |
... | @@ -61,7 +68,6 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -61,7 +68,6 @@ const RegisterContainer = (props : RegisterProps) => { |
61 | } | 68 | } |
62 | }; | 69 | }; |
63 | 70 | ||
64 | - | ||
65 | const validateRegisterForm = () => { | 71 | const validateRegisterForm = () => { |
66 | if(page === 1) { | 72 | if(page === 1) { |
67 | if (!validator.isEmail(registerForm.userId)) { | 73 | if (!validator.isEmail(registerForm.userId)) { |
... | @@ -74,9 +80,8 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -74,9 +80,8 @@ const RegisterContainer = (props : RegisterProps) => { |
74 | setError('비밀번호가 일치하지 않습니다.') | 80 | setError('비밀번호가 일치하지 않습니다.') |
75 | } else setError(null); | 81 | } else setError(null); |
76 | } else if(page === 2) { | 82 | } else if(page === 2) { |
77 | - if(!registerForm.info.doctorLicense.length && | 83 | + if(!doctorInfoFile) { |
78 | - !validator.isAlphanumeric(registerForm.info.doctorLicense)) { | 84 | + setError('의사 자격 인증 파일을 첨부해야 합니다.'); |
79 | - setError('의사 자격 번호를 입력해야 합니다.'); | ||
80 | } else if(registerForm.info.doctorNm.length < 2) { | 85 | } else if(registerForm.info.doctorNm.length < 2) { |
81 | setError('의사 이름을 올바르게 입력해야 합니다.'); | 86 | setError('의사 이름을 올바르게 입력해야 합니다.'); |
82 | } else if(!registerForm.info.contact) { | 87 | } else if(!registerForm.info.contact) { |
... | @@ -116,33 +121,11 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -116,33 +121,11 @@ const RegisterContainer = (props : RegisterProps) => { |
116 | }; | 121 | }; |
117 | 122 | ||
118 | const onSetDoctorLicense = (e : React.ChangeEvent<HTMLInputElement>) => { | 123 | const onSetDoctorLicense = (e : React.ChangeEvent<HTMLInputElement>) => { |
119 | - setRegisterForm({ | 124 | + setDoctorInfoFile(e.target.files); |
120 | - ...registerForm, | ||
121 | - info : { | ||
122 | - ...registerForm.info, | ||
123 | - doctorLicense : e.target.value, | ||
124 | - }, | ||
125 | - }); | ||
126 | }; | 125 | }; |
127 | 126 | ||
128 | const onSetHospitalNm = (e : React.ChangeEvent<HTMLInputElement>) => { | 127 | const onSetHospitalNm = (e : React.ChangeEvent<HTMLInputElement>) => { |
129 | - setRegisterForm({ | 128 | + setHospitalNm(e.target.value); |
130 | - ...registerForm, | ||
131 | - info : { | ||
132 | - ...registerForm.info, | ||
133 | - hospitalNm : e.target.value, | ||
134 | - }, | ||
135 | - }); | ||
136 | - }; | ||
137 | - | ||
138 | - const onSetHospitalAddr = (e : React.ChangeEvent<HTMLInputElement>) => { | ||
139 | - setRegisterForm({ | ||
140 | - ...registerForm, | ||
141 | - info : { | ||
142 | - ...registerForm.info, | ||
143 | - hospitalAddr : e.target.value, | ||
144 | - }, | ||
145 | - }); | ||
146 | }; | 129 | }; |
147 | 130 | ||
148 | const onSetContact = (e : React.ChangeEvent<HTMLInputElement>) => { | 131 | const onSetContact = (e : React.ChangeEvent<HTMLInputElement>) => { |
... | @@ -175,6 +158,56 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -175,6 +158,56 @@ const RegisterContainer = (props : RegisterProps) => { |
175 | }); | 158 | }); |
176 | }; | 159 | }; |
177 | 160 | ||
161 | + const onSearchHospital = async () => { | ||
162 | + try { | ||
163 | + setLoading(true); | ||
164 | + setSearchHospital(true); | ||
165 | + const result = await authApi.searchHospital(hospitalNm, hospitalSearchPage); | ||
166 | + if(result.statusText === 'OK') { | ||
167 | + setLoading(false); | ||
168 | + setHospitalSearchPageList(new Array(result.data.totalPage).fill(null).map((item : null, index : number) => index + 1)); | ||
169 | + setHospitalList(result.data.hospitalList.length ? result.data.hospitalList : [result.data.hospitalList]); | ||
170 | + } | ||
171 | + } catch(e : any) { | ||
172 | + setLoading(false); | ||
173 | + Alert.onError('알 수 없는 에러로 검색에 실패했습니다.', () => null); | ||
174 | + } | ||
175 | + }; | ||
176 | + | ||
177 | + const onSetSearchPrevPage = () => { | ||
178 | + //set Prev Page | ||
179 | + const pageSlice = 5; | ||
180 | + if(hospitalSearchPage > pageSlice) { | ||
181 | + setHospitalSearchPage(Math.floor((hospitalSearchPage - 1) / pageSlice) * pageSlice); | ||
182 | + } | ||
183 | + }; | ||
184 | + | ||
185 | + const onSetSearchNextPage = () => { | ||
186 | + //set Next Page | ||
187 | + const pageSlice = 5; | ||
188 | + if(hospitalSearchPage <= Math.floor((hospitalSearchPageList.length - 1) / pageSlice) * pageSlice) { | ||
189 | + setHospitalSearchPage(Math.ceil(hospitalSearchPage / pageSlice) * pageSlice + 1); | ||
190 | + } | ||
191 | + }; | ||
192 | + | ||
193 | + const onConfirmSelectHospital = () => { | ||
194 | + setSearchHospital(false); | ||
195 | + setHospitalSearchPage(1); | ||
196 | + setHospitalSearchPageList([1]); | ||
197 | + setHospitalList([]); | ||
198 | + }; | ||
199 | + | ||
200 | + const onCancelSelectHospital = () => { | ||
201 | + Alert.onCheck('병원 등록이 취소됩니다. 계속하시겠습니까?', () => { | ||
202 | + setSearchHospital(false); | ||
203 | + setHospitalNm(''); | ||
204 | + setHospitalSearchPage(1); | ||
205 | + setHospitalSearchPageList([1]); | ||
206 | + setHospitalList([]); | ||
207 | + setSelectHospital(null); | ||
208 | + }, () => null); | ||
209 | + }; | ||
210 | + | ||
178 | const onSubmitButton = () => { | 211 | const onSubmitButton = () => { |
179 | if(error) { | 212 | if(error) { |
180 | Alert.onError(error, () => null); | 213 | Alert.onError(error, () => null); |
... | @@ -186,26 +219,80 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -186,26 +219,80 @@ const RegisterContainer = (props : RegisterProps) => { |
186 | } else if(page === 2) { | 219 | } else if(page === 2) { |
187 | setPage(3); | 220 | setPage(3); |
188 | } else if(page === 3) { | 221 | } else if(page === 3) { |
222 | + | ||
223 | + const Data = new FormData(); | ||
224 | + Data.append('userId', registerForm.userId); | ||
225 | + Data.append('password', registerForm.password); | ||
226 | + Data.append('passwordCheck', registerForm.passwordCheck); | ||
227 | + | ||
228 | + Data.append('hospitalNm', registerForm.info.hospitalNm); | ||
229 | + Data.append('hospitalAddr', registerForm.info.hospitalAddr); | ||
230 | + Data.append('contact', registerForm.info.contact); | ||
231 | + Data.append('doctorNm', registerForm.info.doctorNm); | ||
232 | + Data.append('doctorType', registerForm.info.doctorType); | ||
233 | + | ||
234 | + Data.append('doctorInfoFile', doctorInfoFile ? doctorInfoFile[0] : ''); | ||
235 | + | ||
236 | + | ||
189 | const onRegisterDoctor = async () => { | 237 | const onRegisterDoctor = async () => { |
238 | + //로딩 진행 | ||
239 | + setLoading(true); | ||
240 | + | ||
190 | try { | 241 | try { |
191 | - const result = await authApi.registerDoctor(registerForm); | 242 | + const result = await authApi.registerDoctor(Data); |
192 | if(result.data === 'Created') { | 243 | if(result.data === 'Created') { |
244 | + setLoading(false); | ||
193 | Alert.onSuccess('회원가입 성공, 관리자의 승인을 대기하세요.', () => props.history.push('/login')); | 245 | Alert.onSuccess('회원가입 성공, 관리자의 승인을 대기하세요.', () => props.history.push('/login')); |
194 | } | 246 | } |
195 | - } catch(e) { | 247 | + } catch(e : any) { |
248 | + setLoading(false); | ||
196 | Alert.onError(e.response.data.error, () => null); | 249 | Alert.onError(e.response.data.error, () => null); |
197 | } | 250 | } |
198 | }; | 251 | }; |
199 | 252 | ||
200 | - Alert.onCheck('입력하신 정보로 회원가입을 진행하시겠습니까?', onRegisterDoctor, () => null); | 253 | + if(selectHospital) { |
254 | + Alert.onCheck('입력하신 정보로 회원가입을 진행하시겠습니까?', onRegisterDoctor, () => null); | ||
255 | + } else { | ||
256 | + Alert.onError('검색 버튼을 눌러 병원을 선택해주세요.', () => null); | ||
257 | + } | ||
258 | + | ||
201 | } | 259 | } |
202 | 260 | ||
203 | }; | 261 | }; |
204 | 262 | ||
263 | + | ||
264 | + | ||
205 | useEffect(() => { | 265 | useEffect(() => { |
206 | validateRegisterForm(); | 266 | validateRegisterForm(); |
207 | - }, [registerForm, page]); | 267 | + }, [registerForm, doctorInfoFile, page]); |
268 | + | ||
269 | + useEffect(() => { | ||
270 | + if(selectHospital) { | ||
271 | + setHospitalNm(selectHospital.yadmNm); | ||
272 | + setRegisterForm({ | ||
273 | + ...registerForm, | ||
274 | + info : { | ||
275 | + ...registerForm.info, | ||
276 | + hospitalNm : selectHospital.yadmNm, | ||
277 | + hospitalAddr : selectHospital.addr, | ||
278 | + }, | ||
279 | + }); | ||
280 | + } else { | ||
281 | + setHospitalNm(''); | ||
282 | + setRegisterForm({ | ||
283 | + ...registerForm, | ||
284 | + info : { | ||
285 | + ...registerForm.info, | ||
286 | + hospitalNm : '', | ||
287 | + hospitalAddr : '', | ||
288 | + }, | ||
289 | + }); | ||
290 | + } | ||
291 | + }, [selectHospital]); | ||
208 | 292 | ||
293 | + useEffect(() => { | ||
294 | + if(searchHospital) onSearchHospital(); | ||
295 | + }, [hospitalSearchPage]); | ||
209 | 296 | ||
210 | useEffect(() => { | 297 | useEffect(() => { |
211 | fetchData(); | 298 | fetchData(); |
... | @@ -218,6 +305,8 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -218,6 +305,8 @@ const RegisterContainer = (props : RegisterProps) => { |
218 | <Header {...props}/> | 305 | <Header {...props}/> |
219 | <RegisterPresenter | 306 | <RegisterPresenter |
220 | registerForm = {registerForm} | 307 | registerForm = {registerForm} |
308 | + doctorInfoFile = {doctorInfoFile} | ||
309 | + doctorInfoFile_Select = {doctorInfoFile_Select} | ||
221 | page = {page} | 310 | page = {page} |
222 | error = {error} | 311 | error = {error} |
223 | 312 | ||
... | @@ -228,12 +317,28 @@ const RegisterContainer = (props : RegisterProps) => { | ... | @@ -228,12 +317,28 @@ const RegisterContainer = (props : RegisterProps) => { |
228 | onSetPassword = {onSetPassword} | 317 | onSetPassword = {onSetPassword} |
229 | onSetPasswordCheck = {onSetPasswordCheck} | 318 | onSetPasswordCheck = {onSetPasswordCheck} |
230 | onSetDoctorLicense = {onSetDoctorLicense} | 319 | onSetDoctorLicense = {onSetDoctorLicense} |
320 | + hospitalNm = {hospitalNm} | ||
231 | onSetHospitalNm = {onSetHospitalNm} | 321 | onSetHospitalNm = {onSetHospitalNm} |
232 | - onSetHospitalAddr = {onSetHospitalAddr} | ||
233 | onSetContact = {onSetContact} | 322 | onSetContact = {onSetContact} |
234 | onSetDoctorType = {onSetDoctorType} | 323 | onSetDoctorType = {onSetDoctorType} |
235 | onSetDoctorNm = {onSetDoctorNm} | 324 | onSetDoctorNm = {onSetDoctorNm} |
236 | onSubmitButton = {onSubmitButton} | 325 | onSubmitButton = {onSubmitButton} |
326 | + | ||
327 | + searchHospital = {searchHospital} | ||
328 | + setSearchHospital = {setSearchHospital} | ||
329 | + onSearchHospital = {onSearchHospital} | ||
330 | + hospitalSearchPage = {hospitalSearchPage} | ||
331 | + setHospitalSearchPage = {setHospitalSearchPage} | ||
332 | + hospitalSearchPageList = {hospitalSearchPageList} | ||
333 | + onSetSearchPrevPage = {onSetSearchPrevPage} | ||
334 | + onSetSearchNextPage = {onSetSearchNextPage} | ||
335 | + | ||
336 | + onConfirmSelectHospital = {onConfirmSelectHospital} | ||
337 | + onCancelSelectHospital = {onCancelSelectHospital} | ||
338 | + | ||
339 | + hospitalList = {hospitalList} | ||
340 | + selectHospital = {selectHospital} | ||
341 | + setSelectHospital = {setSelectHospital} | ||
237 | /> | 342 | /> |
238 | </> | 343 | </> |
239 | ) | 344 | ) | ... | ... |
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | 2 | ||
3 | +import Modal from '../../components/Modal'; | ||
3 | import * as styled from './RegisterStyled'; | 4 | import * as styled from './RegisterStyled'; |
4 | 5 | ||
5 | 6 | ||
7 | +const lensImg = '/static/img/lens.png'; | ||
8 | +const check = '/static/img/check.png'; | ||
9 | +const uncheck = '/static/img/uncheck.png' | ||
10 | +const next = '/static/img/next.png'; | ||
11 | +const prev = '/static/img/prev.png'; | ||
12 | + | ||
13 | + | ||
6 | interface RegisterProps { | 14 | interface RegisterProps { |
7 | registerForm : { | 15 | registerForm : { |
8 | userId : string; | 16 | userId : string; |
9 | password : string; | 17 | password : string; |
10 | passwordCheck : string; | 18 | passwordCheck : string; |
11 | info : { | 19 | info : { |
12 | - doctorLicense : string; | ||
13 | hospitalNm : string; | 20 | hospitalNm : string; |
14 | hospitalAddr : string; | 21 | hospitalAddr : string; |
15 | contact : string; | 22 | contact : string; |
... | @@ -17,6 +24,8 @@ interface RegisterProps { | ... | @@ -17,6 +24,8 @@ interface RegisterProps { |
17 | doctorNm : string; | 24 | doctorNm : string; |
18 | }, | 25 | }, |
19 | }; | 26 | }; |
27 | + doctorInfoFile : FileList | null; | ||
28 | + doctorInfoFile_Select : any; | ||
20 | page : number; | 29 | page : number; |
21 | error : string | null; | 30 | error : string | null; |
22 | 31 | ||
... | @@ -27,18 +36,122 @@ interface RegisterProps { | ... | @@ -27,18 +36,122 @@ interface RegisterProps { |
27 | onSetPassword : React.ChangeEventHandler<HTMLInputElement>; | 36 | onSetPassword : React.ChangeEventHandler<HTMLInputElement>; |
28 | onSetPasswordCheck : React.ChangeEventHandler<HTMLInputElement>; | 37 | onSetPasswordCheck : React.ChangeEventHandler<HTMLInputElement>; |
29 | onSetDoctorLicense : React.ChangeEventHandler<HTMLInputElement>; | 38 | onSetDoctorLicense : React.ChangeEventHandler<HTMLInputElement>; |
39 | + hospitalNm : string; | ||
30 | onSetHospitalNm : React.ChangeEventHandler<HTMLInputElement>; | 40 | onSetHospitalNm : React.ChangeEventHandler<HTMLInputElement>; |
31 | - onSetHospitalAddr : React.ChangeEventHandler<HTMLInputElement>; | ||
32 | onSetContact : React.ChangeEventHandler<HTMLInputElement>; | 41 | onSetContact : React.ChangeEventHandler<HTMLInputElement>; |
33 | onSetDoctorType : React.ChangeEventHandler<HTMLInputElement>; | 42 | onSetDoctorType : React.ChangeEventHandler<HTMLInputElement>; |
34 | onSetDoctorNm : React.ChangeEventHandler<HTMLInputElement>; | 43 | onSetDoctorNm : React.ChangeEventHandler<HTMLInputElement>; |
35 | onSubmitButton : () => void; | 44 | onSubmitButton : () => void; |
36 | 45 | ||
46 | + searchHospital : boolean; | ||
47 | + setSearchHospital : (arg0 : boolean) => void; | ||
48 | + onSearchHospital : () => void; | ||
49 | + | ||
50 | + hospitalSearchPage : number; | ||
51 | + setHospitalSearchPage : (arg0 : number) => void; | ||
52 | + hospitalSearchPageList : number[]; | ||
53 | + onSetSearchPrevPage : () => void; | ||
54 | + onSetSearchNextPage : () => void; | ||
55 | + | ||
56 | + onConfirmSelectHospital : () => void; | ||
57 | + onCancelSelectHospital : () => void; | ||
58 | + | ||
59 | + hospitalList : any[]; | ||
60 | + selectHospital : any; | ||
61 | + setSelectHospital : (arg0 : any) => void; | ||
37 | } | 62 | } |
38 | 63 | ||
39 | const RegisterPresenter = (props : RegisterProps) => { | 64 | const RegisterPresenter = (props : RegisterProps) => { |
40 | return ( | 65 | return ( |
41 | <styled.Container> | 66 | <styled.Container> |
67 | + { | ||
68 | + props.searchHospital ? | ||
69 | + <Modal onModalClose = {props.onCancelSelectHospital}> | ||
70 | + <> | ||
71 | + <styled.SearchTitle> | ||
72 | + {`[${props.hospitalNm}] 에 대한 검색 결과 : `} | ||
73 | + <styled.SearchResultCount style = {{marginLeft : 5, marginRight : 5,}}>총 </styled.SearchResultCount> | ||
74 | + {props.hospitalSearchPageList.length} | ||
75 | + <styled.SearchResultCount>페이지</styled.SearchResultCount> | ||
76 | + </styled.SearchTitle> | ||
77 | + <styled.HospitalListWrapper> | ||
78 | + <styled.HospitalListInfo> | ||
79 | + <styled.HospitalListInfoEach isLast = {false}>이름</styled.HospitalListInfoEach> | ||
80 | + <styled.HospitalListInfoEach isLast = {false}>주소</styled.HospitalListInfoEach> | ||
81 | + <styled.HospitalListInfoEach isLast = {true}>선택</styled.HospitalListInfoEach> | ||
82 | + </styled.HospitalListInfo> | ||
83 | + { | ||
84 | + props.hospitalList.map((hospital : any) => { | ||
85 | + return ( | ||
86 | + <styled.HospitalListEach | ||
87 | + key = {hospital.addr} | ||
88 | + > | ||
89 | + <styled.HospitalListEachInfo isLast = {false}> | ||
90 | + {hospital.yadmNm} | ||
91 | + </styled.HospitalListEachInfo> | ||
92 | + <styled.HospitalListEachInfo isLast = {false}> | ||
93 | + {hospital.addr} | ||
94 | + </styled.HospitalListEachInfo> | ||
95 | + <styled.HospitalListEachInfo isLast = {true}> | ||
96 | + <styled.CheckButton | ||
97 | + onClick = {() => props.setSelectHospital(hospital)} | ||
98 | + > | ||
99 | + <styled.CheckButtonImg src = { | ||
100 | + props.selectHospital && props.selectHospital.addr === hospital.addr ? | ||
101 | + check : uncheck | ||
102 | + }/> | ||
103 | + </styled.CheckButton> | ||
104 | + </styled.HospitalListEachInfo> | ||
105 | + </styled.HospitalListEach> | ||
106 | + ) | ||
107 | + }) | ||
108 | + } | ||
109 | + </styled.HospitalListWrapper> | ||
110 | + <styled.PageWrapper> | ||
111 | + <styled.PageButton | ||
112 | + isSelect = {false} | ||
113 | + onClick = {props.onSetSearchPrevPage} | ||
114 | + > | ||
115 | + <styled.PageArrowImg src = {prev}/> | ||
116 | + </styled.PageButton> | ||
117 | + { | ||
118 | + props.hospitalSearchPageList.slice(Math.floor((props.hospitalSearchPage - 1) / 5) * 5, Math.floor((props.hospitalSearchPage - 1) / 5) * 5 + 5) | ||
119 | + .map((page : number) => { | ||
120 | + return ( | ||
121 | + <styled.PageButton | ||
122 | + key = {page} | ||
123 | + isSelect = {props.hospitalSearchPage === page} | ||
124 | + onClick = {() => props.setHospitalSearchPage(page)} | ||
125 | + > | ||
126 | + {page} | ||
127 | + </styled.PageButton> | ||
128 | + ) | ||
129 | + }) | ||
130 | + } | ||
131 | + <styled.PageButton | ||
132 | + isSelect = {false} | ||
133 | + onClick = {props.onSetSearchNextPage} | ||
134 | + > | ||
135 | + <styled.PageArrowImg src = {next}/> | ||
136 | + </styled.PageButton> | ||
137 | + </styled.PageWrapper> | ||
138 | + <styled.ModalButtonWrapper> | ||
139 | + <styled.ModalButton | ||
140 | + isCloseButton = {false} | ||
141 | + onClick = {props.onConfirmSelectHospital} | ||
142 | + > | ||
143 | + 확인 | ||
144 | + </styled.ModalButton> | ||
145 | + <styled.ModalButton | ||
146 | + isCloseButton = {true} | ||
147 | + onClick = {props.onCancelSelectHospital} | ||
148 | + > | ||
149 | + 취소 | ||
150 | + </styled.ModalButton> | ||
151 | + </styled.ModalButtonWrapper> | ||
152 | + </> | ||
153 | + </Modal> : null | ||
154 | + } | ||
42 | <styled.RegisterWrapper> | 155 | <styled.RegisterWrapper> |
43 | <styled.RegisterBackButtonWrapper> | 156 | <styled.RegisterBackButtonWrapper> |
44 | <styled.RegisterBackButton | 157 | <styled.RegisterBackButton |
... | @@ -92,11 +205,19 @@ const RegisterPresenter = (props : RegisterProps) => { | ... | @@ -92,11 +205,19 @@ const RegisterPresenter = (props : RegisterProps) => { |
92 | <> | 205 | <> |
93 | <styled.RegisterInputWrapper> | 206 | <styled.RegisterInputWrapper> |
94 | <styled.RegisterInputText>의사 자격증 번호</styled.RegisterInputText> | 207 | <styled.RegisterInputText>의사 자격증 번호</styled.RegisterInputText> |
95 | - <styled.RegisterInput | 208 | + <input type = 'file' |
96 | - placeholder = "Doctor's License" | 209 | + style = {{ display : 'none' }} |
97 | - value = {props.registerForm.info.doctorLicense} | ||
98 | onChange = {props.onSetDoctorLicense} | 210 | onChange = {props.onSetDoctorLicense} |
211 | + ref = {props.doctorInfoFile_Select} | ||
99 | /> | 212 | /> |
213 | + <styled.RegisterFileUploadWrapper> | ||
214 | + <styled.RegisterFileUploadButton onClick = {() => props.doctorInfoFile_Select.current.click()}> | ||
215 | + 파일 첨부 | ||
216 | + </styled.RegisterFileUploadButton> | ||
217 | + <styled.RegisterFileUploadInfoText> | ||
218 | + {props.doctorInfoFile ? props.doctorInfoFile[0].name : ''} | ||
219 | + </styled.RegisterFileUploadInfoText> | ||
220 | + </styled.RegisterFileUploadWrapper> | ||
100 | </styled.RegisterInputWrapper> | 221 | </styled.RegisterInputWrapper> |
101 | <styled.RegisterInputWrapper> | 222 | <styled.RegisterInputWrapper> |
102 | <styled.RegisterInputText>이름</styled.RegisterInputText> | 223 | <styled.RegisterInputText>이름</styled.RegisterInputText> |
... | @@ -118,27 +239,33 @@ const RegisterPresenter = (props : RegisterProps) => { | ... | @@ -118,27 +239,33 @@ const RegisterPresenter = (props : RegisterProps) => { |
118 | props.page === 3 ? | 239 | props.page === 3 ? |
119 | <> | 240 | <> |
120 | <styled.RegisterInputWrapper> | 241 | <styled.RegisterInputWrapper> |
121 | - <styled.RegisterInputText>전문 분야</styled.RegisterInputText> | 242 | + <styled.RegisterInputText>병원</styled.RegisterInputText> |
122 | - <styled.RegisterInput | 243 | + <styled.RegisterInputWrapperForSearch> |
123 | - placeholder = "Doctor's Type" | 244 | + <styled.RegisterInput |
124 | - value = {props.registerForm.info.doctorType} | 245 | + placeholder = 'Hospital' |
125 | - onChange = {props.onSetDoctorType} | 246 | + value = {props.hospitalNm} |
126 | - /> | 247 | + onChange = {props.onSetHospitalNm} |
248 | + /> | ||
249 | + <styled.RegisterInputSearchButton | ||
250 | + onClick = {props.onSearchHospital} | ||
251 | + > | ||
252 | + <styled.RegisterInputSearchButtonImg src = {lensImg}/> | ||
253 | + </styled.RegisterInputSearchButton> | ||
254 | + </styled.RegisterInputWrapperForSearch> | ||
127 | </styled.RegisterInputWrapper> | 255 | </styled.RegisterInputWrapper> |
128 | <styled.RegisterInputWrapper> | 256 | <styled.RegisterInputWrapper> |
129 | - <styled.RegisterInputText>병원 이름</styled.RegisterInputText> | 257 | + <styled.RegisterInputText>주소</styled.RegisterInputText> |
130 | <styled.RegisterInput | 258 | <styled.RegisterInput |
131 | - placeholder = 'Hospital' | 259 | + placeholder = 'Address' |
132 | - value = {props.registerForm.info.hospitalNm} | 260 | + value = {props.registerForm.info.hospitalAddr} |
133 | - onChange = {props.onSetHospitalNm} | ||
134 | /> | 261 | /> |
135 | </styled.RegisterInputWrapper> | 262 | </styled.RegisterInputWrapper> |
136 | <styled.RegisterInputWrapper> | 263 | <styled.RegisterInputWrapper> |
137 | - <styled.RegisterInputText>병원 주소</styled.RegisterInputText> | 264 | + <styled.RegisterInputText>전문 분야</styled.RegisterInputText> |
138 | <styled.RegisterInput | 265 | <styled.RegisterInput |
139 | - placeholder = 'Address' | 266 | + placeholder = "Doctor's Type" |
140 | - value = {props.registerForm.info.hospitalAddr} | 267 | + value = {props.registerForm.info.doctorType} |
141 | - onChange = {props.onSetHospitalAddr} | 268 | + onChange = {props.onSetDoctorType} |
142 | /> | 269 | /> |
143 | </styled.RegisterInputWrapper> | 270 | </styled.RegisterInputWrapper> |
144 | </> : null | 271 | </> : null | ... | ... |
... | @@ -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