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

Fix-5698/show featured campaigns #5714

39 changes: 30 additions & 9 deletions app/assets/javascripts/actions/campaign_actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RECEIVE_COURSE_CAMPAIGNS, SORT_CAMPAIGNS_WITH_STATS, DELETE_CAMPAIGN, API_FAIL, RECEIVE_ALL_CAMPAIGNS, ADD_CAMPAIGN, RECEIVE_CAMPAIGNS_WITH_STATS } from '../constants';
import { RECEIVE_COURSE_CAMPAIGNS, SORT_CAMPAIGNS_WITH_STATS, DELETE_CAMPAIGN, API_FAIL, RECEIVE_ALL_CAMPAIGNS, ADD_CAMPAIGN, RECEIVE_CAMPAIGNS_WITH_STATS, SET_FEATURED_CAMPAIGNS } from '../constants';
import filterFeaturedCampaigns from '../utils/filter_featured_campaigns';
import logErrorMessage from '../utils/log_error_message';
import request from '../utils/request';

Expand All @@ -7,8 +8,8 @@ const fetchCampaignsPromise = async (courseId) => {
if (!response.ok) {
logErrorMessage(response);
const data = await response.text();
response.responseText = data;
throw response;
response.responseText = data;
throw response;
}
return response.json();
};
Expand Down Expand Up @@ -78,7 +79,7 @@ export const addCampaign = (courseId, campaignId) => (dispatch) => {
data
});
})
.catch(response => (dispatch({ type: API_FAIL, data: response })))
.catch(response => (dispatch({ type: SET_FEATURED_CAMPAIGNS, data: response })))
);
};

Expand Down Expand Up @@ -107,24 +108,44 @@ export const fetchAllCampaigns = () => (dispatch) => {
);
};

