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

Commit 441079f

Browse files
committed
Handle issues with city data causing errors
Some events ended up with data like: `{"nameWithHierarchy":"City Name","$$hashKey":"object:1234"}` in the city field. The hashKey prop appears to be coming from angular. This was breaking editing the events as the prop key was not allowed but was submitted with the form. We now pull out the `nameWithHierarchy` prop if it is there and strip the rest. We also handle the case where the dojo city value is null or empty when creating your first event.
1 parent 2a931d4 commit 441079f

File tree

4 files changed

+117
-38
lines changed

4 files changed

+117
-38
lines changed

src/events/cd-event-form.vue

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
<i class="fa fa-pencil pointer" @click="customAddressFormIsVisible = true" v-show="!customAddressFormIsVisible"></i>
2222
<i class="fa fa-times pointer" @click="customAddressFormIsVisible = false" v-show="customAddressFormIsVisible"></i>
2323
<div v-if="customAddressFormIsVisible">
24-
<input type="text" name="city" v-model="eventCity" class="form-control">
25-
<textarea name="address" v-model="eventAddress" rows="3" class="form-control"></textarea>
24+
<label for="city">City</label>
25+
<input id="city" type="text" name="city" v-model="eventCity" class="form-control">
26+
<label for="address">Address</label>
27+
<textarea id="address" name="address" v-model="eventAddress" rows="3" class="form-control"></textarea>
2628
</div>
2729
</div>
2830

