Skip to content

Commit

Permalink
feat(editor): improve attendee and resource status display
Browse files Browse the repository at this point in the history
Signed-off-by: Richard Steinmetz <[email protected]>
  • Loading branch information
st3iny committed Jan 10, 2025
1 parent 4ebf8d3 commit ec5076f
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 107 deletions.
246 changes: 150 additions & 96 deletions src/components/Editor/AvatarParticipationStatus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,101 +16,13 @@
:display-name="commonName"
:is-no-user="true" />
<template v-if="!isGroup">
<template v-if="participationStatus === 'ACCEPTED' && isViewedByOrganizer">
<IconCheck class="avatar-participation-status__indicator"
fill-color="#32CD32"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Invitation accepted') }}
</div>
</template>
<template v-else-if="isResource && participationStatus === 'ACCEPTED'">
<IconCheck class="avatar-participation-status__indicator"
fill-color="#32CD32"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Available') }}
</div>
</template>
<template v-else-if="isSuggestion">
<IconCheck class="avatar-participation-status__indicator"
fill-color="#32CD32"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Suggested') }}
</div>
</template>
<template v-else-if="participationStatus === 'TENTATIVE'">
<IconCheck class="avatar-participation-status__indicator"
fill-color="#32CD32"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Participation marked as tentative') }}
</div>
</template>
<template v-else-if="participationStatus === 'ACCEPTED' && !isViewedByOrganizer">
<IconCheck class="avatar-participation-status__indicator"
fill-color="#32CD32"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Accepted {organizerName}\'s invitation', {
organizerName: organizerDisplayName,
}) }}
</div>
</template>
<template v-else-if="isResource && participationStatus === 'DECLINED'">
<IconClose class="avatar-participation-status__indicator"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Not available') }}
</div>
</template>
<template v-else-if="participationStatus === 'DECLINED' && isViewedByOrganizer">
<IconClose class="avatar-participation-status__indicator"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Invitation declined') }}
</div>
</template>
<template v-else-if="participationStatus === 'DECLINED' && !isViewedByOrganizer">
<IconClose class="avatar-participation-status__indicator"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Declined {organizerName}\'s invitation', {
organizerName: organizerDisplayName,
}) }}
</div>
</template>
<template v-else-if="participationStatus === 'DELEGATED'">
<IconDelegated class="avatar-participation-status__indicator"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Invitation is delegated') }}
</div>
</template>
<template v-else-if="isResource">
<IconNoResponse class="avatar-participation-status__indicator"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Checking availability') }}
</div>
</template>
<template v-else-if="isViewedByOrganizer">
<IconNoResponse class="avatar-participation-status__indicator"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Awaiting response') }}
</div>
</template>
<template v-else>
<IconNoResponse class="avatar-participation-status__indicator"
:size="20" />
<div class="avatar-participation-status__text">
{{ t('calendar', 'Has not responded to {organizerName}\'s invitation yet', {
organizerName: organizerDisplayName,
}) }}
</div>
</template>
<component :is="status.icon"
class="avatar-participation-status__indicator"
:fill-color="status.fillColor"
:size="20" />
<div class="avatar-participation-status__text">
{{ status.text }}
</div>
</template>
</div>
</template>
Expand All @@ -132,7 +44,6 @@ export default {
IconNoResponse,
IconClose,
IconDelegated,

},
props: {
avatarLink: {
Expand All @@ -143,6 +54,10 @@ export default {
type: String,
required: true,
},
scheduleStatus: {

Check warning on line 57 in src/components/Editor/AvatarParticipationStatus.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Prop 'scheduleStatus' requires default value to be set
type: String,
required: false,
},
commonName: {
type: String,
required: true,
Expand Down Expand Up @@ -172,6 +87,145 @@ export default {
required: true,
},
},
computed: {
/**
* @return {icon: object, fillColor: string|undefined, text: string}

Check warning on line 92 in src/components/Editor/AvatarParticipationStatus.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Syntax error in type: icon: object, fillColor: string|undefined, text: string
*/
status() {
const acceptedIcon = {

Check warning on line 95 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L95

Added line #L95 was not covered by tests
icon: IconCheck,
fillColor: '#32CD32',
}
const declinedIcon = {
icon: IconClose,

Check warning on line 100 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L100

Added line #L100 was not covered by tests
fillColor: '#ff4402',
}

if (this.isSuggestion) {
return {
...acceptedIcon,
text: t('calendar', 'Suggested'),

Check warning on line 107 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L106-L107

Added lines #L106 - L107 were not covered by tests
}
}

// Try to use the participation status first
switch (this.participationStatus) {

Check warning on line 112 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L111-L112

Added lines #L111 - L112 were not covered by tests
case 'ACCEPTED':
if (this.isResource) {
return {
...acceptedIcon,

Check warning on line 116 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L116

Added line #L116 was not covered by tests
text: t('calendar', 'Available'),
}

Check warning on line 118 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L118

Added line #L118 was not covered by tests
}

if (this.attendeeIsOrganizer && !this.isViewedByOrganizer) {
return {
...acceptedIcon,
text: t('calendar', 'Invited you'),

Check warning on line 124 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L124

Added line #L124 was not covered by tests
}
}

if (this.isViewedByOrganizer) {
return {

Check warning on line 129 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L129

Added line #L129 was not covered by tests
...acceptedIcon,
text: t('calendar', 'Invitation accepted'),
}
}

return {
...acceptedIcon,
text: t('calendar', 'Accepted {organizerName}\'s invitation', {
organizerName: this.organizerDisplayName,
}),
}
case 'TENTATIVE':
return {
...acceptedIcon,

Check warning on line 143 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L143

Added line #L143 was not covered by tests
text: t('calendar', 'Participation marked as tentative'),
}
case 'DELEGATED':
return {
icon: IconDelegated,

Check warning on line 148 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L148

Added line #L148 was not covered by tests
text: t('calendar', 'Invitation is delegated'),
}
case 'DECLINED':
if (this.isResource) {
return {
...declinedIcon,
text: t('calendar', 'Not available'),
}
}

Check warning on line 158 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L158

Added line #L158 was not covered by tests
if (this.isViewedByOrganizer) {
return {
...declinedIcon,
text: t('calendar', 'Invitation declined'),
}

Check warning on line 163 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L163

Added line #L163 was not covered by tests
}

return {
...declinedIcon,
text: t('calendar', 'Declined {organizerName}\'s invitation', {

Check warning on line 168 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L168

Added line #L168 was not covered by tests
organizerName: this.organizerDisplayName,
}),
}
}

// Schedule status is only present on the original event of the organizer

Check warning on line 174 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L174

Added line #L174 was not covered by tests
// TODO: Is this a bug or compliant with RFCs?
if (this.isViewedByOrganizer) {
// No status or status 1.0 indicate that the invitation is pending
if (!this.scheduleStatus || this.scheduleStatus === '1.0') {
if (this.isResource) {

Check warning on line 179 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L179

Added line #L179 was not covered by tests
return {
icon: IconNoResponse,
text: t('calendar', 'Availability will be checked'),
}
}

return {
icon: IconNoResponse,
text: t('calendar', 'Invitation will be sent'),
}
}

// Status 3.7, 3.8, 5.1, 5.2 and 5.3 indicate delivery failures.

Check warning on line 192 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L189-L192

Added lines #L189 - L192 were not covered by tests
// Could be due to insufficient permissions or some temporary failure.
if (this.scheduleStatus[0] === '3' || this.scheduleStatus[0] === '5') {
if (this.isResource) {
return {
icon: IconNoResponse,

Check warning on line 197 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L197

Added line #L197 was not covered by tests
text: t('calendar', 'Failed to check availability'),
}
}

return {
icon: IconNoResponse,

Check warning on line 203 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L202-L203

Added lines #L202 - L203 were not covered by tests
text: t('calendar', 'Failed to deliver invitation'),
}
}

return {
icon: IconNoResponse,
text: t('calendar', 'Awaiting response'),
}
}

Check warning on line 213 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L213

Added line #L213 was not covered by tests
if (this.isResource) {
return {
icon: IconNoResponse,
text: t('calendar', 'Checking availability'),
}
}

return {
icon: IconNoResponse,
text: t('calendar', 'Has not responded to {organizerName}\'s invitation yet', {

Check warning on line 223 in src/components/Editor/AvatarParticipationStatus.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/AvatarParticipationStatus.vue#L223

Added line #L223 was not covered by tests
organizerName: this.organizerDisplayName,
}),
}
},
},
}
</script>
<style lang="scss" scoped>
Expand Down
6 changes: 6 additions & 0 deletions src/components/Editor/Invitees/InviteesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
:is-shared-with-me="isSharedWithMe"
:organizer="calendarObjectInstance.organizer"
:organizer-selection="organizerSelection"
:is-viewed-by-organizer="isViewedByOrganizer"
@change-organizer="changeOrganizer" />
<InviteesListItem v-for="invitee in limitedInviteesWithoutOrganizer"
:key="invitee.email"
:attendee="invitee"
:is-read-only="isReadOnly"
:organizer-display-name="organizerDisplayName"
:members="invitee.members"
:is-viewed-by-organizer="isViewedByOrganizer"
@remove-attendee="removeAttendee" />
<div v-if="limit > 0 && inviteesWithoutOrganizer.length > limit"
class="invitees-list__more">
Expand Down Expand Up @@ -287,6 +289,10 @@ export default {

return false
},
isViewedByOrganizer() {
const organizerEmail = removeMailtoPrefix(this.calendarObjectInstance.organizer.uri)
return organizerEmail === this.principalsStore.getCurrentUserPrincipalEmail
},

Check warning on line 295 in src/components/Editor/Invitees/InviteesList.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/Invitees/InviteesList.vue#L295

Added line #L295 was not covered by tests
statusHeader() {
if (!this.isReadOnly) {
return ''
Expand Down
9 changes: 5 additions & 4 deletions src/components/Editor/Invitees/InviteesListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
:is-resource="false"
:avatar-link="avatarLink"
:participation-status="attendee.participationStatus"
:schedule-status="attendee.attendeeProperty.getParameterFirstValue('SCHEDULE-STATUS')"
:organizer-display-name="organizerDisplayName"
:common-name="commonName"
:is-group="isGroup" />
Expand Down Expand Up @@ -136,6 +137,10 @@ export default {
default: () => [],
required: false,
},
isViewedByOrganizer: {
type: Boolean,

Check warning on line 141 in src/components/Editor/Invitees/InviteesListItem.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/Invitees/InviteesListItem.vue#L140-L141

Added lines #L140 - L141 were not covered by tests
default: false,
},
},
data() {
return {
Expand Down Expand Up @@ -191,10 +196,6 @@ export default {
isNonParticipant() {
return this.attendee.role === 'NON-PARTICIPANT'
},
isViewedByOrganizer() {
// TODO: check if also viewed by organizer
return !this.isReadOnly
},
isGroup() {
return this.attendee.attendeeProperty.userType === 'GROUP'
},
Expand Down
8 changes: 5 additions & 3 deletions src/components/Editor/Invitees/OrganizerListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
:is-resource="isResource"
:common-name="commonName"
:organizer-display-name="commonName"
:schedule-status="organizer.attendeeProperty.getParameterFirstValue('SCHEDULE-STATUS')"
participation-status="ACCEPTED" />
<div class="invitees-list-item__displayname">
{{ commonName }}
Expand Down Expand Up @@ -77,6 +78,10 @@ export default {
type: Boolean,
required: true,
},
isViewedByOrganizer: {
type: Boolean,
default: false,
},
},
computed: {
/**
Expand All @@ -101,9 +106,6 @@ export default {

return ''
},
isViewedByOrganizer() {
return true
},
isResource() {
// The organizer does not have a tooltip
return false
Expand Down
6 changes: 6 additions & 0 deletions src/components/Editor/Resources/ResourceList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
:resource="resource"
:is-read-only="isReadOnly"
:organizer-display-name="organizerDisplayName"
:is-viewed-by-organizer="isViewedByOrganizer"
@remove-resource="removeResource" />

<NoAttendeesView v-if="isListEmpty && hasUserEmailAddress"
Expand All @@ -34,6 +35,7 @@
:is-read-only="false"
:organizer-display-name="organizerDisplayName"
:is-suggestion="true"
:is-viewed-by-organizer="isViewedByOrganizer"
@add-suggestion="addResource" />
</div>
</template>
Expand Down Expand Up @@ -105,6 +107,10 @@ export default {
const emailAddress = this.principalsStore.getCurrentUserPrincipal?.emailAddress
return !!emailAddress
},
isViewedByOrganizer() {
const organizerEmail = removeMailtoPrefix(this.calendarObjectInstance.organizer.uri)

Check warning on line 111 in src/components/Editor/Resources/ResourceList.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/Resources/ResourceList.vue#L111

Added line #L111 was not covered by tests
return organizerEmail === this.principalsStore.getCurrentUserPrincipalEmail
},

Check warning on line 113 in src/components/Editor/Resources/ResourceList.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Editor/Resources/ResourceList.vue#L113

Added line #L113 was not covered by tests
},
watch: {
resources() {
Expand Down
Loading

0 comments on commit ec5076f

Please sign in to comment.