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

Commit 3dfc15c

Browse files
authored
Display pending requests to join (#250)
* Display pending requests to join * Add time restriction for display * Review copy * Upgrade cp-translations
1 parent de53de3 commit 3dfc15c

File tree

8 files changed

+289
-5
lines changed

8 files changed

+289
-5
lines changed

cypress/integration/dashboard/events_spec.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import moment from 'moment';
22
import eventPage from '../../pages/events';
3+
import homePage from '../../pages/home';
34

45
describe('Homepage events', () => {
56
beforeEach(() => {
@@ -172,6 +173,89 @@ describe('Homepage events', () => {
172173
cy.get(eventPage.bookedTickets).should('be.visible');
173174
cy.get(eventPage.bookedTickets).should('have.text', '1 "Mentor" tickets booked');
174175
});
176+
it('should show the pending request and the newcomer hints', () => {
177+
cy.route('/api/3.0/leads?userId=u1&deleted=0', []).as('leads');
178+
cy.route('/api/2.0/users/instance', {
179+
ok: true,
180+
login: {
181+
id: 'l1',
182+
},
183+
user: {
184+
id: 'u1',
185+
joinRequests: [{
186+
dojoId: 'd1',
187+
timestamp: new Date(),
188+
userType: 'mentor',
189+
}],
190+
},
191+
}).as('loggedIn');
192+
cy.route('POST', '/api/2.0/dojos/users', [{ dojoId: 'd1', userPermissions: [], userTypes: ['mentor'] }]).as('userDojos');
193+
cy.route(/\/api\/3\.0\/dojos\/d1\/events\?query\[status\]=published&query\[afterDate\]=\d+&query\[utcOffset\]=\d+&related=sessions\.tickets$/,
194+
{
195+
results: [{
196+
id: 'e1', name: 'event1', dojoId: 'd1', dates: ['2018-08-26T11:00:00.000'],
197+
sessions: [{ tickets: [{ type: 'mentor', quantity: 1, approvedApplications: 0 }] }]
198+
}]
199+
}).as('dojoEvent1');
200+
cy.route('/api/2.0/dojos/d1', { id: 'd1', created: '2015-08-26T11:46:14.308Z' }).as('dojo1');
201+
cy.route('POST', '/api/2.0/dojos', [{ id: 'd1', created: '2015-08-26T11:46:14.308Z' }]).as('dojos');
202+
cy.route('/api/3.0/users/u1/orders?query[eventId]=e1',
203+
{ results: [{ id: 'o1', applications: [{ ticketType: 'mentor' }] }] }).as('orders1');
204+
cy.visit('/home');
205+
cy.wait('@loggedIn');
206+
cy.wait('@leads');
207+
cy.wait('@userDojos');
208+
cy.wait('@dojoEvent1');
209+
cy.wait('@orders1');
210+
cy.wait('@dojo1');
211+
cy.wait('@dojos');
212+
cy.get(homePage.pendingRequests).should('have.length', 1);
213+
cy.get(homePage.pendingRequestsInfo).should('be.visible');
214+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
215+
});
216+
217+
it('should show the pending request, but not the newcomer hints when hes a returning user', () => {
218+
const createdAt = moment().add(-3, 'months');
219+
cy.route('/api/3.0/leads?userId=u1&deleted=0', []).as('leads');
220+
cy.route('/api/2.0/users/instance', {
221+
ok: true,
222+
login: {
223+
id: 'l1',
224+
},
225+
user: {
226+
id: 'u1',
227+
joinRequests: [{
228+
dojoId: 'd1',
229+
timestamp: new Date(),
230+
userType: 'mentor',
231+
}],
232+
when: createdAt,
233+
},
234+
}).as('loggedIn');
235+
cy.route('POST', '/api/2.0/dojos/users', [{ dojoId: 'd1', userPermissions: [], userTypes: ['mentor'] }]).as('userDojos');
236+
cy.route(/\/api\/3\.0\/dojos\/d1\/events\?query\[status\]=published&query\[afterDate\]=\d+&query\[utcOffset\]=\d+&related=sessions\.tickets$/,
237+
{
238+
results: [{
239+
id: 'e1', name: 'event1', dojoId: 'd1', dates: ['2018-08-26T11:00:00.000'],
240+
sessions: [{ tickets: [{ type: 'mentor', quantity: 1, approvedApplications: 0 }] }]
241+
}]
242+
}).as('dojoEvent1');
243+
cy.route('/api/2.0/dojos/d1', { id: 'd1', created: '2015-08-26T11:46:14.308Z' }).as('dojo1');
244+
cy.route('POST', '/api/2.0/dojos', [{ id: 'd1', created: '2015-08-26T11:46:14.308Z' }]).as('dojos');
245+
cy.route('/api/3.0/users/u1/orders?query[eventId]=e1',
246+
{ results: [{ id: 'o1', applications: [{ ticketType: 'mentor' }] }] }).as('orders1');
247+
cy.visit('/home');
248+
cy.wait('@loggedIn');
249+
cy.wait('@leads');
250+
cy.wait('@userDojos');
251+
cy.wait('@dojoEvent1');
252+
cy.wait('@orders1');
253+
cy.wait('@dojo1');
254+
cy.wait('@dojos');
255+
cy.get(homePage.pendingRequests).should('have.length', 1);
256+
cy.get(homePage.pendingRequestsInfo).should('not.be.visible');
257+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
258+
});
175259
});
176260
describe('as a ticketing-admin', () => {
177261
const after2Weeks = (moment().subtract(2, 'weeks')).format();
@@ -315,6 +399,37 @@ describe('Homepage events', () => {
315399
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
316400
});
317401
});
402+
describe('as a potential mentor', () => {
403+
it('should the pending request and the newcomer hints', () => {
404+
cy.route('/api/3.0/leads?userId=u1&deleted=0', []).as('leads');
405+
cy.route('/api/2.0/users/instance', {
406+
ok: true,
407+
login: {
408+
id: 'l1',
409+
},
410+
user: {
411+
id: 'u1',
412+
joinRequests: [{
413+
dojoId: 'd1',
414+
timestamp: new Date(),
415+
userType: 'mentor',
416+
}],
417+
},
418+
}).as('loggedIn');
419+
cy.route('POST', '/api/2.0/dojos/users', []).as('userDojos');
420+
cy.route('POST', '/api/2.0/dojos', [{
421+
id: 'd1',
422+
created: '2015-08-26T11:46:14.308Z',
423+
}]).as('dojo1');
424+
cy.visit('/home');
425+
cy.wait('@loggedIn');
426+
cy.wait('@leads');
427+
cy.wait('@dojo1');
428+
cy.get(homePage.pendingRequests).should('have.length', 1);
429+
cy.get(homePage.pendingRequestsInfo).should('be.visible');
430+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
431+
});
432+
});
318433
describe('as a normal user without event that is not ticketing-admin', () => {
319434
it('should display two buttons as cta', () => {
320435
cy.route('/api/2.0/users/instance', 'fx:parentLoggedIn').as('loggedIn');

cypress/pages/home.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ export default {
88
newsTitle: '.cd-dashboard-news__header',
99
newsEntries: '.cd-dashboard-news__posts',
1010
newsEntryTitle: '.cd-dashboard-news__post-title',
11-
newsEntryDate : '.cd-dashboard-news__post-date',
11+
newsEntryDate: '.cd-dashboard-news__post-date',
1212
// newsEntryCategory: '.cd-dashboard-news__post-type',
13+
pendingRequests: '.cd-dashboard-pending-requests__request',
14+
pendingRequestsInfo: '.cd-dashboard-pending-requests__info',
1315
projectTitle: '.cd-dashboard-projects__header',
1416
projectCards: '.cd-dashboard-projects__cards',
1517
projectCard: '.cd-dashboard-projects__card',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"dependencies": {
2323
"@coderdojo/cd-common": "1.1.11",
2424
"bootstrap": "^3.4.1",
25-
"cp-translations": "1.0.125",
25+
"cp-translations": "1.0.126",
2626
"font-awesome": "^4.7.0",
2727
"handlebars": "^4.1.0",
2828
"js-cookie": "^2.1.4",

src/dashboard/cd-dashboard-events.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
<div v-if="usersDojos && dojoAdmins.length === 1 && hasDojos && dojoAge(firstDojo, 'weeks') < 2">
1313
<dashboard-admin-survey :dojo-name="firstDojo.name"></dashboard-admin-survey>
1414
</div>
15+
<div v-if="hasRequests">
16+
<dashboard-pending-volunteering :user-is-new="userIsNew" :requests-to-join="loggedInUser.joinRequests"></dashboard-pending-volunteering>
17+
</div>
1518
<dashboard-cta :has-events="events.length > 0 && events[0].id" :is-ticketing-admin="ticketingAdmins.length > 0"></dashboard-cta>
1619
</div>
1720
</div>
@@ -28,6 +31,7 @@
2831
import DashboardCreateEvent from '@/dashboard/events/cd-dashboard-create-event';
2932
import DashboardAdminSurvey from '@/dashboard/cd-dashboard-admin-survey';
3033
import DashboardCta from '@/dashboard/cd-dashboard-cta';
34+
import DashboardPendingVolunteering from '@/dashboard/cd-dashboard-pending-volunteering';
3135
import UpcomingEvent from './events/cd-dashboard-upcoming-event';
3236
3337
export default {
@@ -38,6 +42,7 @@
3842
DashboardCreateEvent,
3943
DashboardAdminSurvey,
4044
DashboardCta,
45+
DashboardPendingVolunteering,
4146
},
4247
data() {
4348
return {
@@ -67,6 +72,10 @@
6772
return this.usersDojos.filter(usersDojo =>
6873
usersDojo.userPermissions && usersDojo.userPermissions.find(perm => perm.name === 'dojo-admin'));
6974
},
75+
userIsNew() {
76+
// Don't use moment month difference https://github.com/moment/moment/issues/3029
77+
return moment().diff(this.loggedInUser.when, 'days') < 90; // 3 months
78+
},
7079
hasDojos() {
7180
return Object.keys(this.dojos).length > 0;
7281
},
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<template>
2+
<div class="cd-dashboard-pending-requests">
3+
<div :class="[index === (dojos.length -1) && infoIsDisplayed ? 'cd-dashboard-pending-requests__request--last': '', 'cd-dashboard-pending-requests__request']" v-for="(dojo, index) in dojos">
4+
<i class="fa fa-hourglass-half"></i>
5+
<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 })"/>
6+
<p>{{ $t('If the Dojo does not reply/accept within a few days, we suggest you try another club or contacting support.') }}</p>
7+
</div>
8+
<div class="cd-dashboard-pending-requests__info" v-if="infoIsDisplayed">
9+
{{ $t('In the meantime, we recommend that you complete some of the following.') }}
10+
<ul class="cd-dashboard-pending-requests__info-recommendations">
11+
<li><a href="https://www.raspberrypi.org/safeguarding/e-learning-module/">{{ $t('Do our safeguarding module') }}</a></li>
12+
<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>
13+
<li><a href="https://projects.raspberrypi.org/org/coderdojo">{{ $t('Check out some projects you might like') }}</a></li>
14+
</ul>
15+
</div>
16+
</div>
17+
</template>
18+
19+
<script>
20+
import moment from 'moment';
21+
import DojosService from '@/dojos/service';
22+
23+
export default {
24+
name: 'cd-dashboard-pending-requests',
25+
props: ['requestsToJoin', 'userIsNew'],
26+
data() {
27+
return {
28+
dojos: [],
29+
};
30+
},
31+
computed: {
32+
recentRequestsToJoin() {
33+
return this.requestsToJoin.filter(r => moment().diff(r.timestamp, 'days') < 30);
34+
},
35+
infoIsDisplayed() {
36+
return this.userIsNew && this.recentRequestsToJoin.length > 0;
37+
},
38+
},
39+
async created() {
40+
this.dojos = (await DojosService.getDojos({
41+
id: {
42+
in$: this.recentRequestsToJoin.map(r => r.dojoId),
43+
},
44+
})).body;
45+
},
46+
};
47+
</script>
48+
49+
<style scoped lang="less">
50+
@import "~@coderdojo/cd-common/common/_colors";
51+
@import "../common/variables";
52+
53+
.cd-dashboard-pending-requests {
54+
margin: 32px 0;
55+
display: flex;
56+
flex-direction: column;
57+
&__request, &__info {
58+
background: @cd-white;
59+
padding: 20px;
60+
}
61+
&__info {
62+
&-recommendations {
63+
padding-top: 1em;
64+
}
65+
}
66+
&__request {
67+
margin: @margin 0;
68+
& > .fa {
69+
padding-right: @margin;
70+
font-size: 1.2em;
71+
}
72+
&-title {
73+
display: inline-block;
74+
}
75+
&--last {
76+
margin: 0;
77+
border-bottom: @cd-very-light-grey 10px solid;
78+
}
79+
}
80+
}
81+
</style>

test/unit/specs/dashboard/cd-dashboard-events.spec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ describe('Dashboard events component', () => {
3737
});
3838
});
3939

40+
describe('userIsNew', () => {
41+
it('should return true if the user is less than 3 months old', () => {
42+
vm.loggedInUser = { when: new Date() };
43+
expect(vm.userIsNew).to.be.true;
44+
});
45+
it('should return false if the user is 3months + old', () => {
46+
const createdAt = moment().add(-3, 'months');
47+
vm.loggedInUser = { when: createdAt };
48+
expect(vm.userIsNew).to.be.false;
49+
});
50+
});
51+
4052
describe('ticketingAdmins', () => {
4153
it('should return the usersdojos where the user has a ticketingAdmin perm', () => {
4254
vm.usersDojos = [{ dojoId: 'd1', userPermissions: [{ name: 'banana' }] }, { dojoId: 'd2', userPermissions: [{ name: 'ticketing-admin' }] }];
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import vueUnitHelper from 'vue-unit-helper';
2+
import moment from 'moment';
3+
import DashboardHeaderComponent from '!!vue-loader?inject!@/dashboard/cd-dashboard-pending-volunteering';
4+
5+
describe('Dashboard pending volunteering component', () => {
6+
let sandbox;
7+
let vm;
8+
let MockDojosService;
9+
10+
beforeEach(() => {
11+
sandbox = sinon.sandbox.create();
12+
MockDojosService = {
13+
getDojos: sandbox.stub(),
14+
};
15+
vm = vueUnitHelper(DashboardHeaderComponent({
16+
'@/dojos/service': MockDojosService,
17+
}));
18+
});
19+
20+
afterEach(() => {
21+
sandbox.restore();
22+
});
23+
24+
25+
describe('computed', () => {
26+
describe('recentRequestsToJoin', () => {
27+
it('should filter any requests to join which are 30 days older', () => {
28+
const oldDay = moment().add(-31, 'days');
29+
const now = new Date();
30+
// ARRANGE
31+
vm.requestsToJoin = [{ id: 'rq1', timestamp: oldDay }, { id: 'rq2', timestamp: now }];
32+
33+
// ASSERT
34+
expect(vm.recentRequestsToJoin).to.eql([{ id: 'rq2', timestamp: now }]);
35+
});
36+
});
37+
describe('infoIsDisplayed', () => {
38+
it('should return true if there are valid requests to join and the user is new', () => {
39+
// ARRANGE
40+
vm.recentRequestsToJoin = [{ id: 'rq1' }];
41+
vm.userIsNew = true;
42+
43+
// ASSERT
44+
expect(vm.infoIsDisplayed).to.be.true;
45+
});
46+
it('should return false if the user has no leads', () => {
47+
// ARRANGE
48+
vm.recentRequestsToJoin = [];
49+
vm.userIsNew = true;
50+
51+
// ASSERT
52+
expect(vm.infoIsDisplayed).to.be.false;
53+
});
54+
it('should return false if the user is not new', () => {
55+
// ARRANGE
56+
vm.recentRequestsToJoin = [{ id: 'rq1' }];
57+
vm.userIsNew = false;
58+
59+
// ASSERT
60+
expect(vm.infoIsDisplayed).to.be.false;
61+
});
62+
});
63+
});
64+
});

yarn.lock

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,9 +1988,10 @@ cosmiconfig@^2.1.0, cosmiconfig@^2.1.1:
19881988
parse-json "^2.2.0"
19891989
require-from-string "^1.1.0"
19901990

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

19951996
crc32-stream@^2.0.0:
19961997
version "2.0.0"

0 commit comments

Comments
 (0)