@@ -170,7 +172,7 @@
170172
methods: {
171173
async initializeStore() {
172174
if (this.latestEvent) {
173-
EventStore.commit('setCityFromObject', this.latestEvent.city);
175+
EventStore.commit('setCityFromEventObject', this.latestEvent.city);
174176
EventStore.commit('setAddress', this.latestEvent.address);
175177
EventStore.commit('setDescription', this.latestEvent.description);
176178
EventStore.commit('generateNextEventDates',
@@ -181,7 +183,7 @@
181183
} else {
182184
EventStore.commit('setDescription', this.dojo.notes);
183185
EventStore.commit('setAddress', this.dojo.address1);
184-
EventStore.commit('setCity', this.dojo.city.name);
186+
EventStore.commit('setCity', this.dojo);
185187
}
186188
EventStore.commit('setCountry', this.dojo.country);
187189
EventStore.commit('setDojoId', this.dojo.id);
@@ -236,10 +238,10 @@
236238
},
237239
eventCity: {
238240
get() {
239-
return EventStore.getters.city;
241+
return EventStore.getters.city || '';
240242
},
241243
set(value) {
242-
EventStore.commit('setCity', value);
244+
EventStore.commit('setCity', { city: { nameWithHierarchy: value } });
243245
},
244246
},
245247
eventDescription: {

src/events/event-store.js

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@ const store = new Vuex.Store({
55
strict: true,
66
state: {
77
eventDate: moment().format('YYYY-MM-DD'),
8-
startTime: moment.utc().add(2, 'hours').minute(0).format('HH:mm'),
9-
endTime: moment.utc().add(3, 'hours').minute(0).format('HH:mm'),
8+
startTime: moment
9+
.utc()
10+
.add(2, 'hours')
11+
.minute(0)
12+
.format('HH:mm'),
13+
endTime: moment
14+
.utc()
15+
.add(3, 'hours')
16+
.minute(0)
17+
.format('HH:mm'),
1018
event: {
1119
name: '',
1220
description: '',
@@ -24,9 +32,18 @@ const store = new Vuex.Store({
2432
notifyOnApplicant: false,
2533
sendEmails: true,
2634
newForm: true,
27-
dates: [{
28-
startTime: moment.utc().add(2, 'hours').minute(0),
29-
endTime: moment.utc().add(3, 'hours').minute(0) }],
35+
dates: [
36+
{
37+
startTime: moment
38+
.utc()
39+
.add(2, 'hours')
40+
.minute(0),
41+
endTime: moment
42+
.utc()
43+
.add(3, 'hours')
44+
.minute(0),
45+
},
46+
],
3047
sessions: [
3148
{
3249
name: 'Dojo',
@@ -55,19 +72,28 @@ const store = new Vuex.Store({
5572
delete event.position;
5673
delete event.startTime;
5774
delete event.endTime;
75+
76+
// This is a workaround for '$$hashKey': 'value' in the json objects of some events.
77+
// This is something from angular tracking its props being persisted somehow.
78+
let city = {};
79+
if (event.city.nameWithHierarchy) {
80+
city = { nameWithHierarchy: event.city.nameWithHierarchy };
81+
delete event.city;
82+
}
83+
5884
if (event.sessions) {
5985
event.sessions[0].tickets.forEach(t => delete t.approvedApplications);
6086
}
6187

6288
// default is to not send emails when editing
63-
state.event = { ...event, sendEmails: false };
89+
state.event = { ...event, city, sendEmails: false };
6490
},
6591

6692
setEventName(state, name) {
6793
state.event.name = name;
6894
},
6995

70-
setCityFromObject(state, cityObject) {
96+
setCityFromEventObject(state, cityObject) {
7197
if (cityObject) {
7298
state.event.city = {
7399
nameWithHierarchy: cityObject.nameWithHierarchy || cityObject.toponymName,
@@ -83,8 +109,12 @@ const store = new Vuex.Store({
83109
state.event.description = description;
84110
},
85111

86-
setCity(state, value) {
87-
state.event.city = { nameWithHierarchy: value };
112+
setCity(state, dojo) {
113+
if (dojo.city === null || dojo.city.nameWithHierarchy === undefined) {
114+
state.event.city = {};
115+
return;
116+
}
117+
state.event.city = { nameWithHierarchy: dojo.city.nameWithHierarchy };
88118
},
89119

90120
setCountry(state, value) {
@@ -101,9 +131,12 @@ const store = new Vuex.Store({
101131
const inPast = moment().diff(newDate, 'days') > 0;
102132
if (inPast) {
103133
const neededDay = startDate.day();
104-
newDate = (moment().isoWeekday() <= neededDay) ?
105-
moment().isoWeekday(neededDay) :
106-
moment().add(1, 'weeks').isoWeekday(neededDay);
134+
newDate =
135+
moment().isoWeekday() <= neededDay
136+
? moment().isoWeekday(neededDay)
137+
: moment()
138+
.add(1, 'weeks')
139+
.isoWeekday(neededDay);
107140
}
108141

109142
state.eventDate = newDate.format('YYYY-MM-DD');
@@ -129,40 +162,56 @@ const store = new Vuex.Store({
129162
// Any event that has more than one session was created using the old form
130163
// and it is unlikely to map to the new form structure
131164
const previousTickets = event.sessions[0].tickets;
132-
const prevYouthTickets = previousTickets.find(ticket => ticket.name === 'Youth');
133-
const prevMentorTickets = previousTickets.find(ticket => ticket.name === 'Mentor');
165+
const prevYouthTickets = previousTickets.find(
166+
ticket => ticket.name === 'Youth',
167+
);
168+
const prevMentorTickets = previousTickets.find(
169+
ticket => ticket.name === 'Mentor',
170+
);
134171

135172
if (prevYouthTickets !== undefined) {
136-
const youthTickets = state.event.sessions[0].tickets
137-
.find(ticket => ticket.type === 'ninja');
173+
const youthTickets = state.event.sessions[0].tickets.find(
174+
ticket => ticket.type === 'ninja',
175+
);
138176
youthTickets.quantity = prevYouthTickets.quantity;
139177
}
140178
if (prevMentorTickets !== undefined) {
141-
const mentorTickets = state.event.sessions[0]
142-
.tickets.find(ticket => ticket.type === 'mentor');
179+
const mentorTickets = state.event.sessions[0].tickets.find(
180+
ticket => ticket.type === 'mentor',
181+
);
143182
mentorTickets.quantity = prevMentorTickets.quantity;
144183
}
145184
}
146185
},
147186

148187
updateEventDate(state, value) {
149188
state.eventDate = value;
150-
state.event.dates[0].startTime = moment.utc(`${state.eventDate} ${state.startTime}`);
151-
state.event.dates[0].endTime = moment.utc(`${state.eventDate} ${state.endTime}`);
189+
state.event.dates[0].startTime = moment.utc(
190+
`${state.eventDate} ${state.startTime}`,
191+
);
192+
state.event.dates[0].endTime = moment.utc(
193+
`${state.eventDate} ${state.endTime}`,
194+
);
152195
},
153196

154197
updateStartTime(state, value) {
155198
state.startTime = value;
156-
state.event.dates[0].startTime = moment.utc(`${state.eventDate} ${state.startTime}`);
199+
state.event.dates[0].startTime = moment.utc(
200+
`${state.eventDate} ${state.startTime}`,
201+
);
157202
},
158203

159204
updateEndTime(state, value) {
160205
state.endTime = value;
161-
state.event.dates[0].endTime = moment.utc(`${state.eventDate} ${state.endTime}`);
206+
state.event.dates[0].endTime = moment.utc(
207+
`${state.eventDate} ${state.endTime}`,
208+
);
162209
},
163210

164211
updateTicketQuantity(state, { type, quantity }) {
165-
const tickets = state.event.sessions[0].tickets.find(ticket => ticket.type === type);
212+
const tickets = state.event.sessions[0].tickets.find(
213+
ticket => ticket.type === type,
214+
);
166215
tickets.quantity = quantity;
167216
},
168217

@@ -186,7 +235,9 @@ const store = new Vuex.Store({
186235
sendEmails: state => state.event.sendEmails,
187236
// eslint-disable-next-line no-unused-vars
188237
ticketQuantity: state => (type) => {
189-
const tickets = state.event.sessions[0].tickets.find(ticket => ticket.type === type);
238+
const tickets = state.event.sessions[0].tickets.find(
239+
ticket => ticket.type === type,
240+
);
190241
return tickets.quantity;
191242
},
192243
},

test/unit/specs/events/cd-event-form.spec.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,22 +244,24 @@ describe('Event Form component', () => {
244244
it('sets event values in store from dojo', async () => {
245245
const vm = vueUnitHelper(EventFormWithMocks);
246246
vm.loggedInUser = { id: 'U1' };
247-
vm.dojo = {
247+
const dojoObject = {
248248
id: 'd1',
249249
notes: 'The dojo description notes',
250250
address1: 'Address 1 from Dojo',
251251
city: { name: 'Dojo City name' },
252252
country: { alpha2: 'GB' },
253253
};
254254

255+
vm.dojo = dojoObject;
256+
255257
await vm.initializeStore();
256258
expect(MockEventStore.commit).to.have.callCount(6);
257259
expect(MockEventStore.commit).to.have.been
258260
.calledWith('setDescription', 'The dojo description notes');
259261
expect(MockEventStore.commit).to.have.been
260262
.calledWith('setAddress', 'Address 1 from Dojo');
261263
expect(MockEventStore.commit).to.have.been
262-
.calledWith('setCity', 'Dojo City name');
264+
.calledWith('setCity', dojoObject);
263265
});
264266
});
265267
context('when latestEvent is present', () => {
@@ -281,7 +283,7 @@ describe('Event Form component', () => {
281283
await vm.initializeStore();
282284
expect(MockEventStore.commit).to.have.callCount(8);
283285
expect(MockEventStore.commit).to.have.been
284-
.calledWith('setCityFromObject', { nameWithHierarchy: 'City' });
286+
.calledWith('setCityFromEventObject', { nameWithHierarchy: 'City' });
285287
expect(MockEventStore.commit).to.have.been
286288
.calledWith('setAddress', 'Address from event');
287289
expect(MockEventStore.commit).to.have.been

test/unit/specs/events/event-store.spec.js

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,27 @@ describe('Event Store', () => {
5353
describe('mutations', () => {
5454
describe('mutations.setEvent', () => {
5555
it('sets event values correctly with default sendEmails', () => {
56-
const event = { id: 1, name: 'Event name' };
56+
const event = { id: 1, name: 'Event name', city: {} };
5757
EventStore.commit('setEvent', event);
5858

5959
expect(EventStore.state.event).to.deep.equal({
6060
id: 1,
6161
name: 'Event name',
6262
sendEmails: false,
63+
city: {},
64+
});
65+
});
66+
67+
68+
it('removes $$hashKey entries in city', () => {
69+
const event = { id: 1, name: 'Event name', city: { nameWithHierarchy: 'Newcastle upon Tyne', $$hashKey: 'object:1301' } };
70+
EventStore.commit('setEvent', event);
71+
72+
expect(EventStore.state.event).to.deep.equal({
73+
id: 1,
74+
name: 'Event name',
75+
city: { nameWithHierarchy: 'Newcastle upon Tyne' },
76+
sendEmails: false,
6377
});
6478
});
6579
});
@@ -71,16 +85,16 @@ describe('Event Store', () => {
7185
});
7286
});
7387

74-
describe('mutations.setCityFromObject', () => {
88+
describe('mutations.setCityFromEventObject', () => {
7589
context('when city object has nameWithHierarcy', () => {
7690
it('sets city correctly', () => {
77-
EventStore.commit('setCityFromObject', { nameWithHierarchy: 'Sheffield' });
91+
EventStore.commit('setCityFromEventObject', { nameWithHierarchy: 'Sheffield' });
7892
expect(EventStore.state.event.city).to.eql({ nameWithHierarchy: 'Sheffield' });
7993
});
8094
});
8195
context('when city object has toponymName', () => {
8296
it('sets city correctly', () => {
83-
EventStore.commit('setCityFromObject', { toponymName: 'Sheffield' });
97+
EventStore.commit('setCityFromEventObject', { toponymName: 'Sheffield' });
8498
expect(EventStore.state.event.city).to.eql({ nameWithHierarchy: 'Sheffield' });
8599
});
86100
});
@@ -102,9 +116,19 @@ describe('Event Store', () => {
102116

103117
describe('mutations.setCity', () => {
104118
it('sets city correctly', () => {
105-
EventStore.commit('setCity', 'Sheffield');
119+
EventStore.commit('setCity', { city: { nameWithHierarchy: 'Sheffield' } });
106120
expect(EventStore.state.event.city).to.eql({ nameWithHierarchy: 'Sheffield' });
107121
});
122+
123+
it('handles dojo city being null', () => {
124+
EventStore.commit('setCity', { city: null });
125+
expect(EventStore.state.event.city).to.eql({});
126+
});
127+
128+
it('handles dojo city being empty', () => {
129+
EventStore.commit('setCity', { city: {} });
130+
expect(EventStore.state.event.city).to.eql({});
131+
});
108132
});
109133

110134
describe('mutations.setCountry', () => {

0 commit comments

Comments
 (0)