Skip to content

Commit

Permalink
Merge pull request #2010 from rachel-lawrie/lawrie_policy2code_9_5
Browse files Browse the repository at this point in the history
Add info button next to each benefit
  • Loading branch information
anth-volk authored Oct 9, 2024
2 parents 9a3df85 + f332d85 commit 0c5bcc3
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 186 deletions.
8 changes: 4 additions & 4 deletions src/layout/MarkdownFormatter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export function PlotlyChartCode({ data, backgroundColor }) {
);
}

export function MarkdownFormatter({ markdown, backgroundColor, dict }) {
export function MarkdownFormatter({ markdown, backgroundColor, dict, pSize }) {
const displayCategory = useDisplayCategory();
const mobile = displayCategory === "mobile";
const renderers = {
Expand Down Expand Up @@ -197,7 +197,7 @@ export function MarkdownFormatter({ markdown, backgroundColor, dict }) {
<p
style={{
fontFamily: "Roboto Serif",
fontSize: mobile ? 16 : 18,
fontSize: pSize ? pSize : mobile ? 16 : 18,
backgroundColor: backgroundColor,
}}
>
Expand Down Expand Up @@ -243,7 +243,7 @@ export function MarkdownFormatter({ markdown, backgroundColor, dict }) {
paddingLeft: 20,
marginBottom: 20,
fontFamily: "Roboto Serif",
fontSize: mobile ? 16 : 18,
fontSize: pSize ? pSize : mobile ? 16 : 18,
}}
>
{children}
Expand All @@ -255,7 +255,7 @@ export function MarkdownFormatter({ markdown, backgroundColor, dict }) {
paddingLeft: 20,
marginBottom: 20,
fontFamily: "Roboto Serif",
fontSize: mobile ? 16 : 18,
fontSize: pSize ? pSize : mobile ? 16 : 18,
}}
>
{children}
Expand Down
112 changes: 112 additions & 0 deletions src/modals/HouseholdAIModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import { Button, Modal } from "antd";
import { countryApiCall } from "../api/call";
import useCountryId from "../hooks/useCountryId";
import { MarkdownFormatter } from "../layout/MarkdownFormatter";
import { useSearchParams } from "react-router-dom";
import { COUNTRY_BASELINE_POLICIES } from "../data/countries";

/**
* Modal for displaying AI output
* @param {Object} props
* @param {Object} props.variableName The name of the variable
* @param {String} props.value The value of the variable
* @param {Boolean} props.isModalVisible Whether the modal is visible
* @param {Function} props.setIsModalVisible Function to set the visibility of the modal
* @returns {React.Component} The modal component
*/
export default function HouseholdAIModal(props) {
const { isModalVisible, setIsModalVisible, variableName } = props;

const [analysis, setAnalysis] = useState("");
const countryId = useCountryId();

// Check if variable has changed by its name, not the
// object itself; React will treat two objects with same keys
// and values as different if rendered separately
const prevVariableName = useRef(null);

const [searchParams] = useSearchParams();
const householdId = searchParams.get("household");
// Currently not implemented for baseline/reform comparison pairs
const policyId = COUNTRY_BASELINE_POLICIES[countryId];

// Function to hide modal
const handleCancel = () => {
setIsModalVisible(false);
};

// Convert this and fetchTracer to async/await
const fetchAnalysis = useCallback(async () => {
const jsonObject = {
household_id: householdId,
policy_id: policyId,
variable: variableName,
};

const res = await countryApiCall(
countryId,
`/tracer-analysis`,
jsonObject,
"POST",
);

const reader = res.body.getReader();
const decoder = new TextDecoder();

let isComplete = false;
while (!isComplete) {
const { done, value } = await reader.read().catch((error) => {
console.error("Error reading response stream:", error);
});
if (done) {
isComplete = true;
}
const chunks = decoder.decode(value, { stream: true }).split("\n");
for (const chunk of chunks) {
if (chunk) {
const data = JSON.parse(chunk);
if (data.stream) {
setAnalysis((prevAnalysis) => prevAnalysis + data.stream);
}
}
}
}
}, [countryId, householdId, policyId, variableName]);

useEffect(() => {
function resetModalData() {
prevVariableName.current = variableName;
}

// If modal isn't shown, don't do anything
if (!isModalVisible) {
return;
}

// If variable hasn't changed and we generated analysis,
// don't do anything (e.g., user clicked on same variable)
if (variableName === prevVariableName.current) {
return;
}

fetchAnalysis();
resetModalData();
}, [isModalVisible, variableName, fetchAnalysis]);

return (
<Modal
title="Explanation"
open={isModalVisible}
onCancel={handleCancel} // Hide the modal on cancel/close
footer={[
<Button key="back" onClick={handleCancel}>
Close
</Button>,
]}
width="50%"
>
<MarkdownFormatter markdown={analysis} pSize={14} />
</Modal>
);
}
59 changes: 58 additions & 1 deletion src/pages/household/output/NetIncomeBreakdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import {
CaretDownFilled,
CaretUpFilled,
PlusCircleOutlined,
InfoCircleOutlined,
} from "@ant-design/icons";
import { Tooltip } from "antd";
import { Tooltip, Button } from "antd";
import { useState } from "react";
import { getParameterAtInstant } from "../../../api/parameters";
import {
Expand All @@ -14,6 +15,8 @@ import ResultsPanel from "../../../layout/ResultsPanel";
import style from "../../../style";
import useDisplayCategory from "../../../hooks/useDisplayCategory";
import { Helmet } from "react-helmet";
import React from "react";
import HouseholdAIModal from "../../../modals/HouseholdAIModal";

const UpArrow = () => (
<CaretUpFilled
Expand Down Expand Up @@ -57,6 +60,11 @@ function VariableArithmetic(props) {
forceShowChildValuesIfZero,
year,
} = props;

// When providing AI analysis, we don't want to analyze input vars (e.g., employment income)
const isInput =
metadata.variables[variableName].moduleName?.split(".")[0] === "input";

let nodeSign = isAdd;
const value = getValueFromHousehold(
variableName,
Expand Down Expand Up @@ -225,6 +233,14 @@ function VariableArithmetic(props) {
adds.length + subtracts.length > 0 &&
childNodes.length > 0;

// State for modal visibility
const [isModalVisible, setIsModalVisible] = useState(false);

// Function to show modal
const showModal = () => {
setIsModalVisible(true);
};

if (childrenOnly) {
return (
<div
Expand Down Expand Up @@ -282,6 +298,7 @@ function VariableArithmetic(props) {
{valueStr}
</h2>
</Tooltip>

{expandable && (
<PlusCircleOutlined
style={{
Expand All @@ -293,6 +310,41 @@ function VariableArithmetic(props) {
}}
/>
)}

{!expandable && !isInput && !householdReform && (
<div
style={{
position: "relative",
marginLeft: "8px",
}}
className="info-icon-wrapper"
>
<InfoCircleOutlined
style={{
fontSize: 14,
marginLeft: 10,
color: style.colors.DARK_GRAY,
cursor: "pointer", // Pointer cursor to indicate it's interactable
}}
/>
<Button
style={{
position: "absolute",
top: "100%", // Position below the icon
left: 0,
padding: "5px 10px",
borderWidth: 1,
borderColor: nodeColor(nodeSign),
cursor: "pointer",
zIndex: 1, // Ensure the button appears above other content
}}
className="explain-ai-button"
onClick={showModal} // Show modal when button is clicked
>
Explain with AI ✨
</Button>
</div>
)}
</div>
</div>
{expanded && (
Expand All @@ -310,6 +362,11 @@ function VariableArithmetic(props) {
{childNodes}
</div>
)}
<HouseholdAIModal
isModalVisible={isModalVisible}
setIsModalVisible={setIsModalVisible}
variableName={variableName}
/>
</div>
);
}
Expand Down
Loading

0 comments on commit 0c5bcc3

Please sign in to comment.