Skip to content

Commit 9753663

Browse files
committed
feat: add gen ai panel
1 parent c0af10b commit 9753663

8 files changed

Lines changed: 588 additions & 262 deletions

File tree

Lines changed: 79 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,90 @@
1-
import ChatInterface from "./ChatInterface"
2-
import LightBulbIcon from "@heroicons/react/24/solid/LightBulbIcon"
3-
import ChatBubbleOvalLeftEllipsisIcon from "@heroicons/react/24/solid/ChatBubbleOvalLeftEllipsisIcon"
1+
import Markdown from "markdown-to-jsx"
42

5-
const InsightBox = ({ title, description }: { title: string; description: string }) => {
6-
return (
7-
<div className="w-full flex flex-row rounded-lg border border-blue-200">
8-
<div className="h-full w-12 flex flex-col justify-start items-center py-4 px-2 ">
9-
<div className="flex items-center justify-center h-8 w-8 bg-blue-100 rounded-lg">
10-
<LightBulbIcon className="h-4" />
11-
</div>
12-
</div>
13-
<div className="p-4">
14-
<h4 className="font-medium text-blue-900 mb-2">{title}</h4>
15-
<p className="text-sm text-blue-800">{description}</p>
16-
</div>
17-
</div>
18-
)
19-
}
3+
const markdownContent = `
4+
# Executive summary
5+
6+
Significant housing-jobs mismatches exist among East Bay affordable housing applicants, with 52.7% commuting between different cities for work.
7+
8+
## Key numbers:
9+
10+
- **Cross-city commuters:** 7,005 applicants (52.7%)
11+
- **Oakland residential dominance:** 5,129 applicants (38.6%)
12+
- **Central Corridor employment concentration:** 3,663 workers (52.2%)
13+
14+
# Data summary
15+
16+
## Highest Demand:
17+
18+
Central Corridor (Oakland-Berkeley-Alameda) with 5,997 applicants (45.4%)
19+
20+
## Distribution patterns:
21+
22+
- **Residential:** Central Corridor (45.1%), Peninsula Transition (10.0%), Southern Cluster (11.4%)
23+
- **Employment:** Central Corridor (52.2%), Southern Cluster (22.0%), Peninsula Transition (10.0%)
24+
- **Commute:** 52.7% work outside their residential city, 79.5% live in BART-accessible areas
25+
26+
# Cross-analysis:
27+
28+
- Same-city living and working: 6,288 applicants (47.3%)
29+
- Cross-cluster commuting: 4,278 applicants (32.2%)
30+
- Largest commute flow: 1,278 from Central Corridor to Southern Cluster
31+
- BART accessibility: 10,574 applicants (79.5%)
32+
`
2033

2134
export const AiInsightsPanel = () => {
2235
return (
23-
<div className="flex flex-col h-full">
24-
<h3 className="text-lg font-semibold text-gray-900 mb-4">AI Insights</h3>
25-
26-
{/* Static Insights */}
27-
<div className="space-y-4 mb-6">
28-
<InsightBox
29-
title="Application Trends"
30-
description="Your community has seen a steady increase in applications over the past year, with a peak in Q2."
31-
/>
32-
<InsightBox
33-
title="Household Composition"
34-
description="Family sizes are well-distributed across unit types, indicating diverse housing needs in
35-
your community."
36-
/>
36+
<div className="flex flex-col h-full w-full rounded-lg p-6">
37+
{/* Markdown Content */}
38+
<div className="markdown-content mb-6">
39+
<Markdown
40+
options={{
41+
overrides: {
42+
h1: {
43+
component: "h3",
44+
props: {
45+
className: "text-xl font-semibold text-gray-900 mb-4 mt-6 first:mt-0",
46+
},
47+
},
48+
h2: {
49+
component: "h4",
50+
props: {
51+
className: "text-base font-semibold text-gray-900 mb-3 mt-4",
52+
},
53+
},
54+
p: {
55+
props: {
56+
className: "text-sm text-gray-700 leading-relaxed mb-4",
57+
},
58+
},
59+
ul: {
60+
props: {
61+
className: "text-sm text-gray-700 space-y-2 mb-4",
62+
},
63+
},
64+
li: {
65+
props: {
66+
className: "ml-4",
67+
},
68+
},
69+
},
70+
}}
71+
>
72+
{markdownContent}
73+
</Markdown>
3774
</div>
3875

3976
{/* Chat Interface */}
40-
<div className="flex-1 flex flex-col border border-gray-200 rounded-lg bg-white">
41-
<div className="bg-gray-50 px-4 py-3 border-b border-gray-200 rounded-t-lg">
42-
<h4 className="font-medium text-gray-900 flex items-center">
43-
<ChatBubbleOvalLeftEllipsisIcon className="h-6 pr-2" />{" "}
44-
<p className="text-base">Ask our assistant for help exploring your data</p>
45-
</h4>
77+
{/* <div className="mt-auto pt-4 border-t-2 border-dashed border-blue-300">
78+
<div className="bg-white border border-gray-200 rounded-lg">
79+
<div className="bg-gray-50 px-4 py-3 border-b border-gray-200 rounded-t-lg">
80+
<h4 className="font-medium text-gray-900 flex items-center">
81+
<ChatBubbleOvalLeftEllipsisIcon className="h-5 w-5 mr-2" />
82+
<span className="text-sm">Ask for more insights</span>
83+
</h4>
84+
</div>
85+
<ChatInterface />
4686
</div>
47-
48-
<ChatInterface />
49-
</div>
87+
</div> */}
5088
</div>
5189
)
5290
}

