diff --git a/src/images/headshots/ben-ogorek.jpeg b/src/images/headshots/ben-ogorek.jpeg
new file mode 100644
index 000000000..828c31c47
Binary files /dev/null and b/src/images/headshots/ben-ogorek.jpeg differ
diff --git a/src/images/headshots/maria-juaristi.jpeg b/src/images/headshots/maria-juaristi.jpeg
new file mode 100644
index 000000000..4131c9e93
Binary files /dev/null and b/src/images/headshots/maria-juaristi.jpeg differ
diff --git a/src/images/posts/obbba-household-by-household.png b/src/images/posts/obbba-household-by-household.png
new file mode 100644
index 000000000..59f832da8
Binary files /dev/null and b/src/images/posts/obbba-household-by-household.png differ
diff --git a/src/pages/BlogPage.jsx b/src/pages/BlogPage.jsx
index b2c067c40..f8da37a7d 100644
--- a/src/pages/BlogPage.jsx
+++ b/src/pages/BlogPage.jsx
@@ -49,25 +49,29 @@ export default function BlogPage() {
// /uk/research/blog-slug-here
const { postName } = useParams();
const countryId = useCountryId();
+ const [content, setContent] = useState("");
const post = posts.find((post) => post.slug === postName);
- const postDate = formatFullDate(moment(post.date), countryId);
- const imageUrl = post.image ? handleImageLoad(post.image) : "";
- const file = require(`../posts/articles/${post.filename}`);
+ const isNotebook =
+ post && post.filename ? post.filename.endsWith(".ipynb") : false;
+ const file =
+ post && post.filename
+ ? require(`../posts/articles/${post.filename}`)
+ : null;
- const [content, setContent] = useState("");
- const isNotebook = post.filename.endsWith(".ipynb");
useEffect(() => {
- fetch(file)
- .then((response) => response.text())
- .then((text) => {
- if (isNotebook) {
- setContent(JSON.parse(text));
- } else {
- setContent(text);
- }
- });
+ if (file) {
+ fetch(file)
+ .then((response) => response.text())
+ .then((text) => {
+ if (isNotebook) {
+ setContent(JSON.parse(text));
+ } else {
+ setContent(text);
+ }
+ });
+ }
}, [file, isNotebook]);
// Some old links might point to a dated URL format
@@ -76,6 +80,19 @@ export default function BlogPage() {
return ;
}
+ // Handle external URL redirects
+ if (post && post.external_url) {
+ return ;
+ }
+
+ // Handle missing post or filename
+ if (!post || !post.filename) {
+ return ;
+ }
+
+ const postDate = formatFullDate(moment(post.date), countryId);
+ const imageUrl = post.image ? handleImageLoad(post.image) : "";
+
let markdown;
if (isNotebook && content) {
diff --git a/src/pages/home/HomeBlogPreview.jsx b/src/pages/home/HomeBlogPreview.jsx
index 7d1bb2702..7e7e6eef8 100644
--- a/src/pages/home/HomeBlogPreview.jsx
+++ b/src/pages/home/HomeBlogPreview.jsx
@@ -403,8 +403,8 @@ export function FeaturedBlogPreview({ blogs, width, imageHeight }) {
export function MediumBlogPreview({ blog, minHeight }) {
const displayCategory = useDisplayCategory();
const countryId = useCountryId();
- const slug = blog.filename.split(".")[0];
- const link = `/${countryId}/research/${slug}`;
+ const slug = blog.filename ? blog.filename.split(".")[0] : blog.slug;
+ const link = blog.external_url || `/${countryId}/research/${slug}`;
const imageUrl = blog.image ? handleImageLoad(blog.image) : "";
diff --git a/src/posts/authors.json b/src/posts/authors.json
index dcc5c3bd0..ce4b118df 100644
--- a/src/posts/authors.json
+++ b/src/posts/authors.json
@@ -150,5 +150,23 @@
"github": "https://github.com/eccuraa",
"headshot": "elena-cura.png",
"title": "Data Science Intern at PolicyEngine"
+ },
+ "ben-ogorek": {
+ "name": "Ben Ogorek",
+ "email": "ben.ogorek@policyengine.org",
+ "bio": "Ben is a data scientist at PolicyEngine.",
+ "linkedin": "https://www.linkedin.com/in/benogorek/",
+ "github": "https://github.com/baogorek",
+ "headshot": "ben-ogorek.jpeg",
+ "title": "Data Scientist at PolicyEngine"
+ },
+ "maria-juaristi": {
+ "name": "Maria Juaristi",
+ "email": "maria@policyengine.org",
+ "bio": "Maria is a data scientist at PolicyEngine.",
+ "linkedin": "https://www.linkedin.com/in/mariajuaristi/",
+ "github": "https://github.com/juaristi22",
+ "headshot": "maria-juaristi.jpeg",
+ "title": "Data Scientist at PolicyEngine"
}
}
diff --git a/src/posts/postTransformers.js b/src/posts/postTransformers.js
index 5b48e3338..052d48b54 100644
--- a/src/posts/postTransformers.js
+++ b/src/posts/postTransformers.js
@@ -3,7 +3,15 @@ import posts from "./posts.json";
const postsSorted = posts.sort((a, b) => (a.date < b.date ? 1 : -1));
for (let post of postsSorted) {
- post.slug = post.filename.substring(0, post.filename.indexOf("."));
+ if (post.filename) {
+ post.slug = post.filename.substring(0, post.filename.indexOf("."));
+ } else if (post.external_url) {
+ // For external URLs, generate a slug from the title
+ post.slug = post.title
+ .toLowerCase()
+ .replace(/\s+/g, "-")
+ .replace(/[^a-z0-9-]/g, "");
+ }
}
const tags = postsSorted.map((post) => post.tags);
diff --git a/src/posts/posts.json b/src/posts/posts.json
index 021149df8..0a32b186c 100644
--- a/src/posts/posts.json
+++ b/src/posts/posts.json
@@ -2,11 +2,25 @@
{
"title": "Enhanced CPS full launch: Comprehensive microdata for policy analysis",
"description": "The Enhanced Current Population Survey now includes tip, overtime, and auto loan interest imputations, plus upcoming state and congressional district calibration.",
- "date": "2025-08-08",
+ "date": "2025-08-11",
"tags": ["us", "data", "featured"],
"filename": "enhanced-cps-launch.md",
"image": "enhanced-cps-launch.png",
- "authors": ["max-ghenis", "nikhil-woodruff"]
+ "authors": ["max-ghenis", "nikhil-woodruff", "ben-ogorek", "maria-juaristi"]
+ },
+ {
+ "title": "The One Big Beautiful Bill Act, household by household",
+ "description": "Our latest interactive shows how the reconciliation bill affects each of 20,000 representative households across income levels, states, and provisions.",
+ "date": "2025-08-11",
+ "tags": ["us", "policy", "featured", "reconciliation", "interactives"],
+ "external_url": "/us/obbba-household-by-household",
+ "image": "obbba-household-by-household.png",
+ "authors": [
+ "max-ghenis",
+ "pavel-makarchuk",
+ "ben-ogorek",
+ "nikhil-woodruff"
+ ]
},
{
"title": "Analysis of individual income tax provisions in the final reconciliation bill",