Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions components/db/profile/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ export type Profile = {
location?: string
orgCategories?: OrgCategory[] | ""
phoneVerified?: boolean
memberId?: string
website?: string
}
115 changes: 115 additions & 0 deletions components/legislator/LegislatorComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { useTranslation } from "next-i18next"
import styled from "styled-components"

export const formatPhoneNumber = (value: string) => {
if (!value) return value

const phoneNumber = value.replace(/[^\d]/g, "")
const phoneNumberLength = phoneNumber.length

// Format as (XXX) XXX-XXXX
if (phoneNumberLength < 4) return phoneNumber
if (phoneNumberLength < 7) {
return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(3)}`
}
return `
(${phoneNumber.slice(0, 3)})
${phoneNumber.slice(3, 6)}-
${phoneNumber.slice(6, 10)}
`
}

/** Party Labels **/

const DemocraticBubble = styled.div.attrs(props => ({
className: `${props.className}`
}))`
background: #d1d6e7;
color: #1a3185;
font-size: 11px;
font-weight: 700;
padding: 1px 10px;
border-radius: 999px;
width: max-content;
`

const IndependantBubble = styled.div.attrs(props => ({
className: `${props.className}`
}))`
background: #fff3cd;
color: #856404;
font-size: 11px;
font-weight: 700;
padding: 1px 10px;
border-radius: 999px;
width: max-content;
`

const RepublicanBubble = styled.div.attrs(props => ({
className: `${props.className}`
}))`
background: #f29999;
color: #de0100;
font-size: 11px;
font-weight: 700;
padding: 1px 10px;
border-radius: 999px;
width: max-content;
`

export function PartyLabel(props: { party: string }) {
const { t } = useTranslation("legislators")

switch (props.party) {
case "Democrat":
return <DemocraticBubble>{t("party.democratic")}</DemocraticBubble>
case "Republican":
return <RepublicanBubble>{t("party.republican")}</RepublicanBubble>
default:
return (
<IndependantBubble>
{props.party} {t("party.party")}
</IndependantBubble>
)
}
}

/** Social Media components **/

export function Bluesky() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-bluesky"
viewBox="0 0 16 16"
>
<path d="M3.468 1.948C5.303 3.325 7.276 6.118 8 7.616c.725-1.498 2.698-4.29 4.532-5.668C13.855.955 16 .186 16 2.632c0 .489-.28 4.105-.444 4.692-.572 2.04-2.653 2.561-4.504 2.246 3.236.551 4.06 2.375 2.281 4.2-3.376 3.464-4.852-.87-5.23-1.98-.07-.204-.103-.3-.103-.218 0-.081-.033.014-.102.218-.379 1.11-1.855 5.444-5.231 1.98-1.778-1.825-.955-3.65 2.28-4.2-1.85.315-3.932-.205-4.503-2.246C.28 6.737 0 3.12 0 2.632 0 .186 2.145.955 3.468 1.948" />
</svg>
)
}

export function LinkedIn() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-linkedin"
viewBox="0 0 16 16"
>
<path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z" />
</svg>
)
}

export function Twitter() {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="#1a3185">
<path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path>
</svg>
)
}
211 changes: 206 additions & 5 deletions components/legislator/LegislatorPage.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { doc, getDoc } from "firebase/firestore"
import { faChevronRight } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { useTranslation } from "next-i18next"
import ErrorPage from "next/error"
import { useCallback, useEffect, useState } from "react"
import styled from "styled-components"

import { Col, Container, Row, Spinner } from "../bootstrap"
import { usePublicProfile } from "../db"
import { firestore } from "../firebase"
import * as links from "../links"

import {
Bluesky,
formatPhoneNumber,
LinkedIn,
PartyLabel,
Twitter
} from "./LegislatorComponents"
import { LegislatorSidebar } from "./SidebarComponents/LegislatorSidebar"
import { LegislatorTabs } from "./TabComponents/LegislatorTabs"

import { useFlags } from "components/featureFlags"
import { Internal } from "components/links"
import { CircleImage } from "components/shared/LabeledIcon"

const DirectoryPath = styled.div.attrs(props => ({
className: `align-items-center d-flex flex-nowrap ${props.className}`
Expand All @@ -27,6 +39,30 @@ const HeaderBlock = styled.div`
padding: 16px;
`

const HeaderName = styled.div`
font-size: 26px;
font-weight: 700;
color: #0b0a3e;
`

const RoleLine = styled.div.attrs(props => ({
className: `mb-2 ${props.className}`
}))`
color: #6c757d;
font-size: 14px;
`

const PhoneNum = styled.span`
color: #6c757d;
`

const SocialLine = styled.div.attrs(props => ({
className: `d-flex flex-wrap ${props.className}`
}))`
font-size: 12px;
text-decoration: none;
`

const StatBlock = styled(Col).attrs(props => ({
className: `d-flex col-4 flex-grow-1 ${props.className}`,
md: `2`
Expand Down Expand Up @@ -58,7 +94,39 @@ export function LegislatorPage(props: { id: string }) {
const { result: profile, loading } = usePublicProfile(props.id)
const { legislators } = useFlags()

console.log("Pro: ", profile)
// eventually this should be replaced with a profile prop array that
// contains a list of courts the legislator served on
const viableCourts = "194"

const [branch, setBranch] = useState<string>("")
const [cosponsoredBills, setCosponsoredBills] = useState<Array<string>>([""])
const [district, setDistrict] = useState<string>("")
const [email, setEmail] = useState<string>("")
const [party, setParty] = useState<string>("")
const [phoneNumber, setPhoneNumber] = useState<string>("")
const [sponsoredBills, setSponsoredBills] = useState<Array<string>>([""])

const memberData = useCallback(async () => {
const member = await getDoc(
doc(
firestore,
`generalCourts/${viableCourts}/members/${profile?.memberId}`
)
)
const docData = member.data()

setBranch(docData?.content.Branch)
setCosponsoredBills(docData?.content.CoSponsoredBills)
setDistrict(docData?.content.District)
setEmail(docData?.content.EmailAddress)
setParty(docData?.content.Party)
setPhoneNumber(docData?.content.PhoneNumber)
setSponsoredBills(docData?.content.SponsoredBills)
}, [district, party, phoneNumber])

useEffect(() => {
profile ? memberData() : null
}, [memberData, profile])

if (loading) {
return (
Expand All @@ -75,7 +143,7 @@ export function LegislatorPage(props: { id: string }) {
}

return (
<Container className="mt-3 mb-3">
<Container className="my-3">
<DirectoryPath>
<Internal className="text-decoration-none" href="/">
{t("home")}
Expand All @@ -86,7 +154,132 @@ export function LegislatorPage(props: { id: string }) {
<div style={{ color: "#6c757d" }}>{profile.fullName}</div>
</DirectoryPath>

<HeaderBlock>Header Info Goes Here</HeaderBlock>
<HeaderBlock className="d-flex flex-wrap justify-content-between">
<CircleImage className="me-2">
<img
src={`https://malegislature.gov/Legislators/Profile/170/${profile.memberId}.jpg`}
alt={""}
className={`image`}
/>
</CircleImage>
<Col>
<Col className="d-flex" xs="6" sm="12">
<links.External
href={`https://malegislature.gov/Legislators/Profile/${profile.memberId}`}
className="text-decoration-none"
>
<HeaderName>{profile.fullName}</HeaderName>
</links.External>
</Col>

<RoleLine>
{branch == "Senate" ? (
<span>{t("stateSenator")}</span>
) : (
<span>{t("stateRepresentative")}</span>
)}
<span className="px-2">·</span>
{district}
</RoleLine>

<div className="mb-2">
<PartyLabel party={party} />
{/* Incumbent Label */}
{/* District Label */}
</div>

<SocialLine>
<div>
<links.External
href={`mailto:${email}`}
className="text-decoration-none"
>
{email}
</links.External>
</div>

{profile.website ? (
<div>
<span className="px-2">·</span>
<links.External href="#" className="text-decoration-none">
{profile.website}
</links.External>
</div>
) : (
<div>
<span className="px-2">·</span>
<links.External
href={`https://malegislature.gov/Legislators/Profile/${profile.memberId}`}
>
{`malegislature.gov/Legislators/Profile/${profile.memberId}`}
</links.External>
</div>
)}

{phoneNumber ? (
<div>
<span className="px-2">·</span>
<PhoneNum>{formatPhoneNumber(phoneNumber)}</PhoneNum>
</div>
) : (
<></>
)}

<div>
{profile?.social?.twitter ||
profile?.social?.linkedIn ||
profile?.social?.blueSky ? (
<span className="px-2">·</span>
) : (
<></>
)}

{profile?.social?.twitter ? (
<a
href={profile.social.twitter}
className="pe-2"
rel="noreferrer"
target="_blank"
title="Twitter/X"
>
<Twitter />
</a>
) : (
<></>
)}
{profile?.social?.linkedIn ? (
<a
href={profile.social.linkedIn}
className="pe-2"
rel="noreferrer"
target="_blank"
title="linkedIn"
>
<LinkedIn />
</a>
) : (
<></>
)}
{profile?.social?.blueSky ? (
<a
href={profile?.social.blueSky}
className="pe-2"
rel="noreferrer"
target="_blank"
title="Bluesky"
>
<Bluesky />
</a>
) : (
<></>
)}
</div>
</SocialLine>
</Col>
<Col className="col-2">
<div className="">Buttons</div>
</Col>
</HeaderBlock>

<div className="d-flex flex-wrap gap-2 justify-content-between mt-2">
<StatBlock>
Expand All @@ -97,13 +290,21 @@ export function LegislatorPage(props: { id: string }) {
</StatBlock>
<StatBlock>
<Col className="flex-grow-0 mx-auto">
<StatNum>?</StatNum>
<StatNum>
{sponsoredBills?.length ? <>{sponsoredBills.length}</> : <>?</>}
</StatNum>
<StatLine>{t("billsSponsored")}</StatLine>
</Col>
</StatBlock>
<StatBlock>
<Col className="flex-grow-0 mx-auto">
<StatNum>?</StatNum>
<StatNum>
{cosponsoredBills?.length ? (
<>{cosponsoredBills.length}</>
) : (
<>?</>
)}
</StatNum>
<StatLine>{t("cosponsored")}</StatLine>
</Col>
</StatBlock>
Expand Down
Loading
Loading