Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add participant types for activities #2421

Merged
merged 37 commits into from Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
bed7357
Merge branch 'add/improved-testing' into add/activities-require-role
nicksellen Aug 29, 2022
7784b11
yarn fix
nicksellen Aug 29, 2022
0e59fa4
Manual linting fixes
nicksellen Aug 29, 2022
c53c8a3
Restore participant type functionality
nicksellen Aug 29, 2022
287a671
Update GroupActivities test
nicksellen Aug 29, 2022
34f975b
Fixup tests
nicksellen Aug 29, 2022
b26ac96
Removed unneeded eslint disable
nicksellen Aug 30, 2022
bed2f12
Add stub for /api/bootstrap/ mockBackend
nicksellen Aug 30, 2022
a5ad72d
Fixup editing activities with participant types
nicksellen Aug 30, 2022
9f3b631
Fixup participant type editing
nicksellen Aug 30, 2022
1008250
replace route for place default view
nicksellen Aug 30, 2022
ed50461
Refine UX + participants type feature flag
nicksellen Aug 30, 2022
f0c3e6e
Always show advanced mode if using it
nicksellen Aug 30, 2022
bf027e5
Refine participant type editing
nicksellen Aug 31, 2022
369e5c7
Small participant types refinements
nicksellen Aug 31, 2022
c7c3bc8
Small participant type fixes
nicksellen Sep 1, 2022
679ac94
Check series changes before save
nicksellen Sep 3, 2022
8fdd3c6
Add thorough confirm changes dialog for series
nicksellen Sep 8, 2022
4574a2f
Show join button as greyed-out if cannot join
nicksellen Sep 8, 2022
03cf5e9
Require message if people will be removed
nicksellen Sep 8, 2022
e86754b
Refresh activities on series change
nicksellen Sep 8, 2022
445bd4b
Merge remote-tracking branch 'origin/master' into add/activities-requ…
tiltec Sep 9, 2022
5cfc8c5
fix tests
tiltec Sep 9, 2022
fc7e32e
add back getIsUpcoming
tiltec Sep 10, 2022
be124df
Add not very good participant removed notification
nicksellen Sep 12, 2022
2031553
Remove "Participant type n" heading
nicksellen Sep 13, 2022
f5a426c
Remove accidently commited empty file
nicksellen Sep 13, 2022
ba1764d
Updates from review
nicksellen Sep 13, 2022
8d7ec09
Fixup ActivityItem storyshot test
nicksellen Sep 13, 2022
24d8c89
Add check step for activity editing
nicksellen Sep 14, 2022
ae78023
Refine participant removal notifications
nicksellen Sep 15, 2022
1215755
Merge branch 'master' into add/activities-require-role
nicksellen Sep 15, 2022
5a149c4
Fixup tests
nicksellen Sep 15, 2022
e35eecc
Linting fix
nicksellen Sep 15, 2022
39191f8
Only show history details that render nicely
nicksellen Sep 20, 2022
d2ca959
Refine participant removed notification
nicksellen Sep 20, 2022
d00270c
Update snapshot
nicksellen Sep 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .sentryclirc
@@ -0,0 +1,4 @@
[defaults]
url = https://sentry.io/
org = karrot-dev
project = karrot
3 changes: 3 additions & 0 deletions .storybook/preview.js
Expand Up @@ -43,6 +43,9 @@ setupMockBackend()

// Storybook config
import { create } from '@storybook/theming'
import { faker } from '@faker-js/faker'

faker.seed(500)