sites/partners/src/components/explore/ChatInterface.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useState, useRef, useEffect } from "react"
22
import { Button } from "@bloom-housing/ui-seeds"
33
import { chatWithAI } from "../../lib/ai/conversational-ai"
4-
import { reportDataOption1 as reportData } from "../../lib/explore/data-explorer"
4+
import { defaultReport as reportData } from "../../lib/explore/data-explorer"
55
// import Markdown from "react-markdown"
66

77
export interface Message {

sites/partners/src/components/explore/applicantAndHouseholdData.tsx

Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -70,55 +70,79 @@ export default function PrimaryApplicantSection({ chartData }: PrimaryApplicantS
7070
{charts.map(({ title, data, key, tableLabel }) => (
7171
<div key={key}>
7272
<h3 className="text-lg font-semibold mb-2">{title}</h3>
73-
<div className="w-full h-80">
74-
<ResponsiveContainer width="100%" height="100%">
75-
<BarChart data={data} margin={{ top: 30, right: 0, left: 0, bottom: 5 }}>
76-
<CartesianGrid stroke="#EFEFEF" horizontal={true} vertical={false} />
77-
<XAxis
78-
dataKey={key}
79-
tick={{ fontSize: 12, fill: "#4B5563" }}
80-
tickLine={false}
81-
axisLine={false}
82-
textAnchor="middle"
83-
/>
84-
<YAxis
85-
type="number"
86-
tick={{ fontSize: 12, fill: "#4B5563" }}
87-
tickLine={false}
88-
axisLine={false}
89-
/>
90-
<ReTooltip
91-
formatter={(val: number) => [`${val}`, "Count"]}
92-
cursor={{ fill: "#CBD5E1", opacity: 0.3 }}
93-
/>
94-
<Bar dataKey="count" fill={BLUE_500} barSize={24} radius={[6, 6, 0, 0]}>
95-
<LabelList
96-
dataKey="count"
97-
position="top"
98-
style={{ fontSize: "12px", fill: "#4B5563" }}
99-
/>
100-
</Bar>
101-
</BarChart>
102-
</ResponsiveContainer>
103-
</div>
73+
{data.length === 0 ? (
74+
<div className="w-full flex justify-center items-center h-80">
75+
<p className="text-gray-600">No data available for the selected filters.</p>
76+
</div>
77+
) : (
78+
<>
79+
<div className="w-full h-80">
80+
<ResponsiveContainer width="100%" height="100%">
81+
<BarChart data={data} margin={{ top: 30, right: 0, left: 0, bottom: 5 }}>
82+
<CartesianGrid stroke="#EFEFEF" horizontal={true} vertical={false} />
83+
<XAxis
84+
dataKey={key}
85+
tick={{ fontSize: 12, fill: "#4B5563" }}
86+
tickLine={false}
87+
axisLine={false}
88+
textAnchor="middle"
89+
/>
90+
<YAxis
91+
type="number"
92+
tick={{ fontSize: 12, fill: "#4B5563" }}
93+
tickLine={false}
94+
axisLine={false}
95+
/>
96+
<ReTooltip
97+
formatter={(val: number) => [`${val}`, "Count"]}
98+
cursor={{ fill: "#CBD5E1", opacity: 0.3 }}
99+
/>
100+
<Bar dataKey="count" fill={BLUE_500} barSize={24} radius={[6, 6, 0, 0]}>
101+
<LabelList
102+
dataKey="count"
103+
position="top"
104+
style={{ fontSize: "12px", fill: "#4B5563" }}
105+
/>
106+
</Bar>
107+
</BarChart>
108+
</ResponsiveContainer>
109+
</div>
104110

105-
<DataTable title={tableLabel} data={data} dataKey={key} />
111+
<DataTable title={tableLabel} data={data} dataKey={key} />
112+
</>
113+
)}
106114
</div>
107115
))}
108116
</div>
109117

110118
<div className="mt-10 grid grid-cols-1 md:grid-cols-2 gap-8">
111-
<DataTable
112-
title="Accessibility"
113-
data={accessibilityTypeFrequencies}
114-
dataKey="accessibilityType"
115-
/>
119+
{accessibilityTypeFrequencies.length === 0 ? (
120+
<div className="flex justify-center items-center h-32">
121+
<p className="text-gray-600">
122+
No accessibility data available for the selected filters.
123+
</p>
124+
</div>
125+
) : (
126+
<DataTable
127+
title="Accessibility"
128+
data={accessibilityTypeFrequencies}
129+
dataKey="accessibilityType"
130+
/>
131+
)}
116132

117-
<DataTable
118-
title="Subsidy or voucher"
119-
data={subsidyOrVoucherTypeFrequencies}
120-
dataKey="subsidyType"
121-
/>
133+
{subsidyOrVoucherTypeFrequencies.length === 0 ? (
134+
<div className="flex justify-center items-center h-32">
135+
<p className="text-gray-600">
136+
No subsidy/voucher data available for the selected filters.
137+
</p>
138+
</div>
139+
) : (
140+
<DataTable
141+
title="Subsidy or voucher"
142+
data={subsidyOrVoucherTypeFrequencies}
143+
dataKey="subsidyType"
144+
/>
145+
)}
122146
</div>
123147
</div>
124148
</div>

0 commit comments

Comments
 (0)