|
@ -115,6 +115,9 @@ import { |
|
|
serverMessageForUser, |
|
|
serverMessageForUser, |
|
|
} from "../libs/endorserServer"; |
|
|
} from "../libs/endorserServer"; |
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; |
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; |
|
|
|
|
|
import { logger } from "@/utils/logger"; |
|
|
|
|
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; |
|
|
|
|
|
import { NotificationIface } from "@/constants/app"; |
|
|
|
|
|
|
|
|
interface Meeting { |
|
|
interface Meeting { |
|
|
name: string; |
|
|
name: string; |
|
@ -129,19 +132,11 @@ interface Meeting { |
|
|
mixins: [PlatformServiceMixin], |
|
|
mixins: [PlatformServiceMixin], |
|
|
}) |
|
|
}) |
|
|
export default class OnboardMeetingListView extends Vue { |
|
|
export default class OnboardMeetingListView extends Vue { |
|
|
$notify!: ( |
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
|
|
notification: { |
|
|
|
|
|
group: string; |
|
|
|
|
|
type: string; |
|
|
|
|
|
title: string; |
|
|
|
|
|
text: string; |
|
|
|
|
|
onYes?: () => void; |
|
|
|
|
|
yesText?: string; |
|
|
|
|
|
}, |
|
|
|
|
|
timeout?: number, |
|
|
|
|
|
) => void; |
|
|
|
|
|
$router!: Router; |
|
|
$router!: Router; |
|
|
|
|
|
|
|
|
|
|
|
notify!: ReturnType<typeof createNotifyHelpers>; |
|
|
|
|
|
|
|
|
activeDid = ""; |
|
|
activeDid = ""; |
|
|
apiServer = ""; |
|
|
apiServer = ""; |
|
|
attendingMeeting: Meeting | null = null; |
|
|
attendingMeeting: Meeting | null = null; |
|
@ -153,30 +148,66 @@ export default class OnboardMeetingListView extends Vue { |
|
|
selectedMeeting: Meeting | null = null; |
|
|
selectedMeeting: Meeting | null = null; |
|
|
showPasswordDialog = false; |
|
|
showPasswordDialog = false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Vue lifecycle hook - component initialization |
|
|
|
|
|
* |
|
|
|
|
|
* Initializes the component by loading user settings and fetching available |
|
|
|
|
|
* onboarding meetings. This method is called when the component is created |
|
|
|
|
|
* and sets up all necessary data for the meeting list interface. |
|
|
|
|
|
* |
|
|
|
|
|
* Workflow: |
|
|
|
|
|
* 1. Initialize notification system using createNotifyHelpers |
|
|
|
|
|
* 2. Load user account settings (DID, API server, registration status) |
|
|
|
|
|
* 3. Fetch available onboarding meetings from the server |
|
|
|
|
|
* |
|
|
|
|
|
* Dependencies: |
|
|
|
|
|
* - PlatformServiceMixin for settings access ($accountSettings) |
|
|
|
|
|
* - Server API for meeting data (fetchMeetings) |
|
|
|
|
|
* |
|
|
|
|
|
* Error Handling: |
|
|
|
|
|
* - Server errors during meeting fetch are handled in fetchMeetings() |
|
|
|
|
|
* |
|
|
|
|
|
* @author Matthew Raymer |
|
|
|
|
|
*/ |
|
|
async created() { |
|
|
async created() { |
|
|
const settings = await this.$accountSettings(); |
|
|
this.notify = createNotifyHelpers(this.$notify); |
|
|
|
|
|
|
|
|
if (settings?.activeDid) { |
|
|
// Load user account settings |
|
|
try { |
|
|
const settings = await this.$accountSettings(); |
|
|
// Verify database settings are accessible |
|
|
|
|
|
await this.$query("SELECT * FROM settings WHERE accountDid = ?", [ |
|
|
|
|
|
settings.activeDid, |
|
|
|
|
|
]); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
logger.error("Error checking database settings:", error); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.activeDid = settings?.activeDid || ""; |
|
|
this.activeDid = settings?.activeDid || ""; |
|
|
this.apiServer = settings?.apiServer || ""; |
|
|
this.apiServer = settings?.apiServer || ""; |
|
|
this.firstName = settings?.firstName || ""; |
|
|
this.firstName = settings?.firstName || ""; |
|
|
this.isRegistered = !!settings?.isRegistered; |
|
|
this.isRegistered = !!settings?.isRegistered; |
|
|
|
|
|
|
|
|
if (this.isRegistered) { |
|
|
await this.fetchMeetings(); |
|
|
await this.fetchMeetings(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fetches available onboarding meetings from the server |
|
|
|
|
|
* |
|
|
|
|
|
* This method retrieves the list of onboarding meetings that the user can join. |
|
|
|
|
|
* It first checks if the user is already attending a meeting, and if so, |
|
|
|
|
|
* displays that meeting instead of the full list. |
|
|
|
|
|
* |
|
|
|
|
|
* Workflow: |
|
|
|
|
|
* 1. Check if user is already attending a meeting (groupOnboardMember endpoint) |
|
|
|
|
|
* 2. If attending: Fetch meeting details and display single meeting view |
|
|
|
|
|
* 3. If not attending: Fetch all available meetings (groupsOnboarding endpoint) |
|
|
|
|
|
* 4. Handle loading states and error conditions |
|
|
|
|
|
* |
|
|
|
|
|
* API Endpoints Used: |
|
|
|
|
|
* - GET /api/partner/groupOnboardMember - Check current attendance |
|
|
|
|
|
* - GET /api/partner/groupOnboard/{id} - Get meeting details |
|
|
|
|
|
* - GET /api/partner/groupsOnboarding - Get all available meetings |
|
|
|
|
|
* |
|
|
|
|
|
* State Management: |
|
|
|
|
|
* - Sets isLoading flag during API calls |
|
|
|
|
|
* - Updates attendingMeeting or meetings array |
|
|
|
|
|
* - Handles error states with user notifications |
|
|
|
|
|
* |
|
|
|
|
|
* @author Matthew Raymer |
|
|
|
|
|
*/ |
|
|
async fetchMeetings() { |
|
|
async fetchMeetings() { |
|
|
this.isLoading = true; |
|
|
this.isLoading = true; |
|
|
try { |
|
|
try { |
|
@ -226,20 +257,36 @@ export default class OnboardMeetingListView extends Vue { |
|
|
"Error fetching meetings: " + errorStringForLog(error), |
|
|
"Error fetching meetings: " + errorStringForLog(error), |
|
|
true, |
|
|
true, |
|
|
); |
|
|
); |
|
|
this.$notify( |
|
|
this.notify.error( |
|
|
{ |
|
|
serverMessageForUser(error) || "Failed to fetch meetings.", |
|
|
group: "alert", |
|
|
TIMEOUTS.LONG, |
|
|
type: "danger", |
|
|
|
|
|
title: "Error", |
|
|
|
|
|
text: serverMessageForUser(error) || "Failed to fetch meetings.", |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
); |
|
|
} finally { |
|
|
} finally { |
|
|
this.isLoading = false; |
|
|
this.isLoading = false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Opens the password dialog for joining a meeting |
|
|
|
|
|
* |
|
|
|
|
|
* This method initiates the process of joining an onboarding meeting by |
|
|
|
|
|
* opening a modal dialog that prompts the user for the meeting password. |
|
|
|
|
|
* The dialog is focused and ready for input when displayed. |
|
|
|
|
|
* |
|
|
|
|
|
* Workflow: |
|
|
|
|
|
* 1. Clear any previous password input |
|
|
|
|
|
* 2. Store the selected meeting for later use |
|
|
|
|
|
* 3. Show the password dialog modal |
|
|
|
|
|
* 4. Focus the password input field for immediate typing |
|
|
|
|
|
* |
|
|
|
|
|
* UI State Changes: |
|
|
|
|
|
* - Sets showPasswordDialog to true (shows modal) |
|
|
|
|
|
* - Clears password field for fresh input |
|
|
|
|
|
* - Stores selectedMeeting for password submission |
|
|
|
|
|
* |
|
|
|
|
|
* @param meeting - The meeting object the user wants to join |
|
|
|
|
|
* @author Matthew Raymer |
|
|
|
|
|
*/ |
|
|
promptPassword(meeting: Meeting) { |
|
|
promptPassword(meeting: Meeting) { |
|
|
this.password = ""; |
|
|
this.password = ""; |
|
|
this.selectedMeeting = meeting; |
|
|
this.selectedMeeting = meeting; |
|
@ -252,12 +299,61 @@ export default class OnboardMeetingListView extends Vue { |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Cancels the password dialog and resets state |
|
|
|
|
|
* |
|
|
|
|
|
* This method handles the cancellation of the meeting password dialog. |
|
|
|
|
|
* It cleans up the dialog state and resets all related variables to |
|
|
|
|
|
* their initial state, ensuring a clean slate for future dialog interactions. |
|
|
|
|
|
* |
|
|
|
|
|
* State Cleanup: |
|
|
|
|
|
* - Clears password input field |
|
|
|
|
|
* - Removes selected meeting reference |
|
|
|
|
|
* - Hides password dialog modal |
|
|
|
|
|
* |
|
|
|
|
|
* This ensures that if the user reopens the dialog, they start with |
|
|
|
|
|
* a fresh state without any leftover data from previous attempts. |
|
|
|
|
|
* |
|
|
|
|
|
* @author Matthew Raymer |
|
|
|
|
|
*/ |
|
|
cancelPasswordDialog() { |
|
|
cancelPasswordDialog() { |
|
|
this.password = ""; |
|
|
this.password = ""; |
|
|
this.selectedMeeting = null; |
|
|
this.selectedMeeting = null; |
|
|
this.showPasswordDialog = false; |
|
|
this.showPasswordDialog = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Submits the password and joins the selected meeting |
|
|
|
|
|
* |
|
|
|
|
|
* This method handles the complete workflow of joining an onboarding meeting. |
|
|
|
|
|
* It encrypts the user's member data with the provided password and sends |
|
|
|
|
|
* it to the server to register the user as a meeting participant. |
|
|
|
|
|
* |
|
|
|
|
|
* Workflow: |
|
|
|
|
|
* 1. Validate that a meeting is selected (safety check) |
|
|
|
|
|
* 2. Create member data object with user information |
|
|
|
|
|
* 3. Encrypt member data using the meeting password |
|
|
|
|
|
* 4. Send encrypted data to server via groupOnboardMember endpoint |
|
|
|
|
|
* 5. On success: Navigate to meeting members view with credentials |
|
|
|
|
|
* 6. On failure: Show error notification to user |
|
|
|
|
|
* |
|
|
|
|
|
* Data Encryption: |
|
|
|
|
|
* - Member data includes: name, DID, registration status |
|
|
|
|
|
* - Data is encrypted using the meeting password for security |
|
|
|
|
|
* - Encrypted data is sent to server for verification |
|
|
|
|
|
* |
|
|
|
|
|
* Navigation: |
|
|
|
|
|
* - On successful join: Redirects to onboard-meeting-members view |
|
|
|
|
|
* - Passes groupId, password, and memberId as route parameters |
|
|
|
|
|
* - Allows user to see other meeting participants |
|
|
|
|
|
* |
|
|
|
|
|
* Error Handling: |
|
|
|
|
|
* - Invalid passwords result in server rejection |
|
|
|
|
|
* - Network errors are caught and displayed to user |
|
|
|
|
|
* - All errors are logged for debugging purposes |
|
|
|
|
|
* |
|
|
|
|
|
* @author Matthew Raymer |
|
|
|
|
|
*/ |
|
|
async submitPassword() { |
|
|
async submitPassword() { |
|
|
if (!this.selectedMeeting) { |
|
|
if (!this.selectedMeeting) { |
|
|
// this should never happen |
|
|
// this should never happen |
|
@ -316,69 +412,93 @@ export default class OnboardMeetingListView extends Vue { |
|
|
"Error joining meeting: " + errorStringForLog(error), |
|
|
"Error joining meeting: " + errorStringForLog(error), |
|
|
true, |
|
|
true, |
|
|
); |
|
|
); |
|
|
this.$notify( |
|
|
this.notify.error( |
|
|
{ |
|
|
serverMessageForUser(error) || "You failed to join the meeting.", |
|
|
group: "alert", |
|
|
TIMEOUTS.LONG, |
|
|
type: "danger", |
|
|
|
|
|
title: "Error", |
|
|
|
|
|
text: |
|
|
|
|
|
serverMessageForUser(error) || "You failed to join the meeting.", |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Prompts user to confirm leaving the current meeting |
|
|
|
|
|
* |
|
|
|
|
|
* This method initiates the process of leaving an onboarding meeting. |
|
|
|
|
|
* It shows a confirmation dialog to prevent accidental departures, |
|
|
|
|
|
* then handles the server-side removal and UI updates. |
|
|
|
|
|
* |
|
|
|
|
|
* Workflow: |
|
|
|
|
|
* 1. Display confirmation dialog asking user to confirm departure |
|
|
|
|
|
* 2. On confirmation: Send DELETE request to groupOnboardMember endpoint |
|
|
|
|
|
* 3. On success: Clear attending meeting state and refresh meeting list |
|
|
|
|
|
* 4. Show success notification to user |
|
|
|
|
|
* 5. On failure: Show error notification with details |
|
|
|
|
|
* |
|
|
|
|
|
* Server Interaction: |
|
|
|
|
|
* - DELETE /api/partner/groupOnboardMember - Removes user from meeting |
|
|
|
|
|
* - Requires authentication headers for user verification |
|
|
|
|
|
* - Server handles the actual removal from meeting database |
|
|
|
|
|
* |
|
|
|
|
|
* State Management: |
|
|
|
|
|
* - Clears attendingMeeting when successfully left |
|
|
|
|
|
* - Refreshes meetings list to show updated availability |
|
|
|
|
|
* - Updates UI to show meeting list instead of single meeting |
|
|
|
|
|
* |
|
|
|
|
|
* User Experience: |
|
|
|
|
|
* - Confirmation prevents accidental departures |
|
|
|
|
|
* - Clear feedback on success/failure |
|
|
|
|
|
* - Seamless transition back to meeting list |
|
|
|
|
|
* |
|
|
|
|
|
* @author Matthew Raymer |
|
|
|
|
|
*/ |
|
|
async leaveMeeting() { |
|
|
async leaveMeeting() { |
|
|
this.$notify( |
|
|
this.notify.confirm( |
|
|
{ |
|
|
"Are you sure you want to leave this meeting?", |
|
|
group: "modal", |
|
|
async () => { |
|
|
type: "confirm", |
|
|
try { |
|
|
title: "Leave Meeting", |
|
|
const headers = await getHeaders(this.activeDid); |
|
|
text: "Are you sure you want to leave this meeting?", |
|
|
await this.axios.delete( |
|
|
onYes: async () => { |
|
|
this.apiServer + "/api/partner/groupOnboardMember", |
|
|
try { |
|
|
{ headers }, |
|
|
const headers = await getHeaders(this.activeDid); |
|
|
); |
|
|
await this.axios.delete( |
|
|
|
|
|
this.apiServer + "/api/partner/groupOnboardMember", |
|
|
this.attendingMeeting = null; |
|
|
{ headers }, |
|
|
await this.fetchMeetings(); |
|
|
); |
|
|
|
|
|
|
|
|
this.notify.success("You left the meeting.", TIMEOUTS.LONG); |
|
|
this.attendingMeeting = null; |
|
|
} catch (error) { |
|
|
await this.fetchMeetings(); |
|
|
this.$logAndConsole( |
|
|
|
|
|
"Error leaving meeting: " + errorStringForLog(error), |
|
|
this.$notify( |
|
|
true, |
|
|
{ |
|
|
); |
|
|
group: "alert", |
|
|
this.notify.error( |
|
|
type: "success", |
|
|
serverMessageForUser(error) || "You failed to leave the meeting.", |
|
|
title: "Success", |
|
|
TIMEOUTS.LONG, |
|
|
text: "You left the meeting.", |
|
|
); |
|
|
}, |
|
|
} |
|
|
5000, |
|
|
|
|
|
); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
this.$logAndConsole( |
|
|
|
|
|
"Error leaving meeting: " + errorStringForLog(error), |
|
|
|
|
|
true, |
|
|
|
|
|
); |
|
|
|
|
|
this.$notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "danger", |
|
|
|
|
|
title: "Error", |
|
|
|
|
|
text: |
|
|
|
|
|
serverMessageForUser(error) || |
|
|
|
|
|
"You failed to leave the meeting.", |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
}, |
|
|
}, |
|
|
-1, |
|
|
|
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Navigates to the meeting creation page |
|
|
|
|
|
* |
|
|
|
|
|
* This method handles the navigation to the meeting setup page where |
|
|
|
|
|
* registered users can create new onboarding meetings. It's only |
|
|
|
|
|
* available to users who are registered in the system. |
|
|
|
|
|
* |
|
|
|
|
|
* Navigation: |
|
|
|
|
|
* - Routes to onboard-meeting-setup view |
|
|
|
|
|
* - Allows user to configure new meeting settings |
|
|
|
|
|
* - Only accessible to registered users (controlled by template) |
|
|
|
|
|
* |
|
|
|
|
|
* User Flow: |
|
|
|
|
|
* - User clicks "Create Meeting" button |
|
|
|
|
|
* - System navigates to setup page |
|
|
|
|
|
* - User can configure meeting name, password, etc. |
|
|
|
|
|
* - New meeting becomes available to other users |
|
|
|
|
|
* |
|
|
|
|
|
* @author Matthew Raymer |
|
|
|
|
|
*/ |
|
|
createMeeting() { |
|
|
createMeeting() { |
|
|
this.$router.push({ name: "onboard-meeting-setup" }); |
|
|
this.$router.push({ name: "onboard-meeting-setup" }); |
|
|
} |
|
|
} |
|
|