From 0200db8f84be0ea7f0865b9d79e7db3488d5a40e Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Sat, 5 Jul 2025 12:42:31 +0000 Subject: [PATCH] Refactor AccountViewView.vue to use notify helper and PlatformServiceMixin - Migrated all notification calls in AccountViewView.vue to use a notify helper initialized in mounted(), ensuring this.$notify is available and preventing runtime errors. - Removed NotificationMixin and $notifyHelper usage; restored and standardized notify helper pattern. - Migrated all database and platform service operations to use PlatformServiceMixin ultra-concise methods ($accountSettings, $saveSettings, $saveUserSettings, etc.). - Cleaned up unused imports and code related to previous notification and database patterns. - Ensured all linter errors and warnings are resolved in both AccountViewView.vue and notification utility files. --- src/services/ProfileService.ts | 223 +++++++++++++++++++++ src/views/AccountViewView.vue | 340 ++++++++++----------------------- 2 files changed, 325 insertions(+), 238 deletions(-) create mode 100644 src/services/ProfileService.ts diff --git a/src/services/ProfileService.ts b/src/services/ProfileService.ts new file mode 100644 index 00000000..41aab392 --- /dev/null +++ b/src/services/ProfileService.ts @@ -0,0 +1,223 @@ +/** + * ProfileService - Handles user profile operations and API calls + * Extracted from AccountViewView.vue to improve separation of concerns + */ + +import { AxiosInstance, AxiosError } from "axios"; +import { UserProfile } from "@/libs/partnerServer"; +import { UserProfileResponse } from "@/interfaces/accountView"; +import { getHeaders, errorStringForLog } from "@/libs/endorserServer"; +import { handleApiError } from "./api"; +import { logger } from "@/utils/logger"; +import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView"; + +/** + * Profile data interface + */ +export interface ProfileData { + description: string; + latitude: number; + longitude: number; + includeLocation: boolean; +} + +/** + * Profile service class + */ +export class ProfileService { + private axios: AxiosInstance; + private partnerApiServer: string; + + constructor(axios: AxiosInstance, partnerApiServer: string) { + this.axios = axios; + this.partnerApiServer = partnerApiServer; + } + + /** + * Load user profile from the server + * @param activeDid - The user's DID + * @returns ProfileData or null if profile doesn't exist + */ + async loadProfile(activeDid: string): Promise { + try { + const headers = await getHeaders(activeDid); + const response = await this.axios.get( + `${this.partnerApiServer}/api/partner/userProfileForIssuer/${activeDid}`, + { headers }, + ); + + if (response.status === 200) { + const data = response.data.data; + const profileData: ProfileData = { + description: data.description || "", + latitude: data.locLat || 0, + longitude: data.locLon || 0, + includeLocation: !!(data.locLat && data.locLon), + }; + return profileData; + } else { + throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.UNABLE_TO_LOAD_PROFILE); + } + } catch (error) { + if (this.isApiError(error) && error.response?.status === 404) { + // Profile doesn't exist yet - this is normal + return null; + } + + logger.error("Error loading profile:", errorStringForLog(error)); + handleApiError(error as AxiosError, "/api/partner/userProfileForIssuer"); + return null; + } + } + + /** + * Save user profile to the server + * @param activeDid - The user's DID + * @param profileData - The profile data to save + * @returns true if successful, false otherwise + */ + async saveProfile( + activeDid: string, + profileData: ProfileData, + ): Promise { + try { + const headers = await getHeaders(activeDid); + const payload: UserProfile = { + description: profileData.description, + issuerDid: activeDid, + }; + + // Add location data if location is included + if ( + profileData.includeLocation && + profileData.latitude && + profileData.longitude + ) { + payload.locLat = profileData.latitude; + payload.locLon = profileData.longitude; + } + + const response = await this.axios.post( + `${this.partnerApiServer}/api/partner/userProfile`, + payload, + { headers }, + ); + + if (response.status === 200) { + return true; + } else { + throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_SAVED); + } + } catch (error) { + logger.error("Error saving profile:", errorStringForLog(error)); + handleApiError(error as AxiosError, "/api/partner/userProfile"); + return false; + } + } + + /** + * Delete user profile from the server + * @param activeDid - The user's DID + * @returns true if successful, false otherwise + */ + async deleteProfile(activeDid: string): Promise { + try { + const headers = await getHeaders(activeDid); + const response = await this.axios.delete( + `${this.partnerApiServer}/api/partner/userProfile`, + { headers }, + ); + + if (response.status === 200) { + return true; + } else { + throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_DELETED); + } + } catch (error) { + logger.error("Error deleting profile:", errorStringForLog(error)); + handleApiError(error as AxiosError, "/api/partner/userProfile"); + return false; + } + } + + /** + * Update profile location + * @param profileData - Current profile data + * @param latitude - New latitude + * @param longitude - New longitude + * @returns Updated profile data + */ + updateProfileLocation( + profileData: ProfileData, + latitude: number, + longitude: number, + ): ProfileData { + return { + ...profileData, + latitude, + longitude, + includeLocation: true, + }; + } + + /** + * Toggle location inclusion in profile + * @param profileData - Current profile data + * @returns Updated profile data + */ + toggleProfileLocation(profileData: ProfileData): ProfileData { + const includeLocation = !profileData.includeLocation; + return { + ...profileData, + latitude: includeLocation ? profileData.latitude : 0, + longitude: includeLocation ? profileData.longitude : 0, + includeLocation, + }; + } + + /** + * Clear profile location + * @param profileData - Current profile data + * @returns Updated profile data + */ + clearProfileLocation(profileData: ProfileData): ProfileData { + return { + ...profileData, + latitude: 0, + longitude: 0, + includeLocation: false, + }; + } + + /** + * Reset profile to default state + * @returns Default profile data + */ + getDefaultProfile(): ProfileData { + return { + description: "", + latitude: 0, + longitude: 0, + includeLocation: false, + }; + } + + /** + * Type guard for API errors + */ + private isApiError( + error: unknown, + ): error is { response?: { status?: number } } { + return typeof error === "object" && error !== null && "response" in error; + } +} + +/** + * Factory function to create a ProfileService instance + */ +export function createProfileService( + axios: AxiosInstance, + partnerApiServer: string, +): ProfileService { + return new ProfileService(axios, partnerApiServer); +} diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 357b2330..771313e7 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -975,7 +975,6 @@