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

Commit adb5a2f

Browse files
authored
Requests to join rework (#249)
* Requests to join rework This PR will contain multiple rework, this first one is to use the new endpoint to create a join request * Fix unit test Add test for dashboard-header * Split existing dashboard elements To facilitate adding more Add integration tests for the new title Ensure that the new title doesn't create a flash for existing users (mutually exclusive display) * Upgrade cp-translations
1 parent c51cd3b commit adb5a2f

17 files changed

+378
-136
lines changed

cypress/integration/dashboard/events_spec.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('Homepage events', () => {
3030
cy.get(eventPage.events).last().find('h4').should('have.text', '"event2" is available to book');
3131
cy.get(eventPage.dojoName).first().should('have.text', 'dojo1');
3232
cy.get(eventPage.dojoName).last().should('have.text', 'dojo2');
33+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
3334
});
3435
it('should show fully booked event', () => {
3536
cy.route('/api/2.0/users/instance', 'fx:parentLoggedIn').as('loggedIn');
@@ -72,6 +73,7 @@ describe('Homepage events', () => {
7273
cy.wait('@orders1');
7374
cy.get(eventPage.events).first().find('h4').should('have.text', '"event1" is available to book');
7475
cy.get(eventPage.bookButton).should('be.visible');
76+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
7577
});
7678

7779
it('should show number of tickets booked if order exists', () => {
@@ -145,6 +147,7 @@ describe('Homepage events', () => {
145147
cy.wait('@orders1');
146148
cy.get(eventPage.events).first().find('h4').should('have.text', '"event1" is available to book');
147149
cy.get(eventPage.bookButton).should('be.visible');
150+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
148151
});
149152

150153
it('should show number of mentor tickets booked if order exists', () => {
@@ -170,7 +173,6 @@ describe('Homepage events', () => {
170173
cy.get(eventPage.bookedTickets).should('have.text', '1 "Mentor" tickets booked');
171174
});
172175
});
173-
174176
describe('as a ticketing-admin', () => {
175177
const after2Weeks = (moment().subtract(2, 'weeks')).format();
176178
const after1Year = (moment().subtract(1, 'years')).format();
@@ -198,6 +200,7 @@ describe('Homepage events', () => {
198200
cy.get(eventPage.bookButton).should('not.be.visible');
199201
cy.get(eventPage.manageEventLink).last().find('span').first().should('have.text', '2/42 Youth booked');
200202
cy.get(eventPage.manageEventLink).last().find('span').last().should('have.text', '1/42 Mentor booked');
203+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
201204
});
202205
it('should show the eventbrite tickets as always bookable', () => {
203206
cy.route('/api/2.0/users/instance', 'fx:parentLoggedIn').as('loggedIn');
@@ -233,8 +236,7 @@ describe('Homepage events', () => {
233236
cy.wait('@oldEvents');
234237
cy.wait('@dojo');
235238
cy.get(eventPage.noEventMessage).should('be.visible');
236-
cy.get(eventPage.noEventMessage).should('have.text',
237-
'\n We see you don\'t use Zen events.\n If you\'re using Eventbrite for your Dojo you can make it easier for attendees and volunteers to find you by using our one-click Eventbrite plugin (it\'s really easy!)');
239+
cy.get(eventPage.noEventMessage).should('have.text', '\n We see you don\'t use Zen events.\n If you\'re using Eventbrite for your Dojo you can make it easier for attendees and volunteers to find you by using our one-click Eventbrite plugin (it\'s really easy!)');
238240
cy.get(eventPage.fallbackCTAs).should('not.be.visible');
239241
});
240242
it('should show a message about zen events if a dojo is new (> 2 weeks)', () => {
@@ -247,7 +249,7 @@ describe('Homepage events', () => {
247249
cy.wait('@oldEvents');
248250
cy.wait('@dojo');
249251
cy.get(eventPage.noEventMessage).should('be.visible');
250-
cy.get(eventPage.noEventMessage).should('have.text', '\n Create your first event so attendees can book and you can easily see who\'s attending.\n It\'s simple and only takes 2 minutes!\n ');
252+
cy.get(eventPage.noEventMessage).should('have.text', '\n Create your first event so attendees can book and you can easily see who\'s attending.\n It\'s simple and only takes 2 minutes!\n ');
251253
cy.get(eventPage.noEventMessage).find('a').should('have.attr', 'href', '/dashboard/dojo/d1/event-form');
252254
cy.get(eventPage.newDojoMessage).should('not.be.visible');
253255
cy.get(eventPage.fallbackCTAs).should('not.be.visible');
@@ -308,16 +310,21 @@ describe('Homepage events', () => {
308310
cy.get(eventPage.noEventMessage).should('have.text', 'Create your next event so attendees can book in!');
309311
cy.get(eventPage.noEventMessage).find('a').should('have.attr', 'href', '/dashboard/my-dojos');
310312
cy.get(eventPage.newDojoMessage).find('a').should('have.attr', 'href', 'https://docs.google.com/forms/d/e/1FAIpQLSfkYe44Upu9ezRd7FUytxnvgmZuDxbQTPAj1BcdiqxFoBUslA/viewform?usp=pp_url&entry.1799182697=dojo1');
311-
cy.get(eventPage.newDojoMessage).find('a').should('have.text', 'We always ask new Dojos to do a 2 minutes survey. You don\'t have to but it helps the whole community!\n ');
313+
cy.get(eventPage.newDojoMessage).find('a').should('have.text', 'We always ask new Dojos to do a 2 minutes survey. You don\'t have to but it helps the whole community!\n ');
312314
cy.get(eventPage.fallbackCTAs).should('not.be.visible');
315+
cy.get(eventPage.genericHeader).invoke('text').should('match', /(here's what's most important...)/);
313316
});
314317
});
315318
describe('as a normal user without event that is not ticketing-admin', () => {
316319
it('should display two buttons as cta', () => {
317320
cy.route('/api/2.0/users/instance', 'fx:parentLoggedIn').as('loggedIn');
318321
cy.route('POST', '/api/2.0/dojos/users', []).as('userDojos');
319-
cy.route('/api/2.0/dojos/d1', { id: 'd1', created: '2015-08-26T11:46:14.308Z' }).as('dojo');
322+
cy.route('/api/3.0/leads?userId=u1&deleted=0', []).as('leads');
320323
cy.visit('/home');
324+
cy.wait('@userDojos');
325+
cy.wait('@leads');
326+
cy.get(eventPage.defaultHeader).should('be.visible');
327+
cy.get(eventPage.defaultHeader).invoke('text').should('match', /(once you join or start a Dojo this page will have useful information about your Dojos)/);
321328
cy.get(eventPage.fallbackCTAs).should('be.visible');
322329
cy.get(eventPage.fallbackCTAs).should('have.length', 2);
323330
});

cypress/pages/events.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
export default {
2+
defaultHeader: '.cd-dashboard-header--volunteer',
3+
genericHeader: '.cd-dashboard-header--generic',
24
noEventMessage: '.cd-dashboard-events__hint',
3-
newDojoMessage: '.cd-dashboard-events__poll',
4-
fallbackCTAs: '.cd-dashboard-events__cta-link',
5+
newDojoMessage: '.cd-dashboard__poll',
6+
fallbackCTAs: '.cd-dashboard__cta-link',
57
events: '.cd-dashboard-upcoming-event__main',
68
bookButton: '.cd-dashboard-upcoming-event__book',
79
bookedTickets: '.cd-dashboard-upcoming-event__booked',

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.124",
25+
"cp-translations": "1.0.125",
2626
"font-awesome": "^4.7.0",
2727
"handlebars": "^4.1.0",
2828
"js-cookie": "^2.1.4",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<template>
2+
<div class="cd-dashboard__poll" >
3+
<a :href="`https://docs.google.com/forms/d/e/1FAIpQLSfkYe44Upu9ezRd7FUytxnvgmZuDxbQTPAj1BcdiqxFoBUslA/viewform?usp=pp_url&entry.1799182697=${dojoName}`" v-ga-track-exit-nav>
4+
<i class="fa fa-info-circle"></i>{{ $t('We always ask new Dojos to do a 2 minutes survey.') }} {{ $t('You don\'t have to but it helps the whole community!') }}
5+
</a>
6+
</div>
7+
</template>
8+
9+
<script>
10+
export default {
11+
name: 'cd-dashboard-admin-survey',
12+
props: ['dojoName'],
13+
};
14+
</script>
15+
16+
<style scoped lang="less">
17+
@import "~@coderdojo/cd-common/common/_colors";
18+
@import "../common/variables";
19+
20+
.cd-dashboard{
21+
&__poll {
22+
text-align: center;
23+
margin-bottom: @margin;
24+
a {
25+
color: @cd-white;
26+
text-decoration: underline;
27+
i {
28+
margin-right: 8px;
29+
}
30+
}
31+
}
32+
}
33+
</style>

src/dashboard/cd-dashboard-cta.vue

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<template>
2+
<div class="cd-dashboard__cta" >
3+
<div v-if="hasEvents" class="cd-dashboard_cta--returning">
4+
<router-link class="cd-dashboard__cta-link" :to="{ name: 'MyTickets' }" v-ga-track-click="'your_events'">{{ $t('Your events') }}</router-link>
5+
</div>
6+
<div v-if="!hasEvents && !isTicketingAdmin" class="cd-dashboard__cta--start">
7+
<router-link class="cd-dashboard__cta-link" :to="{ name: 'FindDojo' }" v-ga-track-click="'find_dojo'">{{ $t('Find a Dojo to attend') }}</router-link>
8+
<router-link class="cd-dashboard__cta-link" to="start-dojo" v-ga-track-click="'start_dojo'">{{ $t('Start a Dojo') }}</router-link>
9+
</div>
10+
</div>
11+
</template>
12+
13+
<script>
14+
export default {
15+
name: 'cd-dashboard-cta',
16+
props: ['hasEvents', 'isTicketingAdmin'],
17+
};
18+
</script>
19+
20+
<style scoped lang="less">
21+
@import "~@coderdojo/cd-common/common/_colors";
22+
@import "../common/variables";
23+
24+
.cd-dashboard{
25+
&__cta {
26+
.cta;
27+
28+
&-link {
29+
.button-link-sm;
30+
color: @cd-white;
31+
}
32+
}
33+
}
34+
</style>

src/dashboard/cd-dashboard-events.vue

Lines changed: 18 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,17 @@
22
<div class="row">
33
<div class="cd-dashboard-events">
44
<div class="cd-dashboard-events__content">
5-
<h1 class="cd-dashboard-events__header">{{ $t('Hey {name}, here\'s what\'s most important...', { name: loggedInUser.firstName }) }}</h1>
6-
<div class="cd-dashboard-events__list" v-if="events.length > 0">
5+
<dashboard-header :has-dojos="hasDojos" :has-requests="hasRequests" :first-name="loggedInUser.firstName" :user-id="loggedInUser.id"></dashboard-header>
6+
<div class="cd-dashboard-events__list" v-if="events.length > 0">
77
<upcoming-event v-for="event in events" :key="event.id" :event="event" :dojo="dojos[event.dojoId]"></upcoming-event>
88
</div>
99
<div v-if="hasDojos && ticketingAdmins.length > 0 && events.length <= 0">
10-
<p v-if="!usesTicketing && maxDojoAge < 1" class="cd-dashboard-events__hint">
11-
<router-link :to="getTicketingAdminUrl" v-ga-track-click="'create_first_event'">
12-
{{ $t('Create your first event so attendees can book and you can easily see who\'s attending.') }}
13-
{{ $t('It\'s simple and only takes 2 minutes!') }}
14-
</router-link>
15-
</p>
16-
<p v-else-if="!usesTicketing && maxDojoAge >= 1" class="cd-dashboard-events__hint">
17-
{{ $t('We see you don\'t use Zen events.') }}
18-
<span v-html="$t('If you\'re using Eventbrite for your Dojo you can make it easier for attendees and volunteers to find you by using <a href=\'https://coderdojo.com/2017/07/19/launching-eventbrite-integration-on-the-coderdojo-community-platform/\'>our one-click Eventbrite plugin</a> (it\'s really easy!)')"></span></p>
19-
<p v-else-if="usesTicketing" class="cd-dashboard-events__hint">
20-
<router-link :to="getTicketingAdminUrl" v-ga-track-click="'create_event'">{{ $t('Create your next event so attendees can book in!') }}</router-link>
21-
</p>
10+
<dashboard-create-event :dojos="dojos" :old-events="oldEvents" :ticketing-admins="ticketingAdmins" > </dashboard-create-event>
2211
</div>
23-
<div v-if="usersDojos && dojoAdmins.length === 1">
24-
<div v-if="hasDojos && dojoAge(firstDojo, 'weeks') < 2" class="cd-dashboard-events__poll" >
25-
<a :href="`https://docs.google.com/forms/d/e/1FAIpQLSfkYe44Upu9ezRd7FUytxnvgmZuDxbQTPAj1BcdiqxFoBUslA/viewform?usp=pp_url&entry.1799182697=${firstDojo.name}`" v-ga-track-exit-nav>
26-
<i class="fa fa-info-circle"></i>{{ $t('We always ask new Dojos to do a 2 minutes survey.') }} {{ $t('You don\'t have to but it helps the whole community!') }}
27-
</a>
28-
</div>
29-
</div>
30-
<div v-if="events.length > 0 && events[0].id">
31-
<div class="cd-dashboard-events__cta">
32-
<router-link class="cd-dashboard-events__cta-link" :to="{ name: 'MyTickets' }" v-ga-track-click="'your_events'">{{ $t('Your events') }}</router-link>
33-
</div>
34-
</div>
35-
<div v-if="events.length <= 0 && ticketingAdmins.length <= 0" class="cd-dashboard-events__cta">
36-
<router-link class="cd-dashboard-events__cta-link" :to="{ name: 'FindDojo' }" v-ga-track-click="'find_dojo'">{{ $t('Find a Dojo to attend') }}</router-link>
37-
<router-link class="cd-dashboard-events__cta-link" to="start-dojo" v-ga-track-click="'start_dojo'">{{ $t('Start a Dojo') }}</router-link>
12+
<div v-if="usersDojos && dojoAdmins.length === 1 && hasDojos && dojoAge(firstDojo, 'weeks') < 2">
13+
<dashboard-admin-survey :dojo-name="firstDojo.name"></dashboard-admin-survey>
3814
</div>
15+
<dashboard-cta :has-events="events.length > 0 && events[0].id" :is-ticketing-admin="ticketingAdmins.length > 0"></dashboard-cta>
3916
</div>
4017
</div>
4118
</div>
@@ -47,27 +24,32 @@
4724
import DojosService from '@/dojos/service';
4825
import EventService from '@/events/service';
4926
import EventUtils from '@/events/util';
27+
import DashboardHeader from '@/dashboard/cd-dashboard-header';
28+
import DashboardCreateEvent from '@/dashboard/events/cd-dashboard-create-event';
29+
import DashboardAdminSurvey from '@/dashboard/cd-dashboard-admin-survey';
30+
import DashboardCta from '@/dashboard/cd-dashboard-cta';
5031
import UpcomingEvent from './events/cd-dashboard-upcoming-event';
5132
5233
export default {
5334
name: 'cd-dashboard-events',
5435
components: {
5536
UpcomingEvent,
37+
DashboardHeader,
38+
DashboardCreateEvent,
39+
DashboardAdminSurvey,
40+
DashboardCta,
5641
},
5742
data() {
5843
return {
5944
events: [{}, {}],
6045
oldEvents: null,
6146
usersDojos: [],
6247
dojos: {},
48+
leads: [],
6349
};
6450
},
6551
computed: {
66-
...mapGetters(['loggedInUser']),
67-
getTicketingAdminUrl() {
68-
return this.ticketingAdmins.length === 1 ?
69-
`dashboard/dojo/${this.ticketingAdmins[0].dojoId}/event-form` : 'dashboard/my-dojos';
70-
},
52+
...mapGetters(['loggedInUser', 'hasRequests']),
7153
usersDojosMap() {
7254
return this.usersDojos.reduce((map, usersDojo) => {
7355
const dojoId = usersDojo.dojoId;
@@ -81,20 +63,14 @@
8163
return this.usersDojos.filter(usersDojo =>
8264
usersDojo.userPermissions && usersDojo.userPermissions.find(perm => perm.name === 'ticketing-admin'));
8365
},
84-
usesTicketing() {
85-
return this.oldEvents && this.oldEvents.length > 0;
86-
},
8766
dojoAdmins() {
8867
return this.usersDojos.filter(usersDojo =>
8968
usersDojo.userPermissions && usersDojo.userPermissions.find(perm => perm.name === 'dojo-admin'));
9069
},
9170
hasDojos() {
9271
return Object.keys(this.dojos).length > 0;
9372
},
94-
maxDojoAge() {
95-
return this.hasDojos ? Math.max(this.ticketingAdmins.map(ud => ud.dojoId)
96-
.map(dojoId => this.dojoAge(this.dojos[dojoId], 'years'))) : 0;
97-
},
73+
9874
firstDojo() {
9975
return this.hasDojos ? this.dojos[this.dojoAdmins[0].dojoId] : {};
10076
},
@@ -180,49 +156,11 @@
180156
max-width: 824px;
181157
margin: 0 auto;
182158
}
183-
&__poll {
184-
text-align: center;
185-
margin-bottom: @margin;
186-
a {
187-
color: @cd-white;
188-
text-decoration: underline;
189-
i {
190-
margin-right: 8px;
191-
}
192-
}
193-
}
194-
&__hint{
195-
text-align: center;
196-
margin-bottom: @margin;
197-
.subtitle;
198-
color: @cd-white;
199-
text-align: center;
200-
line-break: pre-wrap;
201-
a {
202-
.subtitle;
203-
color: @cd-white;
204-
text-decoration: underline;
205-
}
206-
}
207-
208-
&__header {
209-
color: @cd-white;
210-
margin: 0 0 48px 0;
211-
}
212-
159+
213160
&__list-filler {
214161
background: @cd-very-light-grey;
215162
height: 90px;
216163
margin: @margin*2 0;
217164
}
218-
219-
&__cta {
220-
.cta;
221-
222-
&-link {
223-
.button-link-sm;
224-
color: @cd-white;
225-
}
226-
}
227165
}
228166
</style>

src/dashboard/cd-dashboard-header.vue

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<div class="cd-dashboard-header">
3+
<h1 class="cd-dashboard-header--generic" v-if="hasDojos || hasLeads || hasRequests">{{ $t('Hey {name}, here\'s what\'s most important...', { name: firstName }) }}</h1>
4+
<h1 class="cd-dashboard-header--volunteer" v-if="!hasDojos && !hasLeads && !hasRequests && ready">{{ $t('Hey {name}, once you join or start a Dojo this page will have useful information about your Dojos', { name: firstName }) }}</h1>
5+
</div>
6+
</template>
7+
8+
<script>
9+
import DojosService from '@/dojos/service';
10+
11+
export default {
12+
name: 'cd-dashboard-header',
13+
props: ['hasDojos', 'hasRequests', 'firstName', 'userId'],
14+
data() {
15+
return {
16+
leads: [],
17+
ready: false,
18+
};
19+
},
20+
computed: {
21+
hasLeads() {
22+
return this.leads.length > 0;
23+
},
24+
},
25+
methods: {
26+
async loadLeads() {
27+
const res = await DojosService.lead.list(this.userId);
28+
this.leads = res.body;
29+
},
30+
},
31+
created() {
32+
// Don't await, so we don't block rendering.
33+
// It's unecessary here as 90% of users will not fall under this scenario
34+
this.loadLeads().then(() => {
35+
this.ready = true;
36+
});
37+
},
38+
};
39+
</script>
40+
41+
<style scoped lang="less">
42+
@import "~@coderdojo/cd-common/common/_colors";
43+
44+
.cd-dashboard-header {
45+
color: @cd-white;
46+
text-align: center;
47+
}
48+
</style>

0 commit comments

Comments
 (0)