const fetchCampaignStatisticsPromise = async (userOnly, newest) => {
const fetchFeaturedCampaigns = async (dispatch) => {
const response = await request('/campaigns/featured_campaigns');
if (!response.ok) {
logErrorMessage(response);
const data = await response.text();
response.responseText = data;
throw response;
}
const response_data = await response.json();
const featured_campaigns = response_data.featured_campaigns;
dispatch({ type: SET_FEATURED_CAMPAIGNS, data: response_data });
return featured_campaigns;
};


const fetchCampaignStatisticsPromise = async (userOnly, dispatch) => {
const featured_campaigns = await fetchFeaturedCampaigns(dispatch);
// newest limits the fetched campaigns to the 10 most recent ones
// it is set to false if there are featured campaigns listed
const newest = !(featured_campaigns.length > 0);
const response = await request(`/campaigns/statistics.json?user_only=${userOnly}&newest=${newest}`);
if (!response.ok) {
logErrorMessage(response);
const data = await response.text();
response.responseText = data;
throw response;
}
return response.json();
const response_data = await response.json();
const campaigns = filterFeaturedCampaigns(response_data, featured_campaigns);
return { campaigns };
};


// this function returns the campaigns along with their statistics data
// if userOnly is set to true, only campaigns the user has created will be returned
// newest limits the campaigns to the 10 most recent ones
export const fetchCampaignStatistics = (userOnly = false, newest = false) => (dispatch) => {
export const fetchCampaignStatistics = (userOnly = false) => (dispatch) => {
return (
fetchCampaignStatisticsPromise(userOnly, newest)
fetchCampaignStatisticsPromise(userOnly, dispatch)
.then((data) => {
dispatch({
type: RECEIVE_CAMPAIGNS_WITH_STATS,
Expand Down
67 changes: 66 additions & 1 deletion app/assets/javascripts/actions/settings_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
SET_ADMIN_USERS, SET_SPECIAL_USERS,
SUBMITTING_NEW_SPECIAL_USER, REVOKING_SPECIAL_USER,
SUBMITTING_NEW_ADMIN, REVOKING_ADMIN, SET_COURSE_CREATION_SETTINGS,
SET_DEFAULT_CAMPAIGN, SET_SITE_NOTICE
SET_DEFAULT_CAMPAIGN, SET_FEATURED_CAMPAIGNS, REMOVE_FEATURED_CAMPAIGN,
ADD_FEATURED_CAMPAIGN, SET_SITE_NOTICE
} from '../constants/settings.js';
import { API_FAIL } from '../constants/api';
import { ADD_NOTIFICATION } from '../constants/notifications';
Expand Down Expand Up @@ -455,6 +456,45 @@ export const updateImpactStats = impactStats => (dispatch) => {
.catch(data => dispatch({ type: API_FAIL, data }));
};

const fetchFeaturedCampaignsPromise = async () => {
const response = await request('/campaigns/featured_campaigns');
if (!response.ok) {
logErrorMessage(response);
const data = await response.text();
response.responseText = data;
throw response;
}
return response.json();
};

export function fetchFeaturedCampaigns() {
return (dispatch) => {
return fetchFeaturedCampaignsPromise()
.then((resp) => {
dispatch({
type: SET_FEATURED_CAMPAIGNS,
data: resp,
});
})
.catch((response) => {
dispatch({ type: API_FAIL, data: response });
});
};
}

const removeFeaturedCampaignPromise = async (campaign_slug) => {
const response = await request(`/settings/remove_featured_campaign?featured_campaign_slug=${campaign_slug}`, {
method: 'POST'
});
if (!response.ok) {
logErrorMessage(response);
const data = await response.text();
response.responseText = data;
throw response;
}
return response.json();
};

const updateSiteNoticePromise = async (siteNotice) => {
const body = {
site_notice: siteNotice,
Expand All @@ -472,6 +512,25 @@ const updateSiteNoticePromise = async (siteNotice) => {
return response.json();
};

export const removeFeaturedCampaign = campaign_slug => (dispatch) => {
return removeFeaturedCampaignPromise(campaign_slug)
.then(data => dispatch({ type: REMOVE_FEATURED_CAMPAIGN, data }))
.catch(data => dispatch({ type: API_FAIL, data }));
};

const addFeaturedCampaignPromise = async (campaign_slug) => {
const response = await request(`/settings/add_featured_campaign?featured_campaign_slug=${campaign_slug}`, {
method: 'POST'
});
if (!response.ok) {
logErrorMessage(response);
const data = await response.text();
response.responseText = data;
throw response;
}
return response.json();
};

export const updateSiteNotice = siteNotice => (dispatch) => {
return updateSiteNoticePromise(siteNotice)
.then((data) => {
Expand All @@ -497,6 +556,12 @@ const getSiteNoticePromise = async () => {
return response.json();
};

export const addFeaturedCampaign = campaign_slug => (dispatch) => {
return addFeaturedCampaignPromise(campaign_slug)
.then(data => dispatch({ type: ADD_FEATURED_CAMPAIGN, data }))
.catch(data => dispatch({ type: API_FAIL, data }));
};

export const getSiteNotice = () => (dispatch) => {
return getSiteNoticePromise()
.then((resp) => {
Expand Down
4 changes: 2 additions & 2 deletions app/assets/javascripts/components/campaign/campaign_list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Loading from '../common/loading';
import DropdownSortSelect from '../common/dropdown_sort_select';
import SearchBar from '../common/search_bar';

const CampaignList = ({ keys, showSearch, RowElement, headerText, userOnly, newest, showStatistics = false }) => {
const CampaignList = ({ keys, showSearch, RowElement, headerText, userOnly, showStatistics = false }) => {
const { all_campaigns, all_campaigns_loaded, sort } = useSelector(state => state.campaigns);
const [searchParams, setSearchParams] = useSearchParams();
const search = searchParams.get('search');
Expand Down Expand Up @@ -38,7 +38,7 @@ const CampaignList = ({ keys, showSearch, RowElement, headerText, userOnly, newe

useEffect(() => {
if (showStatistics) {
dispatch(fetchCampaignStatistics(userOnly, newest));
dispatch(fetchCampaignStatistics(userOnly));
} else {
dispatch(fetchAllCampaigns());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import CampaignList from './campaign_list';
import DetailedCampaignRow from './detailed_campaign_row';

const DetailedCampaignList = ({ newest, headerText, userOnly }) => {
const DetailedCampaignList = ({ headerText, userOnly }) => {
const keys = {
title: {
label: I18n.t('campaign.campaigns'),
Expand Down Expand Up @@ -47,7 +47,7 @@ const DetailedCampaignList = ({ newest, headerText, userOnly }) => {
};
}
return (
<CampaignList RowElement={DetailedCampaignRow} keys={keys} newest={newest} headerText={headerText} userOnly={userOnly} showStatistics/>
<CampaignList RowElement={DetailedCampaignRow} keys={keys} headerText={headerText} userOnly={userOnly} showStatistics/>
);
};

Expand Down
28 changes: 16 additions & 12 deletions app/assets/javascripts/components/explore/explore.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import SearchableCourseList from '../course/searchable_course_list';

const Explore = ({ dashboardTitle }) => {
const user = getCurrentUser(useSelector(state => state));
const { featuredCampaigns } = useSelector(state => state.settings);
const isFeaturedCampaigns = featuredCampaigns.length > 0;
const showCreateButton = user.admin || Features.open_course_creation;
return (
<>
Expand All @@ -15,9 +17,11 @@ const Explore = ({ dashboardTitle }) => {
<h1 >{dashboardTitle}</h1>
</div>
</header>
<SearchableCourseList/>
<SearchableCourseList />
<div id="campaigns_list">
<DetailedCampaignList headerText={I18n.t('campaign.newest_campaigns')} newest/>
<DetailedCampaignList
headerText={isFeaturedCampaigns ? I18n.t('campaign.featured_campaigns') : I18n.t('campaign.newest_campaigns')}
/>
<div className="campaigns-actions" >
{showCreateButton && <a className="button dark" href="campaigns/new?create=true">{I18n.t('campaign.create_campaign')}</a>}
<a href="/campaigns" className="button">
Expand All @@ -26,17 +30,17 @@ const Explore = ({ dashboardTitle }) => {
</div>
</div>
{Features.wikiEd
&& (
<div id="active_courses">
<ActiveCourseList campaignOnly={true}/>
<div className="campaigns-actions">
{showCreateButton && <a className="button dark" href="/course_creator">{I18n.t(`${Features.course_string_prefix}.creator.create_new`)}</a>}
<a href={`/campaigns/${Features.default_campaign_slug}/programs`} className="button">
{I18n.t(`${Features.course_string_prefix}.all_courses`)} <span className="icon2 icon-rt_arrow_dark" />
</a>
&& (
<div id="active_courses">
<ActiveCourseList campaignOnly={true} />
<div className="campaigns-actions">
{showCreateButton && <a className="button dark" href="/course_creator">{I18n.t(`${Features.course_string_prefix}.creator.create_new`)}</a>}
<a href={`/campaigns/${Features.default_campaign_slug}/programs`} className="button">
{I18n.t(`${Features.course_string_prefix}.all_courses`)} <span className="icon2 icon-rt_arrow_dark" />
</a>
</div>
</div>
</div>
)}
)}

</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { connect } from 'react-redux';
import { addFeaturedCampaign, removeFeaturedCampaign } from '../../../actions/settings_actions';
import FeaturedCampaignForm from '../views/update_featured_campaign_form';
import FeaturedCampaign from '../views/featured_campaign';

const mapDispatchToProps = {
addFeaturedCampaign,
removeFeaturedCampaign
};

export const ConnectedFeaturedCampaignForm = connect(null, mapDispatchToProps)(FeaturedCampaignForm);
export const ConnectedFeaturedCampaigns = connect(null, mapDispatchToProps)(FeaturedCampaign);
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import List from '../common/list.jsx';
import { ConnectedFeaturedCampaigns } from './containers/featured_campaigns_contrainer.jsx';

const FeaturedCampaignsList = ({ featuredCampaigns }) => {
const elements = featuredCampaigns?.map((campaign, index) => {
return <ConnectedFeaturedCampaigns
slug={campaign.slug}
title={campaign.title}
key={index}
/>;
});

const keys = {
campaign_title: {
label: 'Campaign Title',
desktop_only: false,
},
campaign_slug: {
label: 'Campaign Slug',
desktop_only: false,
}
};
return (
<div>
<List
elements={elements}
keys={keys}
table_key="featured-campaigns-list"
none_message={I18n.t('settings.featured_campaigns.no_featured_campaigns')}
/>
</div>
);
};

export default FeaturedCampaignsList;