Skip to content

Commit 77f43f9

Browse files
committed
feat: Add HIPAA agreement component and integrate it into EMR training flow; refactor training session handling and improve user experience with registration and agreement acceptance.
1 parent 6a6f4bf commit 77f43f9

File tree

5 files changed

+187
-168
lines changed

5 files changed

+187
-168
lines changed

backend/src/middlewares/multer.middleware.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ import { GridFSBucket } from 'mongodb';
33
import mongoose from 'mongoose';
44
import { ApiError } from '../utils/ApiError.js';
55

6-
// Create GridFS bucket
76
let bucket;
87
mongoose.connection.once('open', () => {
98
bucket = new GridFSBucket(mongoose.connection.db, {
109
bucketName: 'uploads'
1110
});
1211
});
1312

14-
// GridFS storage for multer
1513
const gridFSStorage = multer.memoryStorage();
1614

1715
const pdfFileFilter = (req, file, cb) => {
@@ -31,7 +29,6 @@ const documentFileFilter = (req, file, cb) => {
3129
}
3230
};
3331

34-
// Middleware to save to GridFS after multer processes the file
3532
const saveToGridFS = (req, res, next) => {
3633
if (!req.file || !bucket) {
3734
return next();

frontend/src/components/Dashboard/CVBuilder/steps/BasicDetailsStep.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ const BasicDetailsStep = ({ formData, onInputChange }) => {
354354
<h3 className="text-lg font-semibold text-[#04445E]">Address</h3>
355355
<FormField
356356
label="Address"
357-
type="textarea"
357+
type="text"
358358
value={formData.basicDetails.address || ''}
359359
onChange={(value) => handleInputChange('address', value)}
360360
onBlur={() => handleBlur('address')}
@@ -481,7 +481,7 @@ const BasicDetailsStep = ({ formData, onInputChange }) => {
481481
/>
482482
</div>
483483
</div>
484-
484+
{/*
485485
<div className="bg-blue-50 rounded-lg p-4">
486486
<div className="flex items-center gap-2 text-blue-800">
487487
<AlertCircle className="h-5 w-5" />
@@ -493,7 +493,7 @@ const BasicDetailsStep = ({ formData, onInputChange }) => {
493493
: "Please complete all required fields to proceed."
494494
}
495495
</p>
496-
</div>
496+
</div> */}
497497
</div>
498498
);
499499
};

frontend/src/components/Dashboard/CVBuilder/steps/EducationStep.jsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,6 @@ const EducationStep = ({ formData, onInputChange }) => {
287287
onChange={(value) => onInputChange('education', 'graduation', { ...formData.education?.graduation, overallGrade: value })}
288288
placeholder="e.g., 8.5 CGPA or 85%"
289289
/>
290-
291-
<FormField
292-
label="Class/Division"
293-
type="select"
294-
options={['First Class with Distinction', 'First Class', 'Second Class', 'Pass Class']}
295-
value={formData.education?.graduation?.classType || ''}
296-
onChange={(value) => onInputChange('education', 'graduation', { ...formData.education?.graduation, classType: value })}
297-
/>
298290
</FormGrid>
299291
</div>
300292
);
@@ -416,7 +408,7 @@ const EducationStep = ({ formData, onInputChange }) => {
416408
<div className="bg-white rounded-lg border border-gray-200 p-6">
417409
{renderActiveTab()}
418410
</div>
419-
411+
{/*
420412
<div className="bg-green-50 rounded-lg p-4">
421413
<div className="flex items-center gap-2 text-green-800">
422414
<GraduationCap className="h-5 w-5" />
@@ -425,7 +417,7 @@ const EducationStep = ({ formData, onInputChange }) => {
425417
<p className="text-green-700 text-sm mt-1">
426418
Complete all relevant education sections. Post-graduation is optional.
427419
</p>
428-
</div>
420+
</div> */}
429421
</div>
430422
);
431423
};

frontend/src/components/Dashboard/EMRTraining/Emrtraining.jsx

Lines changed: 75 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,14 @@
11
import React, { useState } from 'react';
22
import { Calendar, Play, Clock, Users, ExternalLink, CheckCircle, X } from 'lucide-react';
3+
import HipaaAgreementComponent from './HipaaAgreeement';
34

45
const EmrTrainingComponent = () => {
56
const [activeTab, setActiveTab] = useState('book');
67
const [selectedMonth, setSelectedMonth] = useState('January');
78
const [registrations, setRegistrations] = useState([]);
9+
const [hasAgreedToTerms, setHasAgreedToTerms] = useState(false);
10+
const [showAgreement, setShowAgreement] = useState(false);
811

9-
const trainingSessions = {
10-
// 'January': [
11-
// {
12-
// id: 1,
13-
// title: 'EMR Basics: Getting Started',
14-
// description: 'Learn the fundamentals of Electronic Medical Records, including navigation, patient lookup, and basic documentation.',
15-
// date: '15',
16-
// time: '10:00 AM - 12:00 PM',
17-
// instructor: 'Dr. Sarah Johnson',
18-
// capacity: 25,
19-
// enrolled: 18,
20-
// level: 'Beginner'
21-
// },
22-
// {
23-
// id: 2,
24-
// title: 'Advanced Charting Techniques',
25-
// description: 'Master advanced documentation features, templates, and efficient workflows for comprehensive patient care.',
26-
// date: '22',
27-
// time: '2:00 PM - 4:00 PM',
28-
// instructor: 'Dr. Michael Chen',
29-
// capacity: 20,
30-
// enrolled: 15,
31-
// level: 'Advanced'
32-
// },
33-
// {
34-
// id: 3,
35-
// title: 'Prescription Management',
36-
// description: 'Complete guide to electronic prescribing, drug interactions, and medication history management.',
37-
// date: '29',
38-
// time: '9:00 AM - 11:00 AM',
39-
// instructor: 'Dr. Emily Rodriguez',
40-
// capacity: 30,
41-
// enrolled: 22,
42-
// level: 'Intermediate'
43-
// }
44-
// ],
45-
// 'February': [
46-
// {
47-
// id: 4,
48-
// title: 'Lab Results Integration',
49-
// description: 'Learn to efficiently manage and interpret lab results within the EMR system.',
50-
// date: '5',
51-
// time: '11:00 AM - 1:00 PM',
52-
// instructor: 'Dr. James Wilson',
53-
// capacity: 25,
54-
// enrolled: 12,
55-
// level: 'Intermediate'
56-
// },
57-
// {
58-
// id: 5,
59-
// title: 'Patient Communication Tools',
60-
// description: 'Utilize EMR messaging, portal management, and patient engagement features effectively.',
61-
// date: '12',
62-
// time: '3:00 PM - 5:00 PM',
63-
// instructor: 'Dr. Lisa Thompson',
64-
// capacity: 20,
65-
// enrolled: 8,
66-
// level: 'Beginner'
67-
// }
68-
// ],
69-
// 'March': [
70-
// {
71-
// id: 6,
72-
// title: 'Billing & Documentation',
73-
// description: 'Master proper documentation for accurate billing and compliance requirements.',
74-
// date: '8',
75-
// time: '1:00 PM - 3:00 PM',
76-
// instructor: 'Dr. Robert Kim',
77-
// capacity: 30,
78-
// enrolled: 25,
79-
// level: 'Advanced'
80-
// }
81-
// ]
82-
};
83-
84-
// Sample recordings data
8512
const recordings = [
8613
{
8714
id: 1,
@@ -114,19 +41,27 @@ const EmrTrainingComponent = () => {
11441

11542
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
11643

117-
const handleRegister = (sessionId) => {
118-
const session = Object.values(trainingSessions).flat().find(s => s.id === sessionId);
119-
if (session && !registrations.find(r => r.id === sessionId)) {
120-
setRegistrations([...registrations, {
121-
...session,
122-
registeredDate: new Date().toLocaleDateString(),
123-
status: 'Registered'
124-
}]);
44+
const handleCancelRegistration = (sessionId) => {
45+
setRegistrations(registrations.filter(r => r.id !== sessionId));
46+
};
47+
48+
const handleShowBookingTab = () => {
49+
if (!hasAgreedToTerms) {
50+
setShowAgreement(true);
51+
} else {
52+
setActiveTab('book');
12553
}
12654
};
12755

128-
const handleCancelRegistration = (sessionId) => {
129-
setRegistrations(registrations.filter(r => r.id !== sessionId));
56+
const handleAgreementAccept = () => {
57+
setHasAgreedToTerms(true);
58+
setShowAgreement(false);
59+
setActiveTab('book');
60+
};
61+
62+
const handleAgreementDecline = () => {
63+
setShowAgreement(false);
64+
setActiveTab('recordings'); // Redirect to recordings tab
13065
};
13166

13267
const getLevelColor = (level) => {
@@ -138,57 +73,6 @@ const EmrTrainingComponent = () => {
13873
}
13974
};
14075

141-
const TrainingCard = ({ session, showRegisterButton = true }) => (
142-
<div className="border border-gray-200 rounded-lg p-6 hover:shadow-md transition-shadow bg-white flex flex-col h-full">
143-
<div className="flex justify-between items-start mb-4">
144-
<div className="flex-1">
145-
<h3 className="text-xl font-bold text-[#04445E] mb-2">{session.title}</h3>
146-
<span className={`inline-block px-2 py-1 rounded-full text-xs font-medium ${getLevelColor(session.level)}`}>
147-
{session.level}
148-
</span>
149-
</div>
150-
</div>
151-
152-
<p className="text-gray-600 text-sm mb-4 leading-relaxed flex-grow">
153-
{session.description}
154-
</p>
155-
156-
<div className="space-y-3 mb-6">
157-
<div className="flex items-center gap-2 text-sm text-gray-600">
158-
<Calendar className="h-4 w-4 text-[#169AB4]" />
159-
<span>{selectedMonth} {session.date}, 2025 • {session.time}</span>
160-
</div>
161-
<div className="flex items-center gap-2 text-sm text-gray-600">
162-
<Users className="h-4 w-4 text-[#169AB4]" />
163-
<span>{session.enrolled}/{session.capacity} enrolled • {session.instructor}</span>
164-
</div>
165-
</div>
166-
167-
{showRegisterButton && (
168-
<div className="flex justify-end items-center mt-auto">
169-
<button
170-
onClick={() => handleRegister(session.id)}
171-
disabled={registrations.find(r => r.id === session.id) || session.enrolled >= session.capacity}
172-
className={`px-6 py-2 rounded-lg font-medium transition-colors ${
173-
registrations.find(r => r.id === session.id)
174-
? 'bg-green-100 text-green-800 cursor-not-allowed'
175-
: session.enrolled >= session.capacity
176-
? 'bg-gray-100 text-gray-500 cursor-not-allowed'
177-
: 'bg-[#169AB4] text-white hover:bg-[#147a8f]'
178-
}`}
179-
>
180-
{registrations.find(r => r.id === session.id)
181-
? 'Registered'
182-
: session.enrolled >= session.capacity
183-
? 'Full'
184-
: 'Register Now'
185-
}
186-
</button>
187-
</div>
188-
)}
189-
</div>
190-
);
191-
19276
const RecordingCard = ({ recording }) => (
19377
<div className="border border-gray-200 rounded-lg p-6 hover:shadow-md transition-shadow bg-white flex flex-col h-full">
19478
<div className="flex justify-between items-start mb-4">
@@ -265,16 +149,45 @@ const EmrTrainingComponent = () => {
265149

266150
return (
267151
<div className="max-w-7xl mx-auto p-6">
152+
{showAgreement && (
153+
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
154+
<div className="bg-white rounded-lg p-6 max-w-md w-full text-center">
155+
<h3 className="text-lg font-semibold mb-4">Import Your HIPAA Agreement Component Here</h3>
156+
<p className="text-gray-600 mb-6">Replace this placeholder with your separate HIPAA agreement JSX component.</p>
157+
<HipaaAgreementComponent
158+
onAccept={handleAgreementAccept}
159+
onDecline={handleAgreementDecline}
160+
onClose={() => setShowAgreement(false)}
161+
/>
162+
<div className="flex gap-4 justify-center">
163+
<button
164+
onClick={handleAgreementDecline}
165+
className="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50"
166+
>
167+
Decline
168+
</button>
169+
<button
170+
onClick={handleAgreementAccept}
171+
className="px-4 py-2 bg-[#169AB4] text-white rounded-lg hover:bg-[#147a8f]"
172+
>
173+
Accept
174+
</button>
175+
</div>
176+
</div>
177+
</div>
178+
)}
179+
268180
<div className="flex border-b border-gray-200 mb-8">
269181
<button
270-
onClick={() => setActiveTab('book')}
182+
onClick={handleShowBookingTab}
271183
className={`px-6 py-3 font-medium border-b-2 transition-colors ${
272184
activeTab === 'book'
273185
? 'border-[#169AB4] text-[#169AB4]'
274186
: 'border-transparent text-gray-500 hover:text-gray-700'
275187
}`}
276188
>
277189
Book Training
190+
{!hasAgreedToTerms && <span className="ml-2 text-xs text-red-500">*</span>}
278191
</button>
279192
<button
280193
onClick={() => setActiveTab('recordings')}
@@ -284,7 +197,7 @@ const EmrTrainingComponent = () => {
284197
: 'border-transparent text-gray-500 hover:text-gray-700'
285198
}`}
286199
>
287-
View Recordings
200+
Virtual Training
288201
</button>
289202
<button
290203
onClick={() => setActiveTab('registrations')}
@@ -298,9 +211,9 @@ const EmrTrainingComponent = () => {
298211
</button>
299212
</div>
300213

301-
{activeTab === 'book' && (
214+
{activeTab === 'book' && hasAgreedToTerms && (
302215
<div>
303-
{/* Month Selector */}
216+
{/* Month Selector Only */}
304217
<div className="mb-8">
305218
<h2 className="text-xl font-semibold text-[#04445E] mb-4">Select Month</h2>
306219
<div className="flex flex-wrap gap-2">
@@ -320,20 +233,30 @@ const EmrTrainingComponent = () => {
320233
</div>
321234
</div>
322235

323-
{/* Training Sessions */}
324-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
325-
{trainingSessions[selectedMonth]?.map((session) => (
326-
<TrainingCard key={session.id} session={session} />
327-
)) || (
328-
<div className="col-span-full text-center py-12 text-gray-500">
329-
<Calendar className="h-12 w-12 mx-auto mb-4 text-gray-300" />
330-
<p>No training sessions scheduled for {selectedMonth}</p>
331-
</div>
332-
)}
236+
{/* Placeholder for training sessions - you can fetch/display data here */}
237+
<div className="text-center py-12 text-gray-500">
238+
<Calendar className="h-12 w-12 mx-auto mb-4 text-gray-300" />
239+
<h3 className="text-lg font-semibold mb-2">Training Sessions for {selectedMonth}</h3>
240+
<p>No training sessions currently available for {selectedMonth} 2025</p>
241+
<p className="text-sm mt-2">Training sessions data can be fetched and displayed here</p>
333242
</div>
334243
</div>
335244
)}
336245

246+
{activeTab === 'book' && !hasAgreedToTerms && (
247+
<div className="text-center py-12 text-gray-500">
248+
<CheckCircle className="h-12 w-12 mx-auto mb-4 text-gray-300" />
249+
<p className="mb-2">HIPAA Compliance Agreement Required</p>
250+
<p className="text-sm">Please accept the terms and conditions to access training booking</p>
251+
<button
252+
onClick={() => setShowAgreement(true)}
253+
className="mt-4 px-6 py-2 bg-[#169AB4] text-white rounded-lg hover:bg-[#147a8f] transition-colors font-medium"
254+
>
255+
Review Agreement
256+
</button>
257+
</div>
258+
)}
259+
337260
{activeTab === 'recordings' && (
338261
<div>
339262
<h2 className="text-xl font-semibold text-[#04445E] mb-6">Available Recordings</h2>
@@ -347,7 +270,7 @@ const EmrTrainingComponent = () => {
347270

348271
{activeTab === 'registrations' && (
349272
<div>
350-
<h2 className="text-xl font-semibold text-[#04445E] mb-6">Your Registrations</h2>
273+
<h2 className="text-xl font-semibold text-[[#04445E] mb-6">Your Registrations</h2>
351274
{registrations.length > 0 ? (
352275
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
353276
{registrations.map((registration) => (

0 commit comments

Comments
 (0)