Skip to content
This repository has been archived by the owner on Dec 14, 2023. It is now read-only.

Commit

Permalink
Display pending requests to join (#250)
Browse files Browse the repository at this point in the history
* Display pending requests to join

* Add time restriction for display

* Review copy

* Upgrade cp-translations
  • Loading branch information
Wardormeur committed Mar 26, 2019
1 parent de53de3 commit 3dfc15c
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 5 deletions.
115 changes: 115 additions & 0 deletions cypress/integration/dashboard/events_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import moment from 'moment';
import eventPage from '../../pages/events';
import homePage from '../../pages/home';

describe('Homepage events', () => {
beforeEach(() => {
Expand Down Expand Up @@ -172,6 +173,89 @@ describe('Homepage events', () => {
cy.get(eventPage.bookedTickets).should('be.visible');
cy.get(eventPage.bookedTickets).should('have.text', '1 "Mentor" tickets booked');
});
it('should show the pending request and the newcomer hints', () => {
cy.route('/api/3.0/leads?userId=u1&deleted=0', []).as('leads');
cy.route('/api/2.0/users/instance', {
ok: true,
login: {
id: 'l1',
},
user: {
id: 'u1',
joinRequests: [{
dojoId: 'd1',
timestamp: new Date(),
userType: 'mentor',
}],
},
}).as('loggedIn');
cy.route('POST', '/api/2.0/dojos/users', [{ dojoId: 'd1', userPermissions: [], userTypes: ['mentor'] }]).as('userDojos');
cy.route(/\/api\/3\.0\/dojos\/d1\/events\?query\[status\]=published&query\[afterDate\]=\d+&query\[utcOffset\]=\d+&related=sessions\.tickets$/,
{
results: [{
id: 'e1', name: 'event1', dojoId: 'd1', dates: ['2018-08-26T11:00:00.000'],
sessions: [{ tickets: [{ type: 'mentor', quantity: 1, approvedApplications: 0 }] }]
}]
}).as('dojoEvent1');
cy.route('/api/2.0/dojos/d1', { id: 'd1', created: '2015-08-26T11:46:14.308Z' }).as('dojo1');
cy.route('POST', '/api/2.0/dojos', [{ id: 'd1', created: '2015-08-26T11:46:14.308Z' }]).as('dojos');
cy.route('/api/3.0/users/u1/orders?query[eventId]=e1',
{ results: [{ id: 'o1', applications: [{ ticketType: 'mentor' }] }] }).as('orders1');
cy.visit('/home');
cy.wait('@loggedIn');
cy.wait('@leads');
cy.wait('@userDojos');
cy.wait('@dojoEvent1');
cy.wait('@orders1');
cy.wait('@dojo1');
cy.wait('@dojos');
cy.get(homePage.pendingRequests).should('have.length', 1);
cy.get(homePage.pendingRequestsInfo).should('be.visible');
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
});

it('should show the pending request, but not the newcomer hints when hes a returning user', () => {
const createdAt = moment().add(-3, 'months');
cy.route('/api/3.0/leads?userId=u1&deleted=0', []).as('leads');
cy.route('/api/2.0/users/instance', {
ok: true,
login: {
id: 'l1',
},
user: {
id: 'u1',
joinRequests: [{
dojoId: 'd1',
timestamp: new Date(),
userType: 'mentor',
}],
when: createdAt,
},
}).as('loggedIn');
cy.route('POST', '/api/2.0/dojos/users', [{ dojoId: 'd1', userPermissions: [], userTypes: ['mentor'] }]).as('userDojos');
cy.route(/\/api\/3\.0\/dojos\/d1\/events\?query\[status\]=published&query\[afterDate\]=\d+&query\[utcOffset\]=\d+&related=sessions\.tickets$/,
{
results: [{
id: 'e1', name: 'event1', dojoId: 'd1', dates: ['2018-08-26T11:00:00.000'],
sessions: [{ tickets: [{ type: 'mentor', quantity: 1, approvedApplications: 0 }] }]
}]
}).as('dojoEvent1');
cy.route('/api/2.0/dojos/d1', { id: 'd1', created: '2015-08-26T11:46:14.308Z' }).as('dojo1');
cy.route('POST', '/api/2.0/dojos', [{ id: 'd1', created: '2015-08-26T11:46:14.308Z' }]).as('dojos');
cy.route('/api/3.0/users/u1/orders?query[eventId]=e1',
{ results: [{ id: 'o1', applications: [{ ticketType: 'mentor' }] }] }).as('orders1');
cy.visit('/home');
cy.wait('@loggedIn');
cy.wait('@leads');
cy.wait('@userDojos');
cy.wait('@dojoEvent1');
cy.wait('@orders1');
cy.wait('@dojo1');
cy.wait('@dojos');
cy.get(homePage.pendingRequests).should('have.length', 1);
cy.get(homePage.pendingRequestsInfo).should('not.be.visible');
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
});
});
describe('as a ticketing-admin', () => {
const after2Weeks = (moment().subtract(2, 'weeks')).format();
Expand Down Expand Up @@ -315,6 +399,37 @@ describe('Homepage events', () => {
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
});
});
describe('as a potential mentor', () => {
it('should the pending request and the newcomer hints', () => {
cy.route('/api/3.0/leads?userId=u1&deleted=0', []).as('leads');
cy.route('/api/2.0/users/instance', {
ok: true,
login: {
id: 'l1',
},
user: {
id: 'u1',
joinRequests: [{
dojoId: 'd1',
timestamp: new Date(),
userType: 'mentor',
}],
},
}).as('loggedIn');
cy.route('POST', '/api/2.0/dojos/users', []).as('userDojos');
cy.route('POST', '/api/2.0/dojos', [{
id: 'd1',
created: '2015-08-26T11:46:14.308Z',
}]).as('dojo1');
cy.visit('/home');
cy.wait('@loggedIn');
cy.wait('@leads');
cy.wait('@dojo1');
cy.get(homePage.pendingRequests).should('have.length', 1);
cy.get(homePage.pendingRequestsInfo).should('be.visible');
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
});
});
describe('as a normal user without event that is not ticketing-admin', () => {
it('should display two buttons as cta', () => {
cy.route('/api/2.0/users/instance', 'fx:parentLoggedIn').as('loggedIn');
Expand Down
4 changes: 3 additions & 1 deletion cypress/pages/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ export default {
newsTitle: '.cd-dashboard-news__header',
newsEntries: '.cd-dashboard-news__posts',
newsEntryTitle: '.cd-dashboard-news__post-title',
newsEntryDate : '.cd-dashboard-news__post-date',
newsEntryDate: '.cd-dashboard-news__post-date',
// newsEntryCategory: '.cd-dashboard-news__post-type',
pendingRequests: '.cd-dashboard-pending-requests__request',
pendingRequestsInfo: '.cd-dashboard-pending-requests__info',
projectTitle: '.cd-dashboard-projects__header',
projectCards: '.cd-dashboard-projects__cards',
projectCard: '.cd-dashboard-projects__card',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"dependencies": {
"@coderdojo/cd-common": "1.1.11",
"bootstrap": "^3.4.1",
"cp-translations": "1.0.125",
"cp-translations": "1.0.126",
"font-awesome": "^4.7.0",
"handlebars": "^4.1.0",
"js-cookie": "^2.1.4",
Expand Down
9 changes: 9 additions & 0 deletions src/dashboard/cd-dashboard-events.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
<div v-if="usersDojos && dojoAdmins.length === 1 && hasDojos && dojoAge(firstDojo, 'weeks') < 2">
<dashboard-admin-survey :dojo-name="firstDojo.name"></dashboard-admin-survey>
</div>
<div v-if="hasRequests">
<dashboard-pending-volunteering :user-is-new="userIsNew" :requests-to-join="loggedInUser.joinRequests"></dashboard-pending-volunteering>
</div>
<dashboard-cta :has-events="events.length > 0 && events[0].id" :is-ticketing-admin="ticketingAdmins.length > 0"></dashboard-cta>
</div>
</div>
Expand All @@ -28,6 +31,7 @@
import DashboardCreateEvent from '@/dashboard/events/cd-dashboard-create-event';
import DashboardAdminSurvey from '@/dashboard/cd-dashboard-admin-survey';
import DashboardCta from '@/dashboard/cd-dashboard-cta';
import DashboardPendingVolunteering from '@/dashboard/cd-dashboard-pending-volunteering';
import UpcomingEvent from './events/cd-dashboard-upcoming-event';
export default {
Expand All @@ -38,6 +42,7 @@
DashboardCreateEvent,
DashboardAdminSurvey,
DashboardCta,
DashboardPendingVolunteering,
},
data() {
return {
Expand Down Expand Up @@ -67,6 +72,10 @@
return this.usersDojos.filter(usersDojo =>
usersDojo.userPermissions && usersDojo.userPermissions.find(perm => perm.name === 'dojo-admin'));
},
userIsNew() {
// Don't use moment month difference https://github.com/moment/moment/issues/3029
return moment().diff(this.loggedInUser.when, 'days') < 90; // 3 months
},
hasDojos() {
return Object.keys(this.dojos).length > 0;
},
Expand Down
81 changes: 81 additions & 0 deletions src/dashboard/cd-dashboard-pending-volunteering.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<div class="cd-dashboard-pending-requests">
<div :class="[index === (dojos.length -1) && infoIsDisplayed ? 'cd-dashboard-pending-requests__request--last': '', 'cd-dashboard-pending-requests__request']" v-for="(dojo, index) in dojos">
<i class="fa fa-hourglass-half"></i>
<h4 class="cd-dashboard-pending-requests__request-title" v-html="$t('You have a pending join request for <a href=\'/dojos/{slug}\'>{name}</a>.', { name: dojo.name, slug: dojo.urlSlug })"/>
<p>{{ $t('If the Dojo does not reply/accept within a few days, we suggest you try another club or contacting support.') }}</p>
</div>
<div class="cd-dashboard-pending-requests__info" v-if="infoIsDisplayed">
{{ $t('In the meantime, we recommend that you complete some of the following.') }}
<ul class="cd-dashboard-pending-requests__info-recommendations">
<li><a href="https://www.raspberrypi.org/safeguarding/e-learning-module/">{{ $t('Do our safeguarding module') }}</a></li>
<li><a href="https://help.coderdojo.com/hc/en-us/articles/360000674903-Setting-up-and-running-a-Dojo-a-CoderDojo-guide">{{ $t('Look at our guide to mentoring') }}</a></li>
<li><a href="https://projects.raspberrypi.org/org/coderdojo">{{ $t('Check out some projects you might like') }}</a></li>
</ul>
</div>
</div>
</template>

<script>
import moment from 'moment';
import DojosService from '@/dojos/service';
export default {
name: 'cd-dashboard-pending-requests',
props: ['requestsToJoin', 'userIsNew'],
data() {
return {
dojos: [],
};
},
computed: {
recentRequestsToJoin() {
return this.requestsToJoin.filter(r => moment().diff(r.timestamp, 'days') < 30);
},
infoIsDisplayed() {
return this.userIsNew && this.recentRequestsToJoin.length > 0;
},
},
async created() {
this.dojos = (await DojosService.getDojos({
id: {
in$: this.recentRequestsToJoin.map(r => r.dojoId),
},
})).body;
},
};
</script>

<style scoped lang="less">
@import "~@coderdojo/cd-common/common/_colors";
@import "../common/variables";
.cd-dashboard-pending-requests {
margin: 32px 0;
display: flex;
flex-direction: column;
&__request, &__info {
background: @cd-white;
padding: 20px;
}
&__info {
&-recommendations {
padding-top: 1em;
}
}
&__request {
margin: @margin 0;
& > .fa {
padding-right: @margin;
font-size: 1.2em;
}
&-title {
display: inline-block;
}
&--last {
margin: 0;
border-bottom: @cd-very-light-grey 10px solid;
}
}
}
</style>
12 changes: 12 additions & 0 deletions test/unit/specs/dashboard/cd-dashboard-events.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ describe('Dashboard events component', () => {
});
});

describe('userIsNew', () => {
it('should return true if the user is less than 3 months old', () => {
vm.loggedInUser = { when: new Date() };
expect(vm.userIsNew).to.be.true;
});
it('should return false if the user is 3months + old', () => {
const createdAt = moment().add(-3, 'months');
vm.loggedInUser = { when: createdAt };
expect(vm.userIsNew).to.be.false;
});
});

describe('ticketingAdmins', () => {
it('should return the usersdojos where the user has a ticketingAdmin perm', () => {
vm.usersDojos = [{ dojoId: 'd1', userPermissions: [{ name: 'banana' }] }, { dojoId: 'd2', userPermissions: [{ name: 'ticketing-admin' }] }];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import vueUnitHelper from 'vue-unit-helper';
import moment from 'moment';
import DashboardHeaderComponent from '!!vue-loader?inject!@/dashboard/cd-dashboard-pending-volunteering';

describe('Dashboard pending volunteering component', () => {
let sandbox;
let vm;
let MockDojosService;

beforeEach(() => {
sandbox = sinon.sandbox.create();
MockDojosService = {
getDojos: sandbox.stub(),
};
vm = vueUnitHelper(DashboardHeaderComponent({
'@/dojos/service': MockDojosService,
}));
});

afterEach(() => {
sandbox.restore();
});


describe('computed', () => {
describe('recentRequestsToJoin', () => {
it('should filter any requests to join which are 30 days older', () => {
const oldDay = moment().add(-31, 'days');
const now = new Date();
// ARRANGE
vm.requestsToJoin = [{ id: 'rq1', timestamp: oldDay }, { id: 'rq2', timestamp: now }];

// ASSERT
expect(vm.recentRequestsToJoin).to.eql([{ id: 'rq2', timestamp: now }]);
});
});
describe('infoIsDisplayed', () => {
it('should return true if there are valid requests to join and the user is new', () => {
// ARRANGE
vm.recentRequestsToJoin = [{ id: 'rq1' }];
vm.userIsNew = true;

// ASSERT
expect(vm.infoIsDisplayed).to.be.true;
});
it('should return false if the user has no leads', () => {
// ARRANGE
vm.recentRequestsToJoin = [];
vm.userIsNew = true;

// ASSERT
expect(vm.infoIsDisplayed).to.be.false;
});
it('should return false if the user is not new', () => {
// ARRANGE
vm.recentRequestsToJoin = [{ id: 'rq1' }];
vm.userIsNew = false;

// ASSERT
expect(vm.infoIsDisplayed).to.be.false;
});
});
});
});
7 changes: 4 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1988,9 +1988,10 @@ cosmiconfig@^2.1.0, cosmiconfig@^2.1.1:
parse-json "^2.2.0"
require-from-string "^1.1.0"

cp-translations@1.0.125:
version "1.0.125"
resolved "https://registry.yarnpkg.com/cp-translations/-/cp-translations-1.0.125.tgz#fdda119d524670fc1b35907144ff12cf62a43963"
cp-translations@1.0.126:
version "1.0.126"
resolved "https://registry.yarnpkg.com/cp-translations/-/cp-translations-1.0.126.tgz#1f3be1afa4838a854701ad7c893ad3b9cf18680d"
integrity sha512-OL1g2z+7qniZC+tawAgOir8qyiJkmYCcDALiFIB+hG+GJAzOY/Bm9KmtMqhf7ZesCCDqyyCdJ18m6vvBsVLEuQ==

crc32-stream@^2.0.0:
version "2.0.0"
Expand Down

0 comments on commit 3dfc15c

Please sign in to comment.