Skip to content
This repository was archived by the owner on Jan 12, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f5ff5cc
Page structure & map logic implemented
Yurika-Kan Jun 16, 2025
3d6b870
FIxed Sidebar Tab highlight Blue
Yurika-Kan Jun 17, 2025
c108c8b
Made Thumbnail Image Optional in Template
Yurika-Kan Jun 17, 2025
2167ceb
Edited Education Templates
Yurika-Kan Jun 20, 2025
215b1fb
Implemented Education + FOI Index Pages
Yurika-Kan Jun 20, 2025
e2cbb41
Implemented FOI Guide Components
Yurika-Kan Jun 20, 2025
8bfd16d
Fixes
Yurika-Kan Jun 20, 2025
3bae765
Delete foi-map.svg
Yurika-Kan Jun 20, 2025
cea9595
Quick Style Fix
Yurika-Kan Jun 20, 2025
d6ec285
Link + Style Fixes
Yurika-Kan Jun 20, 2025
9bb49b9
Review Fixes
Yurika-Kan Jun 22, 2025
44adc88
Fix rendering inconsistencies
Yurika-Kan Jun 23, 2025
6f22043
Improved Button Rendering
Yurika-Kan Jul 2, 2025
a84ad6c
Responsive-ed Buttons Panel
Yurika-Kan Jul 2, 2025
3a39300
Added comment about SVG Paths
Yurika-Kan Jul 2, 2025
3838878
Updated Map to be responsive
Yurika-Kan Jul 2, 2025
fa9f9b5
Updated Template for Text Responsiveness
Yurika-Kan Jul 2, 2025
2196446
Fixed HoverButton
Yurika-Kan Jul 2, 2025
65c9f72
Restructured & improved for responsiveness
Yurika-Kan Jul 2, 2025
2d0a602
Improve home page to make it responsive
Yurika-Kan Jul 2, 2025
31679f1
Fixed Rendering Bug & Color Discrepancies
Yurika-Kan Jul 6, 2025
f3bcac2
Update SVG map attribution info
builderpepc Jul 7, 2025
ed84388
Merge branch 'main' into 70-foi-guide-page
builderpepc Jul 7, 2025
2393771
update education initiative sidebar links
builderpepc Jul 7, 2025
0a0e02e
update state/federal FOI guide links
builderpepc Jul 7, 2025
a9fe0b3
resolve merge conflicts
builderpepc Jul 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function EducationInitiativeWrapper({ children }: EducationInitia
items={[
{ title: "30 Minute Skills", link: "/education-initiative" },
{ title: "First Amendment and the Free Press", link: "/education-initiative-abc" },
{ title: "FOI Guide", link: "/education-initiative-def" },
{ title: "FOI Guide", link: "/education/foi-guide" },
{ title: "NEFAC Mentors", link: "/education/nefac-mentors" },
{ title: "Negri Institute", link: "/education-initiative-jkl" },
]}
Expand Down
69 changes: 69 additions & 0 deletions nefac-website/src/components/education/foi-guide/HoverButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from "react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronRight } from "@fortawesome/free-solid-svg-icons";

type Props = {
state: { id?: string; name: string; url: string };
isHovered: boolean;
setHoveredId: (id: string | null) => void;
};

export default function HoverButton({ state, isHovered, setHoveredId }: Props) {
// button hover states
const isWide = state.id === "FOIA";
const scale = isHovered
? isWide
? "w-[93.5%]"
: "w-[86%]"
: isWide
? "w-[98.4%]"
: "w-[97%]";
const bg = isHovered ? "bg-nefacblue" : "bg-white";
const arrowBg = isHovered ? "bg-black" : "bg-nefacblue";
const arrowHover = isHovered ? "text-white right-[0.375rem]" : "text-black";
const textColor = isHovered ? "text-white" : "text-black";

return (
<Button
asChild
variant="outline"
onMouseEnter={() => setHoveredId(state.id ?? null)}
onMouseLeave={() => setHoveredId(null)}
className={`w-full my-4 relative overflow-hidden rounded-[10px]`}
>
<Link
href={state.url}
className="relative w-full h-full flex justify-between overflow-hidden px-4 items-start min-w-0"
>
{/* Text */}
<span
className={`z-30 font-semibold font-poppins pr-10 leading-tight text-base sm:text-lg
${textColor}`}
>
{state.name}
</span>
{/* Hover Fill Background */}
<div
className={`absolute inset-0 h-full z-10 rounded-[10px] transition-all duration-300
${bg} ${scale}
`}
/>
{/* Arrow */}
Comment thread
Yurika-Kan marked this conversation as resolved.
<div
className={`absolute right-0 h-full w-12
transition-all duration-300
flex items-center
${arrowBg}`}
>
<FontAwesomeIcon
icon={faChevronRight}
className={`z-20 transition-all duration-300 absolute w-4 h-4 right-4
${arrowHover}`}
/>
</div>
</Link>
</Button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import HoverButton from "./HoverButton";

// stub urls
const states = [
{ id: "ME", name: "Maine", url: "/education/foi-guide/maine" },
{ id: "MA", name: "Massachusetts", url: "/education/foi-guide/massachusetts" },
{ id: "VT", name: "Vermont", url: "/education/foi-guide/vermont" },
{ id: "RI", name: "Rhode Island", url: "/education/foi-guide/rhode-island" },
{ id: "NH", name: "New Hampshire", url: "/education/foi-guide/new-hampshire" },
{ id: "CN", name: "Connecticut", url: "/education/foi-guide/connecticut" },
{ id: "FOIA", name: "Federal FOIA", url: "/education/foi-guide/federal-foia" },
];

type Props = {
hoveredId: string | null;
setHoveredId: (id: string | null) => void;
};

// hover logic
export default function MapButtonsPanel({ hoveredId, setHoveredId }: Props) {
return (
<div className="flex flex-col w-full ">
{/* Left Column: Title and buttons */}
<div className="flex flex-col">
<h1 className="flex text-2xl font-bold text-nefacblue font-poppins pt-5">
Guide Information
</h1>
<div className="grid grid-cols-2 gap-y-2.5 gap-x-4 h-full">
{states.map((state) => (
<div
key={state.id}
className={`w-full ${state.id === "FOIA" ? "col-span-2" : ""}`}
>
<HoverButton
state={state}
isHovered={hoveredId === state.id}
setHoveredId={setHoveredId}
/>
</div>
))}
</div>
</div>
</div>
);
}
53 changes: 53 additions & 0 deletions nefac-website/src/components/education/foi-guide/NEStates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// New England Map data
type StatePath = {
id: string;
shapePath: string;
labelPath: string;
};

// New England map vector graphics authored by Rohan Biju / Code4Community for use by the New England First Amendment Coalition.
// Exported from Figma project.
export const NEStates: StatePath[] = [
Comment thread
Yurika-Kan marked this conversation as resolved.
{
id: "ME",
shapePath:
"M194.734 395.246L202.311 400.734L207.784 402L209.468 398.201L212.835 391.869V384.272L225.465 372.453V358.101L236.41 357.257V353.458L233.884 345.438L239.777 337.418L244.408 334.463L253.248 331.086H257.879L259.563 343.327L262.089 344.172L263.773 332.353L267.982 326.021L272.192 328.132L275.56 332.353L276.402 324.755L281.032 326.021L283.558 329.398L288.61 319.267V315.046L301.239 323.488L307.132 315.046L310.079 301.117L315.973 290.142V277.901L327.339 267.348L329.865 284.233L341.231 288.876L345.019 288.032L342.073 279.589L349.65 272.414L353.86 277.057L355.123 285.499L358.911 288.876L364.805 281.7L357.648 271.569L361.016 267.348L369.435 269.459L372.803 274.946L373.645 281.7L379.118 284.233V270.725L384.169 272.836L385.432 275.791L387.958 274.946V264.394L391.747 262.283L395.956 264.816H402.692L408.165 262.283V256.374L413.216 255.952L416.584 257.218L418.689 248.354L424.161 249.62L427.529 254.263L435.948 248.776L441 240.756L440.158 233.158L430.055 231.469L428.792 230.203V226.404L437.632 225.982L436.79 218.806L433.423 214.585L433.002 208.676L424.161 201.078L418.689 205.721L411.532 197.701L415.321 188.415L409.006 178.284L410.69 177.018L413.637 170.686L409.427 165.621H399.745L394.694 162.244L392.168 160.134L388.379 159.289L387.958 152.536L389.221 146.626L387.958 144.094L389.221 139.873L385.011 39.4116L368.173 17.04L361.016 13.2411L357.648 6.48737L345.861 8.04813V14.9295L334.916 14.0853L327.76 18.7284H322.287L315.973 25.9042L305.027 18.7284L303.765 1.42211L292.398 1L245.671 70.6474L244.829 93.441L229.253 114.124L230.937 120.878V129.32L229.253 133.963L230.937 139.028L229.253 146.626L225.044 145.782L222.939 151.269L219.992 154.646L222.518 157.179L219.992 160.978L212.835 163.511L208.626 170.264L200.627 175.752L207.784 188.415L205.679 190.103L199.785 188.415L196.839 185.882V192.636L195.576 196.857L195.155 202.344L190.945 199.812L188.419 195.168L184.631 192.636L182.947 196.435H180V198.545V202.344V225.56L181.263 242.866L182.947 290.564L184.631 328.976V357.257V371.608L194.734 384.272V395.246Z",
labelPath:
"M309.042 182.222V192.752H306.477V186.437L304.122 192.752H302.052L299.682 186.422V192.752H297.117V182.222H300.147L303.102 189.512L306.027 182.222H309.042ZM313.452 184.277V186.407H316.887V188.387H313.452V190.697H317.337V192.752H310.887V182.222H317.337V184.277H313.452Z",
},
{
id: "MA",
shapePath:
"M30.5789 451.467L33.9333 427.822L85.507 429.511H104.795L134.146 433.311L162.658 432.467L169.367 428.667L171.882 424.022L176.914 423.178L182.365 418.533L190.332 416L194.944 418.533L195.363 429.089L199.975 437.111L205.846 444.711L201.653 446.822L193.686 448.933V452.311L185.3 459.067L180.688 463.711L181.526 473L187.816 475.956L191.589 471.311L200.814 478.067L202.072 483.978L210.039 489.467V494.533L204.588 493.267L205.426 499.178L214.232 504.244L213.393 512.689L217.167 519.867H227.23L239.389 518.178L248.614 512.267L246.937 503.822L243.582 497.489L232.681 491.156L235.196 487.356L244.84 489.467L257 511.844V529.156V539.711L251.13 532.533L244.002 528.733L233.939 532.533L224.714 528.733L219.682 539.711H211.716L207.523 532.533V522.4L203.33 521.133L197.04 535.489L188.235 536.333L183.204 543.511L179.011 535.067L176.495 537.178L175.237 527.467L171.463 519.444L165.174 515.222L161.4 511.422L157.207 497.067V492.844H156.368L140.435 492.422H130.372H98.9246H67.4772L64.9614 494.111L54.8982 489.889H18L20.5158 469.622L30.5789 451.467Z M246.937 553.644L248.614 550.689L255.323 557.022L257 562.933L255.323 566.311L249.872 568L237.293 560.4L245.679 559.556L248.614 557.022L246.937 553.644Z M218.005 550.689L219.682 547.733L222.618 548.578V555.756H211.716L206.265 557.022L197.879 561.244L194.105 554.911H197.04L199.137 551.956L204.588 547.733L210.877 543.511L214.232 549.422L218.005 550.689Z",
labelPath:
"M108.844 455.403V465.933H106.279V459.618L103.924 465.933H101.854L99.4845 459.603V465.933H96.9195V455.403H99.9495L102.904 462.693L105.829 455.403H108.844ZM117.244 464.073H113.314L112.684 465.933H109.999L113.809 455.403H116.779L120.589 465.933H117.874L117.244 464.073ZM116.584 462.093L115.279 458.238L113.989 462.093H116.584Z",
},
{
id: "VT",
shapePath:
"M152 224.697L34.7692 223L30.9465 238.27L29.6722 258.631L34.7692 291.717L25 315.471L34.7692 340.074L25 357.889L34.7692 353.648V421.092L33.0702 428.303L86.1639 430L81.9164 423.637V414.305L88.7124 406.246L86.1639 400.732L88.7124 393.096V384.189L92.1104 372.736V359.586L99.7559 348.133L107.401 332.863L111.224 321.834L116.321 309.109V303.594V296.383L120.993 287.051H131.187L134.585 282.385L143.08 274.326L146.903 267.963V258.631L143.08 251.42L146.903 245.482L149.027 236.998V230.211L152 224.697Z",
labelPath:
"M70.6444 307.519L66.9094 318.049H63.6994L59.9644 307.519H62.6944L65.3044 315.469L67.9294 307.519H70.6444ZM79.2915 307.519V309.574H76.5015V318.049H73.9365V309.574H71.1465V307.519H79.2915Z",
},
{
id: "RI",
shapePath:
"M130.738 546.03V492L156.148 493.266V495.377L158.197 502.975L160.246 511.417L163.525 515.216L170.902 519.859L174.18 527.879L175 538.01L170.902 548.141H163.525V535.477L158.197 528.724L151.639 535.477L158.197 548.141L151.639 556.583H138.525L130.738 563.759L125 559.96L127.869 556.583L130.738 546.03Z M140.984 570.513L142.623 568.402L148.361 573.889L143.852 576L140.984 570.513Z",
labelPath:
"M144.415 521.663L142.225 517.688H141.61V521.663H139.045V511.133H143.35C144.18 511.133 144.885 511.278 145.465 511.568C146.055 511.858 146.495 512.258 146.785 512.768C147.075 513.268 147.22 513.828 147.22 514.448C147.22 515.148 147.02 515.773 146.62 516.323C146.23 516.873 145.65 517.263 144.88 517.493L147.31 521.663H144.415ZM141.61 515.873H143.2C143.67 515.873 144.02 515.758 144.25 515.528C144.49 515.298 144.61 514.973 144.61 514.553C144.61 514.153 144.49 513.838 144.25 513.608C144.02 513.378 143.67 513.263 143.2 513.263H141.61V515.873ZM151.395 511.133V521.663H148.83V511.133H151.395Z",
},
{
id: "NH",
shapePath:
"M83 423.283L87.6702 428.353H107.2L136.495 433L165.365 432.155L171.733 428.353L173.856 424.128L179.8 422.861L183.621 418.636L187.018 417.791L192.961 415.257L194.235 416.102L197.632 417.791L200.179 413.144L204 409.765V400.893L198.905 398.358L195.933 393.289V385.684L190.839 379.348L184.895 370.053L182.772 274.578L181.074 224.305V198.112L176.828 196.845L175.554 199.802H171.733L167.488 196L165.789 199.802H160.695L157.723 201.914L160.27 203.182V205.294L155.175 217.968L152.204 224.305L150.081 229.797V236.134L147.958 245.428L143.712 250.92L147.958 258.102V266.973L143.712 273.733L135.646 281.337L131.825 286.406H121.635L117.389 296.123V308.374L108.049 332.877L100.832 347.241L93.1895 358.225V371.743L89.793 382.727V392.021L87.6702 399.626L89.793 405.54L83 413.144V423.283Z",
labelPath:
"M139.203 371H136.638L132.348 364.505V371H129.783V360.47H132.348L136.638 366.995V360.47H139.203V371ZM150.182 360.47V371H147.617V366.665H143.627V371H141.062V360.47H143.627V364.595H147.617V360.47H150.182Z",
},
{
id: "CN",
shapePath:
"M54.8511 490H18.6699L16.5663 498.048V536.17L12.3592 556.926L16.5663 564.974L1 573.022L6.46926 587H16.5663L22.4563 577.258L33.3948 573.022H45.1748L54.8511 556.926L62.0032 564.974H91.8738V556.926L100.709 564.974L121.324 556.926L124.689 560.738L128.055 556.926L131 547.607V492.965H113.33H67.0518L64.5275 494.659L54.8511 490Z",
labelPath:
"M60.3947 524.593C60.3947 523.553 60.6197 522.628 61.0697 521.818C61.5197 520.998 62.1447 520.363 62.9447 519.913C63.7547 519.453 64.6697 519.223 65.6897 519.223C66.9397 519.223 68.0097 519.553 68.8997 520.213C69.7897 520.873 70.3847 521.773 70.6847 522.913H67.8647C67.6547 522.473 67.3547 522.138 66.9647 521.908C66.5847 521.678 66.1497 521.563 65.6597 521.563C64.8697 521.563 64.2297 521.838 63.7397 522.388C63.2497 522.938 63.0047 523.673 63.0047 524.593C63.0047 525.513 63.2497 526.248 63.7397 526.798C64.2297 527.348 64.8697 527.623 65.6597 527.623C66.1497 527.623 66.5847 527.508 66.9647 527.278C67.3547 527.048 67.6547 526.713 67.8647 526.273H70.6847C70.3847 527.413 69.7897 528.313 68.8997 528.973C68.0097 529.623 66.9397 529.948 65.6897 529.948C64.6697 529.948 63.7547 529.723 62.9447 529.273C62.1447 528.813 61.5197 528.178 61.0697 527.368C60.6197 526.558 60.3947 525.633 60.3947 524.593ZM81.6755 529.873H79.1105L74.8205 523.378V529.873H72.2555V519.343H74.8205L79.1105 525.868V519.343H81.6755V529.873Z",
},
];
58 changes: 58 additions & 0 deletions nefac-website/src/components/education/foi-guide/NewEnglandMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import StateShape from "./StateShape";
import { NEStates } from "./NEStates";
import React from "react";

// note: color discrepancy between #2C4E91" & nefacblue
type Props = {
hoveredId: string | null;
setHoveredId: (id: string | null) => void;
};

const NewEnglandMap = React.memo(function NewEnglandMap({
hoveredId,
setHoveredId,
}: Props) {
return (
<svg
width="500"
height="650"
viewBox="0 0 442 588"
xmlns="http://www.w3.org/2000/svg"
className="w-full h-auto max-w-[500px]"
preserveAspectRatio="xMidYMid meet"
>
{NEStates.map((state) => {
const isHovered = hoveredId === state.id; // ← boolean, changes for ONE state, memoization
return (
<StateShape
key={state.id}
id={state.id}
isHovered={isHovered}
setHoveredId={setHoveredId}
>
<path
d={state.shapePath}
// #2F5C9F = nefacblue
stroke="#2F5C9F"
strokeWidth="2"
strokeLinejoin="round"
className={`transition-colors ${
isHovered ? "fill-nefacblue" : "fill-white"
}`}
/>
{state.labelPath && (
<path
d={state.labelPath}
className={`transition-colors ${
isHovered ? "fill-white" : "fill-nefacblue"
}`}
/>
)}
</StateShape>
);
})}
</svg>
);
});

export default NewEnglandMap;
32 changes: 32 additions & 0 deletions nefac-website/src/components/education/foi-guide/StateShape.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";

// logic for hover interactivity
type StateShapeProps = {
id: string;
isHovered: boolean;
setHoveredId: (id: string | null) => void;
children: React.ReactNode;
};

// Interactive wrapper for a state — memoized to re-render when hover status changes
const StateShape = React.memo(
function StateShape({
id,
isHovered,
setHoveredId,
children,
}: StateShapeProps) {
return (
<g
id={id}
onMouseEnter={() => setHoveredId(id)}
onMouseLeave={() => setHoveredId(null)}
className="transition-colors duration-300 cursor-pointer"
>
{children}
</g>
);
}
);

export default StateShape;
2 changes: 1 addition & 1 deletion nefac-website/src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const Header = ({ nefacLogo }: HeaderProps) => {
</a>
</li>
<li>
<a href="#" className="block px-4 py-2 hover:text-black">
<a href="/education/foi-guide" className="block px-4 py-2 hover:text-black">
FOI Guide
</a>
</li>
Expand Down
49 changes: 26 additions & 23 deletions nefac-website/src/components/sidebar/InitiativeDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
interface InitiativeDescriptionProps {
header: string;
description: string;
thumbnailUrl: string;
header: string;
description: string;
thumbnailUrl?: string;
}

export default function InitiativeDescription({
header,
description,
thumbnailUrl,
header,
description,
thumbnailUrl,
}: InitiativeDescriptionProps) {
return (
<div className="flex flex-col sm:flex-row w-full">
<div className="w-full sm:w-[60%]">
<div className="text-nefacblue text-2xl sm:text-3xl font-semibold mb-2">
{header}
</div>
<div className="text-base sm:text-lg">
{description}
</div>
</div>

<img
src={thumbnailUrl}
alt="Education Initiative Thumbnail Image"
className="w-full sm:w-[40%] max-w-[400px] h-auto object-cover rounded-3xl"
/>
return (
<div className="flex flex-col sm:flex-row w-full">
<div className="w-full">
{/* ambiguous blue color here for the initiative header */}
<div className="text-[#3C3F60] text-2xl sm:text-3xl font-semibold mb-2">
{header}
</div>
<div className="text-base md:text-xl leading-relaxed">
{description}
</div>
);
</div>

{thumbnailUrl && (
<img
src={thumbnailUrl}
alt="Education Initiative Thumbnail"
className="w-full sm:w-[40%] max-w-[400px] h-auto object-cover rounded-3xl"
/>
)}
</div>
);
}
13 changes: 11 additions & 2 deletions nefac-website/src/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ export default function Sidebar({ items }: SidebarProps) {
key={i}
onClick={() => router.push(item.link)}
className={`cursor-pointer px-4 py-2 rounded-r-3xl
${isActive ? "bg-gray-100 border-l-4 border-blue-600" : "bg-white"}`}
${
isActive
? "bg-gray-100 border-l-4 border-nefacblue"
: "bg-white"
}`}
>
{item.title}
</div>
Expand Down Expand Up @@ -94,8 +98,13 @@ export default function Sidebar({ items }: SidebarProps) {
router.push(item.link);
closeSidebar();
}}
// ambiguous blue color here for the sidebar
className={`cursor-pointer px-4 py-2 rounded-r-3xl text-3xl
${isActive ? "bg-gray-100 border-l-4 border-blue-600" : "bg-white"}`}
${
isActive
? "bg-gray-100 border-l-4 border-blue-600"
: "bg-white"
}`}
>
{item.title}
</div>
Expand Down
2 changes: 1 addition & 1 deletion nefac-website/src/components/sidebar/SidebarWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function SidebarWrapper({
<div className="text-nefacblue text-[36px] font-bold px-8">
{title}
</div>
<div className="w-full flex flex-row px-8 pt-4">
<div className="w-full flex flex-row px-8 md:gap-9 pt-4">
<Sidebar items={items} />
<div className="w-full flex flex-col">
{children}
Expand Down
Loading