diff --git a/client/src/App.jsx b/client/src/App.jsx index 3130c6e4..4d005a08 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -9,6 +9,8 @@ import Navbar from './Components/navBar'; import ChatInterface from './Components/chatInterface'; import MoodLogging from './Components/moodLogging'; import MoodLogs from './Components/moodLogs'; +import CheckInForm from './Components/checkInForm'; +import CheckInsList from './Components/checkInsList'; import { CssBaseline, Box } from '@mui/material'; import { UserContext } from './Components/userContext'; @@ -30,6 +32,9 @@ function App() { } /> } /> } /> + } /> + } /> + } /> diff --git a/client/src/Components/checkInForm.jsx b/client/src/Components/checkInForm.jsx new file mode 100644 index 00000000..fbf10e98 --- /dev/null +++ b/client/src/Components/checkInForm.jsx @@ -0,0 +1,90 @@ +import React, { useState } from 'react'; +import axios from 'axios'; +import PropTypes from 'prop-types'; +import { + Button, + Checkbox, + FormControl, + FormControlLabel, + InputLabel, + MenuItem, + Select, + TextField, + Box, + Tooltip, + } from '@mui/material'; + + +function CheckInForm({ userId, checkInId, update }) { + const [checkInTime, setCheckInTime] = useState(''); + const [frequency, setFrequency] = useState('daily'); + const [notify, setNotify] = useState(false); + + + const handleSubmit = async (event) => { + event.preventDefault(); + const url = update ? `/api/checkIn/update/${checkInId}` : '/api/checkIn/schedule'; + const method = update ? 'patch' : 'post'; + const data = { user_id: userId, check_in_time: checkInTime, frequency, notify }; + console.log('Submitting:', data); + try { + const response = await axios[method](url, data); + console.log('Success:', response.data.message); + // Optionally reset form or handle next steps + } catch (error) { + console.error('Error:', error.response?.data || error); + } + }; + +return ( + + setCheckInTime(e.target.value)} + sx={{ marginBottom: 3 }} + InputLabelProps={{ + shrink: true, + }} + required + helperText="Select the date and time for your check-in." + /> + + Frequency + + + + + + setNotify(e.target.checked)} color="primary" />} + label="Notify me" + sx={{ marginBottom: 2 }} + /> + + + ); +} + +CheckInForm.propTypes = { + userId: PropTypes.string.isRequired, + checkInId: PropTypes.string, + update: PropTypes.bool.isRequired, +}; + +export default CheckInForm; diff --git a/client/src/Components/checkInsList.jsx b/client/src/Components/checkInsList.jsx new file mode 100644 index 00000000..9b19b03a --- /dev/null +++ b/client/src/Components/checkInsList.jsx @@ -0,0 +1,68 @@ +import React, { useState, useEffect} from 'react'; +import axios from 'axios'; +import { useParams } from 'react-router-dom'; +import { List, ListItem, ListItemText, Paper, Typography } from '@mui/material'; + +function CheckInsList() { + const { userId } = useParams(); // Assuming 'user' has 'userId' + const [checkIns, setCheckIns] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + const fetchCheckIns = async () => { + if (!userId) { + setError('User not logged in'); + return; + } + setLoading(true); + try { + const response = await axios.get(`/api/checkIn/retrieveAll?user_id=${userId}`); + console.log("API Response:", response.data); // Confirm what you receive + + // Validate if data is an array and has the correct structure + if (Array.isArray(response.data) && response.data.every(item => item._id && item._id.$oid && item.check_in_time && item.check_in_time.$date)) { + const formattedCheckIns = response.data.map(checkIn => ({ + ...checkIn, + _id: checkIn._id.$oid, + check_in_time: new Date(checkIn.check_in_time.$date).toLocaleString(), // Convert date from MongoDB format to a readable string + })); + setCheckIns(formattedCheckIns); + } else { + console.error('Data received is not in expected array format:', response.data); + setError('Unexpected data format'); + } + setLoading(false); + } catch (err) { + console.error("Error during fetch:", err); + setError(err.message); + setLoading(false); + } + }; + + fetchCheckIns(); + }, [userId]); + + + if (!userId) return Please log in to see your check-ins.; + if (loading) return Loading...; + if (error) return Error: {error}; + + return ( + + Your Check-Ins + + {checkIns.length > 0 ? checkIns.map(checkIn => ( + + + + )) : } + + + ); +} + +export default CheckInsList; diff --git a/client/src/Components/sideBar.jsx b/client/src/Components/sideBar.jsx index 23887e00..fced6164 100644 --- a/client/src/Components/sideBar.jsx +++ b/client/src/Components/sideBar.jsx @@ -4,13 +4,15 @@ import DeckIcon from '@mui/icons-material/Deck'; import InsertEmoticonIcon from '@mui/icons-material/InsertEmoticon'; // Icon for mood logging import ListAltIcon from '@mui/icons-material/ListAlt'; // Icon for mood logs import ExitToAppIcon from '@mui/icons-material/ExitToApp'; +import EventAvailableIcon from '@mui/icons-material/EventAvailable'; +import ScheduleIcon from '@mui/icons-material/Schedule'; import { UserContext } from './userContext'; import { NavLink, useLocation } from 'react-router-dom'; const drawerWidth = 230; function Sidebar() { - const { logout } = useContext(UserContext); + const { logout, user } = useContext(UserContext); const location = useLocation(); // This hook returns the location object that represents the current URL. const isActive = (path) => location.pathname === path; // This function checks if the current path is the same as the path passed as an argument. @@ -40,6 +42,8 @@ function Sidebar() { { text: "Mind Chat", icon: , path: "/" }, { text: "Track Your Vibes", icon: , path: "/user/mood_logging" }, { text: "Mood Logs", icon: , path: "/user/mood_logs" }, + { text: "Schedule Check-In", icon: , path: "/user/check_in" }, // New item for check-in page + { text: "Check-In Reporting", icon: , path: `/user/check_ins/${user?.userId}` } // Dynamically inserting userId ].map((item) => ( { useEffect(() => { const savedUser = localStorage.getItem('user'); if (savedUser) { + console.log('Loaded user from storage:', savedUser); setUser(JSON.parse(savedUser)); } }, []); diff --git a/server/routes/checkIn.py b/server/routes/checkIn.py index 8c2cf6c0..d85e5afe 100644 --- a/server/routes/checkIn.py +++ b/server/routes/checkIn.py @@ -1,10 +1,10 @@ -from flask import Blueprint, request, jsonify +from flask import Blueprint, request, jsonify, current_app,Response from datetime import datetime, timedelta from pydantic import ValidationError from models.check_in import CheckIn, Frequency from dotenv import load_dotenv from services.azure_mongodb import MongoDBClient -from bson import ObjectId +from bson import ObjectId,json_util from pymongo import ReturnDocument from bson.errors import InvalidId from .scheduler_main import scheduler @@ -95,6 +95,49 @@ def update_check_in(check_in_id): except Exception as e: return jsonify({'error': str(e)}), 500 +@checkIn_routes.get('/checkIn/retrieve/') +def retrieve_check_in(check_in_id): + try: + check_in = db.check_ins.find_one({'_id': ObjectId(check_in_id)}) + if check_in: + return jsonify(check_in), 200 + else: + return jsonify({'message': 'Check-in not found'}), 404 + except InvalidId: + return jsonify({'error': 'Invalid check-in ID format'}), 400 + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@checkIn_routes.delete('/checkIn/delete/') +def delete_check_in(check_in_id): + try: + result = db.check_ins.delete_one({'_id': ObjectId(check_in_id)}) + if result.deleted_count: + return jsonify({'message': 'Check-in deleted successfully'}), 200 + else: + return jsonify({'message': 'Check-in not found'}), 404 + except InvalidId: + return jsonify({'error': 'Invalid check-in ID format'}), 400 + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@checkIn_routes.get('/checkIn/retrieveAll') +def retrieve_all_check_ins(): + user_id = request.args.get('user_id') + if not user_id: + return jsonify({'error': 'User ID is required'}), 400 + + try: + check_ins = db.check_ins.find({'user_id': user_id}) + check_ins_list = list(check_ins) # Convert the cursor to a list + if check_ins_list: + return Response(json_util.dumps(check_ins_list), mimetype='application/json'), 200 + else: + return jsonify({'message': 'No check-ins found for the user'}), 404 + except Exception as e: + current_app.logger.error(f'Error retrieving check-ins: {str(e)}') + return jsonify({'error': str(e)}), 500 + @checkIn_routes.get('/checkIn/missed') def check_missed_check_ins():