Skip to content

Commit cc6598c

Browse files
committed
Refactor Health Profile Page and enhance Utilization Model Test inputs
- Reorganized import statements for better readability in the health profile page. - Improved state management and effect hooks for member selection and utilization display. - Updated the utilization model test page to replace numeric inputs with dropdowns and radio buttons for better user experience. - Added validation for family size and K6 distress score inputs to ensure valid ranges. - Enhanced labels for clarity and consistency across the input fields. - Added button on Health Profile page that links to the new prediction page
1 parent 79d4dfc commit cc6598c

File tree

2 files changed

+252
-130
lines changed

2 files changed

+252
-130
lines changed

app/health-profile/page.tsx

Lines changed: 140 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,111 @@
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";
1422

1523
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+
2334
// Handle hydration
2435
useEffect(() => {
25-
setIsHydrated(true)
26-
}, [])
27-
36+
setIsHydrated(true);
37+
}, []);
38+
2839
// Calculate utilization for all members
2940
const memberUtilizations = useMemo(() => {
30-
return members.map(member => ({
41+
return members.map((member) => ({
3142
memberId: member.id,
3243
memberName: member.age ? `Member (Age ${member.age})` : "Member",
3344
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+
3953
// Set default selected member when members change
4054
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 || "");
4460
}
45-
}, [members, selectedMemberId])
46-
61+
}, [members, selectedMemberId]);
62+
4763
// Auto-show utilization if any member has sufficient data
4864
React.useEffect(() => {
49-
const hasMemberWithData = memberUtilizations.some(mu => mu.utilization && mu.hasBasicData)
65+
const hasMemberWithData = memberUtilizations.some(
66+
(mu) => mu.utilization && mu.hasBasicData
67+
);
5068
if (hasMemberWithData) {
51-
setShowUtilization(true)
69+
setShowUtilization(true);
5270
}
53-
}, [memberUtilizations])
54-
71+
}, [memberUtilizations]);
72+
5573
// Calculate profile completeness for basic profile
5674
const calculateBasicCompleteness = (member: Member) => {
5775
// Demographics scoring (age, gender, height, weight)
5876
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,
6179
member.height && member.height > 0 ? 25 : 0,
6280
member.weight && member.weight > 0 ? 25 : 0,
63-
].reduce((a, b) => a + b, 0)
64-
81+
].reduce((a, b) => a + b, 0);
82+
6583
// 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+
6887
// 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+
7191
// 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+
7494
// Preferences scoring (lifestyle factors only, medical services optional)
7595
// Count as answered if user has selected any value
7696
const lifestyleFactors = [
7797
member.smokingStatus !== undefined ? 1 : 0,
7898
member.alcoholUse !== undefined ? 1 : 0,
7999
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+
84105
// 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+
87109
return {
88110
overall: Math.round(overall),
89111
categories: {
@@ -93,39 +115,45 @@ export default function HealthProfilePage() {
93115
providers: 0, // Not used but kept for compatibility
94116
history: Math.round(history),
95117
preferences: Math.round(preferences),
96-
}
97-
}
98-
}
118+
},
119+
};
120+
};
99121

100122
// Handle member addition with announcement
101123
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+
};
105127

106128
// 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+
);
111136

112137
// Toggle card collapse state
113138
const toggleCardCollapse = useCallback((memberId: string) => {
114-
setCollapsedCards(prev => ({
139+
setCollapsedCards((prev) => ({
115140
...prev,
116-
[memberId]: !prev[memberId]
117-
}))
118-
}, [])
141+
[memberId]: !prev[memberId],
142+
}));
143+
}, []);
119144

120145
// Check if card is collapsed (default to open for first member, closed for others)
121146
const isCardCollapsed = (memberId: string, index: number) => {
122-
return collapsedCards[memberId] ?? (index > 0)
123-
}
147+
return collapsedCards[memberId] ?? index > 0;
148+
};
124149

125150
// 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+
);
129157

130158
return (
131159
<SidebarInset>
@@ -146,20 +174,38 @@ export default function HealthProfilePage() {
146174
<div className="max-w-7xl mx-auto px-4 py-8">
147175
<div className="mb-8 flex justify-between items-start">
148176
<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>
150180
<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.
152183
</p>
153184
</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>
158204
</div>
159-
160-
<div className="space-y-4 mb-6">
205+
206+
<div className="space-y-4 mb-6">
161207
{members.map((member, index) => (
162-
<MemberCard
208+
<MemberCard
163209
key={member.id}
164210
member={member}
165211
index={index}
@@ -172,17 +218,17 @@ export default function HealthProfilePage() {
172218
))}
173219
</div>
174220

175-
<Button
176-
onClick={handleAddMember}
177-
variant="outline"
221+
<Button
222+
onClick={handleAddMember}
223+
variant="outline"
178224
className="w-full mb-6 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
179225
aria-label="Add new family member to health profile"
180226
>
181227
<Plus className="w-4 h-4 mr-2" />
182228
Add Family Member
183229
</Button>
184230

185-
{/* Profile Completeness Summary */}
231+
{/* Profile Completeness Summary */}
186232
{/* {isHydrated && members.length > 0 && members[0] && (
187233
<div className="mb-6">
188234
<ProfileCompleteness
@@ -194,15 +240,16 @@ export default function HealthProfilePage() {
194240
)} */}
195241

196242
<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>
198246
<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
200249
</div>
201250
</div>
202251

203-
204-
205-
<UtilizationAnalysis
252+
<UtilizationAnalysis
206253
memberUtilizations={memberUtilizations}
207254
members={members}
208255
showUtilization={showUtilization}
@@ -213,5 +260,5 @@ export default function HealthProfilePage() {
213260
</div>
214261
</div>
215262
</SidebarInset>
216-
)
217-
}
263+
);
264+
}

0 commit comments

Comments
 (0)