diff --git a/.eslintrc.cjs b/.eslintrc.cjs index ee71034..33a3c44 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -3,18 +3,18 @@ module.exports = { env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", ], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, -} +}; diff --git "a/.github/ISSUE_TEMPLATE/\342\234\205-feature.md" "b/.github/ISSUE_TEMPLATE/\342\234\205-feature.md" index 491e97e..e22b8d4 100644 --- "a/.github/ISSUE_TEMPLATE/\342\234\205-feature.md" +++ "b/.github/ISSUE_TEMPLATE/\342\234\205-feature.md" @@ -2,19 +2,22 @@ name: "✅ FEATURE" about: Feature 작업 사항을 입력해주세요. title: "[Feat]" -labels: '' -assignees: '' - +labels: "" +assignees: "" --- ### Description + > 설명을 작성해주세요. ### In Progress + > 작업사항들을 작성해주세요. + - [ ] todo1 - [ ] todo2 - [ ] todo3 ### ETC + > 기타사항 diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\233-bug.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\233-bug.md" index eca6318..791f628 100644 --- "a/.github/ISSUE_TEMPLATE/\360\237\220\233-bug.md" +++ "b/.github/ISSUE_TEMPLATE/\360\237\220\233-bug.md" @@ -2,26 +2,31 @@ name: "\U0001F41B BUG" about: bug 발생 시 작성해주세요. title: "[BUG]" -labels: '' -assignees: '' - +labels: "" +assignees: "" --- ### Describe the bug + > 버그에 대해 설명해주세요. ### To Reproduce + > 버그 발생 과정을 기술하세요. + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error ### Expected behavior -> 본래 작동할 것이라 예상했던 구현 결과에 대해 설명하세요. + +> 본래 작동할 것이라 예상했던 구현 결과에 대해 설명하세요. ### Screenshots + > 화면 첨부파일 ### Additional context(Optional) ->발생한 문제에 대한 추가사항 + +> 발생한 문제에 대한 추가사항 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6e0fd3d..8300110 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,10 +1,13 @@ ## ☑️ Describe your changes -- 작업 내용을 적어주세요 -> ex. - CORS 허용 범위를 (/**) URL 전체로 변경한다. + +- 작업 내용을 적어주세요 + > ex. - CORS 허용 범위를 (/\*\*) URL 전체로 변경한다. ## 📷 Screenshot + - 관련 스크린샷 ## 🔗 Issue number and link + - 이슈 번호를 등록해주세요 -> closed {#이슈번호} \ No newline at end of file + > closed {#이슈번호} diff --git a/README.md b/README.md index 53cddd3..4038e67 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # ReHab-FrontEnd + 비대면 재활치료를 돕는 웹서비스, "Re:Hab" 프론트엔드 팀 개발 저장소입니다. ## 🔥Frontend Team -| FE | FE | -| :---: | :---: | -| | | -| 오소현 | 김경재 | -| [@osohyun0224](https://github.com/osohyun0224) | [@PortalCube](https://github.com/PortalCube) | - +| FE | FE | +| :------------------------------------------------------------------------------: | :------------------------------------------------------------------------------: | +| | | +| 오소현 | 김경재 | +| [@osohyun0224](https://github.com/osohyun0224) | [@PortalCube](https://github.com/PortalCube) | ## 💡 Frontend Commit Convention diff --git a/src/components/Button/RoleButton.jsx b/src/components/Button/RoleButton.jsx index c500e2c..1f15d80 100644 --- a/src/components/Button/RoleButton.jsx +++ b/src/components/Button/RoleButton.jsx @@ -1,5 +1,5 @@ -import { useState } from 'react'; -import styled from 'styled-components'; +import { useState } from "react"; +import styled from "styled-components"; import Patient from "../../assets/images/user/Opatient.png"; import Doctor from "../../assets/images/user/Odoctor.png"; import Therapist from "../../assets/images/user/Otherapist.png"; @@ -8,38 +8,39 @@ const Button = styled.button` width: 240px; height: 50px; border-radius: 10px; - background-color: #F3F3F3; - border: ${({ isSelected }) => (isSelected ? '3px solid #AD5DFD' : '1px solid #BBBBBB')}; + background-color: #f3f3f3; + border: ${({ isSelected }) => + isSelected ? "3px solid #AD5DFD" : "1px solid #BBBBBB"}; display: flex; justify-content: center; align-items: center; gap: 12px; cursor: pointer; - font-family: 'Spoqa Han Sans Neo', 'sans-serif'; + font-family: "Spoqa Han Sans Neo", "sans-serif"; font-weight: 700; font-size: 18px; - color: #333; + color: #333; `; const Icon = styled.img` - width: 40px; - height: 40px; + width: 40px; + height: 40px; `; const RoleButton = ({ role, isSelected, onSelectRole }) => { let iconSrc, buttonText; - switch(role) { - case 'patient': + switch (role) { + case "patient": iconSrc = Patient; buttonText = "환자"; break; - case 'doctor': + case "doctor": iconSrc = Doctor; buttonText = "전문의"; break; - case 'therapist': + case "therapist": iconSrc = Therapist; buttonText = "재활치료사"; break; @@ -53,7 +54,6 @@ const RoleButton = ({ role, isSelected, onSelectRole }) => { {buttonText} ); -} +}; export default RoleButton; - diff --git a/src/components/Chart/ChartSummary.jsx b/src/components/Chart/ChartSummary.jsx index 8caa0c6..5aa8a41 100644 --- a/src/components/Chart/ChartSummary.jsx +++ b/src/components/Chart/ChartSummary.jsx @@ -1,6 +1,9 @@ +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; import styled from "styled-components"; -import { useState, useEffect } from "react"; -import { userLogin } from "../../librarys/dummy-api"; +import { getChartByPatient } from "../../librarys/api/chart"; +import { selectToken } from "../../redux/userSlice"; const Container = styled.div` width: 380px; @@ -10,7 +13,6 @@ const Container = styled.div` border: 1px solid #0064ff; border-radius: 10px; background-color: #ffffff; - font-family: "Spoqa Han Sans Neo", "sans-serif"; position: relative; display: flex; flex-wrap: wrap; @@ -49,24 +51,31 @@ const Value = styled.span` color: #908b8b; `; -const ChartSummary = ({ ...props }) => { +const ChartSummary = () => { const [patientInfo, setPatientInfo] = useState({}); + const { patientMid } = useParams(); + const accessToken = useSelector(selectToken); useEffect(() => { - async function fetchPatientInfo() { - const doctorInfo = await userLogin("doctor", "123456"); - const patient = doctorInfo?.patient; - - if (patient) { - setPatientInfo(patient); + const fetchPatientInfo = async () => { + try { + const chartInfo = await getChartByPatient(accessToken, patientMid); + setPatientInfo({ + diseaseCode: chartInfo.cd, + recentVisitDate: "백엔드에서 제공되지 않음", + nextReservationDate: chartInfo.schedule, + assignedTherapist: chartInfo.therapist_name, + }); + } catch (error) { + console.error("환자 차트 정보를 불러오는데 실패하였습니다.", error); } - } + }; fetchPatientInfo(); - }, []); + }, [patientMid, accessToken]); return ( - + 차트 정보 diff --git a/src/components/Common/UserComponents.jsx b/src/components/Common/UserComponents.jsx index b680cb1..6571655 100644 --- a/src/components/Common/UserComponents.jsx +++ b/src/components/Common/UserComponents.jsx @@ -1,4 +1,4 @@ -import styled from 'styled-components'; +import styled from "styled-components"; import doctorImage from "../../assets/images/user/doctor.png"; import userImage from "../../assets/images/user/user.png"; import therapistImage from "../../assets/images/user/therapist.png"; @@ -33,15 +33,15 @@ const UserComponent = ({ userType, userName }) => { let jobTitle, userImg; switch (userType) { - case 'user': + case "user": jobTitle = "재활의학과 환자"; //임시 구현 userImg = userImage; break; - case 'admin1': + case "admin1": jobTitle = "재활의학과 전문의"; //임시 구현 userImg = doctorImage; break; - case 'admin2': + case "admin2": jobTitle = "재활치료사"; //임시 구현 userImg = therapistImage; break; diff --git a/src/components/DoctorDashBoard/CircleChart.jsx b/src/components/DoctorDashBoard/CircleChart.jsx index f676e96..7962db0 100644 --- a/src/components/DoctorDashBoard/CircleChart.jsx +++ b/src/components/DoctorDashBoard/CircleChart.jsx @@ -2,8 +2,8 @@ import PropTypes from "prop-types"; import styled from "styled-components"; const Outer = styled.div` - width: 100px; - height: 100px; + width: 100px; + height: 100px; border-radius: 50%; background: #dfdfdf; display: flex; @@ -15,20 +15,19 @@ const Outer = styled.div` `; const Inner = styled.div` - width: 70px; - height: 70px; + width: 70px; + height: 70px; border-radius: 50%; background-color: #ffffff; display: flex; align-items: center; justify-content: center; - font-size: 20px; + font-size: 20px; `; - const CircularChart = ({ className, totalExercises, passedExercises }) => { const passedPercentage = Math.round((passedExercises / totalExercises) * 100); - + const gradientCss = `conic-gradient(#3592FF 0% ${passedPercentage}%, #D9D9D9 ${passedPercentage}% 100%)`; return ( diff --git a/src/components/DoctorDashBoard/DoctorChartWrite.jsx b/src/components/DoctorDashBoard/DoctorChartWrite.jsx index 7d02785..5a37bcb 100644 --- a/src/components/DoctorDashBoard/DoctorChartWrite.jsx +++ b/src/components/DoctorDashBoard/DoctorChartWrite.jsx @@ -1,10 +1,10 @@ -import styled from 'styled-components'; -import PropTypes from 'prop-types'; -import XButton from '../../assets/icons/iconx.png'; -import InputTextLong from '../Input/InputTextLong'; -import DateSelect from '../Input/DateSelect'; -import { useState } from 'react'; -import { createRecord } from "../../librarys/api/chart" +import styled from "styled-components"; +import PropTypes from "prop-types"; +import XButton from "../../assets/icons/iconx.png"; +import InputTextLong from "../Input/InputTextLong"; +import DateSelect from "../Input/DateSelect"; +import { useState } from "react"; +import { createRecord } from "../../librarys/api/chart"; const Overlay = styled.div` position: fixed; @@ -21,7 +21,7 @@ const Overlay = styled.div` const ModalContainer = styled.div` width: 600px; height: 600px; - background-color: #FFFFFF; + background-color: #ffffff; border-radius: 10px; padding: 20px; box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); @@ -40,7 +40,7 @@ const CloseIcon = styled.img` right: 20px; top: 20px; cursor: pointer; - margin-top:10px; + margin-top: 10px; `; const Divider = styled.hr` @@ -61,41 +61,39 @@ const DateText = styled.p` const Button = styled.button` width: 140px; height: 30px; - background-color: #3592FF; + background-color: #3592ff; font-weight: 300; - color: #FEFDFD; + color: #fefdfd; font-size: 14px; border: none; border-radius: 5px; cursor: pointer; - margin-top:20px; - display: block; + margin-top: 20px; + display: block; margin-left: auto; - margin-right: auto; + margin-right: auto; `; - -export const DoctorChartWrite = ({ onClose , onSubmit}) => { +export const DoctorChartWrite = ({ onClose, onSubmit }) => { const getCurrentDate = () => { const date = new Date(); const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, '0'); - const day = String(date.getDate()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; }; const [recordData, setRecordData] = useState({ - treatmentRecord: '', - exerciseRequest: '', - nextSchedule: getCurrentDate() + treatmentRecord: "", + exerciseRequest: "", + nextSchedule: getCurrentDate(), }); const handleInputChange = (e) => { const { name, value } = e.target; - setRecordData(prevData => ({ ...prevData, [name]: value })); + setRecordData((prevData) => ({ ...prevData, [name]: value })); }; - const handleDateChange = (date) => { setRecordData({ ...recordData, nextSchedule: date }); }; @@ -105,40 +103,39 @@ export const DoctorChartWrite = ({ onClose , onSubmit}) => { onClose(); }; - return ( e.stopPropagation()}> 진료 기록 추가 - 오늘 날짜: {getCurrentDate()} - 오늘 날짜: {getCurrentDate()} + - - ); -} +}; DoctorChartWrite.propTypes = { onClose: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired, }; -export default DoctorChartWrite; \ No newline at end of file +export default DoctorChartWrite; diff --git a/src/components/DoctorDashBoard/DoctorCheckDetail.jsx b/src/components/DoctorDashBoard/DoctorCheckDetail.jsx index 44c5b23..490a6a3 100644 --- a/src/components/DoctorDashBoard/DoctorCheckDetail.jsx +++ b/src/components/DoctorDashBoard/DoctorCheckDetail.jsx @@ -1,7 +1,7 @@ -import styled from 'styled-components'; -import PropTypes from 'prop-types'; -import XButton from "../../assets/icons/iconx.png" -import InputTextLong from '../Input/InputTextLong'; +import styled from "styled-components"; +import PropTypes from "prop-types"; +import XButton from "../../assets/icons/iconx.png"; +import InputTextLong from "../Input/InputTextLong"; import DoctorDetailChart from "../../components/DoctorDashBoard/DoctorDetailChart"; const Overlay = styled.div` @@ -19,7 +19,7 @@ const Overlay = styled.div` const ModalContainer = styled.div` width: 600px; height: 600px; - background-color: #FFFFFF; + background-color: #ffffff; border-radius: 10px; padding: 20px; box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); @@ -38,7 +38,7 @@ const CloseIcon = styled.img` right: 20px; top: 20px; cursor: pointer; - margin-top:10px; + margin-top: 10px; `; const Divider = styled.hr` @@ -53,16 +53,16 @@ const Divider = styled.hr` const ButtonContainer = styled.div` display: flex; justify-content: space-between; - width: 290px; + width: 290px; margin: 20px auto 0; `; const Button = styled.button` width: 140px; height: 30px; - background-color: ${props => props.primary ? "#3592FF" : "#F3F3F3"}; - color: ${props => props.primary ? "#FEFDFD" : "#000000"}; - border: ${props => props.primary ? "none" : "1px solid #BBBBBB"}; + background-color: ${(props) => (props.primary ? "#3592FF" : "#F3F3F3")}; + color: ${(props) => (props.primary ? "#FEFDFD" : "#000000")}; + border: ${(props) => (props.primary ? "none" : "1px solid #BBBBBB")}; font-weight: 300; font-size: 14px; border-radius: 10px; @@ -73,7 +73,6 @@ const StyledInputTextLong = styled(InputTextLong)` margin-top: 30px; `; - export const DoctorCheckDetail = ({ onClose }) => { return ( @@ -81,7 +80,7 @@ export const DoctorCheckDetail = ({ onClose }) => { 진료 예약 상세 정보 - + @@ -90,10 +89,10 @@ export const DoctorCheckDetail = ({ onClose }) => { ); -} +}; DoctorCheckDetail.propTypes = { onClose: PropTypes.func.isRequired, }; -export default DoctorCheckDetail; \ No newline at end of file +export default DoctorCheckDetail; diff --git a/src/components/DoctorDashBoard/DoctorDetailChart.jsx b/src/components/DoctorDashBoard/DoctorDetailChart.jsx index 8e897b1..21f57ed 100644 --- a/src/components/DoctorDashBoard/DoctorDetailChart.jsx +++ b/src/components/DoctorDashBoard/DoctorDetailChart.jsx @@ -1,6 +1,9 @@ +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; import styled from "styled-components"; -import { useState, useEffect } from "react"; -import { userLogin } from "../../librarys/dummy-api"; +import { getChartSummary } from "../../librarys/api/chart"; +import { selectToken } from "../../redux/userSlice"; const Container = styled.div` width: 380px; @@ -51,19 +54,26 @@ const Value = styled.span` const DoctorDetailChart = () => { const [patientInfo, setPatientInfo] = useState({}); + const { staffId } = useParams(); + const accessToken = useSelector(selectToken); useEffect(() => { - async function fetchPatientInfo() { - const doctorInfo = await userLogin("doctor", "123456"); - const patient = doctorInfo?.patient; - - if (patient) { - setPatientInfo(patient); + const fetchPatientSummary = async () => { + try { + const summary = await getChartSummary(accessToken, staffId); + setPatientInfo({ + diseaseCode: summary.cd, + recentVisitDate: "아직 백엔드에서 넘겨주지 않음", + nextReservationDate: summary.schedule, + assignedTherapist: summary.therapist_name, + }); + } catch (error) { + console.error("환자 요약 정보를 불러오는데 실패하였습니다.", error); } - } + }; - fetchPatientInfo(); - }, []); + fetchPatientSummary(); + }, [staffId, accessToken]); return ( diff --git a/src/components/DoctorDashBoard/DoctorDetailHeader.jsx b/src/components/DoctorDashBoard/DoctorDetailHeader.jsx index ca7ad87..67f4535 100644 --- a/src/components/DoctorDashBoard/DoctorDetailHeader.jsx +++ b/src/components/DoctorDashBoard/DoctorDetailHeader.jsx @@ -1,6 +1,9 @@ -import styled from "styled-components"; import { useEffect, useState } from "react"; -import { userLogin } from "../../librarys/dummy-api"; +import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; +import styled from "styled-components"; +import { getChartByPatient } from "../../librarys/api/chart"; +import { selectToken } from "../../redux/userSlice"; const Container = styled.div` width: 800px; @@ -51,23 +54,27 @@ const getCurrentAge = (birthDateStr) => { const DoctorDetailHeader = () => { const [patientInfo, setPatientInfo] = useState({}); + const { patientMid } = useParams(); + const accessToken = useSelector(selectToken); useEffect(() => { - async function fetchPatientInfo() { - const doctorInfo = await userLogin("doctor", "123456"); - const patient = doctorInfo?.patient; - - if (patient) { - setPatientInfo({ - name: patient.name, - gender: patient.gender, - birth: patient.birth, - }); + async function fetchPatientChart() { + try { + const chartDetail = await getChartByPatient(accessToken, patientMid); + if (chartDetail) { + setPatientInfo({ + name: chartDetail.patient_name, + gender: chartDetail.sex, + birth: chartDetail.birth, + }); + } + } catch (error) { + console.error("환자 차트 정보를 불러오는데 실패하였습니다.", error); } } - fetchPatientInfo(); - }, []); + fetchPatientChart(); + }, [patientMid, accessToken]); return ( diff --git a/src/components/DoctorDashBoard/DoctorFaceRecord.jsx b/src/components/DoctorDashBoard/DoctorFaceRecord.jsx index 0989108..e054047 100644 --- a/src/components/DoctorDashBoard/DoctorFaceRecord.jsx +++ b/src/components/DoctorDashBoard/DoctorFaceRecord.jsx @@ -1,8 +1,8 @@ import styled from "styled-components"; import DoctorChartWrite from "./DoctorChartWrite"; import React, { useState, useEffect } from "react"; -import { useSelector } from 'react-redux'; -import { selectId, selectToken } from '../../redux/userSlice'; +import { useSelector } from "react-redux"; +import { selectId, selectToken } from "../../redux/userSlice"; import { createRecord, getChartOne } from "../../librarys/api/chart"; import { useParams } from "react-router-dom"; @@ -86,7 +86,7 @@ const DoctorFaceRecord = () => { const [isModalOpen, setIsModalOpen] = useState(false); const [records, setRecords] = useState([]); const userId = useSelector(selectId); - const {id: patientId} = useParams(); + const { id: patientId } = useParams(); const accessToken = useSelector(selectToken); const handleModalOpen = () => { @@ -116,7 +116,7 @@ const DoctorFaceRecord = () => { const req = { ...recordData, id: userId, - token: accessToken + token: accessToken, }; try { @@ -136,16 +136,16 @@ const DoctorFaceRecord = () => { {records.map((record, index) => ( - {record.schedule} - {record.treatmentRecord} - - ))} - {isModalOpen && ( - - )} + {record.schedule} + {record.treatmentRecord} + + ))} + {isModalOpen && ( + + )} ); }; diff --git a/src/components/DoctorDashBoard/DoctorUntactRecord.jsx b/src/components/DoctorDashBoard/DoctorUntactRecord.jsx index 45a6a22..8e2051e 100644 --- a/src/components/DoctorDashBoard/DoctorUntactRecord.jsx +++ b/src/components/DoctorDashBoard/DoctorUntactRecord.jsx @@ -1,5 +1,9 @@ +import React, { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; import styled from "styled-components"; -import { getUntactRecords } from "../../librarys/dummy-api"; +import { getAIRecordDetails } from "../../librarys/api/chart"; +import { selectToken } from "../../redux/userSlice"; const Container = styled.div` width: 800px; @@ -56,21 +60,34 @@ const DoctorInfo = styled.span` `; const DoctorUntactRecord = () => { - const userId = "HL0001"; - const records = getUntactRecords(userId); + const [records, setRecords] = useState([]); + const { patientMid } = useParams(); + const accessToken = useSelector(selectToken); + + useEffect(() => { + async function fetchAIRecords() { + try { + const aiRecords = await getAIRecordDetails(accessToken, patientMid); + setRecords(aiRecords); + } catch (error) { + console.error("비대면 진료 기록을 불러오는데 실패하였습니다.", error); + } + } + + fetchAIRecords(); + }, [patientMid, accessToken]); return ( 비대면 진료 기록 - {records && - records.map((record) => ( - <> - {record.date} - {record.doctorName} - {record.record} - - ))} + {records.map((record, index) => ( + + {record.regDate} + {record.staff_id} + {record.summary} + + ))} ); }; diff --git a/src/components/Input/DateSelect.jsx b/src/components/Input/DateSelect.jsx index e7f3c2b..fe9ad1a 100644 --- a/src/components/Input/DateSelect.jsx +++ b/src/components/Input/DateSelect.jsx @@ -1,9 +1,9 @@ -import styled from 'styled-components'; -import { useState, useRef, useEffect } from 'react'; -import Datetime from 'react-datetime'; -import IconCalender from '../../assets/icons/iconcalender.png'; -import 'react-datetime/css/react-datetime.css'; -import PropTypes from 'prop-types'; +import styled from "styled-components"; +import { useState, useRef, useEffect } from "react"; +import Datetime from "react-datetime"; +import IconCalender from "../../assets/icons/iconcalender.png"; +import "react-datetime/css/react-datetime.css"; +import PropTypes from "prop-types"; const Container = styled.div` display: flex; @@ -16,24 +16,24 @@ const DateInput = styled.input` width: 200px; height: 40px; border-radius: 10px; - background-color: #FAFAFA; - border: 1px solid #BBBBBB; - margin-right: 10px; + background-color: #fafafa; + border: 1px solid #bbbbbb; + margin-right: 10px; `; const CalendarButton = styled.button` width: 40px; height: 40px; - background-color: #FAFAFA; + background-color: #fafafa; cursor: pointer; border: none; color: white; border-radius: 10px; background-image: url(${IconCalender}); - background-size: 25px 25px; + background-size: 25px 25px; background-repeat: no-repeat; background-position: center; - border: 1px solid #BBBBBB; + border: 1px solid #bbbbbb; `; const CalendarModal = styled.div` @@ -60,17 +60,17 @@ const InputContainer = styled.div` `; const Label = styled.label` - font-family: 'Spoqa Han Sans Neo', 'sans-serif'; + font-family: "Spoqa Han Sans Neo", "sans-serif"; font-size: 16px; font-weight: 500; margin-bottom: 5px; `; const DateSelect = ({ labelText, value, onChange, ...props }) => { - const [date, setDate] = useState(''); + const [date, setDate] = useState(""); const [open, setOpen] = useState(false); - const format = 'YYYY-MM-DD'; + const format = "YYYY-MM-DD"; const handleClickButton = () => { setOpen(!open); @@ -139,15 +139,15 @@ const DateSelect = ({ labelText, value, onChange, ...props }) => { return ( - {labelText && } + {labelText && } - + {open && ( <> @@ -170,7 +170,7 @@ const DateSelect = ({ labelText, value, onChange, ...props }) => { DateSelect.propTypes = { labelText: PropTypes.string.isRequired, value: PropTypes.string, - onChange: PropTypes.func + onChange: PropTypes.func, }; -export default DateSelect; \ No newline at end of file +export default DateSelect; diff --git a/src/components/Input/InputDText.jsx b/src/components/Input/InputDText.jsx index ff352ad..2a3d9a3 100644 --- a/src/components/Input/InputDText.jsx +++ b/src/components/Input/InputDText.jsx @@ -1,4 +1,4 @@ -import styled from 'styled-components'; +import styled from "styled-components"; const InputContainer = styled.div` display: flex; @@ -6,7 +6,7 @@ const InputContainer = styled.div` `; const Label = styled.label` - font-family: 'Spoqa Han Sans Neo', 'sans-serif'; + font-family: "Spoqa Han Sans Neo", "sans-serif"; font-size: 16px; font-weight: 500; margin-bottom: 5px; @@ -16,9 +16,9 @@ const Input = styled.input` width: 720px; height: 100px; border-radius: 10px; - background-color: #FAFAFA; - border: 1px solid #BBBBBB; - font-family: 'Spoqa Han Sans Neo', 'sans-serif'; + background-color: #fafafa; + border: 1px solid #bbbbbb; + font-family: "Spoqa Han Sans Neo", "sans-serif"; padding-left: 0px; &:focus { diff --git a/src/components/Input/InputTextContainer.jsx b/src/components/Input/InputTextContainer.jsx index 4a99d5c..ba9caf6 100644 --- a/src/components/Input/InputTextContainer.jsx +++ b/src/components/Input/InputTextContainer.jsx @@ -17,11 +17,23 @@ const Label = styled.p` margin-bottom: 6px; `; -function InputTextContainer({ label, name, required, value, onChange, ...props }) { +function InputTextContainer({ + label, + name, + required, + value, + onChange, + ...props +}) { return ( - + ); } diff --git a/src/components/Input/InputTextLong.jsx b/src/components/Input/InputTextLong.jsx index 5682df5..25cc733 100644 --- a/src/components/Input/InputTextLong.jsx +++ b/src/components/Input/InputTextLong.jsx @@ -1,4 +1,4 @@ -import styled from 'styled-components'; +import styled from "styled-components"; const InputContainer = styled.div` display: flex; @@ -8,7 +8,7 @@ const InputContainer = styled.div` `; const Label = styled.label` - font-family: 'Spoqa Han Sans Neo', 'sans-serif'; + font-family: "Spoqa Han Sans Neo", "sans-serif"; font-size: 16px; font-weight: 500; margin-bottom: 5px; @@ -18,11 +18,11 @@ const TextArea = styled.textarea` width: 100%; height: 133px; border-radius: 10px; - background-color: #FAFAFA; - border: 1px solid #BBBBBB; - font-family: 'Spoqa Han Sans Neo', 'sans-serif'; + background-color: #fafafa; + border: 1px solid #bbbbbb; + font-family: "Spoqa Han Sans Neo", "sans-serif"; padding: 12px; - resize: none; + resize: none; &:focus { outline: none; @@ -40,4 +40,3 @@ function InputLongText({ label, name, value, onChange }) { export { TextArea }; export default InputLongText; - diff --git a/src/components/TherapistDashBoard/TheraCheckDetail.jsx b/src/components/TherapistDashBoard/TheraCheckDetail.jsx index 19ad879..922e73b 100644 --- a/src/components/TherapistDashBoard/TheraCheckDetail.jsx +++ b/src/components/TherapistDashBoard/TheraCheckDetail.jsx @@ -1,8 +1,8 @@ -import styled from 'styled-components'; -import PropTypes from 'prop-types'; -import XButton from "../../assets/icons/iconx.png" -import InputTextLong from '../Input/InputTextLong'; -import TheraDetailChart from './TheraDetailChart'; +import styled from "styled-components"; +import PropTypes from "prop-types"; +import XButton from "../../assets/icons/iconx.png"; +import InputTextLong from "../Input/InputTextLong"; +import TheraDetailChart from "./TheraDetailChart"; const Overlay = styled.div` position: fixed; @@ -19,7 +19,7 @@ const Overlay = styled.div` const ModalContainer = styled.div` width: 600px; height: 600px; - background-color: #FFFFFF; + background-color: #ffffff; border-radius: 10px; padding: 20px; box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); @@ -38,7 +38,7 @@ const CloseIcon = styled.img` right: 20px; top: 20px; cursor: pointer; - margin-top:10px; + margin-top: 10px; `; const Divider = styled.hr` @@ -53,16 +53,16 @@ const Divider = styled.hr` const ButtonContainer = styled.div` display: flex; justify-content: space-between; - width: 290px; + width: 290px; margin: 20px auto 0; `; const Button = styled.button` width: 140px; height: 30px; - background-color: ${props => props.primary ? "#3592FF" : "#F3F3F3"}; - color: ${props => props.primary ? "#FEFDFD" : "#000000"}; - border: ${props => props.primary ? "none" : "1px solid #BBBBBB"}; + background-color: ${(props) => (props.primary ? "#3592FF" : "#F3F3F3")}; + color: ${(props) => (props.primary ? "#FEFDFD" : "#000000")}; + border: ${(props) => (props.primary ? "none" : "1px solid #BBBBBB")}; font-weight: 300; font-size: 14px; border-radius: 10px; @@ -73,7 +73,6 @@ const StyledInputTextLong = styled(InputTextLong)` margin-top: 30px; `; - export const TheraCheckDetail = ({ onClose }) => { return ( @@ -81,7 +80,7 @@ export const TheraCheckDetail = ({ onClose }) => { 진료 예약 상세 정보 - + @@ -90,10 +89,10 @@ export const TheraCheckDetail = ({ onClose }) => { ); -} +}; TheraCheckDetail.propTypes = { onClose: PropTypes.func.isRequired, }; -export default TheraCheckDetail; \ No newline at end of file +export default TheraCheckDetail; diff --git a/src/components/TherapistDashBoard/TherafromDoctor.jsx b/src/components/TherapistDashBoard/TherafromDoctor.jsx index c3cd4f9..10bd996 100644 --- a/src/components/TherapistDashBoard/TherafromDoctor.jsx +++ b/src/components/TherapistDashBoard/TherafromDoctor.jsx @@ -10,7 +10,7 @@ const Container = styled.div` background-color: #ffffff; font-family: "Spoqa Han Sans Neo", "sans-serif"; position: relative; - margin-bottom:20px; + margin-bottom: 20px; `; const Title = styled.h1` @@ -31,7 +31,7 @@ const Divider = styled.hr` const DateText = styled.p` font-size: 24px; font-weight: bold; - margin-top:10px; + margin-top: 10px; margin-bottom: 15px; display: inline-block; `; @@ -39,26 +39,24 @@ const DateText = styled.p` const RecordBox = styled.div` width: 100%; height: 122px; - background-color: #FAFAFA; + background-color: #fafafa; border-radius: 10px; - border: 1px solid #BBBBBB; + border: 1px solid #bbbbbb; padding: 10px; font-size: 16px; - word-wrap: break-word; + word-wrap: break-word; overflow: hidden; `; const TherafromDoctor = () => { - return ( 재활 운동 요청서 - 2023.08.31 - - + 2023.08.31 + ); }; -export default TherafromDoctor; \ No newline at end of file +export default TherafromDoctor; diff --git a/src/components/UserDashBoard/UserUntactList.jsx b/src/components/UserDashBoard/UserUntactList.jsx index df0d10f..861e1c7 100644 --- a/src/components/UserDashBoard/UserUntactList.jsx +++ b/src/components/UserDashBoard/UserUntactList.jsx @@ -1,20 +1,20 @@ -import { useState } from 'react'; -import styled from 'styled-components'; -import DoctorImage from '../../assets/images/user/Odoctor.png'; -import IconDoctor from '../../assets/icons/icondoctor.png'; -import IconHospital from '../../assets/icons/iconhospital.png'; -import IconDate from '../../assets/icons/icondate.png'; +import { useState } from "react"; +import styled from "styled-components"; +import DoctorImage from "../../assets/images/user/Odoctor.png"; +import IconDoctor from "../../assets/icons/icondoctor.png"; +import IconHospital from "../../assets/icons/iconhospital.png"; +import IconDate from "../../assets/icons/icondate.png"; const CardContainer = styled.div` display: flex; - align-items: flex-start; + align-items: flex-start; padding: 15px; border: 1px solid #e0e0e0; border-radius: 10px; width: 700px; height: 110px; background-color: #ffffff; - position: relative; + position: relative; margin-bottom: 20px; `; @@ -67,26 +67,28 @@ const AppointmentInfo = styled.div` const DateContain = styled.div` margin-left: 20px; -` +`; const Button = styled.button` width: 160px; height: 32px; - background-color: ${props => props.cancelButton ? "#F3F3F3" : (props.clicked ? "#888888" : "#3592FF")}; - color: ${props => props.cancelButton ? "#000000" : (props.clicked ? "#444444" : "#FEFDFD")}; - border: ${props => props.cancelButton ? "1px solid #BBBBBB" : "none"}; + background-color: ${(props) => + props.cancelButton ? "#F3F3F3" : props.clicked ? "#888888" : "#3592FF"}; + color: ${(props) => + props.cancelButton ? "#000000" : props.clicked ? "#444444" : "#FEFDFD"}; + border: ${(props) => (props.cancelButton ? "1px solid #BBBBBB" : "none")}; border-radius: 10px; border: none; cursor: pointer; font-size: 14px; position: absolute; margin-left: 500px; - top: ${props => (props.topPosition ? props.topPosition : "19px")}; + top: ${(props) => (props.topPosition ? props.topPosition : "19px")}; &:nth-child(2) { - background-color: #F3F3F3; + background-color: #f3f3f3; color: #000000; - border: 1px solid #BBBBBB; + border: 1px solid #bbbbbb; top: 59px; } `; @@ -96,19 +98,21 @@ const UserUntactList = () => { const toggleButtonState = () => { setButtonState(!buttonState); - } + }; return ( - 김정원 + + 김정원 + 재활의학과 전문의 - - 2023/08/30 16:00 + + 2023/08/30 16:00 @@ -119,9 +123,11 @@ const UserUntactList = () => { - + ); }; -export default UserUntactList; \ No newline at end of file +export default UserUntactList; diff --git a/src/librarys/api/chart.js b/src/librarys/api/chart.js index 78c99cb..af98a10 100644 --- a/src/librarys/api/chart.js +++ b/src/librarys/api/chart.js @@ -52,7 +52,6 @@ export async function getChartList(token, id) { exerciseRequest: item.exerciseRequest, })), onlineRecords: (item.onlineRecords || []).map((item) => ({ - // TODO: 정확한 명세 알아올 것 ...item, })), })), @@ -133,3 +132,44 @@ export async function getChartOne(chartId, token) { throw error; } } + +/// ChartSummary + +//cno로 환자 상세 차트 조회 +export async function getChartSummary(token, staffId) { + const axios = getSpringAxios(token); + + try { + const response = await axios.get(`/chart/auth/staff/${staffId}`); + return response.data; + } catch (error) { + console.error("Error fetching chart summary:", error); + throw error; + } +} + +//환자의 mid로 차트 상세 정보를 조회 +export async function getChartByPatient(token, patientMid) { + const axios = getSpringAxios(token); + + try { + const response = await axios.get(`/chart/auth/patient/${patientMid}`); + return response.data; + } catch (error) { + console.error("Error fetching chart data by patient:", error); + throw error; + } +} + +// 환자의 mid로 비대면 진료 기록을 조회 +export async function getAIRecordDetails(token, patientMid) { + const axios = getSpringAxios(token); + + try { + const response = await axios.get(`/chart/auth/aiRecord/${patientMid}`); + return response.data; + } catch (error) { + console.error("Error fetching AI record details:", error); + throw error; + } +} diff --git a/src/librarys/context.js b/src/librarys/context.js index 3ddd569..e862320 100644 --- a/src/librarys/context.js +++ b/src/librarys/context.js @@ -1,4 +1,4 @@ import { createContext } from "react"; export const StateContext = createContext(); -export const DispatchContext = createContext(); \ No newline at end of file +export const DispatchContext = createContext(); diff --git a/src/librarys/webrtc/rtc-signaling.js b/src/librarys/webrtc/rtc-signaling.js index a1bbfc8..79aa390 100644 --- a/src/librarys/webrtc/rtc-signaling.js +++ b/src/librarys/webrtc/rtc-signaling.js @@ -1,73 +1,73 @@ const URL = import.meta.env.VITE_SIGNALING_SERVICE_URL; export default class RTCSignalingClient extends EventTarget { - /** @type {WebSocket} */ - instance = null; + /** @type {WebSocket} */ + instance = null; - get readyState() { - if (this.instance === null) { - return false; - } else { - return this.instance.readyState === 1; - } + get readyState() { + if (this.instance === null) { + return false; + } else { + return this.instance.readyState === 1; } + } - constructor() { - super(); - } + constructor() { + super(); + } - log(...arg) { - console.log("[RTCWebSocket]", ...arg); - } + log(...arg) { + console.log("[RTCWebSocket]", ...arg); + } - logError(...arg) { - console.error("[RTCWebSocket]", ...arg); - } + logError(...arg) { + console.error("[RTCWebSocket]", ...arg); + } - connect(id) { - return new Promise((resolve, reject) => { - this.instance = new WebSocket(URL + id); + connect(id) { + return new Promise((resolve, reject) => { + this.instance = new WebSocket(URL + id); - this.instance.addEventListener("open", () => { - this.log("접속 완료."); - resolve(); - }); + this.instance.addEventListener("open", () => { + this.log("접속 완료."); + resolve(); + }); - this.instance.addEventListener("error", (event) => { - this.logError("에러:", event); - reject(event); - }); + this.instance.addEventListener("error", (event) => { + this.logError("에러:", event); + reject(event); + }); - // 메세지를 받으면, type으로 RTCWebSocket 이벤트 Emit - this.instance.addEventListener("message", ({ data }) => { - const message = JSON.parse(data); - this.dispatchEvent( - new CustomEvent(message.type, { detail: message.payload }), - ); - }); - }); - } + // 메세지를 받으면, type으로 RTCWebSocket 이벤트 Emit + this.instance.addEventListener("message", ({ data }) => { + const message = JSON.parse(data); + this.dispatchEvent( + new CustomEvent(message.type, { detail: message.payload }), + ); + }); + }); + } - disconnect() { - this.instance.close(); - this.instance = null; - } + disconnect() { + this.instance.close(); + this.instance = null; + } - send(type, payload) { - const message = JSON.stringify({ - type, - payload, - }); + send(type, payload) { + const message = JSON.stringify({ + type, + payload, + }); - this.instance.send(message); - } + this.instance.send(message); + } - addEventListener(type, listener) { - const socketEvents = ["open", "close", "message", "error"]; - if (socketEvents.includes(type)) { - this.instance.addEventListener(type, listener); // WebSocket 이벤트 등록 - } else { - super.addEventListener(type, listener); // RTCWebSocket 이벤트 등록 - } + addEventListener(type, listener) { + const socketEvents = ["open", "close", "message", "error"]; + if (socketEvents.includes(type)) { + this.instance.addEventListener(type, listener); // WebSocket 이벤트 등록 + } else { + super.addEventListener(type, listener); // RTCWebSocket 이벤트 등록 } + } } diff --git a/src/librarys/webrtc/util.js b/src/librarys/webrtc/util.js index 4eb40f0..90cc55d 100644 --- a/src/librarys/webrtc/util.js +++ b/src/librarys/webrtc/util.js @@ -1,25 +1,25 @@ export function registerEvents(eventTarget, eventList, thisArg = this) { - Object.entries(eventList).forEach(([event, listener]) => { - eventTarget.addEventListener(event, listener.bind(thisArg)); - }); + Object.entries(eventList).forEach(([event, listener]) => { + eventTarget.addEventListener(event, listener.bind(thisArg)); + }); } function blobToArrayBuffer(blob) { - return new Promise((resolve) => { - const fileReader = new FileReader(); + return new Promise((resolve) => { + const fileReader = new FileReader(); - fileReader.onload = function () { - resolve(fileReader.result); - }; + fileReader.onload = function () { + resolve(fileReader.result); + }; - fileReader.readAsArrayBuffer(blob); - }); + fileReader.readAsArrayBuffer(blob); + }); } export async function getSampleRate(blob) { - const audioContext = new AudioContext(); - const arrayBuffer = await blobToArrayBuffer(blob); - const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); + const audioContext = new AudioContext(); + const arrayBuffer = await blobToArrayBuffer(blob); + const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); - return audioBuffer.sampleRate; + return audioBuffer.sampleRate; } diff --git a/src/pages/Doctor/DoctorDetailPage.jsx b/src/pages/Doctor/DoctorDetailPage.jsx index 2707ee4..d7cb052 100644 --- a/src/pages/Doctor/DoctorDetailPage.jsx +++ b/src/pages/Doctor/DoctorDetailPage.jsx @@ -7,11 +7,11 @@ import DoctorUntactRecord from "../../components/DoctorDashBoard/DoctorUntactRec import DoctorFaceRecord from "../../components/DoctorDashBoard/DoctorFaceRecord"; import PageContainer from "../../components/Common/PageContainer.jsx"; -const RowContainer = styled.div` - display: flex; - align-items: center; - width: 42%; - margin: 20px 0; +const Grid = styled.div` + margin: 20px 20px; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 40px; `; const DoctorDetailPage = () => { @@ -19,10 +19,10 @@ const DoctorDetailPage = () => { - + - + diff --git a/src/reducer/player.js b/src/reducer/player.js index 64848de..0e05c3c 100644 --- a/src/reducer/player.js +++ b/src/reducer/player.js @@ -4,7 +4,7 @@ export const intialPlayerState = { videoId: null, cameraStatus: null, subtitle: - '화면의 사각형 영역에 전신이 들어오도록 카메라를 조정해주세요. 준비되면 "시작하기" 버튼을 누르세요.', + '화면의 사각형 영역에 전신이 들어오도록 \n 카메라를 조정해주세요.\n 준비되면 "시작하기" 버튼을 누르세요.', guideStatus: false, countdown: null, playButtonActive: true, diff --git a/vercel.json b/vercel.json index 30e5a87..00e7ecc 100644 --- a/vercel.json +++ b/vercel.json @@ -1,3 +1,3 @@ { "routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }] -} \ No newline at end of file +}