Skip to content
This repository has been archived by the owner on May 20, 2024. It is now read-only.

Streamline UI for creating activities #2585

Merged
merged 101 commits into from Sep 27, 2022
Merged
Show file tree
Hide file tree
Changes from 86 commits
Commits
Show all changes
101 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
5740272
WIP
tiltec Sep 12, 2022
89dbf39
WIP place gallery
tiltec Sep 12, 2022
261dc72
Merge remote-tracking branch 'origin/master' into refine-places-display
tiltec Sep 13, 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
972718c
remove place list from GroupActivities
tiltec Sep 15, 2022
b0a3517
remove place filters from group service
tiltec Sep 15, 2022
a926e7a
simplify sidenav place list
tiltec Sep 15, 2022
5594a24
add more filters
tiltec Sep 15, 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
4206c02
add activity count
tiltec Sep 15, 2022
a6f3aec
place gallery fiddling
tiltec Sep 15, 2022
a39e6d1
refine place list some more
tiltec Sep 16, 2022
5db28d4
increase description height
tiltec Sep 16, 2022
80b2963
refine cards, move create button into card
tiltec Sep 16, 2022
404b2ec
place gallery: fix subscribed icon
tiltec Sep 16, 2022
fc039fa
merge sidenavPlaces
tiltec Sep 16, 2022
566cc32
refine sidenav
tiltec Sep 16, 2022
4da270d
fix back-button navigation from place
tiltec Sep 16, 2022
5b08bcf
use placeRoute
tiltec Sep 16, 2022
2d643e5
don't sort places by status
tiltec Sep 17, 2022
ba39b69
add more translations
tiltec Sep 17, 2022
f41bf16
add one more translation
tiltec Sep 17, 2022
4252ab9
add a simple test
tiltec Sep 17, 2022
7f926cf
Merge remote-tracking branch 'origin/master' into streamline-activity…
tiltec Sep 17, 2022
2b78186
Merge branch 'refine-places-display' into streamline-activity-creation
tiltec Sep 17, 2022
ebc7aff
make it work!
tiltec Sep 19, 2022
9fc48a6
Merge remote-tracking branch 'origin/add/activities-require-role' int…
tiltec Sep 19, 2022
54f31b1
some more ActivityEditButton improvements
tiltec Sep 21, 2022
39b4cdb
Merge remote-tracking branch 'origin/master' into refine-places-display
tiltec Sep 23, 2022
3724a10
Merge remote-tracking branch 'origin/master' into streamline-activity…
tiltec Sep 23, 2022
af88ae2
favorites -> favourites
tiltec Sep 23, 2022
35279a7
Places test: remove pagination setting
tiltec Sep 23, 2022
2ecefb9
fix and refactor activityCountQuery
tiltec Sep 23, 2022
ee7c7a1
make place card clickable
tiltec Sep 23, 2022
cf626e5
left-align filters
tiltec Sep 23, 2022
36221f6
move create button to toolbar
tiltec Sep 23, 2022
4e24fa2
move chips to bottom
tiltec Sep 23, 2022
b046684
add button to mark as favourite
tiltec Sep 23, 2022
46d9c5e
adjust message
tiltec Sep 24, 2022
80894e8
make chips grey when they show 0
tiltec Sep 24, 2022
8f0521f
adjust message again
tiltec Sep 24, 2022
b1773cd
Merge branch 'refine-places-display' into streamline-activity-creation
tiltec Sep 24, 2022
d73ee4e
remove unused imports
tiltec Sep 24, 2022
f2ec177
remove more unused stuff
tiltec Sep 24, 2022
a700aa8
WIP
tiltec Sep 26, 2022
25b7451
Merge remote-tracking branch 'origin/master' into streamline-activity…
tiltec Sep 26, 2022
505b5aa
fix activitycreatebutton
tiltec Sep 26, 2022
75c7769
some more async components
tiltec Sep 26, 2022
f1e4df0
new activity edit button menu
tiltec Sep 26, 2022
56d25a6
update i18n
tiltec Sep 26, 2022
10b7438
remove unused vars
tiltec Sep 26, 2022
28810b0
remove hover effect
tiltec Sep 26, 2022
ffc8982
update snapshot
tiltec Sep 26, 2022
a3b07be
remove placeholder
tiltec Sep 26, 2022
f007aac
rename "only this activity"
tiltec Sep 27, 2022
7ded91e
add places autocomplete
tiltec Sep 27, 2022
7368213
add create success toast
tiltec Sep 27, 2022
0e57cae
remove two unrefs
tiltec Sep 27, 2022
c8de284
render menu and fab only after click
tiltec Sep 27, 2022
4d17724
add button to create a new place
tiltec Sep 27, 2022
c826719
work around some test problems
tiltec Sep 27, 2022
7713e71
raise node memory once more...
tiltec Sep 27, 2022
60abdcc
use 4 workers
tiltec Sep 27, 2022
f128636
merge master
tiltec Sep 27, 2022
b81a011
remove debug
tiltec Sep 27, 2022
76fa7bb
Merge remote-tracking branch 'origin/master' into streamline-activity…
tiltec Sep 27, 2022
7e242db
remove debug
tiltec Sep 27, 2022
f30b398
fix tests
tiltec Sep 27, 2022
16ac4cc
fix import order
tiltec Sep 27, 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
1 change: 1 addition & 0 deletions src/__snapshots__/storyshots.spec.js.snap
Expand Up @@ -799,6 +799,7 @@ exports[`Storyshots activities/components/ActivityItem Normal 1`] = `
<div class="content-inner">
<div class="row no-wrap items-start justify-between">
<div><strong class="featured-text">10:00 AM <!--v-if--></strong>Monday, December 25, 2017</div>
<!--v-if-->
</div>
<!--v-if-->
<!--v-if-->
Expand Down
66 changes: 66 additions & 0 deletions src/activities/components/ActivityCreateButton.spec.js
@@ -0,0 +1,66 @@
import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event'
import { render } from '@testing-library/vue'
import { times } from 'lodash'

import { resetServices } from '@/utils/datastore/helpers'

import { withDefaults } from '>/helpers'
import {
db,
useMockBackend,
createUser,
createGroup,
loginAs,
setPageSize,
createPlace,
createPlaceType,
createActivityType,
} from '>/mockBackend'
import { addUserToGroup } from '>/mockBackend/groups'
import '>/routerMocks'

import ActivityCreateButton from './ActivityCreateButton'

describe('ActivityCreateButton', () => {
let activityType
let places
useMockBackend()

beforeEach(() => {
jest.resetModules()
resetServices()
})

beforeEach(() => {
const user = createUser()
const group = createGroup()
addUserToGroup(user, group)
user.currentGroup = group.id
setPageSize(3) // make it have to do pagination stuff too...
createPlaceType({ group: group.id })
places = times(3, () => createPlace({ group: group.id }))
activityType = createActivityType({ group: group.id })
loginAs(user)
})

it('creates a new activity', async () => {
const { click } = userEvent.setup()

const { findByRole, getByRole, getAllByRole } = render(ActivityCreateButton, withDefaults())

await click(getByRole('button', { title: /create/i }))
await click(getAllByRole('button', { name: activityType.name })[0])

await click(getByRole('combobox', { name: 'Choose a place' }))
await click(await findByRole('option', { name: places[0].name }))
await click(await findByRole('option', { name: places[0].name }))

expect(db.activities.length).toEqual(0)
await click(getByRole('button', { name: /create/i }))

expect(db.activities.length).toEqual(1)
expect(db.activities[0].place).toEqual(places[0].id)
expect(db.activities[0].activityType).toEqual(activityType.id)
})
})
238 changes: 238 additions & 0 deletions src/activities/components/ActivityCreateButton.vue
@@ -0,0 +1,238 @@
<template>
<QFab
class="fab-top-fix"
vertical-actions-align="right"
size="sm"
color="secondary"
icon="fas fa-plus"
direction="down"
unelevated
padding="0px 13px"
:title="$t('BUTTON.CREATE')"
>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the non-round-ness of this button, the "place edit" circular button looks nicer to me. I don't think this needs changing though, would make more sense as a general UI-rationalization.

<QFabAction
v-for="activityType in activityTypes"
:key="activityType.id"
class="fab-action-fix"
label-position="left"
v-bind="getIconProps(activityType)"
@click="selectActivityTypeAndOpen(activityType)"
/>
<QFabAction
class="fab-action-fix bg-white"
:label="$t('ACTIVITY_TYPES.MANAGE_TYPES')"
outline
:to="{ name: 'groupEditActivityTypes' }"
/>
</QFab>
<QDialog
v-model="isOpen"
>
<div
class="bg-white"
style="width: 700px; overflow-x: hidden"
>
<Component
:is="isSeries ? ActivitySeriesEdit : ActivityEdit"
:value="isSeries ? newSeries : newActivity"
:status="isSeries ? createSeriesStatus : createActivityStatus"
can-cancel
@save="data => isSeries ? saveNewSeries(data) : saveNewActivity(data)"
@cancel="isOpen = false"
@reset="() => isSeries ? resetNewSeries() : resetNewSeries()"
>
<QSelect
v-model="placeId"
:options="places.map(({ name, id, placeType }) => ({ label: name, value: id, icon: getPlaceTypeById(placeType).icon }))"
:label="$t('CREATEACTIVITY.PLACE')"
emit-value
map-options
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add outline as in other comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and maybe ordered
... and maybe autocomplete

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or even better, if we can make a PlaceSelect component - such that it can be used for the activity filter too (that already has autocomplete, ordering, and "show favourite places at the top" feature), all of which would also be useful here.

your one has the added "place type icon" feature which would also be nice for the activity filter.

>
<template #option="scope">
<QItem
:key="scope.index"
dense
v-bind="scope.itemProps"
>
<QItemSection side>
<QIcon
:name="scope.opt.icon"
size="1.1em"
color="positive"
/>
</QItemSection>
<QItemSection>
<QItemLabel>{{ scope.opt.label }}</QItemLabel>
</QItemSection>
</QItem>
</template>
<template #selected-item="scope">
<div class="row no-wrap ellipsis">
<QIcon
:name="scope.opt.icon"
size="1.1em"
class="on-left q-ml-xs"
color="positive"
/>
<div class="ellipsis">
{{ scope.opt.label }}
</div>
</div>
</template>
</QSelect>
<QOptionGroup
v-model="isSeries"
:options="[
{ label: $t('CREATEACTIVITY.ONE_TIME_ACTIVITY'), value: false },
{ label: $t('ACTIVITYMANAGE.SERIES'), value: true },
]"
color="primary"
inline
class="q-mb-md"
/>
</Component>
</div>
</QDialog>
</template>

<script setup>
import addHours from 'date-fns/addHours'
import addSeconds from 'date-fns/addSeconds'
import startOfTomorrow from 'date-fns/startOfTomorrow'
import {
QItem,
QItemSection,
QItemLabel,
QIcon,
QFab,
QFabAction,
QDialog,
QSelect,
QOptionGroup,
} from 'quasar'
import { ref, computed, watch, unref, defineAsyncComponent } from 'vue'

import { useActivityTypeHelpers } from '@/activities/helpers'
import { useCreateActivityMutation, useCreateActivitySeriesMutation } from '@/activities/mutations'
import { useActivityTypeService } from '@/activities/services'
import { defaultDuration } from '@/activities/settings'
import { useCurrentGroupService } from '@/group/services'
import { usePlaceService, usePlaceTypeService } from '@/places/services'

const ActivityEdit = defineAsyncComponent(() => import('./ActivityEdit.vue'))
const ActivitySeriesEdit = defineAsyncComponent(() => import('./ActivitySeriesEdit.vue'))

const isOpen = ref(false)
const placeId = ref(null)
const isSeries = ref(false)

const newActivity = ref({})
const newSeries = ref({})

const {
groupId,
} = useCurrentGroupService()

const {
getPlacesByGroup,
} = usePlaceService()

const {
getPlaceTypeById,
} = usePlaceTypeService()

const places = computed(() => getPlacesByGroup(groupId).filter(place => place.status === 'active'))

const {
mutate: saveNewActivity,
status: createActivityStatus,
reset: resetNewActivity,
} = useCreateActivityMutation({
onSuccess () {
isOpen.value = false
},
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I did use this pattern in places (passing the opts like onSuccess through), I actually came to prefer using the mutateAsync function, then having a local function call it, e.g.

const {
  mutateAsync: createActivity,
  status: createActivityStatus,
  reset: resetNewActivity,
} = useCreateActivityMutation()

async function saveNewActivity (data) {
  await createActivity(data)
  isOpen.value = false
}

... which can then keep the onSuccess usable inside the mutation definition if there is a reason to do something at that level (which there sometimes is).

(Additionally, the non-async mutate function can take options, including onSuccess, but with a caveat, which is in-part what led me to prefer the async method)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(... not suggesting we need to change the style right now though)


const {
mutate: saveNewSeries,
reset: resetNewSeries,
status: createSeriesStatus,
} = useCreateActivitySeriesMutation({
onSuccess () {
isOpen.value = false
},
})

const {
getIconProps,
} = useActivityTypeHelpers()

const {
getActivityTypesByGroup,
} = useActivityTypeService()

const activityTypes = computed(() => getActivityTypesByGroup(groupId, { status: 'active' }))

watch(placeId, id => {
// replace object to trigger 'value' watcher in editMixin
newActivity.value = {
...newActivity.value,
place: unref(id),
tiltec marked this conversation as resolved.
Show resolved Hide resolved
}
newSeries.value = {
...newSeries.value,
place: unref(id),
}
})

function selectActivityTypeAndOpen (activityType) {
createNewActivity(activityType.id)
createNewSeries(activityType.id)
resetNewSeries()
resetNewActivity()
isOpen.value = true
}

function createNewActivity (activityType) {
const date = addHours(startOfTomorrow(), 10) // default to 10am tomorrow
newActivity.value = {
activityType,
participantTypes: [
{
role: 'member',
maxParticipants: 2,
description: '',
},
],
maxParticipants: 2,
description: '',
place: unref(placeId),
date,
dateEnd: addSeconds(date, defaultDuration),
hasDuration: false,
}
}

function createNewSeries (activityType) {
newSeries.value = {
activityType,
participantTypes: [
{
role: 'member',
maxParticipants: 2,
description: '',
},
],
maxParticipants: 2,
description: '',
startDate: addHours(startOfTomorrow(), 10),
duration: null,
place: unref(placeId),
rule: {
isCustom: false,
byDay: ['MO'],
freq: 'WEEKLY',
},
}
}
</script>
11 changes: 9 additions & 2 deletions src/activities/components/ActivityEdit.vue
Expand Up @@ -24,6 +24,7 @@
/>
{{ activityTypeIconProps.title }}
</h3>
<slot />
<template v-if="canEditDate">
<div class="row q-mt-xs">
<QInput
Expand Down Expand Up @@ -207,7 +208,7 @@
/>
<QSpace />
<QBtn
v-if="isNew"
v-if="canCancel"
type="button"
@click="$emit('cancel')"
>
Expand Down Expand Up @@ -286,6 +287,7 @@ import {
QItemSection,
QItemLabel,
} from 'quasar'
import { defineAsyncComponent } from 'vue'

import activityAPI from '@/activities/api/activities'
import { useActivityHelpers, useActivityTypeHelpers } from '@/activities/helpers'
Expand All @@ -297,11 +299,12 @@ 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'

const ActivityItem = defineAsyncComponent(() => import('@/activities/components/ActivityItem'))

export default {
name: 'ActivityEdit',
components: {
Expand Down Expand Up @@ -332,6 +335,10 @@ export default {
type: Object,
default: null,
},
canCancel: {
type: Boolean,
default: false,
},
},
emits: [
'cancel',
Expand Down