11import mongoose from 'mongoose' ;
22
3+ const timeRegex = / ^ ( [ 0 1 ] \d | 2 [ 0 - 3 ] ) : ( [ 0 - 5 ] \d ) $ / ;
4+
5+ const slotSchema = new mongoose . Schema ( {
6+ startTime : {
7+ type : String ,
8+ required : true ,
9+ match : timeRegex
10+ } ,
11+ endTime : {
12+ type : String ,
13+ required : true ,
14+ match : timeRegex
15+ }
16+ } , { _id : false } ) ;
17+
18+ const workingDaySchema = new mongoose . Schema ( {
19+ day : {
20+ type : String ,
21+ required : true ,
22+ enum : [ "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" , "Sunday" ]
23+ } ,
24+ isAvailable : {
25+ type : Boolean ,
26+ default : true
27+ } ,
28+ slots : {
29+ type : [ slotSchema ] ,
30+ default : [ ]
31+ }
32+ } , { _id : false } ) ;
33+
34+ workingDaySchema . pre ( 'validate' , function ( next ) {
35+
36+ if ( ! this . isAvailable && this . slots . length > 0 ) {
37+ return next ( new Error ( "Slots cannot exist when doctor is unavailable" ) ) ;
38+ }
39+
40+ const sorted = [ ...this . slots ] . sort ( ( a , b ) =>
41+ a . startTime . localeCompare ( b . startTime )
42+ ) ;
43+
44+ for ( let i = 0 ; i < sorted . length ; i ++ ) {
45+
46+ if ( sorted [ i ] . startTime >= sorted [ i ] . endTime ) {
47+ return next ( new Error ( "Start time must be before end time" ) ) ;
48+ }
49+
50+ if ( i > 0 && sorted [ i - 1 ] . endTime > sorted [ i ] . startTime ) {
51+ return next ( new Error ( "Overlapping time slots detected" ) ) ;
52+ }
53+ }
54+
55+ next ( ) ;
56+ } ) ;
57+
58+ const DEFAULT_WORKING_HOURS = [
59+ { day : "Monday" , isAvailable : true , slots : [ { startTime : "09:00" , endTime : "17:00" } ] } ,
60+ { day : "Tuesday" , isAvailable : true , slots : [ { startTime : "09:00" , endTime : "17:00" } ] } ,
61+ { day : "Wednesday" , isAvailable : true , slots : [ { startTime : "09:00" , endTime : "17:00" } ] } ,
62+ { day : "Thursday" , isAvailable : true , slots : [ { startTime : "09:00" , endTime : "17:00" } ] } ,
63+ { day : "Friday" , isAvailable : true , slots : [ { startTime : "09:00" , endTime : "17:00" } ] } ,
64+ { day : "Saturday" , isAvailable : false , slots : [ ] } ,
65+ { day : "Sunday" , isAvailable : false , slots : [ ] } ,
66+ ] ;
67+
368const userSchema = new mongoose . Schema ( {
69+
470 email : {
571 type : String ,
672 required : true ,
773 unique : true ,
8- match : / .+ \@ .+ \. .+ /
74+ match : / .+ \@ .+ \. .+ /
75+ } ,
76+
77+ password : {
78+ type : String ,
79+ required : true
980 } ,
10- password : { type : String , required : true } ,
1181
1282 role : {
1383 type : String ,
1484 required : true ,
1585 enum : [ 'admin' , 'doctor' , 'receptionist' , 'billing' ]
1686 } ,
1787
18-
1988 name : { type : String } ,
89+
2090 phno : {
2191 type : String ,
2292 match : / ^ [ 0 - 9 ] { 10 } $ /
2393 } ,
94+
2495 spec : { type : String } ,
96+
2597 dept : {
2698 type : mongoose . Schema . Types . ObjectId ,
2799 ref : 'Department'
28100 } ,
101+
29102 exp : { type : String } ,
30103 qual : { type : String } ,
104+
31105 status : {
32106 type : String ,
33107 enum : [ 'Active' , 'Inactive' ] ,
34108 default : 'Active'
109+ } ,
110+
111+ workingHours : {
112+ type : [ workingDaySchema ]
35113 }
114+
36115} , { timestamps : true } ) ;
37116
117+
118+ userSchema . pre ( 'validate' , function ( next ) {
119+
120+ if ( this . role === 'doctor' && this . workingHours ) {
121+ const days = this . workingHours . map ( d => d . day ) ;
122+ const uniqueDays = new Set ( days ) ;
123+
124+ if ( days . length !== uniqueDays . size ) {
125+ return next ( new Error ( "Duplicate days are not allowed" ) ) ;
126+ }
127+ }
128+
129+ next ( ) ;
130+ } ) ;
131+
132+ userSchema . pre ( 'save' , function ( next ) {
133+
134+ if ( this . role === 'doctor' && ( ! this . workingHours || this . workingHours . length === 0 ) ) {
135+ this . workingHours = DEFAULT_WORKING_HOURS ;
136+ }
137+
138+ next ( ) ;
139+ } ) ;
140+
141+
38142const User = mongoose . models . User || mongoose . model ( 'User' , userSchema ) ;
39143export default User ;
0 commit comments