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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fellowships directory pages #1046

Merged
merged 4 commits into from
Feb 13, 2018
Merged
Show file tree
Hide file tree
Changes from 3 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
12 changes: 11 additions & 1 deletion network-api/networkapi/fellows/templates/fellows_directory.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
{% extends "fellowships-base.html" %}

{% block template_id %}fellows-directory{% endblock %}

{% block body %}
fellows directory
<div class="row">
<div class="col-md-12 col-lg-9">
<div class="mb-4">
<h1 class="h1-white">Fellows Directory</h1>
</div>
</div>
</div>

<div id="fellows-directory-featured-fellows"></div>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends "fellowships-base.html" %}

{% block template_id %}fellows-directory-{{type|slugify}}{% endblock %}

{% block body %}
<div id="fellowship-breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item small-gray">
<a href="{% url 'fellowships-directory' %}" class="small-gray">Directory</a>
</li>
<li class="breadcrumb-item small-gray active text-capitalize">
{{ type }} Fellows
</li>
</ol>
</div>

<div class="row">
<div class="col-12">
<div class="mb-4">
<h1 class="h1-white text-capitalize">{{ type }} Fellows</h1>
</div>
</div>
</div>

<div id="fellows-directory-fellows-by-type" data-type="{{type}}">
</div>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ <h4 class="h4-headingcontract-black">Engaging Title for Funders</h4>
</div>
</div>

<div class="row mb-5">
<div class="mb-5">
<div id="featured-fellow-support-page" class="featured-fellow"></div>
</div>

{% endblock %}
2 changes: 0 additions & 2 deletions network-api/networkapi/fellows/templates/fellows_type.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ <h4 class="h2-headings-white">Eligibility Criteria</h4>
<div class="col-md-4">img</div>
</div>

<div class="row mb-5">
{% block featured_fellow %}
{% endblock %}
</div>

<div class="row py-5 px-md-2">
<div class="col-md-2">img</div>
Expand Down
7 changes: 5 additions & 2 deletions network-api/networkapi/fellows/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@

urlpatterns = [
url(r'^$', views.fellows_home, name='fellowships-home'),
url(r'^science/$', views.fellows_science, name='fellowships-science'),
url(r'^open-web/$', views.fellows_openweb, name='fellowships-open-web'),
url(r'^directory/$',
views.fellows_directory,
name='fellowships-directory'),
url(r'^directory/(?P<program_type_slug>[-\w]+)/$',
views.fellows_directoy_type,
name='fellowships-directory-senior'),
Copy link
Contributor

@alanmoo alanmoo Feb 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this called "senior"?

url(r'^support/$', views.fellows_support, name='fellowships-support'),
url(r'^science/$', views.fellows_science, name='fellowships-science'),
url(r'^open-web/$', views.fellows_openweb, name='fellowships-open-web'),
url(r'^apply/$', views.fellows_apply, name='fellowships-apply'),
]
20 changes: 13 additions & 7 deletions network-api/networkapi/fellows/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,26 @@ def fellows_home(request):
return render(request, 'fellows_home.html')


def fellows_science(request):
return render(request, 'fellows_science.html')


def fellows_openweb(request):
return render(request, 'fellows_openweb.html')


def fellows_directory(request):
return render(request, 'fellows_directory.html')


def fellows_support(request):
return render(request, 'fellows_support.html')

def fellows_directoy_type(request, program_type_slug):
context = {'type': program_type_slug.replace('-', ' ')}

def fellows_science(request):
return render(request, 'fellows_science.html')
return render(request, 'fellows_directory_type.html', context)


def fellows_openweb(request):
return render(request, 'fellows_openweb.html')
def fellows_support(request):
return render(request, 'fellows_support.html')


def fellows_apply(request):
Expand Down
2 changes: 1 addition & 1 deletion network-api/networkapi/templates/fellowships-base.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
<div class="col">
<div id="multipage-nav" class="d-flex flex-row align-items-center">
<div><a href="{% url 'fellowships-home' %}" class="{% fellowship_active_nav request 'fellowships-home fellowships-science fellowships-open-web' %}">Fellowships</a></div>
<div><a href="{% url 'fellowships-directory' %}" class="{% fellowship_active_nav request 'fellowships-directory' %}">Directory</a></div>
<div><a href="{% url 'fellowships-directory' %}" class="{% fellowship_active_nav request 'fellowships-directory fellowships-directory-senior' %}">Directory</a></div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, I'm not clear on what the senior bit is doing here

<div><a href="{% url 'fellowships-support' %}" class="{% fellowship_active_nav request 'fellowships-support' %}">Support the Program</a></div>
<div><a href="{% url 'fellowships-apply' %}" class="{% fellowship_active_nav request 'fellowships-apply' %}">Apply to be a Fellow</a></div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion source/js/components/multipage-nav/multipage-nav.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#multipage-nav-mobile {
background: #f2f2f2;
background: $off-white;

