Skip to content

Commit

Permalink
#16281 adding todo items for affiliation invitation - access request (b…
Browse files Browse the repository at this point in the history
…cgov#531)

* #16281 - adding todo items for affiliation invitations.

* making sure existing tc-s are passing with added changes.

* adding test for visual display of todo items for affiliation invitations (request access)

* fixed test (override feature flag). Updated position of imports.

* Removing axios dependency in tests. Replaced by stubing service call.

* Updates to better fit style of existing code, and to make some function calls intents more clear.

* Updating falsy/truthy check to see if todo item is affiliation invite.

* Add generic error dialoge. Invoke it on failed axios call for fetching affiliation invitations.

* Removed some leftovers from copypasting the dialog.

* Updates to modal to generic error modal to match the new design.
  • Loading branch information
hfekete authored Jul 26, 2023
1 parent 28a2864 commit 638977a
Show file tree
Hide file tree
Showing 7 changed files with 417 additions and 16 deletions.
136 changes: 132 additions & 4 deletions src/components/Dashboard/TodoList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
attach="#todo-list"
/>

<GenericErrorDialog
:dialog="fetchAffiliationInvitationsErrorDialog"
attach="#todo-list"
icon-color="error"
summary="Error retrieving affiliation invitations."
@okay="fetchAffiliationInvitationsErrorDialog=false"
>
<template #description>
<div class="text-center">
There was an error retrieving pending affiliation invitations.
<br> Please try again later.
</div>
</template>
</GenericErrorDialog>

<DeleteErrorDialog
:dialog="deleteErrorDialog"
:errors="deleteErrors"
Expand Down Expand Up @@ -91,9 +106,10 @@
>
<!-- NB: blocks below are mutually exclusive, and order is important -->

<!-- affiliation invitation todo task -->
<!-- new todo task -->
<div
v-if="isStatusNew(item)"
v-if="isStatusNew(item) || isAffiliationInvitation(item)"
class="todo-subtitle"
>
<span v-if="!!item.subtitle">{{ item.subtitle }}</span>
Expand Down Expand Up @@ -201,7 +217,32 @@
</div> <!-- end of other subtitles -->
</div> <!-- end of todo label -->

<div class="list-item__actions">
<!-- Affiliation invite todo actions -->
<div
v-if="isAffiliationInvitation(item)"
class="list-item__actions"
style="flex: 1"
>
<v-btn
class="ma-1 affiliation-invitation-action-button"
color="primary"
@click.native.stop="authorizeAffiliationInvitation(true, item)"
>
<span>Authorize</span>
</v-btn>
<v-btn
class="ma-1 affiliation-invitation-action-button"
outlined
color="primary"
@click.native.stop="authorizeAffiliationInvitation(false, item)"
>
<span>Do not authorize</span>
</v-btn>
</div>
<div
v-else
class="list-item__actions"
>
<div style="width:100%">
<!-- BEN/BC/CCC/ULC AR special case -->
<template v-if="isBenBcCccUlc && item.enabled && isTypeAnnualReport(item) && isStatusNew(item)">
Expand Down Expand Up @@ -407,6 +448,11 @@
<v-expansion-panel-content>
<!-- NB: blocks below are mutually exclusive, and order is important -->

<!-- affiliation invitation todo details -->
<template v-if="isAffiliationInvitation(item)">
<AffiliationInvitationDetails :affiliationInvitationTodo="item" />
</template>

