1
- "use client"
2
-
3
- import * as React from "react"
4
- import { useState , useMemo , useEffect , useCallback } from "react"
5
- import { SidebarInset , SidebarTrigger } from "@/components/ui/sidebar"
6
- import { Separator } from "@/components/ui/separator"
7
- import { Breadcrumb , BreadcrumbItem , BreadcrumbList , BreadcrumbPage } from "@/components/ui/breadcrumb"
8
- import { Button } from "@/components/ui/button"
9
- import { Plus , Trash2 } from "lucide-react"
10
- import { useHealthProfileStore , type Member } from "@/lib/health-profile-store"
11
- import { calculateHealthcareUtilization } from "@/lib/utilization-engine"
12
- import { useScreenReaderAnnouncement , ScreenReaderAnnouncement } from "@/lib/hooks/use-screen-reader"
13
- import { MemberCard , UtilizationAnalysis } from "@/components/health-profile"
1
+ "use client" ;
2
+
3
+ import { MemberCard , UtilizationAnalysis } from "@/components/health-profile" ;
4
+ import {
5
+ Breadcrumb ,
6
+ BreadcrumbItem ,
7
+ BreadcrumbList ,
8
+ BreadcrumbPage ,
9
+ } from "@/components/ui/breadcrumb" ;
10
+ import { Button } from "@/components/ui/button" ;
11
+ import { Separator } from "@/components/ui/separator" ;
12
+ import { SidebarInset , SidebarTrigger } from "@/components/ui/sidebar" ;
13
+ import { useHealthProfileStore , type Member } from "@/lib/health-profile-store" ;
14
+ import {
15
+ ScreenReaderAnnouncement ,
16
+ useScreenReaderAnnouncement ,
17
+ } from "@/lib/hooks/use-screen-reader" ;
18
+ import { calculateHealthcareUtilization } from "@/lib/utilization-engine" ;
19
+ import { Plus , Trash2 } from "lucide-react" ;
20
+ import * as React from "react" ;
21
+ import { useCallback , useEffect , useMemo , useState } from "react" ;
14
22
15
23
export default function HealthProfilePage ( ) {
16
- const { members, addMember, removeMember, updateMember, clearProfile } = useHealthProfileStore ( )
17
- const [ showUtilization , setShowUtilization ] = useState ( false )
18
- const [ selectedMemberId , setSelectedMemberId ] = useState < string > ( "" )
19
- const { announce, announcementRef } = useScreenReaderAnnouncement ( )
20
- const [ isHydrated , setIsHydrated ] = useState ( false )
21
- const [ collapsedCards , setCollapsedCards ] = useState < Record < string , boolean > > ( { } )
22
-
24
+ const { members, addMember, removeMember, updateMember, clearProfile } =
25
+ useHealthProfileStore ( ) ;
26
+ const [ showUtilization , setShowUtilization ] = useState ( false ) ;
27
+ const [ selectedMemberId , setSelectedMemberId ] = useState < string > ( "" ) ;
28
+ const { announce, announcementRef } = useScreenReaderAnnouncement ( ) ;
29
+ const [ isHydrated , setIsHydrated ] = useState ( false ) ;
30
+ const [ collapsedCards , setCollapsedCards ] = useState < Record < string , boolean > > (
31
+ { }
32
+ ) ;
33
+
23
34
// Handle hydration
24
35
useEffect ( ( ) => {
25
- setIsHydrated ( true )
26
- } , [ ] )
27
-
36
+ setIsHydrated ( true ) ;
37
+ } , [ ] ) ;
38
+
28
39
// Calculate utilization for all members
29
40
const memberUtilizations = useMemo ( ( ) => {
30
- return members . map ( member => ( {
41
+ return members . map ( ( member ) => ( {
31
42
memberId : member . id ,
32
43
memberName : member . age ? `Member (Age ${ member . age } )` : "Member" ,
33
44
utilization : member . age ? calculateHealthcareUtilization ( member ) : null ,
34
- hasConditions : member . conditions && member . conditions . length > 0 && ! member . conditions . includes ( "NONE" ) ,
35
- hasBasicData : Boolean ( member . age && member . age . trim ( ) !== '' )
36
- } ) )
37
- } , [ members ] )
38
-
45
+ hasConditions :
46
+ member . conditions &&
47
+ member . conditions . length > 0 &&
48
+ ! member . conditions . includes ( "NONE" ) ,
49
+ hasBasicData : Boolean ( member . age && member . age . trim ( ) !== "" ) ,
50
+ } ) ) ;
51
+ } , [ members ] ) ;
52
+
39
53
// Set default selected member when members change
40
54
React . useEffect ( ( ) => {
41
- if ( ! selectedMemberId || ! members . find ( m => m . id === selectedMemberId ) ) {
42
- const firstMemberWithData = members . find ( m => m . age && m . age . trim ( ) !== '' )
43
- setSelectedMemberId ( firstMemberWithData ?. id || members [ 0 ] ?. id || "" )
55
+ if ( ! selectedMemberId || ! members . find ( ( m ) => m . id === selectedMemberId ) ) {
56
+ const firstMemberWithData = members . find (
57
+ ( m ) => m . age && m . age . trim ( ) !== ""
58
+ ) ;
59
+ setSelectedMemberId ( firstMemberWithData ?. id || members [ 0 ] ?. id || "" ) ;
44
60
}
45
- } , [ members , selectedMemberId ] )
46
-
61
+ } , [ members , selectedMemberId ] ) ;
62
+
47
63
// Auto-show utilization if any member has sufficient data
48
64
React . useEffect ( ( ) => {
49
- const hasMemberWithData = memberUtilizations . some ( mu => mu . utilization && mu . hasBasicData )
65
+ const hasMemberWithData = memberUtilizations . some (
66
+ ( mu ) => mu . utilization && mu . hasBasicData
67
+ ) ;
50
68
if ( hasMemberWithData ) {
51
- setShowUtilization ( true )
69
+ setShowUtilization ( true ) ;
52
70
}
53
- } , [ memberUtilizations ] )
54
-
71
+ } , [ memberUtilizations ] ) ;
72
+
55
73
// Calculate profile completeness for basic profile
56
74
const calculateBasicCompleteness = ( member : Member ) => {
57
75
// Demographics scoring (age, gender, height, weight)
58
76
const demographics = [
59
- member . age && member . age . trim ( ) !== '' ? 25 : 0 ,
60
- member . gender && member . gender !== ' prefer_not_to_say' ? 25 : 0 ,
77
+ member . age && member . age . trim ( ) !== "" ? 25 : 0 ,
78
+ member . gender && member . gender !== " prefer_not_to_say" ? 25 : 0 ,
61
79
member . height && member . height > 0 ? 25 : 0 ,
62
80
member . weight && member . weight > 0 ? 25 : 0 ,
63
- ] . reduce ( ( a , b ) => a + b , 0 )
64
-
81
+ ] . reduce ( ( a , b ) => a + b , 0 ) ;
82
+
65
83
// Conditions scoring - 100% if at least one condition is added OR explicitly marked as "NONE"
66
- const conditions = ( member . conditions && member . conditions . length > 0 ) ? 100 : 0
67
-
84
+ const conditions =
85
+ member . conditions && member . conditions . length > 0 ? 100 : 0 ;
86
+
68
87
// Medications scoring - 100% if at least one medication is added OR explicitly marked as "NONE"
69
- const medications = ( member . medications && member . medications . length > 0 ) ? 100 : 0
70
-
88
+ const medications =
89
+ member . medications && member . medications . length > 0 ? 100 : 0 ;
90
+
71
91
// History scoring - 100% if allergies are specified (including "NONE")
72
- const history = ( member . allergies && member . allergies . length > 0 ) ? 100 : 0
73
-
92
+ const history = member . allergies && member . allergies . length > 0 ? 100 : 0 ;
93
+
74
94
// Preferences scoring (lifestyle factors only, medical services optional)
75
95
// Count as answered if user has selected any value
76
96
const lifestyleFactors = [
77
97
member . smokingStatus !== undefined ? 1 : 0 ,
78
98
member . alcoholUse !== undefined ? 1 : 0 ,
79
99
member . exerciseFrequency !== undefined ? 1 : 0 ,
80
- ]
81
- const answeredCount = lifestyleFactors . reduce ( ( a , b ) => a + b , 0 )
82
- const preferences = answeredCount === 3 ? 100 : Math . round ( ( answeredCount / 3 ) * 100 )
83
-
100
+ ] ;
101
+ const answeredCount = lifestyleFactors . reduce ( ( a , b ) => a + b , 0 ) ;
102
+ const preferences =
103
+ answeredCount === 3 ? 100 : Math . round ( ( answeredCount / 3 ) * 100 ) ;
104
+
84
105
// Calculate overall score (average of all categories, excluding providers)
85
- const overall = ( demographics + conditions + medications + history + preferences ) / 5
86
-
106
+ const overall =
107
+ ( demographics + conditions + medications + history + preferences ) / 5 ;
108
+
87
109
return {
88
110
overall : Math . round ( overall ) ,
89
111
categories : {
@@ -93,39 +115,45 @@ export default function HealthProfilePage() {
93
115
providers : 0 , // Not used but kept for compatibility
94
116
history : Math . round ( history ) ,
95
117
preferences : Math . round ( preferences ) ,
96
- }
97
- }
98
- }
118
+ } ,
119
+ } ;
120
+ } ;
99
121
100
122
// Handle member addition with announcement
101
123
const handleAddMember = ( ) => {
102
- addMember ( )
103
- announce ( `New family member added. Total members: ${ members . length + 1 } ` )
104
- }
124
+ addMember ( ) ;
125
+ announce ( `New family member added. Total members: ${ members . length + 1 } ` ) ;
126
+ } ;
105
127
106
128
// Handle member removal with announcement
107
- const handleRemoveMember = useCallback ( ( memberId : string , index : number ) => {
108
- removeMember ( memberId )
109
- announce ( `Member ${ index + 1 } removed from health profile` )
110
- } , [ removeMember , announce ] )
129
+ const handleRemoveMember = useCallback (
130
+ ( memberId : string , index : number ) => {
131
+ removeMember ( memberId ) ;
132
+ announce ( `Member ${ index + 1 } removed from health profile` ) ;
133
+ } ,
134
+ [ removeMember , announce ]
135
+ ) ;
111
136
112
137
// Toggle card collapse state
113
138
const toggleCardCollapse = useCallback ( ( memberId : string ) => {
114
- setCollapsedCards ( prev => ( {
139
+ setCollapsedCards ( ( prev ) => ( {
115
140
...prev ,
116
- [ memberId ] : ! prev [ memberId ]
117
- } ) )
118
- } , [ ] )
141
+ [ memberId ] : ! prev [ memberId ] ,
142
+ } ) ) ;
143
+ } , [ ] ) ;
119
144
120
145
// Check if card is collapsed (default to open for first member, closed for others)
121
146
const isCardCollapsed = ( memberId : string , index : number ) => {
122
- return collapsedCards [ memberId ] ?? ( index > 0 )
123
- }
147
+ return collapsedCards [ memberId ] ?? index > 0 ;
148
+ } ;
124
149
125
150
// Memoize the updateMember callback to prevent infinite loops
126
- const handleUpdateMember = useCallback ( ( memberId : string , updates : Partial < Member > ) => {
127
- updateMember ( memberId , updates )
128
- } , [ updateMember ] )
151
+ const handleUpdateMember = useCallback (
152
+ ( memberId : string , updates : Partial < Member > ) => {
153
+ updateMember ( memberId , updates ) ;
154
+ } ,
155
+ [ updateMember ]
156
+ ) ;
129
157
130
158
return (
131
159
< SidebarInset >
@@ -146,20 +174,38 @@ export default function HealthProfilePage() {
146
174
< div className = "max-w-7xl mx-auto px-4 py-8" >
147
175
< div className = "mb-8 flex justify-between items-start" >
148
176
< div >
149
- < h1 className = "text-3xl font-bold text-gray-900 mb-2" > Health Profile</ h1 >
177
+ < h1 className = "text-3xl font-bold text-gray-900 mb-2" >
178
+ Health Profile
179
+ </ h1 >
150
180
< p className = "text-gray-600" >
151
- Add your healthcare information to get personalized insurance analysis and recommendations.
181
+ Add your healthcare information to get personalized insurance
182
+ analysis and recommendations.
152
183
</ p >
153
184
</ div >
154
- < Button variant = "outline" onClick = { clearProfile } className = "text-red-600 hover:text-red-700" >
155
- < Trash2 className = "w-4 h-4 mr-2" />
156
- Clear All
157
- </ Button >
185
+ < div className = "flex gap-3" >
186
+ < Button
187
+ variant = "outline"
188
+ onClick = { ( ) =>
189
+ ( window . location . href = "/utilization-model-test" )
190
+ }
191
+ className = "text-blue-600 hover:text-blue-700 border-blue-200 hover:border-blue-300"
192
+ >
193
+ Try our new model here
194
+ </ Button >
195
+ < Button
196
+ variant = "outline"
197
+ onClick = { clearProfile }
198
+ className = "text-red-600 hover:text-red-700"
199
+ >
200
+ < Trash2 className = "w-4 h-4 mr-2" />
201
+ Clear All
202
+ </ Button >
203
+ </ div >
158
204
</ div >
159
-
160
- < div className = "space-y-4 mb-6" >
205
+
206
+ < div className = "space-y-4 mb-6" >
161
207
{ members . map ( ( member , index ) => (
162
- < MemberCard
208
+ < MemberCard
163
209
key = { member . id }
164
210
member = { member }
165
211
index = { index }
@@ -172,17 +218,17 @@ export default function HealthProfilePage() {
172
218
) ) }
173
219
</ div >
174
220
175
- < Button
176
- onClick = { handleAddMember }
177
- variant = "outline"
221
+ < Button
222
+ onClick = { handleAddMember }
223
+ variant = "outline"
178
224
className = "w-full mb-6 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
179
225
aria-label = "Add new family member to health profile"
180
226
>
181
227
< Plus className = "w-4 h-4 mr-2" />
182
228
Add Family Member
183
229
</ Button >
184
230
185
- { /* Profile Completeness Summary */ }
231
+ { /* Profile Completeness Summary */ }
186
232
{ /* {isHydrated && members.length > 0 && members[0] && (
187
233
<div className="mb-6">
188
234
<ProfileCompleteness
@@ -194,15 +240,16 @@ export default function HealthProfilePage() {
194
240
)} */ }
195
241
196
242
< div className = "flex justify-between items-center pt-4" >
197
- < p className = "text-sm text-gray-500" > Your health profile is automatically saved to your browser</ p >
243
+ < p className = "text-sm text-gray-500" >
244
+ Your health profile is automatically saved to your browser
245
+ </ p >
198
246
< div className = "text-sm text-gray-500" >
199
- { members . length } member{ members . length !== 1 ? "s" : "" } in profile
247
+ { members . length } member{ members . length !== 1 ? "s" : "" } in
248
+ profile
200
249
</ div >
201
250
</ div >
202
251
203
-
204
-
205
- < UtilizationAnalysis
252
+ < UtilizationAnalysis
206
253
memberUtilizations = { memberUtilizations }
207
254
members = { members }
208
255
showUtilization = { showUtilization }
@@ -213,5 +260,5 @@ export default function HealthProfilePage() {
213
260
</ div >
214
261
</ div >
215
262
</ SidebarInset >
216
- )
217
- }
263
+ ) ;
264
+ }
0 commit comments