-
{formatIncentiveType(incentive, msg)}
-
- {formatTitle(incentive, msg)}
-
-
- {incentive.program}
-
-
-
- {incentive.short_description} {locationEligibilityText}
-
- {futureStartYear && (
-
- {msg(str`Expected in ${futureStartYear}`)}
-
+const IncentiveCard: FC<{
+ cardStyle: CardStyle;
+ typeChip: string;
+ headline: string;
+ subHeadline: string;
+ body: string;
+ warningChip: string | null;
+ buttonUrl: string;
+ buttonContent: string | React.ReactElement;
+}> = ({
+ cardStyle,
+ typeChip,
+ headline,
+ subHeadline,
+ body,
+ warningChip,
+ buttonUrl,
+ buttonContent,
+}) => (
+
+
+
{typeChip}
+
{headline}
+
+ {subHeadline}
+
+
+
{buttonContent}
+ >
+ {body}
-
- );
-};
+ {warningChip &&
{warningChip}}
+
{buttonContent}
+
+
+);
function scrollToForm(event: React.MouseEvent) {
const calculator = (
@@ -329,7 +326,7 @@ const renderNoResults = (emailSubmitter: ((email: string) => void) | null) => {
);
return (
-
+
{msg('No incentives available for this project')}
@@ -346,23 +343,86 @@ const renderNoResults = (emailSubmitter: ((email: string) => void) | null) => {
);
};
-const renderCardCollection = (incentives: Incentive[]) => (
-
- {incentives
- // Sort incentives that haven't started yet at the end
- .sort(
- (a, b) =>
- (getStartYearIfInFuture(a) ?? 0) - (getStartYearIfInFuture(b) ?? 0),
- )
- .map((incentive, index) => (
-
- ))}
-
-);
+const renderCardCollection = (
+ incentives: Incentive[],
+ iraRebates: IRARebate[],
+) => {
+ const { msg } = useTranslated();
+ return (
+
+ {incentives
+ // Sort incentives that haven't started yet at the end
+ .sort(
+ (a, b) =>
+ (getStartYearIfInFuture(a) ?? 0) - (getStartYearIfInFuture(b) ?? 0),
+ )
+ .map((incentive, index) => {
+ const futureStartYear = getStartYearIfInFuture(incentive);
+
+ // The API cannot precisely tell, from zip code alone, whether the
+ // user is in a specific city or county; it takes a permissive
+ // approach and returns incentives for localities the user *might* be
+ // in. So this indicates that the user should check for themselves.
+ //
+ // This is a blunt-instrument approach; in many cases there's actually
+ // no ambiguity as to which city or county a zip code is in, but the
+ // API currently doesn't take that into account.
+ const locationEligibilityText = ['city', 'county', 'other'].includes(
+ incentive.authority_type,
+ )
+ ? msg('Eligibility depends on residence location.')
+ : '';
+
+ const [buttonUrl, buttonContent] = incentive.more_info_url
+ ? [incentive.more_info_url, msg('Learn more')]
+ : [
+ incentive.program_url,
+ <>
+ {msg('Visit site')}
+
+ >,
+ ];
+ return (
+
+ );
+ })
+ .concat(
+ iraRebates.map((rebate, index) => (
+
+ )),
+ )}
+
+ );
+};
type IncentiveGridProps = {
heading: string;
incentives: Incentive[];
+ iraRebates: IRARebate[];
tabs: Project[];
selectedTab: Project;
onTabSelected: (newSelection: Project) => void;
@@ -371,7 +431,15 @@ type IncentiveGridProps = {
const IncentiveGrid = forwardRef(
(
- { heading, incentives, tabs, selectedTab, onTabSelected, emailSubmitter },
+ {
+ heading,
+ incentives,
+ iraRebates,
+ tabs,
+ selectedTab,
+ onTabSelected,
+ emailSubmitter,
+ },
ref,
) => {
return tabs.length > 0 ? (
@@ -384,8 +452,8 @@ const IncentiveGrid = forwardRef(
selectedTab={selectedTab}
onTabSelected={onTabSelected}
/>
- {incentives.length > 0
- ? renderCardCollection(incentives)
+ {incentives.length > 0 || iraRebates.length > 0
+ ? renderCardCollection(incentives, iraRebates)
: renderNoResults(emailSubmitter)}
) : null;
@@ -418,7 +486,11 @@ export const StateIncentives: FC