diff --git a/backend/server/src/requirements_mac.txt b/backend/server/src/requirements_mac.txt new file mode 100644 index 0000000..4ee67fd --- /dev/null +++ b/backend/server/src/requirements_mac.txt @@ -0,0 +1,71 @@ +annotated-types==0.6.0 +anyio==4.2.0 +blinker==1.7.0 +CacheControl==0.13.1 +cachetools==5.3.2 +certifi==2023.11.17 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +cryptography==41.0.7 +distro==1.9.0 +filelock==3.13.1 +firebase-admin==6.3.0 +Flask==3.0.0 +Flask-Cors==4.0.0 +Flask-Mail==0.9.1 +fsspec==2023.12.2 +google-api-core==2.15.0 +google-api-python-client==2.112.0 +google-auth==2.26.1 +google-auth-httplib2==0.2.0 +google-cloud-core==2.4.1 +google-cloud-firestore==2.14.0 +google-cloud-storage==2.14.0 +google-crc32c==1.5.0 +google-resumable-media==2.7.0 +googleapis-common-protos==1.62.0 +grpcio==1.60.0 +grpcio-status==1.60.0 +h11==0.14.0 +httpcore==1.0.2 +httplib2==0.22.0 +httpx==0.26.0 +idna==3.6 +itsdangerous==2.1.2 +Jinja2==3.1.2 +llvmlite==0.41.1 +load-dotenv==0.1.0 +MarkupSafe==2.1.3 +more-itertools==10.1.0 +mpmath==1.3.0 +msgpack==1.0.7 +networkx==3.2.1 +numba==0.58.1 +numpy==1.26.3 +openai==1.6.1 +openai-whisper==20231117 +proto-plus==1.23.0 +protobuf==4.25.1 +pyasn1==0.5.1 +pyasn1-modules==0.3.0 +pycparser==2.21 +pydantic==2.5.3 +pydantic_core==2.14.6 +PyJWT==2.8.0 +pyparsing==3.1.1 +python-dotenv==1.0.0 +regex==2023.12.25 +requests==2.31.0 +rsa==4.9 +six==1.16.0 +sniffio==1.3.0 +sympy==1.12 +tiktoken==0.5.2 +torch==2.1.2 +tqdm==4.66.1 +typing_extensions==4.9.0 +uritemplate==4.1.1 +urllib3==2.1.0 +Werkzeug==3.0.1 +whisper==1.1.10 \ No newline at end of file diff --git a/frontend/src/assets/circs.png b/frontend/src/assets/circs.png new file mode 100644 index 0000000..69f1605 Binary files /dev/null and b/frontend/src/assets/circs.png differ diff --git a/frontend/src/assets/glutes.png b/frontend/src/assets/glutes.png new file mode 100644 index 0000000..e679ce6 Binary files /dev/null and b/frontend/src/assets/glutes.png differ diff --git a/frontend/src/assets/quartSquat.png b/frontend/src/assets/quartSquat.png new file mode 100644 index 0000000..496b9bd Binary files /dev/null and b/frontend/src/assets/quartSquat.png differ diff --git a/frontend/src/assets/raises.webp b/frontend/src/assets/raises.webp new file mode 100644 index 0000000..0f679d1 Binary files /dev/null and b/frontend/src/assets/raises.webp differ diff --git a/frontend/src/assets/singleLegGlute.png b/frontend/src/assets/singleLegGlute.png new file mode 100644 index 0000000..9b4ceac Binary files /dev/null and b/frontend/src/assets/singleLegGlute.png differ diff --git a/frontend/src/assets/squat.png b/frontend/src/assets/squat.png new file mode 100644 index 0000000..5d7e3fd Binary files /dev/null and b/frontend/src/assets/squat.png differ diff --git a/frontend/src/views/patient/PatientHome.jsx b/frontend/src/views/patient/PatientHome.jsx index 48a9de5..88bbdc3 100644 --- a/frontend/src/views/patient/PatientHome.jsx +++ b/frontend/src/views/patient/PatientHome.jsx @@ -1,8 +1,9 @@ import Navbar from "./components/Navbar"; import RecordButton from "./components/RecordButton"; -import Ai3D from './components/Ai3D'; +// import Ai3D from './components/Ai3D'; import Conversation from './components/Conversation'; import Exercises from './components/Exercises'; +import "./styles.css"; const PatientHome = () => { const messages = [ @@ -27,9 +28,9 @@ const PatientHome = () => { return (
-
+
-
+

