From 5e50ecf6d00f51a7ad5c0be959f59295707da80e Mon Sep 17 00:00:00 2001 From: Moises Narvaez Date: Sun, 3 Sep 2023 22:38:46 -0500 Subject: [PATCH 01/10] PoC for Assotiates Directory --- package-lock.json | 11 + package.json | 1 + src/components/components.js | 4 + .../associatesDirectory.js | 18 + .../associatesDirectoryPage.js | 46 ++ .../legacyDirectory/LegacyDirectory.jsx | 80 ++++ .../legacyDirectory/app.css | 11 + .../legacyDirectory/counter/Counter.jsx | 9 + .../legacyDirectory/keys.js | 7 + .../legacyDirectory/letter/Letter.jsx | 45 ++ .../legacyDirectory/letter/letter.css | 11 + .../legacyDirectory/nameTabs/NameTabs.jsx | 451 ++++++++++++++++++ .../legacyDirectory/nameTabs/Tab.jsx | 15 + .../legacyDirectory/nameTabs/Tabs.css | 43 ++ .../legacyDirectory/nameTabs/Tabs.jsx | 36 ++ .../legacyDirectory/nameTabs/nameTabs.css | 40 ++ .../legacyDirectory/newMembers/NewMembers.jsx | 21 + .../legacyDirectory/newMembers/newMembers.css | 3 + 18 files changed, 852 insertions(+) create mode 100644 src/components/page-types/associatesDirectoryPage/associatesDirectory.js create mode 100644 src/components/page-types/associatesDirectoryPage/associatesDirectoryPage.js create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css diff --git a/package-lock.json b/package-lock.json index 2778d7e90..68e5fc249 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "cnbuilder": "^3.1.0", "cookie-parser": "^1.4.6", "decanter": "^7.0.0-rc.2", + "fast-sort": "^3.4.0", "gatsby": "^4.14.0", "gatsby-link": "^4.14.0", "gatsby-plugin-fontawesome-css": "^1.2.0", @@ -10006,6 +10007,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fast-sort": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-3.4.0.tgz", + "integrity": "sha512-c/cMBGA5mH3OYjaXedtLIM3hQjv+KuZuiD2QEH5GofNOZeQVDIYIN7Okc2AW1KPhk44g5PTZnXp8t2lOMl8qhQ==" + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -55299,6 +55305,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "fast-sort": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-3.4.0.tgz", + "integrity": "sha512-c/cMBGA5mH3OYjaXedtLIM3hQjv+KuZuiD2QEH5GofNOZeQVDIYIN7Okc2AW1KPhk44g5PTZnXp8t2lOMl8qhQ==" + }, "fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", diff --git a/package.json b/package.json index ecf127113..da470d1f3 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "cnbuilder": "^3.1.0", "cookie-parser": "^1.4.6", "decanter": "^7.0.0-rc.2", + "fast-sort": "^3.4.0", "gatsby": "^4.14.0", "gatsby-link": "^4.14.0", "gatsby-plugin-fontawesome-css": "^1.2.0", diff --git a/src/components/components.js b/src/components/components.js index 6cafc74ea..31efa5a09 100644 --- a/src/components/components.js +++ b/src/components/components.js @@ -84,12 +84,16 @@ import VerticalNavWrapper from './navigation/verticalNavWrapper'; import VerticalNavItem from './navigation/verticalNavItem'; import Wysiwyg from './simple/wysiwyg'; import MembershipPaymentOptions from './page-types/membershipFormPage/membershipPaymentOptions'; +import AssociatesDirectoryPage from './page-types/associatesDirectoryPage/associatesDirectoryPage'; +import AssociatesDirectory from './page-types/associatesDirectoryPage/associatesDirectory'; const ComponentList = { accordion: Accordion, accordionItem: AccordionItem, alert: SBAlert, alertCtaLink: SBAlertCtaLink, + associatesDirectoryPage: AssociatesDirectoryPage, + associatesDirectory: AssociatesDirectory, basicCard: BasicCard, basicCardHorizontal: BasicCardHorizontal, basicPage: BasicPage, diff --git a/src/components/page-types/associatesDirectoryPage/associatesDirectory.js b/src/components/page-types/associatesDirectoryPage/associatesDirectory.js new file mode 100644 index 000000000..c459bc3bf --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/associatesDirectory.js @@ -0,0 +1,18 @@ +import React from 'react'; +import SbEditable from 'storyblok-react'; +import { Container } from '../../layout/Container'; +import LegacyDirectory from './legacyDirectory/LegacyDirectory'; + +const AssociatesDirectory = (props) => { + const { blok } = props; + + return ( + + + + + + ); +}; + +export default AssociatesDirectory; diff --git a/src/components/page-types/associatesDirectoryPage/associatesDirectoryPage.js b/src/components/page-types/associatesDirectoryPage/associatesDirectoryPage.js new file mode 100644 index 000000000..c5ee4ae24 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/associatesDirectoryPage.js @@ -0,0 +1,46 @@ +import React from 'react'; +import SbEditable from 'storyblok-react'; +import { dcnb } from 'cnbuilder'; +import { Container } from '../../layout/Container'; +import { Heading } from '../../simple/Heading'; +import Layout from '../../partials/layout'; +import hasRichText from '../../../utilities/hasRichText'; +import RichTextRenderer from '../../../utilities/richTextRenderer'; +import CreateBloks from '../../../utilities/createBloks'; + +const AssociatesDirectoryPage = (props) => { + const { + blok: { title, intro = {}, directory }, + blok, + } = props; + + return ( + + + +
+ + + {title} + + +
+ {hasRichText(intro) && ( +
+ +
+ )} +
+ + + + +
+
+ ); +}; + +export default AssociatesDirectoryPage; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx new file mode 100644 index 000000000..cda739eee --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx @@ -0,0 +1,80 @@ +/* eslint-disable*/ + +import React from 'react'; +import axios from 'axios'; +import keys from './keys.js'; +import Counter from './counter/Counter.jsx'; +//import Search from './search/Search.jsx'; +import NameTabs from './nameTabs/NameTabs.jsx'; +import './app.css'; + +class LegacyDirectory extends React.Component { + constructor() { + super(); + + this.state = {}; + } + + componentDidMount() { + console.log('keys', keys); + + const names = []; + axios + .get( + `https://cdn.contentful.com/spaces/${keys.space}/entries?access_token=${keys.accessToken}&limit=1000&order=sys.id` + ) + .then((response) => { + response.data.items.forEach((person) => { + names.push(person.fields); + }); + + this.setState({ + names, + total: response.data.total, + }); + + const loops = Math.ceil(response.data.total / 1000); + let skip = 0; + for (let i = 0; i < loops; i++) { + skip += 1000; + axios + .get( + `https://cdn.contentful.com/spaces/${keys.space}/entries?access_token=${keys.accessToken}&limit=1000&skip=${skip}&order=sys.id` + ) + .then((response) => { + let newNames = []; + response.data.items.forEach((person) => { + newNames.push(person.fields); + }); + let totalNames = this.state.names.concat(newNames); + this.setState({ + names: totalNames, + }); + }); + } + }) + .catch((error) => { + console.log(error); + }); + } + + render() { + return ( +
+
+
+ + {/* */} +
+ {this.state.names && this.state.names.length === this.state.total && ( + + )} +
+ ); + } +} + +export default LegacyDirectory; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css new file mode 100644 index 000000000..7a61a6200 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css @@ -0,0 +1,11 @@ +#su-associates main { + margin-top: 2em; +} + +#su-associates header { + margin-left: 3%; +} + +#su-associates .counter { + margin-bottom: 1em; +} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx new file mode 100644 index 000000000..0f75ab751 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx @@ -0,0 +1,9 @@ +/* eslint-disable*/ + +import React from 'react'; + +const Counter = (props) => { + return
{props.count} Associates Total
; +}; + +export default Counter; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js b/src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js new file mode 100644 index 000000000..a7123bf7b --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js @@ -0,0 +1,7 @@ +// It's not necessary to set these as environment variables because they are used on the client side. +const keys = { + space: '0f39zonxf59w', + accessToken: '10OGNlSRGeKn81WAaTUxMjVL0nhXFEUszwRJIY7vPeI', +}; + +export default keys; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx new file mode 100644 index 000000000..466584b81 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx @@ -0,0 +1,45 @@ +/* eslint-disable*/ + +import React from 'react'; +import './letter.css'; + +const Letter = (props) => { + return ( +
+ {props.names.map((person, index) => { + let degrees = []; + for (const key in person.years) { + let degree; + degree = person.years[key]; + if (degree.slice(0, 2) !== 'AB' && degree.slice(0, 2) !== 'BS') { + degrees.push( + `${degree.slice(0, -4)} '${degree.substr(degree.length - 2)}` + ); + } else { + degrees.push(`'${degree.substring(5)}`); + } + } + + if (person.yearAdded === props.recentlyAdded) { + return ( +
+ +
{`${person.entryTitle}`}
+
{`${degrees.join(', ')}`}
+
+
+ ); + } else { + return ( +
+
{`${person.entryTitle}`}
+
{`${degrees.join(', ')}`}
+
+ ); + } + })} +
+ ); +}; + +export default Letter; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css new file mode 100644 index 000000000..b798018d1 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css @@ -0,0 +1,11 @@ +#su-associates .name { + margin-left: 5%; + padding: 0.3em 0; + display: inline-block; + width: 40%; +} + +#su-associates .degrees { + margin-left: 5%; + display: inline; +} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx new file mode 100644 index 000000000..d774f2ae7 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx @@ -0,0 +1,451 @@ +/* eslint-disable*/ + +import React from 'react'; +import { sort } from 'fast-sort'; +import NewMembers from '../newMembers/NewMembers.jsx'; +import Letter from '../letter/Letter.jsx'; +import './nameTabs.css'; +import './tabs.css'; +import Tabs from './Tabs.jsx'; +import Tab from './Tab.jsx'; + +class NameTabs extends React.Component { + constructor(props) { + super(props); + + this.state = { + checked: true, + }; + + this.sortNames = this.sortNames.bind(this); + this.handleChange = this.handleChange.bind(this); + } + + UNSAFE_componentWillMount() { + this.sortNames(this.props.names); + + let mostRecent = Math.max.apply( + Math, + this.props.names.map((name) => { + if (name.yearAdded) { + return name.yearAdded; + } else { + return 0; + } + }) + ); + + this.setState({ + recentlyAdded: mostRecent, + }); + } + + sortNames(names) { + let a = []; + let b = []; + let c = []; + let d = []; + let e = []; + let f = []; + let g = []; + let h = []; + let i = []; + let j = []; + let k = []; + let l = []; + let m = []; + let n = []; + let o = []; + let p = []; + let q = []; + let r = []; + let s = []; + let t = []; + let u = []; + let v = []; + let w = []; + let x = []; + let y = []; + let z = []; + + const sortedNames = sort(names).asc([ + (person) => person.name.last, + (person) => person.name.first, + (person) => person.name.middle, + ]); + + sortedNames.forEach((person) => { + if (person.name.last[0] === 'A') { + a.push(person); + } + if (person.name.last[0] === 'B') { + b.push(person); + } + if (person.name.last[0] === 'C') { + c.push(person); + } + if (person.name.last[0] === 'D') { + d.push(person); + } + if (person.name.last[0] === 'E') { + e.push(person); + } + if (person.name.last[0] === 'F') { + f.push(person); + } + if (person.name.last[0] === 'G') { + g.push(person); + } + if (person.name.last[0] === 'H') { + h.push(person); + } + if (person.name.last[0] === 'I') { + i.push(person); + } + if (person.name.last[0] === 'J') { + j.push(person); + } + if (person.name.last[0] === 'K') { + k.push(person); + } + if (person.name.last[0] === 'L') { + l.push(person); + } + if (person.name.last[0] === 'M') { + m.push(person); + } + if (person.name.last[0] === 'N') { + n.push(person); + } + if (person.name.last[0] === 'O') { + o.push(person); + } + if (person.name.last[0] === 'P') { + p.push(person); + } + if (person.name.last[0] === 'Q') { + q.push(person); + } + if (person.name.last[0] === 'R') { + r.push(person); + } + if (person.name.last[0] === 'S') { + s.push(person); + } + if (person.name.last[0] === 'T') { + t.push(person); + } + if (person.name.last[0] === 'U') { + u.push(person); + } + if (person.name.last[0] === 'V') { + v.push(person); + } + if (person.name.last[0] === 'W') { + w.push(person); + } + if (person.name.last[0] === 'X') { + x.push(person); + } + if (person.name.last[0] === 'Y') { + y.push(person); + } + if (person.name.last[0] === 'Z') { + z.push(person); + } + }); + + this.setState({ + a, + b, + c, + d, + e, + f, + g, + h, + i, + j, + k, + l, + m, + n, + o, + p, + q, + r, + s, + t, + u, + v, + w, + x, + y, + z, + }); + } + + handleChange() { + this.setState({ + checked: !this.state.checked, + }); + + if (this.state.checked) { + let recentlyAdded = []; + + this.props.names.forEach((person) => { + if (person.yearAdded === this.state.recentlyAdded) { + recentlyAdded.push(person); + } + }); + + this.sortNames(recentlyAdded); + } else { + this.sortNames(this.props.names); + } + } + + render() { + return ( +
+ + + +
A
+ + + back to top + +
B
+ + + back to top + +
+ +
C
+ + + back to top + +
D
+ + + back to top + +
+ +
E
+ + + back to top + +
F
+ + + back to top + +
+ +
G
+ + + back to top + +
H
+ + + back to top + +
+ +
I
+ + + back to top + +
J
+ + + back to top + +
+ +
K
+ + + back to top + +
L
+ + + back to top + +
+ +
M
+ + + back to top + +
N
+ + + back to top + +
+ +
O
+ + + back to top + +
P
+ + + back to top + +
+ +
Q
+ + + back to top + +
R
+ + + back to top + +
+ +
S
+ + + back to top + +
T
+ + + back to top + +
+ +
U
+ + + back to top + +
V
+ + + back to top + +
+ +
W
+ + + back to top + +
X
+ + + back to top + +
Y
+ + + back to top + +
Z
+ + + back to top + +
+
+
+ ); + } +} + +export default NameTabs; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx new file mode 100644 index 000000000..34310cce6 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx @@ -0,0 +1,15 @@ +import React from 'react'; + +function Tab({ active, eventKey, onClick, title }) { + const tabClass = active ? 'active' : ''; + + return ( + // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions +
  • onClick(eventKey)}> + + {title} + +
  • + ); +} +export default Tab; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css new file mode 100644 index 000000000..ffa62d927 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css @@ -0,0 +1,43 @@ +/* Add your own styles to customize the tabs */ +.tabs { + width: 100%; + margin: 20px auto; + border: 1px solid #ccc; +} + +.nav-tabs { + background-color: #f5f5f5; + padding: 10px; +} + +.nav-tabs li { + display: inline-block; + margin-right: 10px; + cursor: pointer; +} + +.nav-tabs a { + text-decoration: none; + padding: 10px 20px; + color: #333; + border: 1px solid #ccc; + border-radius: 5px 5px 0 0; +} + +.nav-tabs li.active a { + background-color: #007bff; + color: #fff; +} + +.tab-content { + padding: 20px; + border-top: 1px solid #ccc; +} + +.tab-pane { + display: none; +} + +.tab-pane.active { + display: block; +} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx new file mode 100644 index 000000000..5a58a3412 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; +import Tab from './Tab'; + +function Tabs({ defaultActiveKey, children }) { + const [activeTab, setActiveTab] = useState(defaultActiveKey); + + const handleTabClick = (eventKey) => { + setActiveTab(eventKey); + }; + + return ( +
    + +
    + {React.Children.map(children, (child) => { + if (child.type === Tab && activeTab === child.props.eventKey) { + return child.props.children; + } + return null; + })} +
    +
    + ); +} + +export default Tabs; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css new file mode 100644 index 000000000..22c402eb4 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css @@ -0,0 +1,40 @@ +.newMembersToggle { + margin-top: 2em; + margin-left: 3%; +} + +.nav-tabs .nav-item.active { + background-color: #726543; + color: white; +} + +.nav-tabs .nav-item { + background-color: #dcdac1; + color: #726543; + border: 1px solid #c1bda2; + border-radius: 0; +} + +.tab-pane { + background-color: #f3f1ed; + border-color: #dddbc3; +} + +#su-associates .nav { + margin-top: 3em; +} + +#su-associates h5 { + width: 95%; + color: #7d9a18; + border-bottom: 1px dashed #e0ddd6; + padding: 10px; + margin: 0 auto 20px auto; +} + +#su-associates .topLink { + color: #8c1515; + float: right; + margin-right: 2%; + margin-top: -1.7em; +} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx new file mode 100644 index 000000000..e69d28132 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx @@ -0,0 +1,21 @@ +/* eslint-disable*/ + +import React from 'react'; +import './newMembers.css'; + +const NewMembers = (props) => { + return ( +
    + + +
    + ); +}; + +export default NewMembers; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css new file mode 100644 index 000000000..bb0d4e8cb --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css @@ -0,0 +1,3 @@ +#su-associates .newMembersToggle { + margin-top: 3em; +} From f2b4ddeaf2b1d1c73a10f41e9935779db214cef8 Mon Sep 17 00:00:00 2001 From: Moises Narvaez Date: Tue, 12 Sep 2023 10:16:54 -0500 Subject: [PATCH 02/10] Adding new Directory component --- src/api/contentful/api.js | 23 +++++ src/api/contentful/associates/index.js | 52 +++++++++++ src/api/contentful/keys.js | 7 ++ .../Directory/Associate.jsx | 33 +++++++ .../Directory/AssociateList.jsx | 52 +++++++++++ .../Directory/Directory.jsx | 91 +++++++++++++++++++ .../Directory/Results.jsx | 33 +++++++ .../Directory/Tabs.jsx | 75 +++++++++++++++ .../associatesDirectory.js | 4 +- .../legacyDirectory/LegacyDirectory.jsx | 2 - 10 files changed, 368 insertions(+), 4 deletions(-) create mode 100644 src/api/contentful/api.js create mode 100644 src/api/contentful/associates/index.js create mode 100644 src/api/contentful/keys.js create mode 100644 src/components/page-types/associatesDirectoryPage/Directory/Associate.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/Directory/Results.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx diff --git a/src/api/contentful/api.js b/src/api/contentful/api.js new file mode 100644 index 000000000..3cc73e1fb --- /dev/null +++ b/src/api/contentful/api.js @@ -0,0 +1,23 @@ +import axios from 'axios'; +import keys from './keys'; + +class ContentFulAPI { + constructor(space = keys.space, accessToken = keys.accessToken) { + this.space = space; + this.accessToken = accessToken; + } + + async fethEntries(limit, skip) { + const apiUrl = `https://cdn.contentful.com/spaces/${this.space}/entries?access_token=${this.accessToken}&limit=${limit}&skip=${skip}&order=sys.id`; + + const response = await axios.get(apiUrl); + const { items, total } = response.data; + + return { + items: items.map((item) => item.fields), + total, + }; + } +} + +export default ContentFulAPI; diff --git a/src/api/contentful/associates/index.js b/src/api/contentful/associates/index.js new file mode 100644 index 000000000..9092f97fd --- /dev/null +++ b/src/api/contentful/associates/index.js @@ -0,0 +1,52 @@ +import { sort } from 'fast-sort'; + +import ContentFulAPI from '../api'; +import keys from '../keys'; + +const fetchNames = async ( + space = keys.space, + accessToken = keys.accessToken +) => { + const client = new ContentFulAPI(space, accessToken); + const { items, total } = await client.fethEntries(1000, 0); + let associates = [...items]; + + const loops = Math.ceil(total / 1000); + let skip = 0; + const requests = []; + + for (let i = 0; i < loops; i += 1) { + skip += 1000; + requests.push(client.fethEntries(1000, skip)); + } + + const responses = await Promise.all(requests); + const newAssociates = responses.reduce( + (acc, response) => acc.concat(response.items), + [] + ); + + associates = associates.concat(newAssociates); + const sortedNames = sort(associates).asc([ + (person) => person.name.last, + (person) => person.name.first, + (person) => person.name.middle, + ]); + + const grouped = sortedNames.reduce((acc, person) => { + const firstLetter = person.name.last[0].toUpperCase(); + if (!acc[firstLetter]) { + acc[firstLetter] = []; + } + acc[firstLetter].push(person); + return acc; + }, {}); + + return { + list: sortedNames, + grouped, + total: sortedNames.length, + }; +}; + +export default fetchNames; diff --git a/src/api/contentful/keys.js b/src/api/contentful/keys.js new file mode 100644 index 000000000..ca60d19e4 --- /dev/null +++ b/src/api/contentful/keys.js @@ -0,0 +1,7 @@ +// It's not necessary to set these as environment variables, they are public and being used on the client side. +const keys = { + space: '0f39zonxf59w', + accessToken: '10OGNlSRGeKn81WAaTUxMjVL0nhXFEUszwRJIY7vPeI', +}; + +export default keys; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/Associate.jsx b/src/components/page-types/associatesDirectoryPage/Directory/Associate.jsx new file mode 100644 index 000000000..692c8e2b9 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/Directory/Associate.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const AssociateProps = { + isEnabled: PropTypes.bool, + person: PropTypes.shape({ + name: PropTypes.shape({ + first: PropTypes.string, + last: PropTypes.string, + }), + yearAdded: PropTypes.number, + }), +}; + +const Associate = ({ isEnabled = true, person }) => { + if (!isEnabled) return null; + + return ( +
    +
    + {person.name.first} {person.name.last} +
    +
    +
    + {Object.values(person.years || []).join(', ')} +
    +
    + ); +}; + +Associate.propTypes = AssociateProps; + +export default Associate; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx new file mode 100644 index 000000000..8f820d3a0 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Associate from './Associate'; + +const AssociateListProps = { + isEnabled: PropTypes.bool, + letter: PropTypes.string, + associates: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.shape({ + first: PropTypes.string, + last: PropTypes.string, + }), + yearAdded: PropTypes.number, + }) + ), + onlyNewMembers: PropTypes.bool, + recentYear: PropTypes.number, +}; + +const AssociateList = ({ + isEnabled, + letter, + associates, + onlyNewMembers, + recentYear, +}) => { + if (!isEnabled) return null; + + return ( +
    +

    + {letter} +

    +
    + {associates?.map((person, index) => ( + + ))} +
    +
    + ); +}; + +AssociateList.propTypes = AssociateListProps; + +export default AssociateList; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx b/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx new file mode 100644 index 000000000..e40b64f86 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx @@ -0,0 +1,91 @@ +import React, { useState, useEffect } from 'react'; +import fetchNames from '../../../../api/contentful/associates'; +import Tabs from './Tabs'; +import Results from './Results'; + +const Directory = () => { + const [associatesData, setAssociatesData] = useState({}); + const [onlyNewMembers, setOnlyNewMembers] = useState(false); + const [recentYear, setRecentYear] = useState(null); + const [search, setSearch] = useState(''); + const [filteredList, setFilteredList] = useState([]); + + const filterResult = () => { + const result = associatesData.list?.filter((person) => { + const fullName = `${person.name.first} ${person.name.last}`; + const isVisible = + fullName.toLowerCase().includes(search.toLowerCase()) && + (!onlyNewMembers || person.yearAdded === recentYear); + return isVisible; + }); + setFilteredList(result); + }; + + const handleNewMembersToggle = (event) => { + setOnlyNewMembers(event.target.checked); + }; + + const handleSearch = (event) => { + setSearch(event.target.value); + }; + + useEffect(() => { + const fetchData = async () => { + const data = await fetchNames(); + setAssociatesData(data); + + const mostRecent = Math.max( + ...data.list.map((person) => person.yearAdded || 0) + ); + setRecentYear(mostRecent); + }; + + fetchData(); + }, []); + + useEffect(() => { + filterResult(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [onlyNewMembers, search]); + + return ( +
    +
    {associatesData.total} Associates Total
    +
    + +
    +
    + +
    + + {search.length ? : ''} + + {!search.length && ( + + )} +
    + ); +}; + +export default Directory; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/Results.jsx b/src/components/page-types/associatesDirectoryPage/Directory/Results.jsx new file mode 100644 index 000000000..292ec2694 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/Directory/Results.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Associate from './Associate'; + +const ResultsProps = { + onlyNewMembers: PropTypes.bool, + recentYear: PropTypes.number, +}; + +const Results = ({ filteredList }) => { + const total = filteredList?.length; + if (!total) { + return
    No associates found.
    ; + } + return ( +
    +
    {total} associates found:
    +
      + {filteredList?.map((person, index) => ( + + ))} +
    +
    + ); +}; + +Results.propTypes = ResultsProps; + +export default Results; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx b/src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx new file mode 100644 index 000000000..2f97e1ccf --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx @@ -0,0 +1,75 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; + +import AssociateList from './AssociateList'; + +const TabsProps = { + onlyNewMembers: PropTypes.bool, + recentYear: PropTypes.number, +}; + +const tabsGroups = { + 'A-B': ['A', 'B'], + 'C-D': ['C', 'D'], + 'E-F': ['E', 'F'], + 'G-H': ['G', 'H'], + 'I-J': ['I', 'J'], + 'K-L': ['K', 'L'], + 'M-N': ['M', 'N'], + 'O-P': ['O', 'P'], + 'Q-R': ['Q', 'R'], + 'S-T': ['S', 'T'], + 'U-V': ['U', 'V'], + 'W-X': ['W', 'X'], + 'Y-Z': ['Y', 'Z'], +}; +const Tabs = ({ groupedNames, onlyNewMembers, recentYear }) => { + const [activeTab, setActiveTab] = useState('A-B'); + + const handleTabClick = (event) => { + setActiveTab(event.target.dataset.group); + }; + + return ( +
    + +
    + {Object.keys(tabsGroups).map((group) => ( +
    + {tabsGroups[group].map((letter) => ( + + ))} +
    + ))} +
    +
    + ); +}; + +Tabs.propTypes = TabsProps; + +export default Tabs; diff --git a/src/components/page-types/associatesDirectoryPage/associatesDirectory.js b/src/components/page-types/associatesDirectoryPage/associatesDirectory.js index c459bc3bf..787ab03ca 100644 --- a/src/components/page-types/associatesDirectoryPage/associatesDirectory.js +++ b/src/components/page-types/associatesDirectoryPage/associatesDirectory.js @@ -1,7 +1,7 @@ import React from 'react'; import SbEditable from 'storyblok-react'; import { Container } from '../../layout/Container'; -import LegacyDirectory from './legacyDirectory/LegacyDirectory'; +import Directory from './Directory/Directory'; const AssociatesDirectory = (props) => { const { blok } = props; @@ -9,7 +9,7 @@ const AssociatesDirectory = (props) => { return ( - + ); diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx index cda739eee..20913a30e 100644 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx +++ b/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx @@ -16,8 +16,6 @@ class LegacyDirectory extends React.Component { } componentDidMount() { - console.log('keys', keys); - const names = []; axios .get( From 2eb979488a0d245da9ba4dc1c8cf00979ec1a8ec Mon Sep 17 00:00:00 2001 From: Moises Narvaez Date: Sun, 17 Sep 2023 15:56:30 -0500 Subject: [PATCH 03/10] Refactoring contentful API implementation --- src/api/contentful/associates.js | 6 ++++++ .../Directory/Directory.jsx | 21 ++++++++++++------- src/{api => utilities}/contentful/api.js | 5 +++-- .../contentful/associates/index.js | 4 ++-- src/{api => utilities}/contentful/keys.js | 1 - 5 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 src/api/contentful/associates.js rename src/{api => utilities}/contentful/api.js (65%) rename src/{api => utilities}/contentful/associates/index.js (90%) rename src/{api => utilities}/contentful/keys.js (53%) diff --git a/src/api/contentful/associates.js b/src/api/contentful/associates.js new file mode 100644 index 000000000..7fa4d36eb --- /dev/null +++ b/src/api/contentful/associates.js @@ -0,0 +1,6 @@ +import fetchNames from '../../utilities/contentful/associates'; + +export default async function handler(req, res) { + const names = await fetchNames(); + res.json(names); +} diff --git a/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx b/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx index e40b64f86..0219ca4cc 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import fetchNames from '../../../../api/contentful/associates'; +import axios from 'axios'; import Tabs from './Tabs'; import Results from './Results'; @@ -31,13 +31,20 @@ const Directory = () => { useEffect(() => { const fetchData = async () => { - const data = await fetchNames(); - setAssociatesData(data); + axios + .get('/api/contentful/associates') + .then((response) => { + setAssociatesData(response.data); - const mostRecent = Math.max( - ...data.list.map((person) => person.yearAdded || 0) - ); - setRecentYear(mostRecent); + const mostRecent = Math.max( + ...response.data.list.map((person) => person.yearAdded || 0) + ); + setRecentYear(mostRecent); + }) + .catch((error) => { + // eslint-disable-next-line no-console + console.error(error); + }); }; fetchData(); diff --git a/src/api/contentful/api.js b/src/utilities/contentful/api.js similarity index 65% rename from src/api/contentful/api.js rename to src/utilities/contentful/api.js index 3cc73e1fb..30a8a4d47 100644 --- a/src/api/contentful/api.js +++ b/src/utilities/contentful/api.js @@ -3,12 +3,13 @@ import keys from './keys'; class ContentFulAPI { constructor(space = keys.space, accessToken = keys.accessToken) { + this.host = 'https://cdn.contentful.com'; this.space = space; this.accessToken = accessToken; } - async fethEntries(limit, skip) { - const apiUrl = `https://cdn.contentful.com/spaces/${this.space}/entries?access_token=${this.accessToken}&limit=${limit}&skip=${skip}&order=sys.id`; + async fetchEntries(limit, skip) { + const apiUrl = `${this.host}/spaces/${this.space}/entries?access_token=${this.accessToken}&limit=${limit}&skip=${skip}&order=sys.id`; const response = await axios.get(apiUrl); const { items, total } = response.data; diff --git a/src/api/contentful/associates/index.js b/src/utilities/contentful/associates/index.js similarity index 90% rename from src/api/contentful/associates/index.js rename to src/utilities/contentful/associates/index.js index 9092f97fd..68cfe62ab 100644 --- a/src/api/contentful/associates/index.js +++ b/src/utilities/contentful/associates/index.js @@ -8,7 +8,7 @@ const fetchNames = async ( accessToken = keys.accessToken ) => { const client = new ContentFulAPI(space, accessToken); - const { items, total } = await client.fethEntries(1000, 0); + const { items, total } = await client.fetchEntries(1000, 0); let associates = [...items]; const loops = Math.ceil(total / 1000); @@ -17,7 +17,7 @@ const fetchNames = async ( for (let i = 0; i < loops; i += 1) { skip += 1000; - requests.push(client.fethEntries(1000, skip)); + requests.push(client.fetchEntries(1000, skip)); } const responses = await Promise.all(requests); diff --git a/src/api/contentful/keys.js b/src/utilities/contentful/keys.js similarity index 53% rename from src/api/contentful/keys.js rename to src/utilities/contentful/keys.js index ca60d19e4..1ce0ec3dc 100644 --- a/src/api/contentful/keys.js +++ b/src/utilities/contentful/keys.js @@ -1,4 +1,3 @@ -// It's not necessary to set these as environment variables, they are public and being used on the client side. const keys = { space: '0f39zonxf59w', accessToken: '10OGNlSRGeKn81WAaTUxMjVL0nhXFEUszwRJIY7vPeI', From 933404a28fb9c247070bf3e21d058a149c576573 Mon Sep 17 00:00:00 2001 From: Moises Narvaez Date: Thu, 21 Sep 2023 17:39:23 -0500 Subject: [PATCH 04/10] Reving legacy code, error handling and implementing ENV vars --- src/api/contentful/associates.js | 10 +- .../Directory/AssociateList.jsx | 1 + .../legacyDirectory/LegacyDirectory.jsx | 78 --- .../legacyDirectory/app.css | 11 - .../legacyDirectory/counter/Counter.jsx | 9 - .../legacyDirectory/keys.js | 7 - .../legacyDirectory/letter/Letter.jsx | 45 -- .../legacyDirectory/letter/letter.css | 11 - .../legacyDirectory/nameTabs/NameTabs.jsx | 451 ------------------ .../legacyDirectory/nameTabs/Tab.jsx | 15 - .../legacyDirectory/nameTabs/Tabs.css | 43 -- .../legacyDirectory/nameTabs/Tabs.jsx | 36 -- .../legacyDirectory/nameTabs/nameTabs.css | 40 -- .../legacyDirectory/newMembers/NewMembers.jsx | 21 - .../legacyDirectory/newMembers/newMembers.css | 3 - src/utilities/contentful/keys.js | 4 +- 16 files changed, 11 insertions(+), 774 deletions(-) delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx delete mode 100644 src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css diff --git a/src/api/contentful/associates.js b/src/api/contentful/associates.js index 7fa4d36eb..ff0f0b993 100644 --- a/src/api/contentful/associates.js +++ b/src/api/contentful/associates.js @@ -1,6 +1,12 @@ import fetchNames from '../../utilities/contentful/associates'; export default async function handler(req, res) { - const names = await fetchNames(); - res.json(names); + try { + const names = await fetchNames(); + res.status(200).json(names); + } catch (error) { + res.status(200).json([]); + // eslint-disable-next-line no-console + console.log(error.config); + } } diff --git a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx index 8f820d3a0..31835e01c 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx @@ -26,6 +26,7 @@ const AssociateList = ({ onlyNewMembers, recentYear, }) => { + console.log(associates); if (!isEnabled) return null; return ( diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx deleted file mode 100644 index 20913a30e..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/LegacyDirectory.jsx +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable*/ - -import React from 'react'; -import axios from 'axios'; -import keys from './keys.js'; -import Counter from './counter/Counter.jsx'; -//import Search from './search/Search.jsx'; -import NameTabs from './nameTabs/NameTabs.jsx'; -import './app.css'; - -class LegacyDirectory extends React.Component { - constructor() { - super(); - - this.state = {}; - } - - componentDidMount() { - const names = []; - axios - .get( - `https://cdn.contentful.com/spaces/${keys.space}/entries?access_token=${keys.accessToken}&limit=1000&order=sys.id` - ) - .then((response) => { - response.data.items.forEach((person) => { - names.push(person.fields); - }); - - this.setState({ - names, - total: response.data.total, - }); - - const loops = Math.ceil(response.data.total / 1000); - let skip = 0; - for (let i = 0; i < loops; i++) { - skip += 1000; - axios - .get( - `https://cdn.contentful.com/spaces/${keys.space}/entries?access_token=${keys.accessToken}&limit=1000&skip=${skip}&order=sys.id` - ) - .then((response) => { - let newNames = []; - response.data.items.forEach((person) => { - newNames.push(person.fields); - }); - let totalNames = this.state.names.concat(newNames); - this.setState({ - names: totalNames, - }); - }); - } - }) - .catch((error) => { - console.log(error); - }); - } - - render() { - return ( -
    -
    -
    - - {/* */} -
    - {this.state.names && this.state.names.length === this.state.total && ( - - )} -
    - ); - } -} - -export default LegacyDirectory; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css deleted file mode 100644 index 7a61a6200..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/app.css +++ /dev/null @@ -1,11 +0,0 @@ -#su-associates main { - margin-top: 2em; -} - -#su-associates header { - margin-left: 3%; -} - -#su-associates .counter { - margin-bottom: 1em; -} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx deleted file mode 100644 index 0f75ab751..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/counter/Counter.jsx +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable*/ - -import React from 'react'; - -const Counter = (props) => { - return
    {props.count} Associates Total
    ; -}; - -export default Counter; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js b/src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js deleted file mode 100644 index a7123bf7b..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/keys.js +++ /dev/null @@ -1,7 +0,0 @@ -// It's not necessary to set these as environment variables because they are used on the client side. -const keys = { - space: '0f39zonxf59w', - accessToken: '10OGNlSRGeKn81WAaTUxMjVL0nhXFEUszwRJIY7vPeI', -}; - -export default keys; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx deleted file mode 100644 index 466584b81..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/Letter.jsx +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable*/ - -import React from 'react'; -import './letter.css'; - -const Letter = (props) => { - return ( -
    - {props.names.map((person, index) => { - let degrees = []; - for (const key in person.years) { - let degree; - degree = person.years[key]; - if (degree.slice(0, 2) !== 'AB' && degree.slice(0, 2) !== 'BS') { - degrees.push( - `${degree.slice(0, -4)} '${degree.substr(degree.length - 2)}` - ); - } else { - degrees.push(`'${degree.substring(5)}`); - } - } - - if (person.yearAdded === props.recentlyAdded) { - return ( -
    - -
    {`${person.entryTitle}`}
    -
    {`${degrees.join(', ')}`}
    -
    -
    - ); - } else { - return ( -
    -
    {`${person.entryTitle}`}
    -
    {`${degrees.join(', ')}`}
    -
    - ); - } - })} -
    - ); -}; - -export default Letter; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css deleted file mode 100644 index b798018d1..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/letter/letter.css +++ /dev/null @@ -1,11 +0,0 @@ -#su-associates .name { - margin-left: 5%; - padding: 0.3em 0; - display: inline-block; - width: 40%; -} - -#su-associates .degrees { - margin-left: 5%; - display: inline; -} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx deleted file mode 100644 index d774f2ae7..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/NameTabs.jsx +++ /dev/null @@ -1,451 +0,0 @@ -/* eslint-disable*/ - -import React from 'react'; -import { sort } from 'fast-sort'; -import NewMembers from '../newMembers/NewMembers.jsx'; -import Letter from '../letter/Letter.jsx'; -import './nameTabs.css'; -import './tabs.css'; -import Tabs from './Tabs.jsx'; -import Tab from './Tab.jsx'; - -class NameTabs extends React.Component { - constructor(props) { - super(props); - - this.state = { - checked: true, - }; - - this.sortNames = this.sortNames.bind(this); - this.handleChange = this.handleChange.bind(this); - } - - UNSAFE_componentWillMount() { - this.sortNames(this.props.names); - - let mostRecent = Math.max.apply( - Math, - this.props.names.map((name) => { - if (name.yearAdded) { - return name.yearAdded; - } else { - return 0; - } - }) - ); - - this.setState({ - recentlyAdded: mostRecent, - }); - } - - sortNames(names) { - let a = []; - let b = []; - let c = []; - let d = []; - let e = []; - let f = []; - let g = []; - let h = []; - let i = []; - let j = []; - let k = []; - let l = []; - let m = []; - let n = []; - let o = []; - let p = []; - let q = []; - let r = []; - let s = []; - let t = []; - let u = []; - let v = []; - let w = []; - let x = []; - let y = []; - let z = []; - - const sortedNames = sort(names).asc([ - (person) => person.name.last, - (person) => person.name.first, - (person) => person.name.middle, - ]); - - sortedNames.forEach((person) => { - if (person.name.last[0] === 'A') { - a.push(person); - } - if (person.name.last[0] === 'B') { - b.push(person); - } - if (person.name.last[0] === 'C') { - c.push(person); - } - if (person.name.last[0] === 'D') { - d.push(person); - } - if (person.name.last[0] === 'E') { - e.push(person); - } - if (person.name.last[0] === 'F') { - f.push(person); - } - if (person.name.last[0] === 'G') { - g.push(person); - } - if (person.name.last[0] === 'H') { - h.push(person); - } - if (person.name.last[0] === 'I') { - i.push(person); - } - if (person.name.last[0] === 'J') { - j.push(person); - } - if (person.name.last[0] === 'K') { - k.push(person); - } - if (person.name.last[0] === 'L') { - l.push(person); - } - if (person.name.last[0] === 'M') { - m.push(person); - } - if (person.name.last[0] === 'N') { - n.push(person); - } - if (person.name.last[0] === 'O') { - o.push(person); - } - if (person.name.last[0] === 'P') { - p.push(person); - } - if (person.name.last[0] === 'Q') { - q.push(person); - } - if (person.name.last[0] === 'R') { - r.push(person); - } - if (person.name.last[0] === 'S') { - s.push(person); - } - if (person.name.last[0] === 'T') { - t.push(person); - } - if (person.name.last[0] === 'U') { - u.push(person); - } - if (person.name.last[0] === 'V') { - v.push(person); - } - if (person.name.last[0] === 'W') { - w.push(person); - } - if (person.name.last[0] === 'X') { - x.push(person); - } - if (person.name.last[0] === 'Y') { - y.push(person); - } - if (person.name.last[0] === 'Z') { - z.push(person); - } - }); - - this.setState({ - a, - b, - c, - d, - e, - f, - g, - h, - i, - j, - k, - l, - m, - n, - o, - p, - q, - r, - s, - t, - u, - v, - w, - x, - y, - z, - }); - } - - handleChange() { - this.setState({ - checked: !this.state.checked, - }); - - if (this.state.checked) { - let recentlyAdded = []; - - this.props.names.forEach((person) => { - if (person.yearAdded === this.state.recentlyAdded) { - recentlyAdded.push(person); - } - }); - - this.sortNames(recentlyAdded); - } else { - this.sortNames(this.props.names); - } - } - - render() { - return ( -
    - - - -
    A
    - - - back to top - -
    B
    - - - back to top - -
    - -
    C
    - - - back to top - -
    D
    - - - back to top - -
    - -
    E
    - - - back to top - -
    F
    - - - back to top - -
    - -
    G
    - - - back to top - -
    H
    - - - back to top - -
    - -
    I
    - - - back to top - -
    J
    - - - back to top - -
    - -
    K
    - - - back to top - -
    L
    - - - back to top - -
    - -
    M
    - - - back to top - -
    N
    - - - back to top - -
    - -
    O
    - - - back to top - -
    P
    - - - back to top - -
    - -
    Q
    - - - back to top - -
    R
    - - - back to top - -
    - -
    S
    - - - back to top - -
    T
    - - - back to top - -
    - -
    U
    - - - back to top - -
    V
    - - - back to top - -
    - -
    W
    - - - back to top - -
    X
    - - - back to top - -
    Y
    - - - back to top - -
    Z
    - - - back to top - -
    -
    -
    - ); - } -} - -export default NameTabs; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx deleted file mode 100644 index 34310cce6..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tab.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -function Tab({ active, eventKey, onClick, title }) { - const tabClass = active ? 'active' : ''; - - return ( - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions -
  • onClick(eventKey)}> - - {title} - -
  • - ); -} -export default Tab; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css deleted file mode 100644 index ffa62d927..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.css +++ /dev/null @@ -1,43 +0,0 @@ -/* Add your own styles to customize the tabs */ -.tabs { - width: 100%; - margin: 20px auto; - border: 1px solid #ccc; -} - -.nav-tabs { - background-color: #f5f5f5; - padding: 10px; -} - -.nav-tabs li { - display: inline-block; - margin-right: 10px; - cursor: pointer; -} - -.nav-tabs a { - text-decoration: none; - padding: 10px 20px; - color: #333; - border: 1px solid #ccc; - border-radius: 5px 5px 0 0; -} - -.nav-tabs li.active a { - background-color: #007bff; - color: #fff; -} - -.tab-content { - padding: 20px; - border-top: 1px solid #ccc; -} - -.tab-pane { - display: none; -} - -.tab-pane.active { - display: block; -} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx deleted file mode 100644 index 5a58a3412..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/Tabs.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { useState } from 'react'; -import Tab from './Tab'; - -function Tabs({ defaultActiveKey, children }) { - const [activeTab, setActiveTab] = useState(defaultActiveKey); - - const handleTabClick = (eventKey) => { - setActiveTab(eventKey); - }; - - return ( -
    -
      - {React.Children.map(children, (child) => { - if (child.type === Tab) { - return React.cloneElement(child, { - active: activeTab === child.props.eventKey, - onClick: handleTabClick, - }); - } - return null; - })} -
    -
    - {React.Children.map(children, (child) => { - if (child.type === Tab && activeTab === child.props.eventKey) { - return child.props.children; - } - return null; - })} -
    -
    - ); -} - -export default Tabs; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css deleted file mode 100644 index 22c402eb4..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/nameTabs/nameTabs.css +++ /dev/null @@ -1,40 +0,0 @@ -.newMembersToggle { - margin-top: 2em; - margin-left: 3%; -} - -.nav-tabs .nav-item.active { - background-color: #726543; - color: white; -} - -.nav-tabs .nav-item { - background-color: #dcdac1; - color: #726543; - border: 1px solid #c1bda2; - border-radius: 0; -} - -.tab-pane { - background-color: #f3f1ed; - border-color: #dddbc3; -} - -#su-associates .nav { - margin-top: 3em; -} - -#su-associates h5 { - width: 95%; - color: #7d9a18; - border-bottom: 1px dashed #e0ddd6; - padding: 10px; - margin: 0 auto 20px auto; -} - -#su-associates .topLink { - color: #8c1515; - float: right; - margin-right: 2%; - margin-top: -1.7em; -} diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx b/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx deleted file mode 100644 index e69d28132..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/NewMembers.jsx +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable*/ - -import React from 'react'; -import './newMembers.css'; - -const NewMembers = (props) => { - return ( -
    - - -
    - ); -}; - -export default NewMembers; diff --git a/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css b/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css deleted file mode 100644 index bb0d4e8cb..000000000 --- a/src/components/page-types/associatesDirectoryPage/legacyDirectory/newMembers/newMembers.css +++ /dev/null @@ -1,3 +0,0 @@ -#su-associates .newMembersToggle { - margin-top: 3em; -} diff --git a/src/utilities/contentful/keys.js b/src/utilities/contentful/keys.js index 1ce0ec3dc..1b3b2ecc2 100644 --- a/src/utilities/contentful/keys.js +++ b/src/utilities/contentful/keys.js @@ -1,6 +1,6 @@ const keys = { - space: '0f39zonxf59w', - accessToken: '10OGNlSRGeKn81WAaTUxMjVL0nhXFEUszwRJIY7vPeI', + space: process.env.ASSOCIATES_CONTENTFUL_SPACE, + accessToken: process.env.ASSOCIATES_CONTENTFUL_TOKEN, }; export default keys; From 796a9633a3150f6d19911ff359753b7093486357 Mon Sep 17 00:00:00 2001 From: Moises Narvaez Date: Fri, 22 Sep 2023 08:40:25 -0500 Subject: [PATCH 05/10] Removing console.log --- .../associatesDirectoryPage/Directory/AssociateList.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx index 31835e01c..8f820d3a0 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx @@ -26,7 +26,6 @@ const AssociateList = ({ onlyNewMembers, recentYear, }) => { - console.log(associates); if (!isEnabled) return null; return ( From 39651a86dfe8cb5b829f276709aca3f5903c7049 Mon Sep 17 00:00:00 2001 From: Sherakama Date: Fri, 22 Sep 2023 10:04:52 -0700 Subject: [PATCH 06/10] Update AssociateList.jsx Trigger builds --- .../associatesDirectoryPage/Directory/AssociateList.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx index 8f820d3a0..923678609 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx @@ -36,7 +36,6 @@ const AssociateList = ({
    {associates?.map((person, index) => ( Date: Fri, 22 Sep 2023 10:10:43 -0700 Subject: [PATCH 07/10] Update AssociateList.jsx Trigger build --- .../associatesDirectoryPage/Directory/AssociateList.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx index 923678609..8f820d3a0 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx @@ -36,6 +36,7 @@ const AssociateList = ({
    {associates?.map((person, index) => ( Date: Fri, 22 Sep 2023 20:13:41 -0500 Subject: [PATCH 08/10] Navigation and style adjustments --- .../Directory/AssociateList.jsx | 43 +++++++---------- .../Directory/BackToTopLink.jsx | 11 +++++ .../Directory/Directory.jsx | 7 +-- .../Directory/Results.jsx | 2 + .../Directory/TabHeader.jsx | 36 +++++++++++++++ .../Directory/Tabs.jsx | 46 +++++++++++++------ .../associatesDirectoryPage.js | 17 +++++-- 7 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 src/components/page-types/associatesDirectoryPage/Directory/BackToTopLink.jsx create mode 100644 src/components/page-types/associatesDirectoryPage/Directory/TabHeader.jsx diff --git a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx index 8f820d3a0..1f193f9ff 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/AssociateList.jsx @@ -2,9 +2,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import Associate from './Associate'; +import BackToTopLink from './BackToTopLink'; const AssociateListProps = { - isEnabled: PropTypes.bool, letter: PropTypes.string, associates: PropTypes.arrayOf( PropTypes.shape({ @@ -19,33 +19,24 @@ const AssociateListProps = { recentYear: PropTypes.number, }; -const AssociateList = ({ - isEnabled, - letter, - associates, - onlyNewMembers, - recentYear, -}) => { - if (!isEnabled) return null; - - return ( +const AssociateList = ({ letter, associates, onlyNewMembers, recentYear }) => ( +
    +

    + {letter} +

    -

    - {letter} -

    -
    - {associates?.map((person, index) => ( - - ))} -
    + {associates?.map((person, index) => ( + + ))}
    - ); -}; + +
    +); AssociateList.propTypes = AssociateListProps; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/BackToTopLink.jsx b/src/components/page-types/associatesDirectoryPage/Directory/BackToTopLink.jsx new file mode 100644 index 000000000..9a7fd2fc1 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/Directory/BackToTopLink.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const BackToTopLink = () => ( + +); + +export default BackToTopLink; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx b/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx index 0219ca4cc..382ccffb5 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/Directory.jsx @@ -58,8 +58,9 @@ const Directory = () => { return (
    -
    {associatesData.total} Associates Total
    -
    +
    +
    {associatesData.total} Associates Total
    +
    { onChange={handleSearch} />
    -
    +
    ); }; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/TabHeader.jsx b/src/components/page-types/associatesDirectoryPage/Directory/TabHeader.jsx new file mode 100644 index 000000000..0dd54a505 --- /dev/null +++ b/src/components/page-types/associatesDirectoryPage/Directory/TabHeader.jsx @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const TabHeaderProps = { + group: PropTypes.string, + activeTab: PropTypes.string, + handleTabClick: PropTypes.func, + handleKeyPress: PropTypes.func, +}; + +const TabHeader = ({ group, activeTab, handleTabClick, handleKeyPress }) => { + const isActive = activeTab === group; + + return ( + + {group} + + ); +}; +TabHeader.propTypes = TabHeaderProps; + +export default TabHeader; diff --git a/src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx b/src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx index 2f97e1ccf..55f097dd7 100644 --- a/src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx +++ b/src/components/page-types/associatesDirectoryPage/Directory/Tabs.jsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import AssociateList from './AssociateList'; +import TabHeader from './TabHeader'; const TabsProps = { onlyNewMembers: PropTypes.bool, @@ -30,32 +31,47 @@ const Tabs = ({ groupedNames, onlyNewMembers, recentYear }) => { setActiveTab(event.target.dataset.group); }; + const handleKeyPress = (event) => { + if (event.key === 'ArrowLeft') { + const currentIndex = Object.keys(tabsGroups).indexOf(activeTab); + if (currentIndex === 0) return; + + const nextIndex = currentIndex - 1; + const nextTab = Object.keys(tabsGroups)[nextIndex]; + setActiveTab(nextTab); + } + if (event.key === 'ArrowRight') { + const currentIndex = Object.keys(tabsGroups).indexOf(activeTab); + if (currentIndex === Object.keys(tabsGroups).length - 1) return; + + const nextIndex = currentIndex + 1; + const nextTab = Object.keys(tabsGroups)[nextIndex]; + setActiveTab(nextTab); + } + }; + return (
    -