export const parameters = {
options: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -70,6 +70,7 @@
"mitt": "^3.0.0",
"quasar": "^2.7.5",
"reconnecting-websocket": "^4.4.0",
"rrule": "^2.7.0",
"twemoji": "^14.0.2",
"v-tooltip": "^2.1.3",
"vue": "^3.2.37",
Expand Down
5 changes: 4 additions & 1 deletion src/AppTests/givingFeedback.spec.js
Expand Up @@ -18,6 +18,7 @@ import {
createActivityType,
createPlaceType,
} from '>/mockBackend'
import { joinActivity } from '>/mockBackend/activities'
import { addUserToGroup } from '>/mockBackend/groups'

useMockBackend()
Expand All @@ -32,7 +33,7 @@ test('give activity feedback', async () => {

createPlaceType({ group: group.id })

createActivity({
const activity = createActivity({
place: createPlace({ group: group.id }).id,
activityType: createActivityType({
group: group.id,
Expand All @@ -43,6 +44,8 @@ test('give activity feedback', async () => {
startDate: subDays(new Date(), 5),
})

joinActivity(activity, user)

user.currentGroup = group.id
loginAs(user)

Expand Down
542 changes: 333 additions & 209 deletions src/__snapshots__/storyshots.spec.js.snap

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions src/activities/api/activities.js
Expand Up @@ -58,8 +58,13 @@ export default {
return convert((await axios.patch(`/api/activities/${activity.id}/`, convertDateToRange(activity))).data)
},

async join (activityId) {
return convert((await axios.post(`/api/activities/${activityId}/add/`, {})).data)
async checkSave (activity) {
const { id } = activity
return (await axios.patch(`/api/activities/${id}/check/`, convertDateToRange(activity))).data
},

async join ({ activityId, participantTypeId }) {
return convert((await axios.post(`/api/activities/${activityId}/add/`, { participantType: participantTypeId })).data)
},

async leave (activityId) {
Expand Down
5 changes: 5 additions & 0 deletions src/activities/api/activitySeries.js
Expand Up @@ -19,6 +19,11 @@ export default {
return convert((await axios.patch(`/api/activity-series/${id}/`, serialize(series))).data)
},

async checkSave (series) {
const { id } = series
return (await axios.patch(`/api/activity-series/${id}/check/`, serialize(series))).data
},

delete (seriesId) {
return axios.delete(`/api/activity-series/${seriesId}/`)
},
Expand Down
206 changes: 116 additions & 90 deletions src/activities/components/ActivityEdit.vue
Expand Up @@ -33,6 +33,7 @@
:error="hasError('date')"
size="9"
hide-bottom-space
outlined
class="q-mr-sm"
@focus="$refs.qStartDateProxy.show()"
>
Expand Down Expand Up @@ -63,6 +64,7 @@
size="3"
:error="hasError('date')"
hide-bottom-space
outlined
@focus="$refs.qStartTimeProxy.show()"
>
<Component
Expand Down Expand Up @@ -105,6 +107,7 @@
size="3"
:error="hasError('date')"
hide-bottom-space
outlined
@focus="$refs.qEndTimeProxy.show()"
>
<Component
Expand Down Expand Up @@ -151,85 +154,58 @@
</div>
</template>

<div>
<QInput
v-model.number="edit.maxParticipants"
type="number"
stack-label
:label="$t('CREATEACTIVITY.MAX_PARTICIPANTS')"
:hint="$t('CREATEACTIVITY.MAX_PARTICIPANTS_HELPER')"
:placeholder="$t('CREATEACTIVITY.UNLIMITED')"
:error="hasError('maxParticipants')"
:error-message="firstError('maxParticipants')"
:input-style="{ maxWidth: '100px' }"
>
<template #before>
<QIcon name="group" />
</template>
<QSlider
v-if="edit.maxParticipants > 0 && edit.maxParticipants <= 10"
v-model="edit.maxParticipants"
:min="1"
:max="10"
label
markers
class="q-mx-sm self-end"
style="min-width: 60px"
<MarkdownInput
v-model="edit.description"
:error="hasError('description')"
:error-message="firstError('description')"
:label="$t('CREATEACTIVITY.COMMENT')"
:hint="$t('CREATEACTIVITY.COMMENT_HELPER')"
icon="info"
maxlength="500"
:input-style="{ minHeight: 'auto' }"
mentions
outlined
:bg-color="series && series.description !== edit.description ? 'orange-1' : null"
@keyup.ctrl.enter="maybeSave"
>
<template #after>
<QBtn
v-if="series ? series.description !== edit.description : false"
icon="undo"
unelevated
dense
@click="edit.description = series.description"
/>
<template #after>
<QIcon
v-if="series ? series.maxParticipants !== edit.maxParticipants : false"
name="undo"
@click="edit.maxParticipants = series.maxParticipants"
/>
</template>
</QInput>
<div
v-if="seriesMeta.isMaxParticipantsChanged"
class="q-ml-lg col-12 q-field__bottom text-warning"
>
<QIcon name="warning" />
{{ $t('CREATEACTIVITY.DIFFERS_WARNING') }}
</div>
</template>
</MarkdownInput>
<div
v-if="series && series.description !== edit.description"
class="q-ml-lg col-12 q-field__bottom text-warning"
>
<QIcon name="warning" />
{{ $t('CREATEACTIVITY.DIFFERS_WARNING') }}
</div>

<div>
<MarkdownInput
v-model="edit.description"
:error="hasError('description')"
:error-message="firstError('description')"
:label="$t('CREATEACTIVITY.COMMENT')"
:hint="$t('CREATEACTIVITY.COMMENT_HELPER')"
icon="info"
mentions
maxlength="500"
@keyup.ctrl.enter="maybeSave"
>
<template #after>
<QIcon
v-if="series ? series.description !== edit.description : false"
name="undo"
@click="edit.description = series.description"
/>
</template>
</MarkdownInput>

<div
v-if="seriesMeta.isDescriptionChanged"
class="q-ml-lg col-12 q-field__bottom text-warning"
>
<QIcon name="warning" />
{{ $t('CREATEACTIVITY.DIFFERS_WARNING') }}
</div>
</div>
<ParticipantTypesEdit
v-model="edit.participantTypes"
:series="series"
:participants="value.participants"
@maybe-save="maybeSave"
/>

<div
v-if="hasNonFieldError"
class="text-negative"
>
{{ firstNonFieldError }}
</div>

<div class="row justify-end q-gutter-sm q-mt-lg">
<QToggle
v-model="showPreview"
:label="$t('BUTTON.PREVIEW')"
/>
<QSpace />
<QBtn
v-if="isNew"
type="button"
Expand Down Expand Up @@ -274,6 +250,13 @@
{{ $t(isNew ? 'BUTTON.CREATE' : 'BUTTON.SAVE_CHANGES') }}
</QBtn>
</div>

<div v-if="showPreview">
<ActivityItem
preview
:activity="previewActivity"
/>
</div>
</form>
</div>
</template>
Expand All @@ -283,6 +266,9 @@ import addDays from 'date-fns/addDays'
import addSeconds from 'date-fns/addSeconds'
import differenceInSeconds from 'date-fns/differenceInSeconds'
import {
QCard,
QCardSection,
QCardActions,
QDate,
QTime,
QSlider,
Expand All @@ -291,23 +277,36 @@ import {
QIcon,
QMenu,
QDialog,
QToggle,
QSelect,
QSpace,
Dialog,
date,
QItem,
QItemSection,
QItemLabel,
} from 'quasar'

import { useActivityTypeHelpers } from '@/activities/helpers'
import activityAPI from '@/activities/api/activities'
import { useActivityHelpers, useActivityTypeHelpers } from '@/activities/helpers'
import { useActivityTypeService } from '@/activities/services'
import { defaultDuration } from '@/activities/settings'
import { formatSeconds } from '@/activities/utils'
import editMixin from '@/utils/mixins/editMixin'
import statusMixin from '@/utils/mixins/statusMixin'
import reactiveNow from '@/utils/reactiveNow'
import { objectDiff } from '@/utils/utils'

import ActivityItem from '@/activities/components/ActivityItem'
import ConfirmChangesDialog from '@/activities/components/ConfirmChangesDialog'
import ParticipantTypesEdit from '@/activities/components/ParticipantTypesEdit'
import MarkdownInput from '@/utils/components/MarkdownInput'

export default {
name: 'ActivityEdit',
components: {
ParticipantTypesEdit,
ActivityItem,
QDate,
QTime,
QSlider,
Expand All @@ -316,7 +315,16 @@ export default {
QIcon,
QMenu,
QDialog,
QToggle,
QSelect,
QCard,
QCardSection,
QCardActions,
QSpace,
MarkdownInput,
QItem,
QItemSection,
QItemLabel,
},
mixins: [editMixin, statusMixin],
props: {
Expand All @@ -330,12 +338,29 @@ export default {
'save',
],
setup () {
const { roleOptions } = useActivityHelpers()
const { getActivityTypeById } = useActivityTypeService()
const { getIconProps } = useActivityTypeHelpers()
return { getIconProps }
return {
roleOptions,
getActivityTypeById,
getIconProps,
}
},
data () {
return {
showPreview: false,
}
},
computed: {
previewActivity () {
return {
...this.edit,
participants: [],
}
},
activityType () {
return this.value && this.value.activityType
return this.value && this.getActivityTypeById(this.value.activityType)
},
activityTypeIconProps () {
return this.activityType ? this.getIconProps(this.activityType) : {}
Expand All @@ -353,10 +378,6 @@ export default {
if (this.edit.series) return false
return true
},
seriesMeta () {
if (!this.edit.seriesMeta) return {}
return this.edit.seriesMeta
},
date: {
get () {
return this.edit.date
Expand Down Expand Up @@ -430,29 +451,34 @@ export default {
return this.$q.screen.width < 450 || this.$q.screen.height < 450
},
},
watch: {
'edit.maxParticipants' (val) {
if (val === '') {
// if we have 'unlimited' participants, val gets parsed to empty string, but the server expects null
this.edit.maxParticipants = null
}
},
},
methods: {
futureDates (dateString) {
return date.extractDate(`${dateString} 23:59`, 'YYYY/MM/DD HH:mm') > this.now
},
toggleDuration () {
this.hasDuration = !this.hasDuration
},
maybeSave () {
async maybeSave () {
if (!this.canSave) return
this.save()
},
getCreateData () {
return {
...this.edit,
activityType: this.activityType.id,
if (this.isNew) {
this.save()
}
else {
const { users } = await activityAPI.checkSave({ ...this.getPatchData(), id: this.value.id })
Dialog.create({
component: ConfirmChangesDialog,
componentProps: {
users,
},
})
.onOk(({ updatedMessage }) => {
if (updatedMessage) {
this.save({ updatedMessage })
}
else {
this.save()
}
})
}
},
// Overrides mixin method to always provide start date if we have modified end date
Expand Down