diff --git a/.gitignore b/.gitignore index 793f813..f887cb8 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ yarn-error.log* /.tmp /.envrc + +/public/_redirects.contentful diff --git a/Makefile b/Makefile index 7c925eb..4783a95 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,8 @@ dev: build: yarn build [ ! -f out/robots.txt.$$CONTEXT ] || cp out/robots.txt.$$CONTEXT out/robots.txt - rm -f out/robots.txt.* + [ ! -f out/_redirects.* ] || cat out/_redirects.* >> out/_redirects + rm -f out/robots.txt.* out/_redirects.* start: yarn start diff --git a/package.json b/package.json index 21c02ef..67a1093 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ }, "dependencies": { "@contentful/rich-text-html-renderer": "^16.0.2", + "@contentful/rich-text-react-renderer": "^15.16.3", + "@contentful/rich-text-types": "^16.0.3", "@emotion/css": "^11.1.3", "@emotion/react": "^11.1.5", "@emotion/server": "^11.0.0", diff --git a/src/components/home/upcoming-events.js b/src/components/home/upcoming-events.js index a4ca328..8dd708e 100644 --- a/src/components/home/upcoming-events.js +++ b/src/components/home/upcoming-events.js @@ -2,7 +2,7 @@ import tw, { css } from 'twin.macro' import PropTypes from 'prop-types' import Link from 'next/link' -export const UpcomingEventsSection = ({title, children, ...props}) => ( +export const UpcomingEventsSection = ({ title, children, ...props }) => (

( -
( +
* { + ${tw`my-4`} } `} {...props}>
{ + if (!node) { + return + } + + const { file, title } = node.data.target.fields + const aProps = {} + if (Boolean(file.contentType.match(/^application\/pdf($|;)/i))) { + aProps.download = file.fileName + aProps.target = '_blank' + } + + return {children} + }, + } } +export const config = { preview, access_token, host, space_id, renderOptions } export default config diff --git a/src/lib/contentful/entries.js b/src/lib/contentful/entries.js index c5cdd34..4aa2145 100644 --- a/src/lib/contentful/entries.js +++ b/src/lib/contentful/entries.js @@ -10,6 +10,14 @@ export async function fetchEvents(query = {}) { return items } +export async function fetchRedirects() { + const items = await client.getEntriesCachedItems({ + content_type: 'redirect' + }) + + return items +} + export async function fetchBusinesses() { const items = await client.getEntriesCachedItems({ content_type: 'business' diff --git a/src/lib/contentful/netlify-redirects.js b/src/lib/contentful/netlify-redirects.js new file mode 100644 index 0000000..20cdc88 --- /dev/null +++ b/src/lib/contentful/netlify-redirects.js @@ -0,0 +1,35 @@ +import path from 'path' +import { writeFile } from 'fs/promises' +import { fetchRedirects } from './entries' + +const mediaTarget = (media) => media && `https:${media.fields.file.url}` +const entryTarget = (entry) => { + if (!entry) return + + const contentType = entry.sys.contentType.sys.id + const slug = entry.fields.slug + + return `/${contentType}/${slug}` +} +const urlTarget = (url) => url + +export async function writeRedirects() { + const file_name = path.join(__dirname, '..', '..', '..', 'public', '_redirects.contentful') + const redirects = await fetchRedirects() + + const data = redirects.map(redirect => { + const { alias, code, media, entry, url } = redirect.fields + const statusCode = code.split(' ')[0] + const target = mediaTarget(media) || entryTarget(entry) || urlTarget(url) + + if (!target) { + throw new Error(`invalid target: ${target}`) + } + + return { alias, target, statusCode } + }) + + const output = data.map(({ alias, target, statusCode }) => [`/${alias}`, target, `${statusCode}!`].join(' ')).join('\n') + + await writeFile(file_name, output) +} diff --git a/src/lib/contentful/render.js b/src/lib/contentful/render.js new file mode 100644 index 0000000..beff39c --- /dev/null +++ b/src/lib/contentful/render.js @@ -0,0 +1,10 @@ +import { documentToHtmlString } from '@contentful/rich-text-html-renderer' +import { documentToReactComponents } from '@contentful/rich-text-react-renderer' +import config from './config' + +export const renderComponents = + (content, options = config.renderOptions) => + documentToReactComponents(content, options) + +export const render = { renderComponents, documentToHtmlString, documentToReactComponents } +export default render diff --git a/src/lib/utils.js b/src/lib/utils.js index 273dc3c..1f2bd83 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -1,3 +1,40 @@ import { DateTime } from 'luxon' export const YEAR = DateTime.now().year + +// size: short, med(ium)?, full, huge +export const formatDateTime = (date, options = {}) => { + const dt = DateTime.fromISO(date) + const { size } = { size: 'medium', ...options } + + switch (size) { + // TODO: case 'full': {} + + case 'short': { + // year … Jan 1, 2000 at 1pm + // … Jan 1 at 1pm + // weekday … Mon, Jan 1 at 1pm + // weekday,minutes … Mon, Jan 1 at 1:30pm + // weekday,year,minutes … Mon, Jan 1, 2000 at 1:30pm + const { year, weekday, minutes } = { year: true, weekday: false, minutes: false, ...options } + const wd = weekday ? `${dt.toFormat('ccc')}, ` : '' + const md = dt.toFormat('LLL d') + const y = year ? `, ${dt.toFormat('yyyy')}` : '' + const t = minutes ? dt.toFormat('t').toLowerCase().replace(' ', '') : dt.toFormat('h') + dt.toFormat('a').toLowerCase() + return [wd, md, y, ' at ', t].join('') + } + case 'med': + case 'medium': + default: { + // January 1, 2000 at 1:30 PM + // Monday, January 1, 2000 at 1:30 PM + // Monday, January 1, at 1:30 PM + const { year, weekday } = { year: true, weekday: false, ...options } + const wd = weekday ? `${dt.toFormat('cccc')}, ` : '' + const md = dt.toFormat('LLLL d') + const y = year ? `, ${dt.toFormat('yyyy')}` : '' + const t = ` at ${dt.toFormat('t')}` + return [wd, md, y, t].join('') + } + } +} diff --git a/src/pages/about-us.js b/src/pages/about-us.js index dccb1ec..f56f10d 100644 --- a/src/pages/about-us.js +++ b/src/pages/about-us.js @@ -14,7 +14,7 @@ import EntrySign from '@/images/wix/EntrySign.jpg' import { fetchBoardMembers } from '@/lib/contentful/entries' -export default function AboutUs({boardMembers}) { +export default function AboutUs({ boardMembers }) { return <> About Us @@ -53,6 +53,13 @@ export default function AboutUs({boardMembers}) {
+
+
Land Acknowledgement
+

The Renaissance Neighborhood of Tulsa recognizes that our community is built on the original land assigned to two Native Muscogee Creek Citizens, Emma Adeline "Addie" Perryman and Mary Jane Perryman. The Perrymans were prominent Tulsans, often called the “First Family of Tulsa.” To learn more about these Creek women and other neighborhood history, visit the RNA History Project.

+
+
+ +
A Little History Lesson

The Renaissance Neighborhood Association was established in 1994. An early accomplishment was successfully preventing the demolition of the Casa Loma Building at 11th and Columbia Ave. This building is now known as the historic Max Campbell Building and is listed on the National Register of Historic Places. It has been completely renovated and is open for business today.

diff --git a/src/pages/events.js b/src/pages/events.js index 7edd29d..a417fbf 100644 --- a/src/pages/events.js +++ b/src/pages/events.js @@ -1,4 +1,5 @@ import React from 'react' +import { DateTime } from 'luxon' import Title from '@/components/title' import Banner from '@/components/banner' @@ -13,10 +14,11 @@ import YardSale from '@/images/wix/8ddcb11aa53c45ce954624a4aea25994.jpg' import First from '@/images/wix/events/scavenger-hunt/first.png' import Second from '@/images/wix/events/scavenger-hunt/second.png' import styles from '@/styles/events.module.css' -import sectionStyles from '@/styles/section.module.css' -import { documentToHtmlString } from '@contentful/rich-text-html-renderer' import { fetchEvents } from '@/lib/contentful/entries' +import { renderComponents } from '@/lib/contentful/render' +import { formatDateTime } from '@/lib/utils' + const CHILIFEST_IMAGES = [ require('@/images/wix/ChiliFest/BannerOnTable.jpg'), @@ -61,25 +63,33 @@ const PARKFEST_IMAGES = [ export async function getStaticProps() { return { props: { - events: await fetchEvents({order: 'fields.date'}) + events: await fetchEvents({ order: 'fields.date' }) } } } -function Event({event}) { - const __html = documentToHtmlString(event.fields.content) +function Event({ event }) { + const { title, date, location, content } = event.fields + + const formatDate = (date) => { + return formatDateTime(date, { weekday: true }) + } return <> -
+
-

{event.fields.title}

-
+

{title}

+ + {date &&
{formatDate(date)}
} + {location &&
{location}
} + + {renderComponents(content)}
} -export default function Events({events}) { +export default function Events({ events }) { return <> Events diff --git a/src/pages/index.js b/src/pages/index.js index f4ed91c..0b60e97 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,10 +1,11 @@ import React from 'react' import Title from '@/components/title' +import { DateTime } from 'luxon' import { css } from 'twin.macro' import { Gallery, GalleryText, GalleryButtons, GalleryButton } from '@/components/gallery' -import { UpcomingEventsSection, UpcomingEvent} from '@/components/home/upcoming-events' -import { WhatsNewsSection, WhatsNewsItem} from '@/components/home/whats-news' +import { UpcomingEventsSection, UpcomingEvent } from '@/components/home/upcoming-events' +import { WhatsNewsSection, WhatsNewsItem } from '@/components/home/whats-news' import RNAalbum7 from '@/images/wix/home-page/gallery/RNAalbum7.jpg'; const IMAGES = [ @@ -23,30 +24,32 @@ import RNAshirt1 from '@/images/wix/home-page/news/RNAshirt1.jpg' import Alert from '@/images/wix/home-page/news/Alert.jpg' import NewRNAsign1 from '@/images/wix/home-page/news/NewRNAsign1.jpg' -import { documentToHtmlString } from '@contentful/rich-text-html-renderer' +import { renderComponents } from '@/lib/contentful/render' import { fetchEvents } from '@/lib/contentful/entries' +import { writeRedirects } from '@/lib/contentful/netlify-redirects' +import { formatDateTime } from '@/lib/utils' export async function getStaticProps() { + await writeRedirects() + return { props: { - events: await fetchEvents({order: 'fields.date'}) + events: await fetchEvents({ order: 'fields.date' }) } } } -function UpcomingEventContent({event}) { - const field = event.fields.content - const __html = documentToHtmlString({ - ...field, - content: field.content.slice(0, 1) - }) +function UpcomingEventContent({ event }) { + const { title, date, location, content } = event.fields -return <> -
- + return
+ {date &&

{formatDateTime(date, { size: 'short', year: false, weekday: true })}

} + {location &&

{location}

} + {content && renderComponents({ ...content, content: content.content.slice(0, 1) })} +
} -export default function Home({events}) { +export default function Home({ events }) { return <> Home @@ -65,15 +68,16 @@ export default function Home({events}) { {events.map((event, i) => ( - + ))} - -

- “Second Friday” Meet-n-Greet
- Renaissance Brewery -

+ +
+

Every 2nd Friday

+

Renaissance Brewery at 12th & Lewis

+

A social event for residents of our neighborhood.

+
diff --git a/yarn.lock b/yarn.lock index eecf5cb..5177b77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -917,11 +917,23 @@ "@contentful/rich-text-types" "^16.0.2" escape-html "^1.0.3" +"@contentful/rich-text-react-renderer@^15.16.3": + version "15.16.3" + resolved "https://registry.yarnpkg.com/@contentful/rich-text-react-renderer/-/rich-text-react-renderer-15.16.3.tgz#f6536c517ae903351e265243f33d6f56deed4d1f" + integrity sha512-aFtgKn8Yt19qpsez9RdV65/khPhy7GnWXhjhiCMSDVY9vDwyPIPfoKkInt+vePAk7rxlleXds5s7ba6Sm5zKQg== + dependencies: + "@contentful/rich-text-types" "^16.0.3" + "@contentful/rich-text-types@^16.0.2": version "16.0.2" resolved "https://registry.yarnpkg.com/@contentful/rich-text-types/-/rich-text-types-16.0.2.tgz#779bc76b5159152b3a9e908e6a04cb9342faa9b1" integrity sha512-ovbmCKQjlyGek4NuABoqDesC3FBV3e5jPMMdtT2mpOy9ia31MKO0NSFMRGZu7Q+veZzmDMja8S1i/XeFCUT9Pw== +"@contentful/rich-text-types@^16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@contentful/rich-text-types/-/rich-text-types-16.0.3.tgz#c590268483e07881fb01b6799e837337093e42bf" + integrity sha512-BsUtXj93jo5XUt0YeUwfCkMWRoZIzJDPUIY4vMy9SwGIO9olTsMoQKadjA2ktlmK+Gg6710WH3eFKZxj2q39Iw== + "@dabh/diagnostics@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"