Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create standalone evictor pages, date last updated, and backlinks to AEMP site #11

Open
wants to merge 5 commits into
base: staging
Choose a base branch
from
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
9 changes: 6 additions & 3 deletions plugins/data-fetch/fetchEvictorData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "dotenv/config"
import { createClient } from "contentful"
import type { EntryCollection } from "contentful"
import getEBEntry from "./getEBEntry"
import { formatLink } from "../../src/utils/string"

export default async function fetchEvictorData() {
const client = createClient({
Expand All @@ -25,6 +26,8 @@ export default async function fetchEvictorData() {
const { ebEntry, name, pullQuote, citywideListDescription } =
item.fields

const evictorNameFormatted = formatLink(name) // created formatted version of name

if (!ebEntry?.length) {
console.warn(
`${name} doesn't have an Evictorbook entry entered on Contentful`
Expand Down Expand Up @@ -93,9 +96,8 @@ export default async function fetchEvictorData() {
await ebData.reduce(
async (shells: Promise<string[]>, business) => {
const networkId = business.networkDetails[0].network_id
const networkUrl = `${
process.env.EB_DOMAIN || "https://evictorbook.com"
}/api/network/${networkId}/nodes`
const networkUrl = `${process.env.EB_DOMAIN || "https://evictorbook.com"
}/api/network/${networkId}/nodes`
const nodes = await fetch(networkUrl).then((res) =>
res.json()
)
Expand Down Expand Up @@ -131,6 +133,7 @@ export default async function fetchEvictorData() {
...item.fields,
id: item.sys.id,
ebData,
nameFormatted: evictorNameFormatted, // add in formatted name as queryable field
totalEvictions,
activeSince,
totalUnits,
Expand Down
27 changes: 21 additions & 6 deletions src/components/Evictor.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import React from 'react'
import renderContent from '../utils/contentful-render'
import {OutboundLink} from './OutboundLink'
import type {EvictorProps} from '../queries/list'
import {getImage} from 'gatsby-plugin-image'
import { OutboundLink } from './OutboundLink'
import type { EvictorProps } from '../queries/list'
import { getImage } from 'gatsby-plugin-image'
import {
FormatBusinessAddress,
titleCase,
} from '../utils/string'
import EvictorImage from './EvictorImage'
import pin from '../images/pin.svg'
import {formatLink} from '../utils/string'
import { formatLink } from '../utils/string'

import '../styles/list.scss'

const EvictorProfile: React.FC<{
content: EvictorProps
city: string
}> = ({content, city}) => {
const {networkDetails, details} = content.ebData[0]
}> = ({ content, city }) => {
const { networkDetails, details } = content.ebData[0]
const activeSince = content.activeSince

const formatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' })
const dateLastUpdated = formatter.format(new Date(content.lastUpdated))

return (
<section
className="evictor-profile"
Expand Down Expand Up @@ -68,6 +71,14 @@ const EvictorProfile: React.FC<{
name={content.photoCaption}
hideEyebrow
/>
{content.aempUrl && (
<OutboundLink
href={content.aempUrl}
className="btn btn-primary"
>
See AEMP's original profile on this landlord
</OutboundLink>
)}
{content.banks?.length ? (
<p>
<span className="stat-name">Funded By</span>
Expand Down Expand Up @@ -120,6 +131,10 @@ const EvictorProfile: React.FC<{
's portfolio
</OutboundLink>
))}
{content.lastUpdated && (<span className="date_updated">
Profile last updated on {dateLastUpdated}
</span>
)}
</>
)}
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const Layout = ({
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={url} />
<meta property="og:image" content={shareImage} />
<meta property="og:image" content={shareImageURL} />
<meta property="og:type" content="website" />

<meta name="twitter:card" content="summary_large_image" />
Expand All @@ -58,7 +58,7 @@ const Layout = ({
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:url" content={url} />
<meta name="twitter:image" content={shareImage} />
<meta name="twitter:image" content={shareImageURL} />
<meta
name="twitter:image:alt"
content="The Worst Evictors of San Francisco and Oakland"
Expand Down
53 changes: 53 additions & 0 deletions src/pages/evictor/{Evictor.nameFormatted}.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react'
import Layout from '../../components/Layout'
import EvictorProfile from '../../components/Evictor'
import type { EvictorProps } from '../../queries/list'
import useListQuery from '../../queries/list'
import Header from '../../components/Header'
import Footer from '../../components/Footer'
import { renderToString } from 'react-dom/server';

import '../../styles/list.scss'
import { sortEvictors } from '../../utils/misc'
import renderContent from '../../utils/contentful-render'

const SingleEvictorPage = (props) => {
// pull the evictor ID populated through Gatsby's "collection route" setup for dynamic pages
// See more here: https://www.gatsbyjs.com/docs/reference/routing/file-system-route-api
const evictorId = props.pageContext.id

const data = useListQuery() // pull the full list of evictors
const evictors = sortEvictors(
data.allEvictor.nodes
) as EvictorProps[]

/* Because static GraphQL queries don't allow for variables / dynamic searches, easiest to
* pull the full list of evictors and then filter here to the evictor for this page.
*
* TODO - if this becomes a performance issue, can restructure around a dynamic query
*/

const evictorToDisplay = evictors.filter((evictor) => evictor.id == evictorId)[0]
const evictorCity = evictorToDisplay.city == "sf" ? 'San Francisco' : 'Oakland'

return (
<Layout
customTitle={`${evictorToDisplay.name} | The Worst Evictors of San Francisco and Oakland`}
customDescription={evictorToDisplay.citywideListDescription &&
renderToString(renderContent(evictorToDisplay.citywideListDescription))}
customImage={evictorToDisplay.localFile.childImageSharp.gatsbyImageData.images.fallback.src}
className="page"
hideFooter
>
<div className="header-container">
<Header isDescription={false} />
</div>
<div className="scroll-container">
<EvictorProfile content={evictorToDisplay} city={evictorCity} />
<Footer />
</div>
</Layout>
)
}

export default SingleEvictorPage
14 changes: 11 additions & 3 deletions src/queries/list.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import {useStaticQuery, graphql} from 'gatsby'
import {ImageDataLike} from 'gatsby-plugin-image'
import { useStaticQuery, graphql } from 'gatsby'
import { ImageDataLike } from 'gatsby-plugin-image'

export default function useIndexQuery() {
export default function useListQuery() {
const data = useStaticQuery(graphql`
query {
contentfulCitywideListPage {
title
}
allEvictor {
nodes {
id
name
nameFormatted
city
nonprofitOrLowIncome
corporation
photoCaption
shellCompanies
tags
lastUpdated
aempUrl
evictions {
type
evict_date
Expand Down Expand Up @@ -62,10 +66,14 @@ export default function useIndexQuery() {

/** might as well define some proptypes */
export type EvictorProps = {
id: number
name: string
nameFormatted: string
corporation: string
city: string
tags?: string[]
lastUpdated?: string
aempUrl?: string
evictions: {
type: string
evict_date: string
Expand Down
28 changes: 26 additions & 2 deletions src/styles/list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
display: grid;
grid-template-rows: 1fr auto;
max-height: 100vh;

@media (min-width: 700px) {
.scroll-container {
overflow-y: scroll;
Expand All @@ -28,35 +29,43 @@
border-bottom: solid 1px white;
z-index: 1;
width: 100%;

h1 {
font-size: 1.5rem !important;
margin: 0 !important;
margin-right: 0.5rem !important;
}

.header {
width: 100%;
padding: 0.5rem;

.title-container {
margin: 0 !important;
}

.title {
place-items: center;
margin: 0 !important;

h1 {
text-align: center;
}

img {
height: 1.5rem;
}
}
}

.title-links {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-around;
flex-wrap: wrap;
}

.links {
place-items: center;
place-content: center;
Expand All @@ -74,38 +83,52 @@
and .link-target has position: absolute;
*/
position: relative;

.spacer {
height: 10rem;
}

.city-name {
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;

img {
height: 1.5rem;
}

span {
font-size: 1.5rem;
margin: 0;
}

margin: 1rem 0;
}

.tags {
margin: 1rem 0;

.tag {
font-style: italic;
font-size: 1.5rem;
}
}
}

.date_updated {
margin: 2rem 0;
font-style: italic;
font-size: 1rem;
}

.col-container {
min-height: 100vh;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;

.left,
.right {
flex: 1 1 50ch;
Expand All @@ -116,6 +139,7 @@
flex-direction: column;
place-items: center;
margin: 0 1.5rem;

.left-width-constrainer,
.right-width-constrainer {
width: 100%;
Expand All @@ -139,8 +163,8 @@
font-weight: bold;
text-transform: uppercase;
}

li {
margin-left: 1rem;
}
}

}