|
|
@ -22,7 +22,9 @@ |
|
|
|
title="Edit Meeting" |
|
|
|
> |
|
|
|
<fa icon="pen" class="fa-fw" /> |
|
|
|
<span class="sr-only">{{ isInCreateMode() ? 'Create Meeting' : 'Edit Meeting' }}</span> |
|
|
|
<span class="sr-only">{{ |
|
|
|
isInCreateMode() ? "Create Meeting" : "Edit Meeting" |
|
|
|
}}</span> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
<button |
|
|
@ -33,28 +35,41 @@ |
|
|
|
title="Delete Meeting" |
|
|
|
> |
|
|
|
<fa icon="trash-can" class="fa-fw" /> |
|
|
|
<span class="sr-only">{{ isDeleting ? 'Deleting...' : 'Delete Meeting' }}</span> |
|
|
|
<span class="sr-only">{{ |
|
|
|
isDeleting ? "Deleting..." : "Delete Meeting" |
|
|
|
}}</span> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
<div class="space-y-2"> |
|
|
|
<p><strong>Name:</strong> {{ currentMeeting.name }}</p> |
|
|
|
<p><strong>Expires:</strong> {{ formatExpirationTime(currentMeeting.expiresAt) }}</p> |
|
|
|
<p> |
|
|
|
<strong>Expires:</strong> |
|
|
|
{{ formatExpirationTime(currentMeeting.expiresAt) }} |
|
|
|
</p> |
|
|
|
|
|
|
|
<div v-if="currentMeeting.password" class="mt-4"> |
|
|
|
<p class="text-gray-600">Share the password with the people you want to onboard.</p> |
|
|
|
<p class="text-gray-600"> |
|
|
|
Share the password with the people you want to onboard. |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
<div v-else class="text-red-600"> |
|
|
|
Your copy of the password is not saved. Edit the meeting, or delete it and create a new meeting. |
|
|
|
Your copy of the password is not saved. Edit the meeting, or delete it |
|
|
|
and create a new meeting. |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<!-- Delete Confirmation Dialog --> |
|
|
|
<div v-if="showDeleteConfirm" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4"> |
|
|
|
<div |
|
|
|
v-if="showDeleteConfirm" |
|
|
|
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4" |
|
|
|
> |
|
|
|
<div class="bg-white rounded-lg p-6 max-w-sm w-full"> |
|
|
|
<h3 class="text-lg font-medium mb-4">Delete Meeting?</h3> |
|
|
|
<p class="text-gray-600 mb-6">This action cannot be undone. Are you sure you want to delete this meeting?</p> |
|
|
|
<p class="text-gray-600 mb-6"> |
|
|
|
This action cannot be undone. Are you sure you want to delete this |
|
|
|
meeting? |
|
|
|
</p> |
|
|
|
<div class="flex justify-between space-x-4"> |
|
|
|
<button |
|
|
|
@click="showDeleteConfirm = false" |
|
|
@ -74,14 +89,27 @@ |
|
|
|
|
|
|
|
<!-- Create/Edit Meeting Form --> |
|
|
|
<div |
|
|
|
v-if="!isLoading && isInEditOrCreateMode() && newOrUpdatedMeeting != null /* duplicate check is for typechecks */" |
|
|
|
v-if=" |
|
|
|
!isLoading && |
|
|
|
isInEditOrCreateMode() && |
|
|
|
newOrUpdatedMeeting != null /* duplicate check is for typechecks */ |
|
|
|
" |
|
|
|
class="mt-8" |
|
|
|
> |
|
|
|
<h2 class="text-2xl mb-4">{{ isInCreateMode() ? 'Create New Meeting' : 'Edit Meeting' }}</h2> |
|
|
|
<!-- This is my first form. Not sure whether I like it or not; gotta see if the browser benefits extend to the native app. --> |
|
|
|
<form @submit.prevent="isInCreateMode() ? createMeeting() : updateMeeting()" class="space-y-4"> |
|
|
|
<h2 class="text-2xl mb-4"> |
|
|
|
{{ isInCreateMode() ? "Create New Meeting" : "Edit Meeting" }} |
|
|
|
</h2> |
|
|
|
<!-- This is my first form. Not sure if I like it; will see if the browser benefits extend to the native app. --> |
|
|
|
<form |
|
|
|
@submit.prevent="isInCreateMode() ? createMeeting() : updateMeeting()" |
|
|
|
class="space-y-4" |
|
|
|
> |
|
|
|
<div> |
|
|
|
<label for="meetingName" class="block text-sm font-medium text-gray-700">Meeting Name</label> |
|
|
|
<label |
|
|
|
for="meetingName" |
|
|
|
class="block text-sm font-medium text-gray-700" |
|
|
|
>Meeting Name</label |
|
|
|
> |
|
|
|
<input |
|
|
|
id="meetingName" |
|
|
|
v-model="newOrUpdatedMeeting.name" |
|
|
@ -93,7 +121,11 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<div> |
|
|
|
<label for="expirationTime" class="block text-sm font-medium text-gray-700">Meeting Expiration Time</label> |
|
|
|
<label |
|
|
|
for="expirationTime" |
|
|
|
class="block text-sm font-medium text-gray-700" |
|
|
|
>Meeting Expiration Time</label |
|
|
|
> |
|
|
|
<input |
|
|
|
id="expirationTime" |
|
|
|
v-model="newOrUpdatedMeeting.expiresAt" |
|
|
@ -105,7 +137,9 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<div> |
|
|
|
<label for="password" class="block text-sm font-medium text-gray-700">Meeting Password</label> |
|
|
|
<label for="password" class="block text-sm font-medium text-gray-700" |
|
|
|
>Meeting Password</label |
|
|
|
> |
|
|
|
<input |
|
|
|
id="password" |
|
|
|
v-model="newOrUpdatedMeeting.password" |
|
|
@ -117,7 +151,9 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<div> |
|
|
|
<label for="userName" class="block text-sm font-medium text-gray-700">Your Name</label> |
|
|
|
<label for="userName" class="block text-sm font-medium text-gray-700" |
|
|
|
>Your Name</label |
|
|
|
> |
|
|
|
<input |
|
|
|
id="userName" |
|
|
|
v-model="newOrUpdatedMeeting.userFullName" |
|
|
@ -133,7 +169,15 @@ |
|
|
|
class="w-full bg-gradient-to-b from-green-400 to-green-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md hover:from-green-500 hover:to-green-800" |
|
|
|
:disabled="isLoading" |
|
|
|
> |
|
|
|
{{ isLoading ? (isInCreateMode() ? 'Creating...' : 'Updating...' ) : (isInCreateMode() ? 'Create Meeting' : 'Update Meeting') }} |
|
|
|
{{ |
|
|
|
isLoading |
|
|
|
? isInCreateMode() |
|
|
|
? "Creating..." |
|
|
|
: "Updating..." |
|
|
|
: isInCreateMode() |
|
|
|
? "Create Meeting" |
|
|
|
: "Update Meeting" |
|
|
|
}} |
|
|
|
</button> |
|
|
|
<button |
|
|
|
v-if="isInEditOrCreateMode()" |
|
|
@ -147,12 +191,15 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Members Section --> |
|
|
|
<div v-if="!isLoading && currentMeeting != null" class="mt-8 p-4 border rounded-lg bg-white shadow"> |
|
|
|
<div |
|
|
|
v-if="!isLoading && currentMeeting != null" |
|
|
|
class="mt-8 p-4 border rounded-lg bg-white shadow" |
|
|
|
> |
|
|
|
<div class="flex items-center justify-between mb-4"> |
|
|
|
<h2 class="text-2xl">Meeting Members</h2> |
|
|
|
</div> |
|
|
|
<router-link |
|
|
|
:to="`/onboard-meeting-members/${currentMeeting.groupId}?password=${encodeURIComponent(currentMeeting.password || '')}`" |
|
|
|
:to="onboardMeetingMembersLink()" |
|
|
|
class="inline-block px-4 text-blue-600" |
|
|
|
target="_blank" |
|
|
|
> |
|
|
@ -176,16 +223,20 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script lang="ts"> |
|
|
|
import { Component, Vue } from 'vue-facing-decorator'; |
|
|
|
import QuickNav from '@/components/QuickNav.vue'; |
|
|
|
import TopMessage from '@/components/TopMessage.vue'; |
|
|
|
import MembersList from '@/components/MembersList.vue'; |
|
|
|
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from '@/db/index'; |
|
|
|
import { errorStringForLog, getHeaders, serverMessageForUser } from '@/libs/endorserServer'; |
|
|
|
import { encryptMessage } from '@/libs/crypto'; |
|
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
|
import TopMessage from "@/components/TopMessage.vue"; |
|
|
|
import MembersList from "@/components/MembersList.vue"; |
|
|
|
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "@/db/index"; |
|
|
|
import { |
|
|
|
errorStringForLog, |
|
|
|
getHeaders, |
|
|
|
serverMessageForUser, |
|
|
|
} from "@/libs/endorserServer"; |
|
|
|
import { encryptMessage } from "@/libs/crypto"; |
|
|
|
|
|
|
|
interface ServerMeeting { |
|
|
|
groupId: string; // from the server |
|
|
|
groupId: number; // from the server |
|
|
|
name: string; // from the server |
|
|
|
expiresAt: string; // from the server |
|
|
|
userFullName?: string; // from the user's session |
|
|
@ -207,16 +258,19 @@ interface MeetingSetupInfo { |
|
|
|
}, |
|
|
|
}) |
|
|
|
export default class OnboardMeetingView extends Vue { |
|
|
|
$notify!: (notification: { group: string; type: string; title: string; text: string }, timeout?: number) => void; |
|
|
|
$notify!: ( |
|
|
|
notification: { group: string; type: string; title: string; text: string }, |
|
|
|
timeout?: number, |
|
|
|
) => void; |
|
|
|
|
|
|
|
currentMeeting: ServerMeeting | null = null; |
|
|
|
newOrUpdatedMeeting: MeetingSetupInfo | null = null; |
|
|
|
activeDid = ''; |
|
|
|
apiServer = ''; |
|
|
|
activeDid = ""; |
|
|
|
apiServer = ""; |
|
|
|
isDeleting = false; |
|
|
|
isLoading = true; |
|
|
|
showDeleteConfirm = false; |
|
|
|
fullName = ''; |
|
|
|
fullName = ""; |
|
|
|
get minDateTime() { |
|
|
|
const now = new Date(); |
|
|
|
now.setMinutes(now.getMinutes() + 5); // Set minimum 5 minutes in the future |
|
|
@ -225,9 +279,9 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
|
|
|
|
async created() { |
|
|
|
const settings = await retrieveSettingsForActiveAccount(); |
|
|
|
this.activeDid = settings.activeDid || ''; |
|
|
|
this.apiServer = settings.apiServer || ''; |
|
|
|
this.fullName = settings.firstName || ''; |
|
|
|
this.activeDid = settings.activeDid || ""; |
|
|
|
this.apiServer = settings.apiServer || ""; |
|
|
|
this.fullName = settings.firstName || ""; |
|
|
|
|
|
|
|
await this.fetchCurrentMeeting(); |
|
|
|
this.isLoading = false; |
|
|
@ -255,10 +309,10 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
// Format a date object to YYYY-MM-DDTHH:mm format for datetime-local input |
|
|
|
private formatDateForInput(date: Date): string { |
|
|
|
const year = date.getFullYear(); |
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0'); |
|
|
|
const day = String(date.getDate()).padStart(2, '0'); |
|
|
|
const hours = String(date.getHours()).padStart(2, '0'); |
|
|
|
const minutes = String(date.getMinutes()).padStart(2, '0'); |
|
|
|
const month = String(date.getMonth() + 1).padStart(2, "0"); |
|
|
|
const day = String(date.getDate()).padStart(2, "0"); |
|
|
|
const hours = String(date.getHours()).padStart(2, "0"); |
|
|
|
const minutes = String(date.getMinutes()).padStart(2, "0"); |
|
|
|
|
|
|
|
return `${year}-${month}-${day}T${hours}:${minutes}`; |
|
|
|
} |
|
|
@ -266,10 +320,10 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
blankMeeting(): MeetingSetupInfo { |
|
|
|
return { |
|
|
|
// no groupId yet |
|
|
|
name: '', |
|
|
|
name: "", |
|
|
|
expiresAt: this.getDefaultExpirationTime(), |
|
|
|
userFullName: this.fullName, |
|
|
|
password: this.currentMeeting?.password || "", |
|
|
|
password: (this.currentMeeting?.password as string) || "", |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
@ -277,8 +331,8 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
try { |
|
|
|
const headers = await getHeaders(this.activeDid); |
|
|
|
const response = await this.axios.get( |
|
|
|
this.apiServer + '/api/partner/groupOnboard', |
|
|
|
{ headers } |
|
|
|
this.apiServer + "/api/partner/groupOnboard", |
|
|
|
{ headers }, |
|
|
|
); |
|
|
|
|
|
|
|
if (response?.data?.data) { |
|
|
@ -302,7 +356,9 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
|
|
|
|
try { |
|
|
|
if (!this.newOrUpdatedMeeting) { |
|
|
|
throw Error('There was no meeting data to create. We should never get here.'); |
|
|
|
throw Error( |
|
|
|
"There was no meeting data to create. We should never get here.", |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// Convert local time to UTC for comparison and server submission |
|
|
@ -311,57 +367,59 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
if (localExpiresAt <= now) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'warning', |
|
|
|
title: 'Invalid Time', |
|
|
|
text: 'Select a future time for the meeting expiration.', |
|
|
|
group: "alert", |
|
|
|
type: "warning", |
|
|
|
title: "Invalid Time", |
|
|
|
text: "Select a future time for the meeting expiration.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!this.newOrUpdatedMeeting.userFullName) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'warning', |
|
|
|
title: 'Invalid Name', |
|
|
|
text: 'Please enter your name.', |
|
|
|
group: "alert", |
|
|
|
type: "warning", |
|
|
|
title: "Invalid Name", |
|
|
|
text: "Please enter your name.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!this.newOrUpdatedMeeting.password) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'warning', |
|
|
|
title: 'Invalid Password', |
|
|
|
text: 'Please enter a password.', |
|
|
|
group: "alert", |
|
|
|
type: "warning", |
|
|
|
title: "Invalid Password", |
|
|
|
text: "Please enter a password.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// create content with user's name and DID encrypted with password |
|
|
|
const content = { |
|
|
|
name: this.newOrUpdatedMeeting.userFullName, |
|
|
|
did: this.activeDid, |
|
|
|
}; |
|
|
|
const encryptedContent = await encryptMessage(JSON.stringify(content), this.newOrUpdatedMeeting.password); |
|
|
|
const encryptedContent = await encryptMessage( |
|
|
|
JSON.stringify(content), |
|
|
|
this.newOrUpdatedMeeting.password, |
|
|
|
); |
|
|
|
|
|
|
|
const headers = await getHeaders(this.activeDid); |
|
|
|
const response = await this.axios.post( |
|
|
|
this.apiServer + '/api/partner/groupOnboard', |
|
|
|
this.apiServer + "/api/partner/groupOnboard", |
|
|
|
{ |
|
|
|
name: this.newOrUpdatedMeeting.name, |
|
|
|
expiresAt: localExpiresAt.toISOString(), |
|
|
|
content: encryptedContent, |
|
|
|
}, |
|
|
|
{ headers } |
|
|
|
{ headers }, |
|
|
|
); |
|
|
|
|
|
|
|
if (response.data && response.data.success) { |
|
|
@ -373,27 +431,32 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
this.newOrUpdatedMeeting = null; |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'success', |
|
|
|
title: 'Success', |
|
|
|
text: 'Meeting created.', |
|
|
|
group: "alert", |
|
|
|
type: "success", |
|
|
|
title: "Success", |
|
|
|
text: "Meeting created.", |
|
|
|
}, |
|
|
|
3000 |
|
|
|
3000, |
|
|
|
); |
|
|
|
} else { |
|
|
|
throw { response: response }; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
logConsoleAndDb('Error creating meeting: ' + errorStringForLog(error), true); |
|
|
|
logConsoleAndDb( |
|
|
|
"Error creating meeting: " + errorStringForLog(error), |
|
|
|
true, |
|
|
|
); |
|
|
|
const errorMessage = serverMessageForUser(error); |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'danger', |
|
|
|
title: 'Error', |
|
|
|
text: errorMessage || 'Failed to create meeting. Try reloading or submitting again.', |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Error", |
|
|
|
text: |
|
|
|
errorMessage || |
|
|
|
"Failed to create meeting. Try reloading or submitting again.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
} finally { |
|
|
|
this.isLoading = false; |
|
|
@ -403,14 +466,16 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
formatExpirationTime(expiresAt: string): string { |
|
|
|
const expiration = new Date(expiresAt); // Server time is in UTC |
|
|
|
const now = new Date(); |
|
|
|
const diffHours = Math.round((expiration.getTime() - now.getTime()) / (1000 * 60 * 60)); |
|
|
|
const diffHours = Math.round( |
|
|
|
(expiration.getTime() - now.getTime()) / (1000 * 60 * 60), |
|
|
|
); |
|
|
|
|
|
|
|
if (diffHours < 0) { |
|
|
|
return 'Expired'; |
|
|
|
return "Expired"; |
|
|
|
} else if (diffHours < 1) { |
|
|
|
return 'Less than an hour'; |
|
|
|
return "Less than an hour"; |
|
|
|
} else if (diffHours === 1) { |
|
|
|
return '1 hour'; |
|
|
|
return "1 hour"; |
|
|
|
} else { |
|
|
|
return `${diffHours} hours`; |
|
|
|
} |
|
|
@ -424,10 +489,9 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
this.isDeleting = true; |
|
|
|
try { |
|
|
|
const headers = await getHeaders(this.activeDid); |
|
|
|
await this.axios.delete( |
|
|
|
this.apiServer + '/api/partner/groupOnboard', |
|
|
|
{ headers } |
|
|
|
); |
|
|
|
await this.axios.delete(this.apiServer + "/api/partner/groupOnboard", { |
|
|
|
headers, |
|
|
|
}); |
|
|
|
|
|
|
|
this.currentMeeting = null; |
|
|
|
this.newOrUpdatedMeeting = this.blankMeeting(); |
|
|
@ -435,23 +499,23 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
|
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'success', |
|
|
|
title: 'Success', |
|
|
|
text: 'Meeting deleted successfully.', |
|
|
|
group: "alert", |
|
|
|
type: "success", |
|
|
|
title: "Success", |
|
|
|
text: "Meeting deleted successfully.", |
|
|
|
}, |
|
|
|
3000 |
|
|
|
3000, |
|
|
|
); |
|
|
|
} catch (error) { |
|
|
|
console.error('Error deleting meeting:', error); |
|
|
|
console.error("Error deleting meeting:", error); |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'danger', |
|
|
|
title: 'Error', |
|
|
|
text: serverMessageForUser(error) || 'Failed to delete meeting.', |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Error", |
|
|
|
text: serverMessageForUser(error) || "Failed to delete meeting.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
} finally { |
|
|
|
this.isDeleting = false; |
|
|
@ -465,11 +529,13 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
this.newOrUpdatedMeeting = { |
|
|
|
name: this.currentMeeting.name, |
|
|
|
expiresAt: this.formatDateForInput(localExpiresAt), |
|
|
|
userFullName: this.currentMeeting.userFullName || '', |
|
|
|
password: this.currentMeeting.password || '', |
|
|
|
userFullName: this.currentMeeting.userFullName || "", |
|
|
|
password: this.currentMeeting.password || "", |
|
|
|
}; |
|
|
|
} else { |
|
|
|
console.error('There is no current meeting to edit. We should never get here.'); |
|
|
|
console.error( |
|
|
|
"There is no current meeting to edit. We should never get here.", |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -481,7 +547,7 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
async updateMeeting() { |
|
|
|
this.isLoading = true; |
|
|
|
if (!this.newOrUpdatedMeeting) { |
|
|
|
throw Error('There was no meeting data to update.'); |
|
|
|
throw Error("There was no meeting data to update."); |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
@ -491,36 +557,36 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
if (localExpiresAt <= now) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'warning', |
|
|
|
title: 'Invalid Time', |
|
|
|
text: 'Select a future time for the meeting expiration.', |
|
|
|
group: "alert", |
|
|
|
type: "warning", |
|
|
|
title: "Invalid Time", |
|
|
|
text: "Select a future time for the meeting expiration.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!this.newOrUpdatedMeeting.userFullName) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'warning', |
|
|
|
title: 'Invalid Name', |
|
|
|
text: 'Please enter your name.', |
|
|
|
group: "alert", |
|
|
|
type: "warning", |
|
|
|
title: "Invalid Name", |
|
|
|
text: "Please enter your name.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!this.newOrUpdatedMeeting.password) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'warning', |
|
|
|
title: 'Invalid Password', |
|
|
|
text: 'Please enter a password.', |
|
|
|
group: "alert", |
|
|
|
type: "warning", |
|
|
|
title: "Invalid Password", |
|
|
|
text: "Please enter a password.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
@ -529,56 +595,73 @@ export default class OnboardMeetingView extends Vue { |
|
|
|
name: this.newOrUpdatedMeeting.userFullName, |
|
|
|
did: this.activeDid, |
|
|
|
}; |
|
|
|
const encryptedContent = await encryptMessage(JSON.stringify(content), this.newOrUpdatedMeeting.password); |
|
|
|
const encryptedContent = await encryptMessage( |
|
|
|
JSON.stringify(content), |
|
|
|
this.newOrUpdatedMeeting.password, |
|
|
|
); |
|
|
|
|
|
|
|
const headers = await getHeaders(this.activeDid); |
|
|
|
const response = await this.axios.put( |
|
|
|
this.apiServer + '/api/partner/groupOnboard', |
|
|
|
this.apiServer + "/api/partner/groupOnboard", |
|
|
|
{ |
|
|
|
// the groupId is in the currentMeeting but it's not necessary while users only have one meeting |
|
|
|
name: this.newOrUpdatedMeeting.name, |
|
|
|
expiresAt: localExpiresAt.toISOString(), |
|
|
|
content: encryptedContent, |
|
|
|
}, |
|
|
|
{ headers } |
|
|
|
{ headers }, |
|
|
|
); |
|
|
|
|
|
|
|
if (response.data && response.data.success) { |
|
|
|
// Update the current meeting with only the necessary fields |
|
|
|
this.currentMeeting = { |
|
|
|
...this.newOrUpdatedMeeting, |
|
|
|
groupId: this.currentMeeting?.groupId || "", |
|
|
|
groupId: (this.currentMeeting?.groupId as number) || -1, |
|
|
|
}; |
|
|
|
this.newOrUpdatedMeeting = null; |
|
|
|
} else { |
|
|
|
throw { response: response }; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
logConsoleAndDb('Error updating meeting: ' + errorStringForLog(error), true); |
|
|
|
logConsoleAndDb( |
|
|
|
"Error updating meeting: " + errorStringForLog(error), |
|
|
|
true, |
|
|
|
); |
|
|
|
const errorMessage = serverMessageForUser(error); |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'danger', |
|
|
|
title: 'Error', |
|
|
|
text: errorMessage || 'Failed to update meeting. Try reloading or submitting again.', |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Error", |
|
|
|
text: |
|
|
|
errorMessage || |
|
|
|
"Failed to update meeting. Try reloading or submitting again.", |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
} finally { |
|
|
|
this.isLoading = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
onboardMeetingMembersLink(): string { |
|
|
|
if (this.currentMeeting) { |
|
|
|
return `/onboard-meeting-members/${this.currentMeeting?.groupId}?password=${encodeURIComponent( |
|
|
|
this.currentMeeting?.password || "", |
|
|
|
)}`; |
|
|
|
} |
|
|
|
return ""; |
|
|
|
} |
|
|
|
|
|
|
|
handleMembersError(message: string) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: 'alert', |
|
|
|
type: 'danger', |
|
|
|
title: 'Error', |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Error", |
|
|
|
text: message, |
|
|
|
}, |
|
|
|
5000 |
|
|
|
5000, |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|