Skip to content

Commit a97b0c2

Browse files
Merge pull request #4975 from manuelmeister/feature/schedule-entry-editing-in-activity
Edit scheduleEntry on activity view without redirection problem
2 parents 46fec0c + 203b7f4 commit a97b0c2

22 files changed

+376
-123
lines changed

frontend/src/components/activity/ScheduleEntry.vue

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,15 @@ Displays a single scheduleEntry
164164
<template v-else>
165165
<!-- Header -->
166166
<v-row dense class="activity-header">
167-
<v-col class="col col-sm-6 col-12 px-0 pt-0">
167+
<v-col class="col col-sm-6 col-12 px-0 pt-0 d-flex flex-wrap gap-x-4">
168168
<table>
169169
<thead>
170170
<tr>
171-
<th scope="col" class="text-right pb-2 pr-4">
171+
<th
172+
v-if="category.numberingStyle !== '-'"
173+
scope="col"
174+
class="text-right pb-2 pr-4"
175+
>
172176
{{ $tc('entity.scheduleEntry.fields.nr') }}
173177
</th>
174178
<th scope="col" class="text-left pb-2 pr-4">
@@ -184,8 +188,20 @@ Displays a single scheduleEntry
184188
v-for="scheduleEntryItem in scheduleEntries"
185189
:key="scheduleEntryItem._meta.self"
186190
>
187-
<th class="text-right tabular-nums pb-2 pr-4">
188-
{{ scheduleEntryItem.number }}
191+
<th
192+
v-if="category.numberingStyle !== '-'"
193+
class="text-right tabular-nums pb-2 pr-4"
194+
>
195+
<RouterLink
196+
v-if="scheduleEntryItem._meta.self !== scheduleEntry._meta.self"
197+
:to="scheduleEntryRoute(scheduleEntryItem)"
198+
class="e-title-link"
199+
>
200+
{{ scheduleEntryItem.number }}
201+
</RouterLink>
202+
<template v-else>
203+
{{ scheduleEntryItem.number }}
204+
</template>
189205
</th>
190206
<td class="text-left tabular-nums pb-2 pr-4">
191207
{{
@@ -196,11 +212,33 @@ Displays a single scheduleEntry
196212
{{ dateShort(scheduleEntryItem.start) }}
197213
</td>
198214
<td class="text-left tabular-nums pb-2 pr-0">
199-
{{ rangeLongEnd(scheduleEntryItem.start, scheduleEntryItem.end) }}
215+
<RouterLink
216+
v-if="
217+
category.numberingStyle === '-' &&
218+
scheduleEntryItem._meta.self !== scheduleEntry._meta.self
219+
"
220+
:to="scheduleEntryRoute(scheduleEntryItem)"
221+
class="e-title-link"
222+
>
223+
{{ rangeLongEnd(scheduleEntryItem.start, scheduleEntryItem.end) }}
224+
</RouterLink>
225+
<template v-else>
226+
{{ rangeLongEnd(scheduleEntryItem.start, scheduleEntryItem.end) }}
227+
</template>
200228
</td>
201229
</tr>
202230
</tbody>
203231
</table>
232+
<DialogActivityEdit
233+
v-if="scheduleEntry && isContributor"
234+
:schedule-entry="scheduleEntry"
235+
hide-header-fields
236+
@activity-updated="activity.$reload()"
237+
>
238+
<template #activator="{ on }">
239+
<ButtonEdit text small class="v-btn--has-bg" v-on="on" />
240+
</template>
241+
</DialogActivityEdit>
204242
</v-col>
205243
<v-col class="col col-sm-6 col-12 px-0">
206244
<api-form :entity="activity" name="activity">
@@ -254,7 +292,11 @@ import RootNode from '@/components/activity/RootNode.vue'
254292
import ActivityResponsibles from '@/components/activity/ActivityResponsibles.vue'
255293
import { dateHelperUTCFormatted } from '@/mixins/dateHelperUTCFormatted.js'
256294
import { campRoleMixin } from '@/mixins/campRoleMixin'
257-
import router, { periodRoute, scheduleEntryRoute } from '@/router.js'
295+
import router, {
296+
firstActivityScheduleEntry,
297+
periodRoute,
298+
scheduleEntryRoute,
299+
} from '@/router.js'
258300
import DownloadNuxtPdf from '@/components/print/print-nuxt/DownloadNuxtPdfListItem.vue'
259301
import DownloadClientPdf from '@/components/print/print-client/DownloadClientPdfListItem.vue'
260302
import { errorToMultiLineToast } from '@/components/toast/toasts'
@@ -264,10 +306,15 @@ import DialogEntityDelete from '@/components/dialog/DialogEntityDelete.vue'
264306
import TogglePaperSize from '@/components/activity/TogglePaperSize.vue'
265307
import ApiForm from '@/components/form/api/ApiForm.vue'
266308
import ApiSelect from '@/components/form/api/ApiSelect.vue'
309+
import ButtonEdit from '@/components/buttons/ButtonEdit.vue'
310+
import DialogActivityEdit from '@/components/activity/dialog/DialogActivityEdit.vue'
311+
import scheduleEntryRouteChange from '@/helpers/scheduleEntryRouteChange.js'
267312
268313
export default {
269314
name: 'ScheduleEntry',
270315
components: {
316+
DialogActivityEdit,
317+
ButtonEdit,
271318
ApiForm,
272319
ApiSelect,
273320
TogglePaperSize,
@@ -290,23 +337,31 @@ export default {
290337
isPaperDisplaySize: () => this.isPaperDisplaySize,
291338
}
292339
},
340+
async beforeRouteUpdate(to, from, next) {
341+
return scheduleEntryRouteChange(this.activityId, to, from, next)
342+
},
293343
props: {
294-
scheduleEntry: {
295-
type: Object,
344+
activityId: {
345+
type: String,
296346
required: true,
297347
},
348+
scheduleEntryId: {
349+
type: String,
350+
default: null,
351+
},
298352
},
299353
data() {
300354
return {
301355
layoutMode: false,
302356
editActivityTitle: false,
303357
categoryChangeState: null,
358+
scheduleEntry: null,
304359
loading: true,
305360
}
306361
},
307362
computed: {
308363
activity() {
309-
return this.scheduleEntry.activity()
364+
return this.api.get().activities({ id: this.activityId })
310365
},
311366
camp() {
312367
return this.activity.camp()
@@ -366,6 +421,19 @@ export default {
366421
},
367422
},
368423
424+
watch: {
425+
scheduleEntryId: {
426+
async handler(id) {
427+
try {
428+
this.scheduleEntry = this.api.get().scheduleEntries({ id })
429+
} catch {
430+
this.scheduleEntry = await firstActivityScheduleEntry(this.activityId)
431+
}
432+
},
433+
immediate: true,
434+
},
435+
},
436+
369437
// reload data every time user navigates to Activity view
370438
async mounted() {
371439
this.loading = true
@@ -390,6 +458,7 @@ export default {
390458
this.$toast.error(errorToMultiLineToast(e))
391459
})
392460
},
461+
scheduleEntryRoute,
393462
countContentNodes(contentType) {
394463
return this.contentNodes.items.filter((cn) => {
395464
return cn.contentType().id === contentType.id

frontend/src/components/program/DialogActivityEdit.vue renamed to frontend/src/components/activity/dialog/DialogActivityEdit.vue

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<dialog-form
33
v-model="showDialog"
4-
:title="$tc('components.program.dialogActivityEdit.title')"
4+
:title="$tc('components.activity.dialog.dialogActivityEdit.title')"
55
:loading="loading"
66
:error="error"
77
icon="mdi-calendar-plus"
@@ -15,30 +15,33 @@
1515
<slot name="activator" v-bind="scope" />
1616
</template>
1717
<template #moreActions>
18-
<v-btn
19-
v-if="!scheduleEntry.tmpEvent"
20-
color="primary"
21-
:to="scheduleEntryRoute(scheduleEntry)"
22-
>
23-
{{ $tc('global.button.open') }}
24-
</v-btn>
18+
<slot name="moreActions" />
2519
</template>
26-
<dialog-activity-form :activity="entityData" :period="scheduleEntry.period()" />
20+
<DialogActivityForm
21+
:activity="entityData"
22+
:current-schedule-entry="scheduleEntry"
23+
:period="period"
24+
:hide-location="hideHeaderFields"
25+
/>
2726
</dialog-form>
2827
</template>
2928

3029
<script>
3130
import DialogForm from '@/components/dialog/DialogForm.vue'
3231
import DialogBase from '@/components/dialog/DialogBase.vue'
3332
import DialogActivityForm from './DialogActivityForm.vue'
34-
import { scheduleEntryRoute } from '@/router.js'
33+
import { firstActivityScheduleEntryRoute } from '@/router.js'
3534
3635
export default {
3736
name: 'DialogActivityEdit',
3837
components: { DialogForm, DialogActivityForm },
3938
extends: DialogBase,
4039
props: {
4140
scheduleEntry: { type: Object, required: true },
41+
hideHeaderFields: {
42+
type: Boolean,
43+
default: false,
44+
},
4245
},
4346
data() {
4447
return {
@@ -47,12 +50,15 @@ export default {
4750
}
4851
},
4952
computed: {
50-
scheduleEntries() {
51-
return this.activity.scheduleEntries()
52-
},
5353
activity() {
5454
return this.scheduleEntry.activity()
5555
},
56+
period() {
57+
return this.scheduleEntry.period()
58+
},
59+
scheduleEntries() {
60+
return this.activity.scheduleEntries()
61+
},
5662
},
5763
watch: {
5864
showDialog: async function (showDialog) {
@@ -95,24 +101,49 @@ export default {
95101
96102
// update existing
97103
if (entry.self) {
98-
return this.api.patch(entry.self, {
104+
return this.api
105+
.patch(entry.self, {
106+
period: entry.period()._meta.self,
107+
start: entry.start,
108+
end: entry.end,
109+
})
110+
.then((serverEntry) => {
111+
entry.start = serverEntry.start
112+
entry.end = serverEntry.end
113+
entry.period = serverEntry.period
114+
})
115+
.catch(async (e) => {
116+
// entry was deleted in the meantime
117+
if (e.response.status === 404) {
118+
if (entry.self === this.scheduleEntry._meta.self) {
119+
// redirect to first entry to not break UI
120+
this.$router.push(await firstActivityScheduleEntryRoute(this.activity))
121+
}
122+
entry.deleted = true
123+
return Promise.resolve()
124+
}
125+
return Promise.reject(e)
126+
})
127+
}
128+
129+
// else: create new entry
130+
return this.scheduleEntries
131+
.$post({
99132
period: entry.period()._meta.self,
100133
start: entry.start,
101134
end: entry.end,
135+
activity: this.activity._meta.self,
136+
})
137+
.then((data) => {
138+
entry.self = data._meta.self
102139
})
103-
}
104-
105-
// else: create new entry
106-
return this.scheduleEntries.$post({
107-
period: entry.period()._meta.self,
108-
start: entry.start,
109-
end: entry.end,
110-
activity: this.activity._meta.self,
111-
})
112140
})
113141
114142
// patch activity entity
115143
const activityPayload = { ...this.entityData }
144+
if (this.hideHeaderFields) {
145+
delete activityPayload.location
146+
}
116147
delete activityPayload.scheduleEntries
117148
promises.push(this.api.patch(this.entityUri, activityPayload))
118149
@@ -126,9 +157,10 @@ export default {
126157
},
127158
updatedSuccessful(data) {
128159
this.close()
160+
this.api.reload(this.activity)
161+
this.api.reload(this.scheduleEntry.period().scheduleEntries())
129162
this.$emit('activity-updated', data)
130163
},
131-
scheduleEntryRoute,
132164
},
133165
}
134166
</script>

frontend/src/components/program/DialogActivityForm.vue renamed to frontend/src/components/activity/dialog/DialogActivityForm.vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@
3636
</template>
3737
</e-select>
3838

39-
<e-text-field v-model="localActivity.location" path="location" />
39+
<e-text-field v-if="!hideLocation" v-model="localActivity.location" path="location" />
4040

4141
<FormScheduleEntryList
4242
v-if="activity.scheduleEntries"
4343
:schedule-entries="activity.scheduleEntries"
44+
:current-schedule-entry="currentScheduleEntry"
4445
:period="period"
4546
:periods="camp.periods().items"
4647
/>
@@ -69,10 +70,18 @@ export default {
6970
type: Object,
7071
required: true,
7172
},
73+
currentScheduleEntry: {
74+
type: Object,
75+
default: null,
76+
},
7277
autoselectTitle: {
7378
type: Boolean,
7479
default: false,
7580
},
81+
hideLocation: {
82+
type: Boolean,
83+
default: false,
84+
},
7685
},
7786
data() {
7887
return {

frontend/src/components/program/FormScheduleEntryItem.vue renamed to frontend/src/components/activity/dialog/FormScheduleEntryItem.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
</v-col>
5151

5252
<v-col cols="1" class="pt-3 text-center">
53-
<button-delete v-if="!isLastItem" icon-only @click="$emit('delete')" />
53+
<button-delete v-if="deletable" icon-only @click="$emit('delete')" />
5454
</v-col>
5555
</v-row>
5656
</v-container>
@@ -82,9 +82,9 @@ export default {
8282
},
8383
8484
// true if current item is the last scheduleEntry
85-
isLastItem: {
85+
deletable: {
8686
type: Boolean,
87-
required: true,
87+
required: false,
8888
},
8989
},
9090
data() {

frontend/src/components/program/FormScheduleEntryList.vue renamed to frontend/src/components/activity/dialog/FormScheduleEntryList.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<v-row no-gutters>
55
<v-col class="header mb-3">
66
<legend class="pa-2 float-left">
7-
{{ $tc('components.program.formScheduleEntryList.name') }}
7+
{{ $tc('components.activity.dialog.formScheduleEntryList.name') }}
88
</legend>
99

1010
<button-add
@@ -22,7 +22,11 @@
2222
class="transition-list-item pa-0 mb-4"
2323
:schedule-entry="scheduleEntry"
2424
:periods="periods"
25-
:is-last-item="scheduleEntriesWithoutDeleted.length === 1"
25+
:deletable="
26+
scheduleEntriesWithoutDeleted.length > 1 &&
27+
($route.name !== 'camp/activity' ||
28+
scheduleEntry.self !== currentScheduleEntry?._meta.self)
29+
"
2630
@delete="deleteEntry(scheduleEntry)"
2731
/>
2832
</transition-group>
@@ -47,6 +51,11 @@ export default {
4751
required: true,
4852
},
4953
54+
currentScheduleEntry: {
55+
type: Object,
56+
default: null,
57+
},
58+
5059
// all available periods
5160
periods: {
5261
type: Array,

0 commit comments

Comments
 (0)