@@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
77import { fetchResources } from "@/util/fetchResources" ;
88import { Resource } from "@/types/resources" ;
99import "@/app/globals.css" ;
10+ import { fetchLocationFromZip } from "@/util/fetchLocation" ;
1011
1112import { PrintableReferralsReport } from "@/util/printReferrals" ;
1213import { fetchActionPlan , ActionPlan } from "@/util/fetchActionPlan" ;
@@ -48,6 +49,8 @@ export default function Page() {
4849 const [ errorMessage , setErrorMessage ] = useState < string | undefined > (
4950 undefined ,
5051 ) ;
52+ const [ requestAfterZipResolution , setRequestAfterZipResolution ] =
53+ useState ( "" ) ;
5154
5255 const searchParams = useSearchParams ( ) ;
5356
@@ -101,14 +104,15 @@ export default function Page() {
101104 setReadyToPrint ( true ) ;
102105 } ;
103106
104- async function handleClick ( ) {
107+ async function findResources ( ) {
105108 const prompt_version_id = searchParams ?. get ( "prompt_version_id" ) ?? null ;
106109
107110 setLoading ( true ) ;
108111 setResult ( null ) ;
109112 setErrorMessage ( undefined ) ;
110113 try {
111- const request = clientDescription + getCollatedReferralOptions ( ) ;
114+ const request = await buildRequestWithResolvedZipCodes ( ) ;
115+ setRequestAfterZipResolution ( request ) ;
112116 const { resultId, resources, errorMessage } = await fetchResources (
113117 request ,
114118 userEmail ,
@@ -142,6 +146,7 @@ export default function Page() {
142146 setSelectedResources ( [ ] ) ;
143147 setActionPlan ( null ) ;
144148 setErrorMessage ( undefined ) ;
149+ setRequestAfterZipResolution ( "" ) ;
145150 }
146151
147152 function handleResourceSelection ( resource : Resource , checked : boolean ) {
@@ -187,9 +192,49 @@ export default function Page() {
187192 }
188193 }
189194
190- const getCollatedReferralOptions = ( ) : string => {
191- const resourceTypeFiltersPrefix =
192- "\nInclude resources that support the following categories: " ;
195+ const buildRequestWithResolvedZipCodes = async ( ) : Promise < string > => {
196+ // Helper function to replace zip codes with "city, state zip_code" format
197+ const replaceZipCodes = async ( text : string ) : Promise < string > => {
198+ if ( ! text ) return text ;
199+
200+ const zipCodeRegex = / \b \d { 5 } (?: - \d { 4 } ) ? \b / g;
201+ const matches = Array . from ( text . matchAll ( zipCodeRegex ) ) ;
202+
203+ if ( matches . length === 0 ) return text ;
204+
205+ // Collect unique zip codes to avoid duplicate API calls
206+ const uniqueZipCodes = Array . from ( new Set ( matches . map ( ( m ) => m [ 0 ] ) ) ) ;
207+
208+ // Fetch locations for all unique zip codes
209+ const zipToLocation = new Map < string , string > ( ) ;
210+ const locationPromises = uniqueZipCodes . map ( async ( zipCode ) => {
211+ // For zip+4 format, only use the 5-digit part for lookup
212+ const zipForLookup = zipCode . split ( "-" ) [ 0 ] ;
213+ const location = await fetchLocationFromZip ( zipForLookup ) ;
214+ zipToLocation . set ( zipCode , location ) ;
215+ } ) ;
216+ await Promise . all ( locationPromises ) ;
217+
218+ // Replace all zip codes with their city, state prepended
219+ let result = text ;
220+ for ( const [ zipCode , location ] of zipToLocation . entries ( ) ) {
221+ if ( location ) {
222+ // Use a global replace to handle all occurrences of this zip code
223+ const zipRegex = new RegExp (
224+ `\\b${ zipCode . replace ( / - / g, "\\-" ) } \\b` ,
225+ "g" ,
226+ ) ;
227+ result = result . replace ( zipRegex , `${ location } ${ zipCode } ` ) ;
228+ }
229+ }
230+
231+ return result ;
232+ } ;
233+
234+ // Process both locationText and clientDescription for zip codes
235+ const processedLocationText = await replaceZipCodes ( locationText ) ;
236+ const processedClientDescription = await replaceZipCodes ( clientDescription ) ;
237+
193238 const resourceTypeFilters = selectedCategories
194239 . map ( ( categoryId ) => {
195240 const category = resourceCategories . find ( ( c ) => c . id === categoryId ) ;
@@ -198,22 +243,21 @@ export default function Page() {
198243 . filter ( Boolean )
199244 . join ( ", " ) ;
200245
201- const providerTypeFiltersPrefix =
202- "\nInclude the following types of providers: " ;
203- const providerTypeFilters = selectedResourceTypes . join ( ", " ) ;
204-
205- const locationFilterPrefix =
206- "\nFocus on resources close to the following location: " ;
207-
208- return (
246+ const options =
209247 ( resourceTypeFilters . length > 0
210- ? resourceTypeFiltersPrefix + resourceTypeFilters
248+ ? "\nInclude resources that support the following categories: " +
249+ resourceTypeFilters
211250 : "" ) +
212- ( providerTypeFilters
213- ? providerTypeFiltersPrefix + providerTypeFilters
251+ ( selectedResourceTypes . length > 0
252+ ? "\nInclude the following types of providers: " +
253+ selectedResourceTypes . join ( ", " )
214254 : "" ) +
215- ( locationText . length > 0 ? locationFilterPrefix + locationText : "" )
216- ) ;
255+ ( processedLocationText . length > 0
256+ ? "\nFocus on resources close to the following location: " +
257+ processedLocationText
258+ : "" ) ;
259+
260+ return processedClientDescription + options ;
217261 } ;
218262
219263 // Show nothing while checking localStorage to prevent flash
@@ -331,7 +375,7 @@ export default function Page() {
331375 onToggleResourceType = { toggleResourceType }
332376 onLocationChange = { setLocationText }
333377 onClientDescriptionChange = { setClientDescription }
334- onFindResources = { ( ) => void handleClick ( ) }
378+ onFindResources = { ( ) => void findResources ( ) }
335379 />
336380 ) }
337381 </ TabsContent >
@@ -373,29 +417,36 @@ export default function Page() {
373417 { resultId && < EmailReferralsButton resultId = { resultId } /> }
374418 </ div >
375419 </ div >
376- < ClientDetailsPromptBubble
377- clientDescription = {
378- clientDescription + getCollatedReferralOptions ( )
379- }
380- />
381- < ResourcesList
382- resources = { retainedResources ?? [ ] }
383- errorMessage = { errorMessage }
384- handleRemoveResource = { handleRemoveResource }
385- />
386- { retainedResources && retainedResources . length > 0 && (
387- < ActionPlanSection
388- resources = { retainedResources }
389- selectedResources = { selectedResources }
390- actionPlan = { actionPlan }
391- isGeneratingActionPlan = { isGeneratingActionPlan }
392- onResourceSelection = { handleResourceSelection }
393- onSelectAllResources = { handleSelectAllResources }
394- onGenerateActionPlan = { ( ) => void generateActionPlan ( ) }
395- />
396- ) }
397420 </ div >
398421 ) }
422+
423+ { requestAfterZipResolution && (
424+ < ClientDetailsPromptBubble
425+ clientDescription = { requestAfterZipResolution }
426+ />
427+ ) }
428+
429+ { readyToPrint && (
430+ < ResourcesList
431+ resources = { retainedResources ?? [ ] }
432+ errorMessage = { errorMessage }
433+ handleRemoveResource = { handleRemoveResource }
434+ />
435+ ) }
436+
437+ { readyToPrint &&
438+ retainedResources &&
439+ retainedResources . length > 0 && (
440+ < ActionPlanSection
441+ resources = { retainedResources }
442+ selectedResources = { selectedResources }
443+ actionPlan = { actionPlan }
444+ isGeneratingActionPlan = { isGeneratingActionPlan }
445+ onResourceSelection = { handleResourceSelection }
446+ onSelectAllResources = { handleSelectAllResources }
447+ onGenerateActionPlan = { ( ) => void generateActionPlan ( ) }
448+ />
449+ ) }
399450 </ div >
400451 </ div >
401452 ) }
0 commit comments