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

STAN-288: Create Associates microservice for StoryBlocks #709

Merged
merged 12 commits into from
Sep 28, 2023
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"cnbuilder": "^3.1.0",
"cookie-parser": "^1.4.6",
"decanter": "^7.0.0-rc.2",
"fast-sort": "^3.4.0",
moisesnarvaez marked this conversation as resolved.
Show resolved Hide resolved
"gatsby": "^4.14.0",
"gatsby-link": "^4.14.0",
"gatsby-plugin-fontawesome-css": "^1.2.0",
Expand Down
12 changes: 12 additions & 0 deletions src/api/contentful/associates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import fetchNames from '../../utilities/contentful/associates';

export default async function handler(req, res) {
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);
}
}
4 changes: 4 additions & 0 deletions src/components/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ 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';
import MegaMenu from './navigation/MegaMenu/megaMenu';
import MegaMenuPanel from './navigation/MegaMenu/megaMenuPanel';
import MegaMenuCard from './navigation/MegaMenu/megaMenuCard';
Expand All @@ -95,6 +97,8 @@ const ComponentList = {
accordionItem: AccordionItem,
alert: SBAlert,
alertCtaLink: SBAlertCtaLink,
associatesDirectoryPage: AssociatesDirectoryPage,
associatesDirectory: AssociatesDirectory,
basicCard: BasicCard,
basicCardHorizontal: BasicCardHorizontal,
basicPage: BasicPage,
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<li className="su-flex even:su-bg-black-10">
<div className="su-flex-1 su-w-[50%] su-py-10 su-pl-30">
{person.name.first} {person.name.last}
</div>
<div> </div>
<div className="su-flex-1 su-w-[50%] su-py-10">
{Object.values(person.years || []).join(', ')}
</div>
</li>
);
};

Associate.propTypes = AssociateProps;

export default Associate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import PropTypes from 'prop-types';

import Associate from './Associate';
import BackToTopLink from './BackToTopLink';

const AssociateListProps = {
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 = ({ letter, associates, onlyNewMembers, recentYear }) => (
<div>
<h4 className="su-p-10 su-text-cardinal-red-xdark su-font-serif su-border-b su-border-dashed su-border-1 su-border-black-30-opacity-40">
{letter}
</h4>
<ul className="su-p-0 su-list-none">
{associates?.map((person, index) => (
<Associate
// eslint-disable-next-line react/no-array-index-key
key={`person-${person.entryTitle}-${index}`}
person={person}
isEnabled={!onlyNewMembers || person.yearAdded === recentYear}
/>
))}
</ul>
<BackToTopLink />
</div>
);

AssociateList.propTypes = AssociateListProps;

export default AssociateList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

const BackToTopLink = () => (
<div className="su-flex su-justify-end">
<a href="#associates--top-of-page" className=" su-text-cardinal-red">
Back to top
</a>
</div>
);

export default BackToTopLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
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 isVisible =
person.fullNameWithYears.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 () => {
axios
.get('/api/contentful/associates')
.then((response) => {
setAssociatesData(response.data);

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();
}, []);

useEffect(() => {
filterResult();

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [onlyNewMembers, search]);

return (
<div className="su-mt-30">
<div id="associates--top-of-page" />
<div className="su-my-50">{associatesData.total} Associates Total</div>
<div className="su-my-50">
<input
type="text"
className="su-py-10 su-px-20 su-text-19 su-border su-border-solid su-border-black-40"
placeholder="Search for a Name"
value={search}
onChange={handleSearch}
/>
</div>
<div className="su-my-50">
<label>
<input
type="checkbox"
checked={onlyNewMembers}
value={onlyNewMembers}
onChange={handleNewMembersToggle}
className="su-peer su-form-checkbox su-text-digital-red-light su-mr-10 su-w-[1.5rem] su-h-[1.5rem] su-cursor-pointer su-rounded su-border-black-40 hocus:su-border-none hocus:su-ring hocus:su-ring-digital-red-light hocus:su-ring-offset-0"
/>{' '}
View Only New Members
</label>
</div>

{search.length ? <Results filteredList={filteredList} /> : ''}

{!search.length && (
<Tabs
groupedNames={associatesData.grouped || {}}
onlyNewMembers={onlyNewMembers}
recentYear={recentYear}
search={search}
/>
)}
</div>
);
};

export default Directory;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
import Associate from './Associate';
import BackToTopLink from './BackToTopLink';

const ResultsProps = {
onlyNewMembers: PropTypes.bool,
recentYear: PropTypes.number,
};

const Results = ({ filteredList }) => {
const total = filteredList?.length;
if (!total) {
return <div className="su-my-50">No associates found.</div>;
}
return (
<div>
<div className="su-my-20">{total} associates found:</div>
<ul className="su-p-0 su-list-none">
{filteredList?.map((person, index) => (
<Associate
// eslint-disable-next-line react/no-array-index-key
sherakama marked this conversation as resolved.
Show resolved Hide resolved
key={`person-${person.entryTitle}-${index}`}
person={person}
/>
))}
</ul>
<BackToTopLink />
</div>
);
};

Results.propTypes = ResultsProps;

export default Results;
Original file line number Diff line number Diff line change
@@ -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 (
<a
key={`tab-${group}`}
role="tab"
aria-selected={isActive}
aria-controls={group}
className={`${
isActive
? 'su-bg-cardinal-red-xdark'
: 'su-bg-saa-black su-bg-opacity-[80%]'
} su-py-[8px] su-px-[15px] su-text-white hover:su-text-white hover:su-no-underline focus:su-text-white focus:su-no-underline su-border su-border-solid su-border-1 su-border-black-30-opacity-40`}
href={`#${group}`}
onClick={handleTabClick}
data-group={group}
onKeyDown={handleKeyPress}
>
{group}
</a>
);
};
TabHeader.propTypes = TabHeaderProps;

export default TabHeader;
Loading
Loading