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

Commit 901eebf

Browse files
committed
Add editing of events.
Move data persistence into a store which allows easier manipulation from the form.
1 parent 3d4d933 commit 901eebf

File tree

16 files changed

+1270
-564
lines changed

16 files changed

+1270
-564
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"ok": true,
3+
"user": {
4+
"id": "u1",
5+
"nick": "[email protected]",
6+
"email": "[email protected]",
7+
"name": "parent 1one",
8+
"firstName": "parent",
9+
"lastName": "1one",
10+
"roles": ["cdf-admin"],
11+
"phone": "+33612345678",
12+
"mailingList": 1,
13+
"termsConditionsAccepted": true,
14+
"profileId": "p1"
15+
},
16+
"login": {
17+
"id": "l1",
18+
"nick": "[email protected]",
19+
"user": "u1",
20+
"token": "t1",
21+
"active": true
22+
}
23+
}

cypress/integration/events/event-form_spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import page from '../../pages/event-form';
33
describe('Event form', () => {
44
beforeEach(() => {
55
cy.server();
6-
cy.route('/api/2.0/users/instance', 'fx:parentLoggedIn');
6+
cy.route('/api/2.0/users/instance', 'fx:cdfAdminLoggedIn');
77
cy.route('/api/2.0/dojos/b850b40e-1e10-4e3a-8a46-d076c94946c6', 'fx:dojo').as('dojo');
88
cy.route('/api/3.0/dojos/b850b40e-1e10-4e3a-8a46-d076c94946c6/events?pageSize=1&page=1&orderBy=startTime&direction=desc&related=sessions.tickets', 'fx:events').as('events')
99
});
1010

1111
it('should display the form', () => {
1212
cy.visit('/dashboard/dojos/b850b40e-1e10-4e3a-8a46-d076c94946c6/events/new');
13-
cy.wait('@events');
1413
cy.wait('@dojo');
14+
cy.wait('@events');
1515
cy.get(page.header).should('be.visible');
1616
cy.get(page.submitButton).should('be.visible');
1717
cy.get(page.submitButton).invoke('text').should('contain', 'Publish');

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"dependencies": {
2424
"@coderdojo/cd-common": "1.1.11",
2525
"bootstrap": "^3.4.1",
26-
"cp-translations": "1.0.133",
26+
"cp-translations": "1.0.135",
2727
"font-awesome": "^4.7.0",
2828
"handlebars": "^4.1.0",
2929
"js-cookie": "^2.1.4",

src/events/cd-event-form.vue

Lines changed: 147 additions & 169 deletions
Large diffs are not rendered by default.

src/events/event-store.js

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// import Vue from 'vue';
2+
import Vuex from 'vuex';
3+
import moment from 'moment';
4+
5+
const store = new Vuex.Store({
6+
strict: true,
7+
state: {
8+
eventDate: moment().format('YYYY-MM-DD'),
9+
startTime: moment.utc().add(2, 'hours').minute(0).format('HH:mm'),
10+
endTime: moment.utc().add(3, 'hours').minute(0).format('HH:mm'),
11+
event: {
12+
name: '',
13+
description: '',
14+
address: '',
15+
createdBy: '',
16+
city: {},
17+
country: {},
18+
dojoId: '',
19+
type: 'one-off',
20+
status: 'published',
21+
recurringType: 'weekly',
22+
public: true,
23+
useDojoAddress: false,
24+
ticketApproval: false,
25+
notifyOnApplicant: false,
26+
sendEmails: true,
27+
newForm: true,
28+
dates: [{
29+
startTime: moment.utc().add(2, 'hours').minute(0),
30+
endTime: moment.utc().add(3, 'hours').minute(0) }],
31+
sessions: [
32+
{
33+
name: 'Dojo',
34+
description: 'Dojo Session',
35+
tickets: [
36+
{ name: 'Youth', type: 'ninja', quantity: 20 },
37+
{ name: 'Mentor', type: 'mentor', quantity: 5 },
38+
],
39+
},
40+
],
41+
},
42+
},
43+
44+
/* eslint-disable no-param-reassign */
45+
mutations: {
46+
setEvent(state, event) {
47+
state.eventDate = moment.utc(event.startTime).format('YYYY-MM-DD');
48+
state.startTime = moment.utc(event.startTime).format('HH:mm');
49+
state.endTime = moment.utc(event.endTime).format('HH:mm');
50+
51+
// Don't want these props to be sent when submitting form.
52+
delete event.createdAt;
53+
delete event.createdBy;
54+
delete event.eventbriteId;
55+
delete event.eventbriteUrl;
56+
delete event.position;
57+
delete event.startTime;
58+
delete event.endTime;
59+
if (event.sessions) {
60+
event.sessions[0].tickets.forEach(t => delete t.approvedApplications);
61+
}
62+
63+
// default is to not send emails when editing
64+
state.event = { ...event, sendEmails: false };
65+
},
66+
67+
setEventName(state, name) {
68+
state.event.name = name;
69+
},
70+
71+
setCityFromObject(state, cityObject) {
72+
if (cityObject) {
73+
state.event.city = {
74+
nameWithHierarchy: cityObject.nameWithHierarchy || cityObject.toponymName,
75+
};
76+
}
77+
},
78+
79+
setAddress(state, address) {
80+
state.event.address = address;
81+
},
82+
83+
setDescription(state, description) {
84+
state.event.description = description;
85+
},
86+
87+
setCity(state, value) {
88+
state.event.city = { nameWithHierarchy: value };
89+
},
90+
91+
setCountry(state, value) {
92+
state.event.country = value;
93+
},
94+
95+
setDojoId(state, value) {
96+
state.event.dojoId = value;
97+
},
98+
99+
generateNextEventDates(state, { lastStartTime, lastEndTime }) {
100+
const startDate = moment.utc(lastStartTime);
101+
let newDate = startDate.add(7, 'days');
102+
const inPast = moment().diff(newDate, 'days') > 0;
103+
if (inPast) {
104+
const neededDay = startDate.day();
105+
newDate = (moment().isoWeekday() <= neededDay) ?
106+
moment().isoWeekday(neededDay) :
107+
moment().add(1, 'weeks').isoWeekday(neededDay);
108+
}
109+
110+
state.eventDate = newDate.format('YYYY-MM-DD');
111+
state.startTime = startDate.format('HH:mm');
112+
113+
const startTime = moment.utc(newDate);
114+
const startTimeElements = state.startTime.split(':');
115+
startTime.hours(startTimeElements[0]);
116+
startTime.minutes(startTimeElements[1]);
117+
state.event.dates[0].startTime = startTime;
118+
119+
state.endTime = moment.utc(lastEndTime).format('HH:mm');
120+
const endTime = moment.utc(newDate);
121+
const endTimeElements = state.endTime.split(':');
122+
endTime.hours(endTimeElements[0]);
123+
endTime.minutes(endTimeElements[1]);
124+
state.event.dates[0].endTime = endTime;
125+
},
126+
127+
setTicketQuantitiesFromEvent(state, event) {
128+
if (event.sessions && event.sessions.length > 0) {
129+
// If there are any sessions for the event just take the first one.
130+
// Any event that has more than one session was created using the old form
131+
// and it is unlikely to map to the new form structure
132+
const previousTickets = event.sessions[0].tickets;
133+
const prevYouthTickets = previousTickets.find(ticket => ticket.name === 'Youth');
134+
const prevMentorTickets = previousTickets.find(ticket => ticket.name === 'Mentor');
135+
136+
if (prevYouthTickets !== undefined) {
137+
const youthTickets = state.event.sessions[0].tickets
138+
.find(ticket => ticket.type === 'ninja');
139+
youthTickets.quantity = prevYouthTickets.quantity;
140+
}
141+
if (prevMentorTickets !== undefined) {
142+
const mentorTickets = state.event.sessions[0]
143+
.tickets.find(ticket => ticket.type === 'mentor');
144+
mentorTickets.quantity = prevMentorTickets.quantity;
145+
}
146+
}
147+
},
148+
149+
updateEventDate(state, value) {
150+
state.eventDate = value;
151+
state.event.dates[0].startTime = moment.utc(`${state.eventDate} ${state.startTime}`);
152+
state.event.dates[0].endTime = moment.utc(`${state.eventDate} ${state.endTime}`);
153+
},
154+
155+
updateStartTime(state, value) {
156+
state.startTime = value;
157+
state.event.dates[0].startTime = moment.utc(`${state.eventDate} ${state.startTime}`);
158+
},
159+
160+
updateEndTime(state, value) {
161+
state.endTime = value;
162+
state.event.dates[0].endTime = moment.utc(`${state.eventDate} ${state.endTime}`);
163+
},
164+
165+
updateTicketQuantity(state, { type, quantity }) {
166+
const tickets = state.event.sessions[0].tickets.find(ticket => ticket.type === type);
167+
tickets.quantity = quantity;
168+
},
169+
170+
setSendEmails(state, value) {
171+
state.event.sendEmails = value;
172+
},
173+
174+
setCreatedBy(state, value) {
175+
state.event.createdBy = value;
176+
},
177+
},
178+
getters: {
179+
event: state => state.event,
180+
eventName: state => state.event.name,
181+
address: state => state.event.address,
182+
city: state => state.event.city.nameWithHierarchy,
183+
description: state => state.event.description,
184+
eventDate: state => state.eventDate,
185+
startTime: state => state.startTime,
186+
endTime: state => state.endTime,
187+
sendEmails: state => state.event.sendEmails,
188+
// eslint-disable-next-line no-unused-vars
189+
ticketQuantity: state => (type) => {
190+
const tickets = state.event.sessions[0].tickets.find(ticket => ticket.type === type);
191+
return tickets.quantity;
192+
},
193+
},
194+
/* eslint-enable no-param-reassign */
195+
});
196+
197+
export default store;

src/events/form/form-tickets.vue

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,60 @@
11
<template>
22
<div class="row row-no-gutters cd-form-ticket">
33
<div class="col-sm-2">
4-
<label class="cd-form-ticket__label" for="quantity">{{ $t(label) }}</label>
4+
<label class="cd-form-ticket__label" for="ticketQuantity">{{ $t(label) }}</label>
55
</div>
66
<div class="col-sm-2">
7-
<input type="number" class="form-control" v-model="quantity" name="quantity" />
7+
<input type="number" class="form-control" v-model="ticketQuantity" name="ticketQuantity" />
88
</div>
99
</div>
1010
</template>
1111
<script>
12-
export default {
13-
$_veeValidate: {
14-
name() {
15-
return this.label;
16-
},
17-
value() {
18-
return this.quantity;
19-
},
20-
},
21-
name: 'form-ticket',
22-
props: [
23-
'label',
24-
'default-quantity',
25-
'type',
26-
],
27-
data() {
28-
return {
29-
quantity: null,
30-
};
31-
},
12+
// import { mutations, getters } from '@/events/event-store';
13+
import EventStore from '@/events/event-store';
3214
33-
methods: {
34-
createTicket() {
15+
export default {
16+
$_veeValidate: {
17+
name() {
18+
return this.label;
19+
},
20+
value() {
21+
return this.ticketQuantity;
22+
},
23+
},
24+
name: 'form-ticket',
25+
props: [
26+
'label',
27+
'default-quantity',
28+
'type',
29+
],
30+
data() {
3531
return {
36-
name: this.label,
37-
type: this.type,
38-
quantity: this.quantity,
32+
quantity: null,
3933
};
4034
},
41-
setQuantity(q) {
42-
this.quantity = q;
43-
},
44-
},
45-
created() {
46-
this.quantity = this.defaultQuantity;
47-
},
48-
};
4935
36+
methods: {
37+
createTicket() {
38+
return {
39+
name: this.label,
40+
type: this.type,
41+
quantity: this.quantity,
42+
};
43+
},
44+
},
45+
computed: {
46+
ticketQuantity: {
47+
get() {
48+
return EventStore.getters.ticketQuantity(this.type);
49+
},
50+
set(value) {
51+
EventStore.commit('updateTicketQuantity', { type: this.type, quantity: value });
52+
},
53+
},
54+
},
55+
};
5056
</script>
57+
5158
<style scoped lang="less">
5259
@import "../../common/variables";
5360

src/events/service.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ const EventsService = {
3838
create(event) {
3939
return Vue.http.post(`${Vue.config.apiServer}/api/3.0/events`, event);
4040
},
41+
load(eventId, options) {
42+
return Vue.http.get(`${Vue.config.apiServer}/api/3.0/events/${eventId}`, options);
43+
},
44+
update(event) {
45+
return Vue.http.put(`${Vue.config.apiServer}/api/3.0/events/${event.id}`, event);
46+
},
4147
},
4248
};
4349

src/router/index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import ManageRequestToJoin from '@/dojos/manage-request-to-join';
1919
import loggedInNavGuard from './loggedInNavGuard';
2020
import loggedInCDFNavGuard from './loggedInCDFNavGuard';
2121
import orderExistsNavGuard from './orderExistsNavGuard';
22+
import ticketingAdminNavGuard from './ticketingAdminNavGuard';
2223

2324

2425
Vue.use(Router);
@@ -89,15 +90,13 @@ const router = new Router({
8990
path: '/dashboard/dojos/:dojoId/events/new',
9091
name: 'NewEventForm',
9192
component: EventForm,
92-
// TODO: restrict access to ticketing-admin ?
93-
beforeEnter: loggedInNavGuard,
93+
beforeEnter: MultiGuard([loggedInNavGuard, ticketingAdminNavGuard]),
9494
},
9595
{
9696
path: '/dashboard/dojos/:dojoId/events/:eventId/edit',
9797
name: 'EditEventForm',
9898
component: EventForm,
99-
// TODO: restrict access to ticketing-admin ?
100-
beforeEnter: loggedInNavGuard,
99+
beforeEnter: MultiGuard([loggedInNavGuard, ticketingAdminNavGuard]),
101100
},
102101
{
103102
path: '/dashboard/dojos/:dojoId/join-requests/:requestId/status/:status',

0 commit comments

Comments
 (0)