Skip to content

Commit

Permalink
Send Invite notification
Browse files Browse the repository at this point in the history
send invite if scoping to a user
email template
calls backend and CHES
  • Loading branch information
TimCsaky committed May 17, 2024
1 parent 8ee6db0 commit e570d2e
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 59 deletions.
108 changes: 66 additions & 42 deletions frontend/src/components/common/InviteButton.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { computed, ref, onMounted, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import ShareLinkContent from '@/components/object/share/ShareLinkContent.vue';
Expand All @@ -15,28 +15,31 @@ import {
useToast,
InputSwitch
} from '@/lib/primevue';
import { Spinner } from '@/components/layout';
import { Permissions } from '@/utils/constants';
import { inviteService } from '@/services';
import { useAuthStore, useConfigStore, useObjectStore, usePermissionStore, useBucketStore } from '@/store';
import type { Ref } from 'vue';
import type { COMSObject, Bucket } from '@/types';
// Props
type Props = {
bucketId?: string;
objectId?: string;
labelText: string;
restricted?: boolean;
};
export type InviteFormData = {
type InviteFormData = {
email?: string;
bucketId?: string;
expiresAt?: number;
objectId?: string;
permCodes?: Array<string>;
};
// TODO: define a stricter type using a union of COMSObject | Bucket
type Resource = any;
const props = withDefaults(defineProps<Props>(), {
bucketId: '',
objectId: '',
Expand All @@ -48,22 +51,26 @@ const props = withDefaults(defineProps<Props>(), {
const objectStore = useObjectStore();
const bucketStore = useBucketStore();
const { getConfig } = storeToRefs(useConfigStore());
const { getUserId } = storeToRefs(useAuthStore());
const { getUserId, getUser } = storeToRefs(useAuthStore());
const permissionStore = usePermissionStore();
const toast = useToast();
// State
const obj: Ref<COMSObject | undefined> = ref(undefined);
const bucket: Ref<Bucket | undefined> = ref(undefined);
const isRestricted: Ref<boolean> = ref(props.restricted);
const resourceType = props.objectId ? ref('object') : ref('bucket');
const resource: Ref<Resource | undefined> = computed(() => {
return props.objectId
? objectStore.getObject(props.objectId)
: bucketStore.getBucket(props.bucketId);
});
const isRestricted: Ref<boolean> = ref(false);
const inviteLoading: Ref<boolean> = ref(false);
const showInviteLink: Ref<boolean> = ref(false);
const hasManagePermission: Ref<boolean> = computed(() => {
return props.objectId
? permissionStore.isObjectActionAllowed(props.objectId, getUserId.value, Permissions.MANAGE, obj.value?.bucketId)
return resourceType.value === 'object'
? permissionStore.isObjectActionAllowed(props.objectId, getUserId.value, Permissions.MANAGE, resource.value?.bucketId)

Check warning on line 71 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

This line has a length of 122. Maximum allowed is 120

Check warning on line 71 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

This line has a length of 122. Maximum allowed is 120

Check warning on line 71 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

This line has a length of 122. Maximum allowed is 120

Check warning on line 71 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

This line has a length of 122. Maximum allowed is 120

Check warning on line 71 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

This line has a length of 122. Maximum allowed is 120

Check warning on line 71 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

This line has a length of 122. Maximum allowed is 120
: permissionStore.isBucketActionAllowed(props.bucketId, getUserId.value, Permissions.MANAGE);
});
const resource = props.objectId ? 'file' : 'bucket';
// Share link
const inviteLink: Ref<string> = ref('');
Expand Down Expand Up @@ -93,7 +100,7 @@ const displayInviteDialog = ref(false);
// Permissions selection
const selectedOptions = computed(() => {
return resource === 'bucket' ? bucketPermCodes : objectPermCodes;
return resourceType.value === 'bucket' ? bucketPermCodes : objectPermCodes;
});
// Share link
const bcBoxLink = computed(() => {
Expand All @@ -116,34 +123,46 @@ watch(isRestricted, () => {
});
//Action
async function sendInvite() {
async function invite() {
inviteLoading.value = true;
try {
// set expiry date
let expiresAt;
if (formData.value.expiresAt) {
expiresAt = Math.floor(Date.now() / 1000) + formData.value.expiresAt;
}
const permissionToken = await inviteService.createInvite(
props.bucketId,
formData.value.email,
// put input email addresses into an array
const emails = isRestricted.value && formData.value.email ? [formData.value.email] : [];
// TODO: add perms to users already in the system
// generate invites (for emails not already in the system)
const invites = await inviteService.createInvites(
resourceType.value,
resource.value,
getUser.value?.profile,
emails,
expiresAt,
props.objectId,
formData.value.permCodes
formData.value.permCodes,
);
inviteLink.value = `${window.location.origin}/invite/${permissionToken.data}`;
toast.success('', 'Invite link is created.');
showInviteLink.value = true;
// if not restricting to an email, show link
if(emails.length == 0) {
inviteLink.value = `${window.location.origin}/invite/${invites[0].token}`;
toast.success('', 'Invite link created.');
showInviteLink.value = true;
}
// else show email confirmation
else {
// TODO: output report (list of invites sent, CHES trx ID (?))
toast.success('', 'Invite notifications sent.', {life: 5000});
showInviteLink.value = false;
}
} catch (error: any) {
toast.error('', 'Error getting token');
console.log('d', error.response);

Check warning on line 161 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement

Check warning on line 161 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 161 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

Check warning on line 161 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 161 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement

Check warning on line 161 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement
toast.error('Creating Invite', error.response.data.detail, {life: 0});
}
inviteLoading.value = false;
}
onMounted(() => {
if (props.objectId) {
obj.value = objectStore.getObject(props.objectId);
} else if (props.bucketId) {
bucket.value = bucketStore.getBucket(props.bucketId);
}
});
</script>

<template>
Expand All @@ -163,7 +182,7 @@ onMounted(() => {
</template>

<h3 class="bcbox-info-dialog-subhead">
{{ obj?.name || bucket?.bucketName }}
{{ resourceType === 'object' ? resource?.name : resource?.bucketName }}
</h3>

<ul class="mb-4">
Expand All @@ -179,7 +198,7 @@ onMounted(() => {
</ul>
<TabView>
<TabPanel
v-if="obj?.public"
v-if="resource?.public"
header="Direct public file link"
>
<ShareLinkContent
Expand All @@ -190,7 +209,7 @@ onMounted(() => {
<!-- Disable for public until unauthed File Details page works -->
<TabPanel
header="Share link"
:disabled="obj?.public"
:disabled="resource?.public"
>
<ShareLinkContent
:share-link="bcBoxLink"
Expand All @@ -200,6 +219,7 @@ onMounted(() => {
<TabPanel
header="Invite link"
:disabled="!hasManagePermission"

Check warning on line 221 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Expected 1 line break before closing bracket, but 2 line breaks found

Check warning on line 221 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Expected 1 line break before closing bracket, but 2 line breaks found

Check warning on line 221 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Expected 1 line break before closing bracket, but 2 line breaks found

Check warning on line 221 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Expected 1 line break before closing bracket, but 2 line breaks found

Check warning on line 221 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Expected 1 line break before closing bracket, but 2 line breaks found

Check warning on line 221 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Expected 1 line break before closing bracket, but 2 line breaks found

>
<h3 class="mt-1 mb-2">{{ (props.labelText) }} Invite</h3>
<p>Make invite available for</p>
Expand Down Expand Up @@ -246,34 +266,38 @@ onMounted(() => {
</div>
</div>
<p class="mt-4 mb-2">Restrict to user's email</p>
<div class="p-inputgroup mb-4">
<!-- <p class="mt-4 mb-2">Restrict invite to a user signing in to BCBox with the following email address</p> -->
<div class="flex flex-column gap-2">
<InputSwitch
v-model="isRestricted"
class="mt-1"
/>
<!-- Add email validation -->
<InputText
v-show="isRestricted"
v-model="formData.email"
name="inviteEmail"
placeholder="Enter email"
required
type="email"
class="ml-5 mr-8"
class="mt-2 max-w-30rem"
/>
</div>
<div class="p-inputgroup mb-4">
<small v-show="isRestricted" id="inviteEmail-help">The Invite will be emailed to this person</small>

Check warning on line 283 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'id' should be on a new line

Check warning on line 283 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'id' should be on a new line

Check warning on line 283 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'id' should be on a new line

Check warning on line 283 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'id' should be on a new line

Check warning on line 283 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'id' should be on a new line

Check warning on line 283 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'id' should be on a new line
</div>
<div class="my-4 inline-flex p-0">
<Button
class="p-button p-button-primary"
@click="sendInvite"
class="p-button p-button-primary mr-3"
@click="invite"
:disabled="inviteLoading"

Check warning on line 289 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Attribute ":disabled" should go before "@click"

Check warning on line 289 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Attribute ":disabled" should go before "@click"

Check warning on line 289 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Attribute ":disabled" should go before "@click"

Check warning on line 289 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Attribute ":disabled" should go before "@click"

Check warning on line 289 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Attribute ":disabled" should go before "@click"

Check warning on line 289 in frontend/src/components/common/InviteButton.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Attribute ":disabled" should go before "@click"
>
<font-awesome-icon
icon="fa fa-envelope"
class="mr-2"
/>
Generate invite link
</Button>
</div>
<Spinner
v-if="inviteLoading"
/>
</div><br />
<ShareLinkContent
v-if="showInviteLink"
Expand Down
33 changes: 33 additions & 0 deletions frontend/src/services/interceptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@ import { AuthService, ConfigService } from './index';

import type { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';

/**
* @function appAxios
* Returns an Axios instance for the application API
* @param {AxiosRequestConfig} options Axios request config options
* @returns {AxiosInstance} An axios instance
*/
export function appAxios(options: AxiosRequestConfig = {}): AxiosInstance {

console.log(new ConfigService().getConfig().apiPath);

Check warning on line 15 in frontend/src/services/interceptors.ts

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement

Check warning on line 15 in frontend/src/services/interceptors.ts

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 15 in frontend/src/services/interceptors.ts

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

Check warning on line 15 in frontend/src/services/interceptors.ts

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 15 in frontend/src/services/interceptors.ts

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement

Check warning on line 15 in frontend/src/services/interceptors.ts

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

const instance = axios.create({
baseURL: '/' + new ConfigService().getConfig().apiPath,
timeout: 10000,
...options
});

instance.interceptors.request.use(
async (cfg: InternalAxiosRequestConfig) => {
const authService = new AuthService();
const user = await authService.getUser();
if (!!user && !user.expired) {
cfg.headers.Authorization = `Bearer ${user.access_token}`;
}
return Promise.resolve(cfg);
},
(error: Error) => {
return Promise.reject(error);
}
);

return instance;
}

/**
* @function comsAxios
* Returns an Axios instance for the COMS API
Expand Down
Loading

0 comments on commit e570d2e

Please sign in to comment.