Welcome Back

John
@@ -44,7 +45,8 @@ const PatientHome = () => {
-
+
+
@@ -52,7 +54,7 @@ const PatientHome = () => {
- + {/* */}
diff --git a/frontend/src/views/patient/components/Conversation.jsx b/frontend/src/views/patient/components/Conversation.jsx index 9760fdf..8818fb8 100644 --- a/frontend/src/views/patient/components/Conversation.jsx +++ b/frontend/src/views/patient/components/Conversation.jsx @@ -4,7 +4,7 @@ const Conversation = ({ messages }) => { const endOfMessagesRef = useRef(null); useEffect(() => { - endOfMessagesRef.current?.scrollIntoView({ behavior: 'smooth' }); + // endOfMessagesRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); return ( diff --git a/frontend/src/views/patient/components/ExerciseCard.jsx b/frontend/src/views/patient/components/ExerciseCard.jsx index 59fb90d..5f5fff3 100644 --- a/frontend/src/views/patient/components/ExerciseCard.jsx +++ b/frontend/src/views/patient/components/ExerciseCard.jsx @@ -1,22 +1,51 @@ +import PropTypes from 'prop-types'; +import { useEffect, useState } from 'react'; + +const ExerciseCard = ({ title, description, imageUrl, instructions, onClick, isExpanded }) => { + const [startAnimation, setStartAnimation] = useState(false); + + useEffect(() => { + if (isExpanded) { + + const timeoutId = window.setTimeout(() => { + setStartAnimation(true); + }, 100); // Delay in milliseconds + + return () => window.clearTimeout(timeoutId); + } + }, [isExpanded]); + + const animationClass = startAnimation ? 'start-animation' : ''; -const ExerciseCard = () => { return ( -
-
- Shoes -
-
-

Shoes!

-

If a dog chews shoes whose shoes does he choose?

-
- +
+
+
+ {title} +
+
+

{title}

+

{description}

+ {isExpanded && ( +
+ {instructions.map((step, index) => ( +

{step}

+ ))} +
+ )}
); }; -export default ExerciseCard; +ExerciseCard.propTypes = { + title: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, + imageUrl: PropTypes.string.isRequired, + instructions: PropTypes.arrayOf(PropTypes.string).isRequired, + onClick: PropTypes.func.isRequired, + isExpanded: PropTypes.bool.isRequired, +}; + +export default ExerciseCard; \ No newline at end of file diff --git a/frontend/src/views/patient/components/Exercises.jsx b/frontend/src/views/patient/components/Exercises.jsx index b27457c..da73c51 100644 --- a/frontend/src/views/patient/components/Exercises.jsx +++ b/frontend/src/views/patient/components/Exercises.jsx @@ -1,10 +1,113 @@ -import Card from "./ExerciseCard"; +import { useState } from 'react'; +// import ExerciseCard from "./ExerciseCard"; +// import PropTypes from 'prop-types'; +import '../styles.css'; +import glutesImage from '../../../assets/raises.webp'; +import circleImage from '../../../assets/circs.png'; +import squatImage from '../../../assets/squat.png' +import quartSquatImage from '../../../assets/quartSquat.png' +import singleLegGluteImage from '../../../assets/singleLegGlute.png'; const Exercises = () => { + // const [expandedCard, setExpandedCard] = useState(null); + const [selectedCard, setSelectedCard] = useState(null); + + const cards = [ + { + id: 1, + title: "Glutes", + description: "Glute Bridges good for glutes", + imageUrl: glutesImage, + instructions: ["Step 1 for Card 1", "Step 2 for Card 1", "Step 3 for Card 1"] + }, + { + id: 2, + title: "Circs", + description: "Single Leg Circles good for glutes", + imageUrl: circleImage, + instructions: ["Step 1 for Card 2", "Step 2 for Card 2", "Step 3 for Card 2"] + }, + { + id: 3, + title: "Squats", + description: "Description for Card 3", + imageUrl: squatImage, // Placeholder image + instructions: ["Step 1 for Card 3", "Step 2 for Card 3", "Step 3 for Card 3"] + }, + { + id: 4, + title: "Card 4", + description: "Description for Card 4", + imageUrl: quartSquatImage, // Placeholder image + instructions: ["Step 1 for Card 4", "Step 2 for Card 4", "Step 3 for Card 4"] + }, + { + id: 5, + title: "Card 5", + description: "Description for Card 5", + imageUrl: singleLegGluteImage, // Placeholder image + instructions: ["Step 1 for Card 5", "Step 2 for Card 5", "Step 3 for Card 5"] + }, + { + id: 5, + title: "Card 5", + description: "Description for Card 5", + imageUrl: glutesImage, // Placeholder image + instructions: ["Step 1 for Card 5", "Step 2 for Card 5", "Step 3 for Card 5"] + }, + { + id: 5, + title: "Card 5", + description: "Description for Card 5", + imageUrl: glutesImage, // Placeholder image + instructions: ["Step 1 for Card 5", "Step 2 for Card 5", "Step 3 for Card 5"] + }, +]; + +const handleCardClick = (card) => { + setSelectedCard(card); + }; + + const closeModal = () => { + setSelectedCard(null); + }; + + const renderCard = (card) => ( +
+
handleCardClick(card)}> + {card.title} +
+

{card.title}

+

{card.description}

+
+
+
+ ); + + const renderModal = (card) => ( + +
+ {card.title} +
+ +

{card.title}

+

{card.description}

+
    + {card.instructions.map((step, index) => ( +
  1. {step}
  2. + ))} +
+
+
+
+ ); + return ( -
- - +
+
+ {cards.map(renderCard)} +
+ {selectedCard && renderModal(selectedCard)}
); }; diff --git a/frontend/src/views/patient/components/Phone.jsx b/frontend/src/views/patient/components/Phone.jsx new file mode 100644 index 0000000..4c1ba78 --- /dev/null +++ b/frontend/src/views/patient/components/Phone.jsx @@ -0,0 +1,22 @@ + +const Phone = () => { + return ( +
+
+ Shoes +
+
+

Phone!

+

I broke my phone, whose fault is it?

+
+ +
+
+
+ ); +}; + +export default Phone; diff --git a/frontend/src/views/patient/styles.css b/frontend/src/views/patient/styles.css new file mode 100644 index 0000000..20e4ec6 --- /dev/null +++ b/frontend/src/views/patient/styles.css @@ -0,0 +1,106 @@ +.exercise-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + overflow: hidden; +} + +.carousel { + width: 80%; + max-height: 75%; + overflow-y: auto; +} + +.carousel-item { + display: flex; + flex-direction: column; + align-items: center; + padding: 1rem; +} + +.card { + background: #f5f5f5; + border-radius: 0.5rem; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + cursor: pointer; + overflow: hidden; + transition: 0.3s; + width: 100%; + margin-bottom: 1rem; +} + +.card:hover { + transform: translateY(-5px); + box-shadow: 0 4px 6px rgba(0,0,0,0.2); +} + +.card img { + width: 100%; + height: auto; + object-fit: cover; +} + +.card-body { + padding: 1rem; +} + +.card-title { + font-size: 1.5rem; + margin-bottom: 0.5rem; +} + +.card-description { + font-size: 1rem; + color: #333; + margin-bottom: 1rem; +} + +.card-steps { + list-style-type: decimal; + padding-left: 1.5rem; +} + +.text-content { + padding: 1rem; +} + +.close-btn { + border: none; + background: transparent; + font-size: 1.5rem; + position: absolute; + top: 1rem; + right: 1rem; + cursor: pointer; +} + +.btn { + align-self: center; + padding: 0.5rem 1rem; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@media (max-width: 768px) { + .modal { + max-width: 90%; + } + + .modal-box { + flex-direction: column; + } + + .modal-image { + max-width: 100%; + margin-bottom: 1rem; + } + + .text-content { + max-width: 100%; + } +}