<!-- does this item have an incomplete payment? -->
<template v-if="isStatusDraft(item) && isPayError(item)">
<PaymentIncomplete :filing="item" />
Expand Down Expand Up @@ -505,9 +551,10 @@
import { Component, Mixins, Prop, Watch } from 'vue-property-decorator'
import { Action, Getter } from 'pinia-class'
import axios from '@/axios-auth'
import { navigate } from '@/utils'
import { GetFeatureFlag, navigate } from '@/utils'
import { CancelPaymentErrorDialog, ConfirmDialog, DeleteErrorDialog } from '@/components/dialogs'
import { NameRequestInfo, ContactInfo } from '@/components/common'
import AffiliationInvitationDetails from './TodoList/AffiliationInvitationDetails.vue'
import ConversionDetails from './TodoList/ConversionDetails.vue'
import CorrectionComment from './TodoList/CorrectionComment.vue'
import PaymentIncomplete from './TodoList/PaymentIncomplete.vue'
Expand All @@ -517,20 +564,23 @@ import PaymentPendingOnlineBanking from './TodoList/PaymentPendingOnlineBanking.
import PaymentUnsuccessful from './TodoList/PaymentUnsuccessful.vue'
import VueRouter from 'vue-router'
import { AllowableActionsMixin, DateMixin, EnumMixin } from '@/mixins'
import { EnumUtilities, LegalServices, PayServices } from '@/services/'
import { AuthServices, EnumUtilities, LegalServices, PayServices } from '@/services/'
import { AllowableActions, CorpTypeCd, FilingNames, FilingStatus, FilingSubTypes, FilingTypes, Routes } from '@/enums'
import { ActionBindingIF, ApiFilingIF, ApiTaskIF, BusinessWarningIF, ConfirmDialogType, TodoItemIF, TodoListResourceIF }
from '@/interfaces'
import { GetCorpFullDescription } from '@bcrs-shared-components/corp-type-module'
import { useBusinessStore, useConfigurationStore, useFilingHistoryListStore, useRootStore } from '@/stores'
import GenericErrorDialog from '@/components/dialogs/GenericErrorDialog.vue'
@Component({
components: {
// dialogs
CancelPaymentErrorDialog,
ConfirmDialog,
DeleteErrorDialog,
GenericErrorDialog,
// components
AffiliationInvitationDetails,
ConversionDetails,
CorrectionComment,
NameRequestInfo,
Expand Down Expand Up @@ -560,11 +610,13 @@ export default class TodoList extends Mixins(AllowableActionsMixin, DateMixin, E
panel: number = null // currently expanded panel
checkTimer: number = null
inProcessFiling: number = null
fetchAffiliationInvitationsErrorDialog = false
@Prop({ default: null }) readonly highlightId!: number
@Getter(useConfigurationStore) getAuthWebUrl!: string
@Getter(useConfigurationStore) getBusinessUrl!: string
@Getter(useConfigurationStore) getAuthApiUrl!: string
@Getter(useBusinessStore) getBusinessWarnings!: Array<BusinessWarningIF>
@Getter(useConfigurationStore) getCreateUrl!: string
@Getter(useConfigurationStore) getEditUrl!: string
Expand All @@ -585,6 +637,7 @@ export default class TodoList extends Mixins(AllowableActionsMixin, DateMixin, E
// for template
readonly EnumUtilities = EnumUtilities
readonly FilingStatus = FilingStatus
/** Whether a COA is pending. */
get isCoaPending (): boolean {
Expand Down Expand Up @@ -656,6 +709,7 @@ export default class TodoList extends Mixins(AllowableActionsMixin, DateMixin, E
if (this.isStatusDraft(item) && this.isTypeRegistration(item) &&
item.nameRequest) return true
if (this.isStatusPending(item)) return true
if (this.isAffiliationInvitation(item)) return true
return false
}
Expand All @@ -672,6 +726,8 @@ export default class TodoList extends Mixins(AllowableActionsMixin, DateMixin, E
async loadData (): Promise<void> {
this.todoItems = []
await this.loadAffiliationInvitationsTodo()
// create 'task items' list from 'tasks' array from API
for (const task of this.getTasks) {
if (task.task?.todo) {
Expand Down Expand Up @@ -796,6 +852,73 @@ export default class TodoList extends Mixins(AllowableActionsMixin, DateMixin, E
}
}
/** check if the item is actually affiliation invite todo; (not a regular filing item). */
isAffiliationInvitation (item): boolean {
// check that affiliation invitation details are set
return !!item.affiliationInvitationDetails
}
authorizeAffiliationInvitation (isAuthorized, affiliationInvitationTodo): void {
AuthServices.authorizeAffiliationInvitation(
this.getAuthApiUrl,
this.getIdentifier,
affiliationInvitationTodo.affiliationId,
isAuthorized
)
.then()
.catch(err => {
// eslint-disable-line no-console
console.log('failed the call for authorization of affiliation invitation', err)
})
}
/** Loads Org Affiliations invitation todo **/
async loadAffiliationInvitationsTodo () {
// feature-flag-it
if (!GetFeatureFlag('enable-affiliation-invitation-request-access')) {
return
}
function buildTodoItemIfFromAffiliationInvitation (affiliationInvitation, order: number) {
const newTodo: TodoItemIF = {
draftTitle: null,
enabled: true,
filingId: -1, // not a filing
name: null,
order: order,
subtitle: `From: ${affiliationInvitation.fromOrg.name}`,
status: null,
title: 'Request for authorization to manage this business',
affiliationInvitationDetails: {
id: affiliationInvitation.id,
fromOrgName: affiliationInvitation.fromOrg.name,
additionalMessage: affiliationInvitation.additionalMessage
}
}
return newTodo
}
// load all the invitations here and push them into todo items
const response =
await AuthServices.fetchAffiliationInvitations(this.getAuthApiUrl, this.getIdentifier)
.catch((err) => {
console.log('Error fetching affiliation invitations for todo', err) // eslint-disable-line no-console
this.fetchAffiliationInvitationsErrorDialog = true
return null
})
const affiliationInvitations = response?.data?.affiliationInvitations ? response.data.affiliationInvitations : []
affiliationInvitations.forEach(affiliationInvitation => {
// only active (pending) affiliation invitations are to be converted into todo item for now
if (affiliationInvitation.type === 'RequestAccess' && affiliationInvitation.status === 'ACTIVE') {
const newTodo = buildTodoItemIfFromAffiliationInvitation(affiliationInvitation, this.todoItems.length)
this.todoItems.push(newTodo)
}
})
}
/** Loads a NEW Conversion todo. */
loadConversionTodo (task: ApiTaskIF): void {
if (!this.isRoleStaff) return // regular users can't file a new conversion
Expand Down Expand Up @@ -1843,6 +1966,11 @@ export default class TodoList extends Mixins(AllowableActionsMixin, DateMixin, E
<style lang="scss" scoped>
@import "@/assets/styles/theme.scss";
.affiliation-invitation-action-button {
width: 45%;
float: right;
}
.todo-item {
// disable expansion generally
pointer-events: none;
Expand Down
49 changes: 49 additions & 0 deletions src/components/Dashboard/TodoList/AffiliationInvitationDetails.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<template>
<div
class="affiliation-invite"
>
<p>
The following account is requesting authorization to manage Registries activities for
{{ getEntityName || 'Unknown name' }}:
</p>
<p><strong>{{ affiliationInvitationTodo.affiliationInvitationDetails.fromOrgName }}</strong></p>
<p>
Only authorize this account if you recognize it.
</p>
<p>
Allowing {{ affiliationInvitationTodo.affiliationInvitationDetails.fromOrgName }} to manage all Registries
activities for {{ getEntityName || 'Unknown name' }} will
allow it to do the following (including but not limited to):
</p>
<ul>
<li>Appoint and cease directors,</li>
<li>File dissolution,</li>
<li>File Annual Reports,</li>
<li>Change the records office of the business, and</li>
<li>Authorize other accounts to manage this business.</li>
</ul>
<div
v-if="affiliationInvitationTodo.affiliationInvitationDetails.additionalMessage"
class="mt-4"
>
<p>
Requestor message:
</p>
<p>{{ affiliationInvitationTodo.affiliationInvitationDetails.additionalMessage }}</p>
</div>
</div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { TodoItemIF } from '@/interfaces'
import { Getter } from 'pinia-class'
import { useBusinessStore } from '@/stores'
@Component({})
export default class AffiliationInvitation extends Vue {
@Prop({ required: true }) readonly affiliationInvitationTodo!: TodoItemIF
@Getter(useBusinessStore) getEntityName!: string
}
</script>
95 changes: 95 additions & 0 deletions src/components/dialogs/GenericErrorDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<template>
<v-dialog
v-model="dialog"
width="45rem"
persistent
:attach="attach"
content-class="generic-error-dialog"
>
<v-card>
<v-container>
<v-row justify="center">
<v-col cols="2"></v-col>
<v-col
cols="8"
lg="8"
class="text-center"
>
<v-icon
size="48"
:color="iconColor"
class="mb-6"
>
{{ icon }}
</v-icon>
</v-col>
<v-col cols="2">
<v-icon
color="primary"
class="mb-6 float-right"
@click="okay()"
>
mdi-close
</v-icon>
</v-col>
</v-row>
<v-row justify="center">
<v-col
cols="12"
lg="8"
class="text-center"
>
<h1
class="mb-5"
style="font-size: 24px"
>
{{ summary }}
</h1>
<p
class="mb-9"
style="font-size: 16px"
>
<slot name="description">
{{ description }}
</slot>
</p>
<slot name="actions">
<v-btn
large
link
color="primary"
@click="okay()"
>
OK
</v-btn>
</slot>
</v-col>
</v-row>
</v-container>
</v-card>
</v-dialog>
</template>

<script lang="ts">
import { Component, Prop, Emit, Vue } from 'vue-property-decorator'
@Component({
components: {}
})
export default class GenericErrorDialog extends Vue {
@Prop({ default: '' }) private summary: string
@Prop({ default: '' }) private description: string
@Prop({ default: 'mdi-information-outline' }) private icon: string
@Prop({ default: 'primary' }) private iconColor: string
/** Prop to display the dialog. */
@Prop({ default: false }) readonly dialog!: boolean
/** Prop to provide attachment selector. */
@Prop({ default: '' }) readonly attach!: string
// Pass click event to parent.
@Emit() okay () {
}
}
</script>
7 changes: 7 additions & 0 deletions src/interfaces/todo-item-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,11 @@ export interface TodoItemIF {

// dissolutions and restorations only
filingSubType?: FilingSubTypes

// affiliation invitations only
affiliationInvitationDetails?: {
id: number
fromOrgName: string
additionalMessage?: string
}
}
Loading

0 comments on commit 638977a

Please sign in to comment.