.multipage-nav {
div:first-child {
Expand Down
20 changes: 17 additions & 3 deletions source/js/components/people/person.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ export default class Person extends React.Component {
<a className="cta-link" href={this.props.metadata.links.interview}>Read Interview</a>
</div>
}
{this.props.metadata.fellow_directory_link &&
{this.props.metadata.custom_link &&
<div>
<a href={this.props.metadata.fellow_directory_link.link} className="cta-link">See all {this.props.metadata.fellow_directory_link.type} fellows</a>
<a href={this.props.metadata.custom_link.link} className="cta-link">{this.props.metadata.custom_link.text}</a>
</div>
}
</div>
Expand Down Expand Up @@ -114,7 +114,21 @@ export default class Person extends React.Component {
</div>
}
<div className="person-social-links mt-3">
{socialLinks}
<div className="row flex-column-reverse flex-md-row justify-content-between">
{socialLinks.length > 0 &&
<div className="col-12 col-md my-1 my-0">{socialLinks}</div>
}
{this.props.metadata.links.interview &&
<div className="col-12 col-md my-1 my-0">
<a className="cta-link" href={this.props.metadata.links.interview}>Read Interview</a>
</div>
}
{this.props.metadata.custom_link &&
<div className="col-12 col-md my-1 my-0 text-md-right">
<a href={this.props.metadata.custom_link.link} className="cta-link">{this.props.metadata.custom_link.text}</a>
</div>
}
</div>
</div>
</div>
</div>
Expand Down
162 changes: 158 additions & 4 deletions source/js/fellowships.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,152 @@ import ReactDOM from 'react-dom';

import Person from './components/people/person.jsx';

let pulseApiDomain = ``;
const DIRECOTRY_PAGE_FILTER_OPTIONS = {'program_year': `2017`};
const DIRECTORY_PAGE_TYPE_ORDER = [ `senior`, `science`, `open web`, `tech policy`, `media`];

function getFellows(params, callback) {
Object.assign(params, {'profile_type': `fellow`});

let queryString = Object.entries(params).map(pair => pair.map(encodeURIComponent).join(`=`)).join(`&`);
let req = new XMLHttpRequest();

req.addEventListener(`load`, () => {
callback.call(this, JSON.parse(req.response));
});

req.open(`GET`, `https://${pulseApiDomain}/api/pulse/profiles/?${queryString}`);
req.send();
}

function renderFellowCard(fellow) {
Copy link
Contributor

@alanmoo alanmoo Feb 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put a comment in describing what this is doing (vs, say, the existing person.jsx component)?

// massage fellow's Pulse profile data and pass it to <Person> to render

let links = {};

if ( fellow.twitter ) {
links.twitter = fellow.twitter;
}

if ( fellow.linkedin ) {
links.linkedIn = fellow.linkedin;
}

let metadata = {
'internet_health_issues': fellow.issues,
links: links,
name: fellow.custom_name,
role: `${fellow.program_type}${fellow.program_year ? `, ${fellow.program_year}` : ``}`,
image: fellow.thumbnail,
affiliations: [ fellow.affiliation ],
'custom_link': { text: `See work`, link: `/fellowships/directory` }
};

return <Person metadata={metadata} key={fellow.custom_name} />;
}

function groupFellowsByAttr(attribute, fellows) {
if (!attribute) {
return false;
}

let fellowsGroup = {};

fellows.forEach(fellow => {
let attr = fellow[attribute];

if (!attr) {
return;
}

if (!fellowsGroup[attr]) {
fellowsGroup[attr] = [fellow];
} else {
fellowsGroup[attr].push(fellow);
}
});

return fellowsGroup;
}

function renderFellowsOnDirectoryPage() {
let getTypeSlug = function(type) {
return type.toLowerCase().replace(`fellow`,``).trim().replace(/\s/g, `-`);
};

let renderFilterOption = function(option) {
return <button
className="btn btn-link text-capitalize"
key={option}
onClick={(event) => { window.scrollTo(0, document.getElementById(event.target.dataset.targetId).offsetTop); }}
data-target-id={`fellowships-directory-${getTypeSlug(option)}`}
>
{`${option}${option === `senior` ? ` Fellow` :``}`}
</button>;
};

getFellows(DIRECOTRY_PAGE_FILTER_OPTIONS, fellows => {
let fellowsByType = groupFellowsByAttr(`program_type`, fellows);

// render filter bar
let filterBar = <div className="row">
<div className="col-12">
<div id="fellowships-directory-filter" className="d-flex flex-wrap p-2">
<div className="d-inline-block mr-2"><strong>Areas:</strong></div>
{ DIRECTORY_PAGE_TYPE_ORDER.map(renderFilterOption) }
</div>
</div>
</div>;

// render program type sections
let sections = Object.keys(fellowsByType).sort((a, b) => {
let aIndex = DIRECTORY_PAGE_TYPE_ORDER.indexOf(a.replace(`fellow`,``).trim());
let bIndex = DIRECTORY_PAGE_TYPE_ORDER.indexOf(b.replace(`fellow`,``).trim());

return aIndex - bIndex;
}).map(type => {
return <div className="row my-4" key={type} id={`fellowships-directory-${getTypeSlug(type)}`}>
<div className="col-12">
<h3 className="h3-black text-capitalize">{type}s</h3>
<div className="row">
{fellowsByType[type].map(renderFellowCard)}
</div>
</div>
<div className="col-12 text-center mt-3 mb-5">
<a href={`/fellowships/directory/${getTypeSlug(type)}`} className="btn btn-ghost">See all {type}s</a>
</div>
</div>;
});

ReactDOM.render(<div>{filterBar}<div className="featured-fellow">{sections}</div></div>, document.getElementById(`fellows-directory-featured-fellows`));
});
}

function renderFellowsOnDirectoryByTypePage() {
let type = document.getElementById(`fellows-directory-fellows-by-type`).dataset.type;

getFellows({'program_type': `${type} fellow`}, fellows => {
let fellowsByYear = groupFellowsByAttr(`program_year`, fellows);

let sections = Object.keys(fellowsByYear).sort().reverse().map(year => {
return <div className="row mb-5" key={year}>
<div className="col-12">
<div className="mb-4">
<h2 className="h2-typeaccents-gray">{year}</h2>
</div>
</div>
{fellowsByYear[year].map(renderFellowCard)}
</div>;
});

ReactDOM.render(<div className="featured-fellow">{sections}</div>, document.getElementById(`fellows-directory-fellows-by-type`));
});
}

// Embed various Fellowships pages related React components
function injectReactComponents() {
function injectReactComponents(pulseApiURL) {
pulseApiDomain = pulseApiURL;

// Science Fellowship
if (document.getElementById(`featured-science-fellow`)) {
let metadata = {
Expand All @@ -19,7 +163,7 @@ function injectReactComponents() {
image: `https://images.pexels.com/photos/264206/pexels-photo-264206.jpeg?w=500`,
quote: `Quote quote quote quote quote quote quote quote quote quote quote quote.`,
affiliations: [`Stanford University Professor; YouthLAB founder`],
'fellow_directory_link': { type: `science`, link: `/fellowships/directory` }
'custom_link': { text: `See all science fellows`, link: `/fellowships/directory` }
};

ReactDOM.render(<Person metadata={metadata} />, document.getElementById(`featured-science-fellow`));
Expand All @@ -37,7 +181,7 @@ function injectReactComponents() {
image: `https://images.pexels.com/photos/802112/pexels-photo-802112.jpeg?w=500`,
quote: `Quote quote quote quote quote quote quote quote quote quote quote quote.`,
affiliations: [`Stanford University Professor; YouthLAB founder`],
'fellow_directory_link': { type: `science`, link: `/fellowships/directory` }
'custom_link': { text: `See all science fellows`, link: `/fellowships/directory` }
};

ReactDOM.render(<Person metadata={metadata} />, document.getElementById(`featured-open-web-fellow`));
Expand All @@ -55,11 +199,21 @@ function injectReactComponents() {
image: `https://static.pexels.com/photos/416138/pexels-photo-416138.jpeg?w=500`,
quote: `Quote quote quote quote quote quote quote quote quote quote quote quote.`,
affiliations: [ `Stanford University Professor; YouthLAB founder`],
'fellow_directory_link': { type: `science`, link: `/fellowships/directory` }
'custom_link': { text: `See all science fellows`, link: `/fellowships/directory` }
};

ReactDOM.render(<Person metadata={metadata} />, document.getElementById(`featured-fellow-support-page`));
}

// Fellows on Fellows Directory page
if (document.querySelectorAll(`#fellows-directory-featured-fellows`)) {
renderFellowsOnDirectoryPage();
}

// Fellows on individual Directory page (e.g., science directory, open web directory)
if (document.getElementById(`fellows-directory-fellows-by-type`)) {
renderFellowsOnDirectoryByTypePage();
}
}

export default { injectReactComponents };
5 changes: 3 additions & 2 deletions source/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ import fellowships from './fellowships';
const SHOW_MEMBER_NOTICE = false;

// To be populated via XHR...
let env, networkSiteURL;
let env, networkSiteURL, pulseApiURL;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to cause problems when promoting heroku pipelines?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh or is it pulling this from the environment json view that the server exposes?

Copy link
Collaborator Author

@mmmavis mmmavis Feb 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanmoo I think it should be fine. I'm following exactly how we are doing to get networkSiteURL from the env var NETWORK_SITE_URL (from environment json).

let main = {
init () {
this.fetchEnv((envData) => {
env = envData;
networkSiteURL = env.NETWORK_SITE_URL;
pulseApiURL = env.PULSE_API_DOMAIN;

// HEROKU_APP_DOMAIN is used by review apps
if (!networkSiteURL && env.HEROKU_APP_NAME) {
Expand Down Expand Up @@ -247,7 +248,7 @@ let main = {
}

// Fellowships pages related components
fellowships.injectReactComponents();
fellowships.injectReactComponents(pulseApiURL);
}
};

Expand Down
Loading