Skip to content

Commit 94c4e17

Browse files
authored
Merge pull request #396 from PolicyEngine/PavelMakarchuk/issue395
Migrate RI CTC Calculator to App V2
2 parents 1aa0f0e + f94a868 commit 94c4e17

File tree

4 files changed

+154
-4
lines changed

4 files changed

+154
-4
lines changed

app/src/Router.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import PopulationsPage from './pages/Populations.page';
1414
import PrivacyPage from './pages/Privacy.page';
1515
import ReportOutputPage from './pages/ReportOutput.page';
1616
import ReportsPage from './pages/Reports.page';
17+
import RICTCCalculatorPage from './pages/RICTCCalculator.page';
1718
import SimulationsPage from './pages/Simulations.page';
1819
import SupportersPage from './pages/Supporters.page';
1920
import TeamPage from './pages/Team.page';
2021
import TermsPage from './pages/Terms.page';
2122
import { CountryGuard } from './routing/guards/CountryGuard';
2223
import { MetadataGuard } from './routing/guards/MetadataGuard';
2324
import { MetadataLazyLoader } from './routing/guards/MetadataLazyLoader';
25+
import { USOnlyGuard } from './routing/guards/USOnlyGuard';
2426
import { RedirectToCountry } from './routing/RedirectToCountry';
2527
import { RedirectToLegacy } from './routing/RedirectToLegacy';
2628

@@ -150,6 +152,21 @@ const router = createBrowserRouter(
150152
},
151153
],
152154
},
155+
// US-only routes
156+
{
157+
element: <USOnlyGuard />,
158+
children: [
159+
{
160+
element: <StaticLayout />,
161+
children: [
162+
{
163+
path: 'rhode-island-ctc-calculator',
164+
element: <RICTCCalculatorPage />,
165+
},
166+
],
167+
},
168+
],
169+
},
153170
// Legacy routes - redirect to legacy.policyengine.org
154171
{
155172
children: [
@@ -179,10 +196,6 @@ const router = createBrowserRouter(
179196
path: 'two-child-limit-comparison',
180197
element: <RedirectToLegacy />,
181198
},
182-
{
183-
path: 'rhode-island-ctc-calculator',
184-
element: <RedirectToLegacy />,
185-
},
186199
// Country-specific legacy routes (must come before :appName catch-all)
187200
{
188201
path: 'cec',
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { useState } from 'react';
2+
3+
interface IframeContentProps {
4+
url: string;
5+
title?: string;
6+
}
7+
8+
export default function IframeContent({ url, title = 'Embedded content' }: IframeContentProps) {
9+
const [isLoading, setIsLoading] = useState(true);
10+
const [hasError, setHasError] = useState(false);
11+
12+
return (
13+
<div style={{ position: 'relative', height: '100%' }}>
14+
{isLoading && (
15+
<div
16+
style={{
17+
position: 'absolute',
18+
top: 0,
19+
left: 0,
20+
right: 0,
21+
bottom: 0,
22+
display: 'flex',
23+
alignItems: 'center',
24+
justifyContent: 'center',
25+
backgroundColor: '#f9fafb',
26+
zIndex: 1,
27+
}}
28+
>
29+
<div style={{ textAlign: 'center', padding: '2rem' }}>
30+
<div
31+
style={{
32+
width: '48px',
33+
height: '48px',
34+
border: '4px solid #e5e7eb',
35+
borderTop: '4px solid #2c6496',
36+
borderRadius: '50%',
37+
margin: '0 auto 1rem',
38+
animation: 'spin 1s linear infinite',
39+
}}
40+
/>
41+
<p style={{ color: '#6b7280', fontSize: '14px' }}>Loading calculator...</p>
42+
</div>
43+
</div>
44+
)}
45+
{hasError && (
46+
<div
47+
style={{
48+
position: 'absolute',
49+
top: 0,
50+
left: 0,
51+
right: 0,
52+
bottom: 0,
53+
display: 'flex',
54+
alignItems: 'center',
55+
justifyContent: 'center',
56+
backgroundColor: '#f9fafb',
57+
zIndex: 1,
58+
}}
59+
>
60+
<div
61+
style={{
62+
textAlign: 'center',
63+
padding: '2rem',
64+
maxWidth: '500px',
65+
}}
66+
>
67+
<h2 style={{ color: '#1f2937', marginBottom: '1rem' }}>Unable to load calculator</h2>
68+
<p style={{ color: '#6b7280', marginBottom: '1rem' }}>
69+
The embedded calculator could not be loaded. You can try opening it directly:
70+
</p>
71+
<a
72+
href={url}
73+
target="_blank"
74+
rel="noopener noreferrer"
75+
style={{
76+
display: 'inline-block',
77+
padding: '0.5rem 1rem',
78+
backgroundColor: '#2c6496',
79+
color: 'white',
80+
textDecoration: 'none',
81+
borderRadius: '4px',
82+
}}
83+
>
84+
Open Calculator in New Tab
85+
</a>
86+
</div>
87+
</div>
88+
)}
89+
<iframe
90+
src={url}
91+
style={{
92+
width: '100%',
93+
height: 'calc(100vh - var(--header-height, 58px))',
94+
border: 'none',
95+
display: 'block',
96+
}}
97+
title={title}
98+
onLoad={() => setIsLoading(false)}
99+
onError={() => {
100+
setIsLoading(false);
101+
setHasError(true);
102+
}}
103+
/>
104+
<style>{`
105+
@keyframes spin {
106+
0% { transform: rotate(0deg); }
107+
100% { transform: rotate(360deg); }
108+
}
109+
`}</style>
110+
</div>
111+
);
112+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import IframeContent from '@/components/IframeContent';
2+
3+
export default function RICTCCalculatorPage() {
4+
return (
5+
<IframeContent
6+
url="https://ri-ctc-calculator-policy-engine.vercel.app/"
7+
title="Rhode Island Child Tax Credit Calculator"
8+
/>
9+
);
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Navigate, Outlet, useParams } from 'react-router-dom';
2+
3+
/**
4+
* Guard component that only allows access for US country routes.
5+
* Redirects to root for any other country.
6+
*/
7+
export function USOnlyGuard() {
8+
const { countryId } = useParams<{ countryId: string }>();
9+
10+
if (countryId !== 'us') {
11+
return <Navigate to="/" replace />;
12+
}
13+
14+
return <Outlet />;
15+
}

0 commit comments

Comments
 (0)