forked from jsnbuchanan/crowd-funder-for-time-pwa
split out group-meeting member list into a separate component, and fix some edit/create mode titles
This commit is contained in:
107
src/components/MembersList.vue
Normal file
107
src/components/MembersList.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- Loading State -->
|
||||||
|
<div v-if="isLoading" class="flex justify-center items-center py-8">
|
||||||
|
<fa icon="spinner" class="fa-spin-pulse" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Members List -->
|
||||||
|
<div v-else class="space-y-4">
|
||||||
|
<div v-for="member in decryptedMembers" :key="member.memberId"
|
||||||
|
class="p-4 bg-gray-50 rounded-lg">
|
||||||
|
<h3 class="text-lg font-medium">{{ member.name }}</h3>
|
||||||
|
<p class="text-sm text-gray-600">{{ member.did }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-if="members.length === 0" class="text-center text-gray-500 py-4">
|
||||||
|
No members have joined this meeting yet
|
||||||
|
</p>
|
||||||
|
<p v-if="decryptedMembers.length < members.length" class="text-center text-red-600 py-4">
|
||||||
|
{{ decryptFailureMessage || "Your password failed. Please go back and try again." }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue, Prop } from 'vue-facing-decorator';
|
||||||
|
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from '@/db/index';
|
||||||
|
import { errorStringForLog, getHeaders, serverMessageForUser } from '@/libs/endorserServer';
|
||||||
|
import { decryptMessage } from '@/libs/crypto';
|
||||||
|
|
||||||
|
interface Member {
|
||||||
|
memberId: number;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DecryptedMember {
|
||||||
|
memberId: number;
|
||||||
|
name: string;
|
||||||
|
did: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class MembersList extends Vue {
|
||||||
|
@Prop({ required: true }) password!: string;
|
||||||
|
@Prop({ default: 'Your password failed. Please go back and try again.' }) decryptFailureMessage!: string;
|
||||||
|
|
||||||
|
decryptedMembers: DecryptedMember[] = [];
|
||||||
|
missingPassword = false;
|
||||||
|
isLoading = false;
|
||||||
|
members: Member[] = [];
|
||||||
|
activeDid = '';
|
||||||
|
apiServer = '';
|
||||||
|
|
||||||
|
async created() {
|
||||||
|
const settings = await retrieveSettingsForActiveAccount();
|
||||||
|
this.activeDid = settings.activeDid || '';
|
||||||
|
this.apiServer = settings.apiServer || '';
|
||||||
|
await this.fetchMembers();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchMembers() {
|
||||||
|
this.isLoading = true;
|
||||||
|
try {
|
||||||
|
const headers = await getHeaders(this.activeDid);
|
||||||
|
const response = await this.axios.get(
|
||||||
|
`${this.apiServer}/api/partner/groupOnboardMembers/`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data && response.data.data) {
|
||||||
|
this.members = response.data.data;
|
||||||
|
await this.decryptMemberContents();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleAndDb('Error fetching members: ' + errorStringForLog(error), true);
|
||||||
|
this.$emit('error', serverMessageForUser(error) || 'Failed to fetch members.');
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async decryptMemberContents() {
|
||||||
|
this.decryptedMembers = [];
|
||||||
|
|
||||||
|
if (!this.password) {
|
||||||
|
this.missingPassword = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const member of this.members) {
|
||||||
|
try {
|
||||||
|
const decryptedContent = await decryptMessage(member.content, this.password);
|
||||||
|
const content = JSON.parse(decryptedContent);
|
||||||
|
|
||||||
|
this.decryptedMembers.push({
|
||||||
|
memberId: member.memberId,
|
||||||
|
name: content.name,
|
||||||
|
did: content.did,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// do nothing, relying on the count of members to determine if there was an error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -138,6 +138,8 @@ export default class OnboardMeetingListView extends Vue {
|
|||||||
|
|
||||||
async submitPassword() {
|
async submitPassword() {
|
||||||
if (!this.selectedMeeting) {
|
if (!this.selectedMeeting) {
|
||||||
|
// this should never happen
|
||||||
|
logConsoleAndDb('No meeting selected when prompting for password, which should never happen.', true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,8 @@
|
|||||||
Back to Meetings
|
Back to Meetings
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Loading State -->
|
|
||||||
<div v-if="isLoading" class="flex justify-center items-center py-8">
|
|
||||||
<fa icon="spinner" class="fa-spin-pulse" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error State -->
|
<!-- Error State -->
|
||||||
<div v-else-if="errorMessage">
|
<div v-if="errorMessage">
|
||||||
<div class="text-center text-red-600 py-8">
|
<div class="text-center text-red-600 py-8">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</div>
|
||||||
@@ -33,20 +28,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Members List -->
|
<!-- Members List -->
|
||||||
<div v-else class="space-y-4">
|
<MembersList
|
||||||
<div v-for="member in decryptedMembers" :key="member.memberId"
|
v-else
|
||||||
class="p-4 bg-white rounded-lg shadow">
|
:password="password"
|
||||||
<h2 class="text-xl font-medium">{{ member.name }}</h2>
|
:decrypt-failure-message="'That password failed. You may be in the wrong meeting. Go back and try again.'"
|
||||||
<p class="text-sm text-gray-600">DID: {{ member.did }}</p>
|
@error="handleError"
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
<p v-if="decryptedMembers.length === 0 && !decryptFailure" class="text-center text-gray-500 py-8">
|
|
||||||
No members found in this meeting
|
|
||||||
</p>
|
|
||||||
<p v-if="decryptFailure" class="text-center text-red-600 py-8">
|
|
||||||
That password failed. You may be in the wrong meeting. Go back and try again.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -54,37 +41,17 @@
|
|||||||
import { Component, Vue } from 'vue-facing-decorator';
|
import { Component, Vue } from 'vue-facing-decorator';
|
||||||
import QuickNav from '@/components/QuickNav.vue';
|
import QuickNav from '@/components/QuickNav.vue';
|
||||||
import TopMessage from '@/components/TopMessage.vue';
|
import TopMessage from '@/components/TopMessage.vue';
|
||||||
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from '@/db/index';
|
import MembersList from '@/components/MembersList.vue';
|
||||||
import { errorStringForLog, getHeaders, serverMessageForUser } from '@/libs/endorserServer';
|
|
||||||
import { decryptMessage } from '@/libs/crypto';
|
|
||||||
|
|
||||||
interface Member {
|
|
||||||
memberId: number;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DecryptedMember {
|
|
||||||
memberId: number;
|
|
||||||
name: string;
|
|
||||||
did: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
QuickNav,
|
QuickNav,
|
||||||
TopMessage,
|
TopMessage,
|
||||||
|
MembersList,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class OnboardMeetingMembersView extends Vue {
|
export default class OnboardMeetingMembersView extends Vue {
|
||||||
$notify!: (notification: { group: string; type: string; title: string; text: string }, timeout?: number) => void;
|
|
||||||
|
|
||||||
activeDid = '';
|
|
||||||
apiServer = '';
|
|
||||||
decryptedMembers: DecryptedMember[] = [];
|
|
||||||
decryptFailure = false;
|
|
||||||
errorMessage = '';
|
errorMessage = '';
|
||||||
isLoading = false;
|
|
||||||
members: Member[] = [];
|
|
||||||
|
|
||||||
get groupId(): string {
|
get groupId(): string {
|
||||||
return this.$route.params.groupId as string;
|
return this.$route.params.groupId as string;
|
||||||
@@ -103,55 +70,10 @@ export default class OnboardMeetingMembersView extends Vue {
|
|||||||
this.errorMessage = 'The password is missing. Go back and try again.';
|
this.errorMessage = 'The password is missing. Go back and try again.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = await retrieveSettingsForActiveAccount();
|
|
||||||
this.activeDid = settings.activeDid || '';
|
|
||||||
this.apiServer = settings.apiServer || '';
|
|
||||||
await this.fetchMembers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchMembers() {
|
handleError(message: string) {
|
||||||
this.isLoading = true;
|
this.errorMessage = message;
|
||||||
this.errorMessage = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const headers = await getHeaders(this.activeDid);
|
|
||||||
const response = await this.axios.get(
|
|
||||||
`${this.apiServer}/api/partner/groupOnboardMembers/`,
|
|
||||||
{ headers }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.data && response.data.data) {
|
|
||||||
this.members = response.data.data;
|
|
||||||
await this.decryptMemberContents();
|
|
||||||
} else {
|
|
||||||
throw { response: response };
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logConsoleAndDb('Error fetching members: ' + errorStringForLog(error), true);
|
|
||||||
this.errorMessage = serverMessageForUser(error) || 'Failed to fetch members.';
|
|
||||||
} finally {
|
|
||||||
this.isLoading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async decryptMemberContents() {
|
|
||||||
this.decryptedMembers = [];
|
|
||||||
|
|
||||||
for (const member of this.members) {
|
|
||||||
try {
|
|
||||||
const decryptedContent = await decryptMessage(member.content, this.password);
|
|
||||||
const content = JSON.parse(decryptedContent);
|
|
||||||
|
|
||||||
this.decryptedMembers.push({
|
|
||||||
memberId: member.memberId,
|
|
||||||
name: content.name,
|
|
||||||
did: content.did,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
this.decryptFailure = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -9,7 +9,10 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<!-- Existing Meeting Section -->
|
<!-- Existing Meeting Section -->
|
||||||
<div v-if="!isLoading && currentMeeting != null && !isEditingOrCreating()" class="mt-8 p-4 border rounded-lg bg-white shadow">
|
<div
|
||||||
|
v-if="!isLoading && currentMeeting != null && !isInEditOrCreateMode()"
|
||||||
|
class="mt-8 p-4 border rounded-lg bg-white shadow"
|
||||||
|
>
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h2 class="text-2xl">Current Meeting</h2>
|
<h2 class="text-2xl">Current Meeting</h2>
|
||||||
@@ -19,7 +22,7 @@
|
|||||||
title="Edit Meeting"
|
title="Edit Meeting"
|
||||||
>
|
>
|
||||||
<fa icon="pen" class="fa-fw" />
|
<fa icon="pen" class="fa-fw" />
|
||||||
<span class="sr-only">Edit Meeting</span>
|
<span class="sr-only">{{ isInCreateMode() ? 'Create Meeting' : 'Edit Meeting' }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -43,16 +46,9 @@
|
|||||||
<div v-else class="text-red-600">
|
<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>
|
||||||
|
</div>
|
||||||
|
|
||||||
<router-link
|
|
||||||
:to="`/onboard-meeting-members/${currentMeeting.groupId}?password=${encodeURIComponent(currentMeeting.password || '')}`"
|
|
||||||
class="mt-4 inline-block px-4 py-2 text-blue-600"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Open page that meeting members see
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Delete Confirmation Dialog -->
|
<!-- 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">
|
||||||
@@ -77,10 +73,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Create/Edit Meeting Form -->
|
<!-- Create/Edit Meeting Form -->
|
||||||
<div v-if="!isLoading && newOrUpdatedMeeting != null" class="mt-8">
|
<div
|
||||||
<h2 class="text-2xl mb-4">{{ isEditingOrCreating() ? 'Edit Meeting' : 'Create New Meeting' }}</h2>
|
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. -->
|
<!-- 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="isEditingOrCreating() ? updateMeeting() : createMeeting()" class="space-y-4">
|
<form @submit.prevent="isInCreateMode() ? createMeeting() : updateMeeting()" class="space-y-4">
|
||||||
<div>
|
<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
|
<input
|
||||||
@@ -134,10 +133,10 @@
|
|||||||
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"
|
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"
|
:disabled="isLoading"
|
||||||
>
|
>
|
||||||
{{ isLoading ? (isEditingOrCreating() ? 'Updating...' : 'Creating...') : (isEditingOrCreating() ? 'Update Meeting' : 'Create Meeting') }}
|
{{ isLoading ? (isInCreateMode() ? 'Creating...' : 'Updating...' ) : (isInCreateMode() ? 'Create Meeting' : 'Update Meeting') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="isEditingOrCreating()"
|
v-if="isInEditOrCreateMode()"
|
||||||
type="button"
|
type="button"
|
||||||
@click="cancelEditing"
|
@click="cancelEditing"
|
||||||
class="w-full bg-slate-500 text-white px-4 py-2 rounded-md hover:bg-slate-600"
|
class="w-full bg-slate-500 text-white px-4 py-2 rounded-md hover:bg-slate-600"
|
||||||
@@ -147,6 +146,27 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Members Section -->
|
||||||
|
<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 || '')}`"
|
||||||
|
class="inline-block px-4 text-blue-600"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Open page that meeting members see
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<MembersList
|
||||||
|
:password="currentMeeting.password || ''"
|
||||||
|
decrypt-failure-message="Unable to decrypt some member information. Please check your password."
|
||||||
|
@error="handleMembersError"
|
||||||
|
class="mt-8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-else-if="isLoading">
|
<div v-else-if="isLoading">
|
||||||
<div class="flex justify-center items-center h-full">
|
<div class="flex justify-center items-center h-full">
|
||||||
<fa icon="spinner" class="fa-spin-pulse" />
|
<fa icon="spinner" class="fa-spin-pulse" />
|
||||||
@@ -159,6 +179,7 @@
|
|||||||
import { Component, Vue } from 'vue-facing-decorator';
|
import { Component, Vue } from 'vue-facing-decorator';
|
||||||
import QuickNav from '@/components/QuickNav.vue';
|
import QuickNav from '@/components/QuickNav.vue';
|
||||||
import TopMessage from '@/components/TopMessage.vue';
|
import TopMessage from '@/components/TopMessage.vue';
|
||||||
|
import MembersList from '@/components/MembersList.vue';
|
||||||
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from '@/db/index';
|
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from '@/db/index';
|
||||||
import { errorStringForLog, getHeaders, serverMessageForUser } from '@/libs/endorserServer';
|
import { errorStringForLog, getHeaders, serverMessageForUser } from '@/libs/endorserServer';
|
||||||
import { encryptMessage } from '@/libs/crypto';
|
import { encryptMessage } from '@/libs/crypto';
|
||||||
@@ -182,6 +203,7 @@ interface MeetingSetupInfo {
|
|||||||
components: {
|
components: {
|
||||||
QuickNav,
|
QuickNav,
|
||||||
TopMessage,
|
TopMessage,
|
||||||
|
MembersList,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class OnboardMeetingView extends Vue {
|
export default class OnboardMeetingView extends Vue {
|
||||||
@@ -211,7 +233,11 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isEditingOrCreating(): boolean {
|
isInCreateMode(): boolean {
|
||||||
|
return this.newOrUpdatedMeeting != null && this.currentMeeting == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInEditOrCreateMode(): boolean {
|
||||||
return this.newOrUpdatedMeeting != null;
|
return this.newOrUpdatedMeeting != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,13 +282,11 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response?.data?.data) {
|
if (response?.data?.data) {
|
||||||
console.log('Response data', response.data.data);
|
|
||||||
this.currentMeeting = {
|
this.currentMeeting = {
|
||||||
...response.data.data,
|
...response.data.data,
|
||||||
userFullName: this.fullName,
|
userFullName: this.fullName,
|
||||||
password: this.currentMeeting?.password || "",
|
password: this.currentMeeting?.password || "",
|
||||||
};
|
};
|
||||||
console.log('Current meeting', this.currentMeeting);
|
|
||||||
} else {
|
} else {
|
||||||
// no meeting found
|
// no meeting found
|
||||||
this.newOrUpdatedMeeting = this.blankMeeting();
|
this.newOrUpdatedMeeting = this.blankMeeting();
|
||||||
@@ -345,6 +369,7 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
...this.newOrUpdatedMeeting,
|
...this.newOrUpdatedMeeting,
|
||||||
groupId: response.data.success.groupId,
|
groupId: response.data.success.groupId,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.newOrUpdatedMeeting = null;
|
this.newOrUpdatedMeeting = null;
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
@@ -436,7 +461,6 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
startEditing() {
|
startEditing() {
|
||||||
// Populate form with existing meeting data
|
// Populate form with existing meeting data
|
||||||
if (this.currentMeeting) {
|
if (this.currentMeeting) {
|
||||||
console.log('Current meeting', this.currentMeeting);
|
|
||||||
const localExpiresAt = new Date(this.currentMeeting.expiresAt);
|
const localExpiresAt = new Date(this.currentMeeting.expiresAt);
|
||||||
this.newOrUpdatedMeeting = {
|
this.newOrUpdatedMeeting = {
|
||||||
name: this.currentMeeting.name,
|
name: this.currentMeeting.name,
|
||||||
@@ -520,14 +544,12 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data && response.data.success) {
|
if (response.data && response.data.success) {
|
||||||
console.log('Updated meeting', response.data);
|
|
||||||
// Update the current meeting with only the necessary fields
|
// Update the current meeting with only the necessary fields
|
||||||
this.currentMeeting = {
|
this.currentMeeting = {
|
||||||
...this.newOrUpdatedMeeting,
|
...this.newOrUpdatedMeeting,
|
||||||
groupId: this.currentMeeting?.groupId || "",
|
groupId: this.currentMeeting?.groupId || "",
|
||||||
};
|
};
|
||||||
this.newOrUpdatedMeeting = null;
|
this.newOrUpdatedMeeting = null;
|
||||||
console.log('Updated meeting now', this.currentMeeting);
|
|
||||||
} else {
|
} else {
|
||||||
throw { response: response };
|
throw { response: response };
|
||||||
}
|
}
|
||||||
@@ -547,5 +569,17 @@ export default class OnboardMeetingView extends Vue {
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMembersError(message: string) {
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: 'alert',
|
||||||
|
type: 'danger',
|
||||||
|
title: 'Error',
|
||||||
|
text: message,
|
||||||
|
},
|
||||||
|
5000
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
Reference in New Issue
Block a user