From 9c7418a40adf07c9ac94659ae64be8016055e2d9 Mon Sep 17 00:00:00 2001 From: Muhsin <12408980+muhsin-k@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:27:14 +0400 Subject: [PATCH] feat(whatsapp): persist phone number health --- .../concerns/whatsapp_health_management.rb | 13 +- .../dashboard/i18n/locale/en/inboxMgmt.json | 87 ++- .../dashboard/settings/inbox/Settings.vue | 22 +- .../inbox/components/AccountHealth.vue | 507 ++++++++++++++---- app/jobs/channels/whatsapp/health_sync_job.rb | 7 + .../whatsapp/health_sync_scheduler_job.rb | 15 + app/jobs/trigger_scheduled_items_job.rb | 3 + app/models/channel/whatsapp.rb | 6 +- app/services/whatsapp/health_service.rb | 188 +++++-- ...phone_number_health_to_channel_whatsapp.rb | 8 + db/schema.rb | 6 +- .../v1/accounts/inboxes_controller_spec.rb | 43 +- .../channels/whatsapp/health_sync_job_spec.rb | 26 + .../health_sync_scheduler_job_spec.rb | 85 +++ spec/jobs/trigger_scheduled_items_job_spec.rb | 5 + spec/services/whatsapp/health_service_spec.rb | 181 +++++++ 16 files changed, 1045 insertions(+), 157 deletions(-) create mode 100644 app/jobs/channels/whatsapp/health_sync_job.rb create mode 100644 app/jobs/channels/whatsapp/health_sync_scheduler_job.rb create mode 100644 db/migrate/20260718000000_add_phone_number_health_to_channel_whatsapp.rb create mode 100644 spec/jobs/channels/whatsapp/health_sync_job_spec.rb create mode 100644 spec/jobs/channels/whatsapp/health_sync_scheduler_job_spec.rb create mode 100644 spec/services/whatsapp/health_service_spec.rb diff --git a/app/controllers/api/v1/accounts/concerns/whatsapp_health_management.rb b/app/controllers/api/v1/accounts/concerns/whatsapp_health_management.rb index 795d7f2a9..32e672779 100644 --- a/app/controllers/api/v1/accounts/concerns/whatsapp_health_management.rb +++ b/app/controllers/api/v1/accounts/concerns/whatsapp_health_management.rb @@ -17,8 +17,19 @@ module Api::V1::Accounts::Concerns::WhatsappHealthManagement end def health - health_data = Whatsapp::HealthService.new(@inbox.channel).fetch_health_status + health_data = Whatsapp::HealthService.new(@inbox.channel).sync_health_status! render json: health_data + rescue Whatsapp::HealthService::ApiError => e + Rails.logger.error "[INBOX HEALTH] Error fetching health data: #{e.message}" + render json: { + error: { + type: e.authorization_error? ? 'authorization' : 'api', + message: e.message, + http_status: e.http_status, + code: e.code, + subcode: e.subcode + }.compact + }, status: :unprocessable_entity rescue StandardError => e Rails.logger.error "[INBOX HEALTH] Error fetching health data: #{e.message}" render json: { error: e.message }, status: :unprocessable_entity diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json index 48f7ab215..0e3b93a7e 100644 --- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json @@ -686,36 +686,93 @@ "WIDGET_FEATURES": "Widget features", "ACCOUNT_HEALTH": { "TITLE": "Manage your WhatsApp account", - "DESCRIPTION": "Review your WhatsApp account status, messaging limits, and quality. Update settings or resolve issues if needed", + "DESCRIPTION": "Review your WhatsApp phone number health, messaging capacity, and linked Meta business details.", "GO_TO_SETTINGS": "Go to Meta Business Manager", "NO_DATA": "Health data is not available", + "ERRORS": { + "AUTHORIZATION": { + "EMBEDDED_TITLE": "WhatsApp connection needs to be refreshed", + "EMBEDDED_DESCRIPTION": "Meta could not validate the connection for this inbox. Go to Configuration to reconfigure it using Embedded Signup.", + "MANUAL_TITLE": "WhatsApp access token needs attention", + "MANUAL_DESCRIPTION": "Meta could not validate the access token for this inbox. Go to Configuration to verify or replace it." + }, + "GENERIC_TITLE": "WhatsApp health data is unavailable", + "GENERIC_DESCRIPTION": "Meta could not return the current health details. Review the error below and try again later.", + "GO_TO_CONFIGURATION": "Go to Configuration" + }, + "SECTIONS": { + "PHONE_NUMBER": "Phone number", + "HEALTH_AND_CAPACITY": "Health and capacity", + "BUSINESS_ACCOUNT": "Business account" + }, "FIELDS": { "DISPLAY_PHONE_NUMBER": { "LABEL": "Display phone number", "TOOLTIP": "Phone number displayed to customers" }, "VERIFIED_NAME": { - "LABEL": "Business name", - "TOOLTIP": "Business name verified by WhatsApp" + "LABEL": "WhatsApp display name", + "TOOLTIP": "Display name associated with this phone number on WhatsApp" + }, + "PHONE_NUMBER_ID": { + "LABEL": "Phone number ID", + "TOOLTIP": "Meta identifier for this WhatsApp phone number" + }, + "COEXISTENCE": { + "LABEL": "Coexistence", + "TOOLTIP": "This number is connected to both the WhatsApp Business app and Cloud API" }, "DISPLAY_NAME_STATUS": { "LABEL": "Display name status", - "TOOLTIP": "Status of your business name verification" + "TOOLTIP": "Review status of the WhatsApp display name" + }, + "LAST_ONBOARDED_TIME": { + "LABEL": "Last onboarded", + "TOOLTIP": "Most recent time Meta onboarded this phone number" + }, + "PHONE_NUMBER_STATUS": { + "LABEL": "Phone number status", + "TOOLTIP": "Current connection status of this phone number on WhatsApp" }, "QUALITY_RATING": { "LABEL": "Quality rating", - "TOOLTIP": "WhatsApp quality rating for your account" + "TOOLTIP": "Quality rating Meta assigns to this phone number based on recent messaging signals" }, "MESSAGING_LIMIT_TIER": { "LABEL": "Messaging limit tier", - "TOOLTIP": "Daily messaging limit for your account" + "TOOLTIP": "Number of customers this phone number can initiate conversations with in 24 hours" }, "ACCOUNT_MODE": { "LABEL": "Account mode", - "TOOLTIP": "Current operating mode of your WhatsApp account" + "TOOLTIP": "Current operating mode of this WhatsApp phone number" + }, + "CODE_VERIFICATION_STATUS": { + "LABEL": "Code verification", + "TOOLTIP": "Verification-code status reported by Meta for this phone number" + }, + "THROUGHPUT_LEVEL": { + "LABEL": "Throughput", + "TOOLTIP": "Message throughput level assigned to this phone number" + }, + "BUSINESS_ACCOUNT_NAME": { + "LABEL": "WhatsApp Business Account name", + "TOOLTIP": "Name of the WhatsApp Business Account linked to this phone number" + }, + "BUSINESS_ACCOUNT_ID": { + "LABEL": "WhatsApp Business Account ID", + "TOOLTIP": "Meta identifier for the linked WhatsApp Business Account" + }, + "BUSINESS_PORTFOLIO_NAME": { + "LABEL": "Business portfolio name", + "TOOLTIP": "Name of the Meta business portfolio that owns the WhatsApp Business Account" + }, + "BUSINESS_PORTFOLIO_ID": { + "LABEL": "Business portfolio ID", + "TOOLTIP": "Meta identifier for the business portfolio that owns the WhatsApp Business Account" } }, "VALUES": { + "NOT_AVAILABLE": "Not available", "TIERS": { "TIER_250": "250 customers per 24h", "TIER_1000": "1K customers per 24h", @@ -727,6 +784,16 @@ }, "STATUSES": { "APPROVED": "Approved", + "CONNECTED": "Connected", + "VERIFIED": "Verified", + "STANDARD": "Standard", + "ACTIVE": "Active", + "EXPIRED": "Expired", + "FLAGGED": "Flagged", + "RESTRICTED": "Restricted", + "BANNED": "Banned", + "DISCONNECTED": "Disconnected", + "UNKNOWN": "Not available", "PENDING_REVIEW": "Pending Review", "AVAILABLE_WITHOUT_REVIEW": "Available Without Review", "REJECTED": "Rejected", @@ -746,7 +813,11 @@ "REGISTER_SUCCESS": "Webhook registered successfully", "REGISTER_ERROR": "Failed to register webhook. Please try again.", "CONFIGURED_SUCCESS": "Webhook configured successfully", - "URL_MISMATCH": "Webhook URL mismatch" + "URL_MISMATCH": "Webhook URL mismatch", + "CONFIGURED_URL": "Configured URL", + "EXPECTED_URL": "Expected URL", + "COPY_URL": "Copy webhook URL", + "COPY_SUCCESS": "Webhook URL copied to clipboard" } }, "SETTINGS": "Settings", diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue index 222d238a8..e6cd129cb 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue @@ -411,6 +411,7 @@ export default { return ( this.isAWhatsAppCloudChannel && this.isEmbeddedSignupWhatsApp && + this.healthError?.type !== 'authorization' && this.isFeatureEnabledonAccount( this.accountId, FEATURE_FLAGS.WHATSAPP_MANUAL_TRANSFER @@ -580,11 +581,27 @@ export default { const response = await InboxHealthAPI.getHealthStatus(this.inbox.id); this.healthData = response.data; } catch (error) { - this.healthError = error.message || 'Failed to fetch health data'; + const apiError = error.response?.data?.error; + this.healthData = null; + this.healthError = + typeof apiError === 'object' + ? apiError + : { + type: 'generic', + message: apiError || error.message, + }; } finally { this.isLoadingHealth = false; } }, + goToWhatsAppConfiguration() { + const configurationTabIndex = this.tabs.findIndex( + tab => tab.key === 'configuration' + ); + if (configurationTabIndex !== -1) { + this.onTabChange(configurationTabIndex); + } + }, async registerWebhook() { if (!this.inbox) return; @@ -1402,8 +1419,11 @@ export default {
import { computed } from 'vue'; import { useI18n } from 'vue-i18n'; +import { copyTextToClipboard } from 'shared/helpers/clipboard'; import ButtonV4 from 'next/button/Button.vue'; import Icon from 'dashboard/components-next/icon/Icon.vue'; +import { useAlert } from 'dashboard/composables'; const props = defineProps({ healthData: { type: Object, default: null, }, + healthError: { + type: Object, + default: null, + }, + isEmbeddedSignup: { + type: Boolean, + default: false, + }, isRegisteringWebhook: { type: Boolean, default: false, }, }); -const emit = defineEmits(['registerWebhook']); +const emit = defineEmits(['registerWebhook', 'goToConfiguration']); -const { t } = useI18n(); +const { t, te, locale } = useI18n(); const QUALITY_COLORS = { GREEN: 'text-n-teal-11', @@ -29,8 +39,17 @@ const QUALITY_COLORS = { const STATUS_COLORS = { APPROVED: 'text-n-teal-11', + CONNECTED: 'text-n-teal-11', + VERIFIED: 'text-n-teal-11', + STANDARD: 'text-n-teal-11', + ACTIVE: 'text-n-teal-11', PENDING_REVIEW: 'text-n-amber-11', AVAILABLE_WITHOUT_REVIEW: 'text-n-teal-11', + EXPIRED: 'text-n-amber-11', + FLAGGED: 'text-n-amber-11', + RESTRICTED: 'text-n-ruby-9', + BANNED: 'text-n-ruby-9', + DISCONNECTED: 'text-n-ruby-9', REJECTED: 'text-n-ruby-9', DECLINED: 'text-n-ruby-9', }; @@ -40,85 +59,267 @@ const MODE_COLORS = { SANDBOX: 'text-n-slate-11', }; -const healthItems = computed(() => { +const formatStatusDisplay = status => { + const translationKey = `INBOX_MGMT.ACCOUNT_HEALTH.VALUES.STATUSES.${status}`; + + if (te(translationKey)) { + return t(translationKey); + } + + return status + .toLowerCase() + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +}; + +const formatDateTime = value => { + const date = new Date(value); + if (Number.isNaN(date.getTime())) return value; + + return new Intl.DateTimeFormat(locale.value, { + dateStyle: 'medium', + timeStyle: 'short', + }).format(date); +}; + +const healthSections = computed(() => { if (!props.healthData) { return []; } const { + id: phoneNumberId, display_phone_number: displayPhoneNumber, verified_name: verifiedName, name_status: nameStatus, quality_rating: qualityRating, messaging_limit_tier: messagingLimitTier, account_mode: accountMode, + status: phoneNumberStatus, + code_verification_status: codeVerificationStatus, + throughput_level: throughputLevel, + last_onboarded_time: lastOnboardedTime, + is_on_biz_app: isOnBizApp, + platform_type: platformType, + business_account_id: businessAccountId, + business_account_name: businessAccountName, + business_portfolio_id: businessPortfolioId, + business_portfolio_name: businessPortfolioName, } = props.healthData; + const notAvailable = t('INBOX_MGMT.ACCOUNT_HEALTH.VALUES.NOT_AVAILABLE'); + return [ { - key: 'displayPhoneNumber', - label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_PHONE_NUMBER.LABEL'), - value: displayPhoneNumber || 'N/A', - tooltip: t( - 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_PHONE_NUMBER.TOOLTIP' - ), - show: true, + key: 'phoneNumber', + title: t('INBOX_MGMT.ACCOUNT_HEALTH.SECTIONS.PHONE_NUMBER'), + items: [ + { + key: 'displayPhoneNumber', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_PHONE_NUMBER.LABEL' + ), + value: displayPhoneNumber || notAvailable, + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_PHONE_NUMBER.TOOLTIP' + ), + }, + { + key: 'verifiedName', + label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.VERIFIED_NAME.LABEL'), + value: verifiedName || notAvailable, + tooltip: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.VERIFIED_NAME.TOOLTIP'), + }, + { + key: 'phoneNumberId', + label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.PHONE_NUMBER_ID.LABEL'), + value: phoneNumberId || notAvailable, + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.PHONE_NUMBER_ID.TOOLTIP' + ), + }, + { + key: 'coexistence', + label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.COEXISTENCE.LABEL'), + value: 'ACTIVE', + tooltip: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.COEXISTENCE.TOOLTIP'), + type: 'status', + show: isOnBizApp === true && platformType === 'CLOUD_API', + }, + { + key: 'displayNameStatus', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_NAME_STATUS.LABEL' + ), + value: nameStatus || 'UNKNOWN', + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_NAME_STATUS.TOOLTIP' + ), + type: 'status', + }, + { + key: 'lastOnboardedTime', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.LAST_ONBOARDED_TIME.LABEL' + ), + value: lastOnboardedTime + ? formatDateTime(lastOnboardedTime) + : notAvailable, + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.LAST_ONBOARDED_TIME.TOOLTIP' + ), + show: Boolean(lastOnboardedTime), + }, + ], }, { - key: 'verifiedName', - label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.VERIFIED_NAME.LABEL'), - value: verifiedName || 'N/A', - tooltip: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.VERIFIED_NAME.TOOLTIP'), - show: true, + key: 'healthAndCapacity', + title: t('INBOX_MGMT.ACCOUNT_HEALTH.SECTIONS.HEALTH_AND_CAPACITY'), + items: [ + { + key: 'phoneNumberStatus', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.PHONE_NUMBER_STATUS.LABEL' + ), + value: phoneNumberStatus || 'UNKNOWN', + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.PHONE_NUMBER_STATUS.TOOLTIP' + ), + type: 'status', + }, + { + key: 'qualityRating', + label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.QUALITY_RATING.LABEL'), + value: qualityRating || 'UNKNOWN', + tooltip: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.QUALITY_RATING.TOOLTIP'), + type: 'quality', + }, + { + key: 'messagingLimitTier', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.MESSAGING_LIMIT_TIER.LABEL' + ), + value: messagingLimitTier || 'UNKNOWN', + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.MESSAGING_LIMIT_TIER.TOOLTIP' + ), + type: 'tier', + }, + { + key: 'accountMode', + label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.ACCOUNT_MODE.LABEL'), + value: accountMode || 'UNKNOWN', + tooltip: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.ACCOUNT_MODE.TOOLTIP'), + type: 'mode', + }, + { + key: 'codeVerificationStatus', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.CODE_VERIFICATION_STATUS.LABEL' + ), + value: codeVerificationStatus || 'UNKNOWN', + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.CODE_VERIFICATION_STATUS.TOOLTIP' + ), + type: 'status', + }, + { + key: 'throughputLevel', + label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.THROUGHPUT_LEVEL.LABEL'), + value: throughputLevel || 'UNKNOWN', + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.THROUGHPUT_LEVEL.TOOLTIP' + ), + type: 'status', + }, + ], }, { - key: 'displayNameStatus', - label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_NAME_STATUS.LABEL'), - value: nameStatus || 'UNKNOWN', - tooltip: t( - 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.DISPLAY_NAME_STATUS.TOOLTIP' - ), - show: true, - type: 'status', + key: 'businessAccount', + title: t('INBOX_MGMT.ACCOUNT_HEALTH.SECTIONS.BUSINESS_ACCOUNT'), + items: [ + { + key: 'businessAccountName', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_ACCOUNT_NAME.LABEL' + ), + value: businessAccountName || notAvailable, + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_ACCOUNT_NAME.TOOLTIP' + ), + }, + { + key: 'businessAccountId', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_ACCOUNT_ID.LABEL' + ), + value: businessAccountId || notAvailable, + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_ACCOUNT_ID.TOOLTIP' + ), + }, + { + key: 'businessPortfolioName', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_PORTFOLIO_NAME.LABEL' + ), + value: businessPortfolioName || notAvailable, + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_PORTFOLIO_NAME.TOOLTIP' + ), + }, + { + key: 'businessPortfolioId', + label: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_PORTFOLIO_ID.LABEL' + ), + value: businessPortfolioId || notAvailable, + tooltip: t( + 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.BUSINESS_PORTFOLIO_ID.TOOLTIP' + ), + }, + ], }, - { - key: 'qualityRating', - label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.QUALITY_RATING.LABEL'), - value: qualityRating || 'UNKNOWN', - tooltip: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.QUALITY_RATING.TOOLTIP'), - show: true, - type: 'quality', - }, - { - key: 'messagingLimitTier', - label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.MESSAGING_LIMIT_TIER.LABEL'), - value: messagingLimitTier || 'UNKNOWN', - tooltip: t( - 'INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.MESSAGING_LIMIT_TIER.TOOLTIP' - ), - show: true, - type: 'tier', - }, - { - key: 'accountMode', - label: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.ACCOUNT_MODE.LABEL'), - value: accountMode || 'UNKNOWN', - tooltip: t('INBOX_MGMT.ACCOUNT_HEALTH.FIELDS.ACCOUNT_MODE.TOOLTIP'), - show: true, - type: 'mode', - }, - ]; + ].map(section => ({ + ...section, + items: section.items.filter(item => item.show !== false), + })); +}); + +const errorState = computed(() => { + if (!props.healthError) return null; + + if (props.healthError.type === 'authorization') { + return { + title: props.isEmbeddedSignup + ? t('INBOX_MGMT.ACCOUNT_HEALTH.ERRORS.AUTHORIZATION.EMBEDDED_TITLE') + : t('INBOX_MGMT.ACCOUNT_HEALTH.ERRORS.AUTHORIZATION.MANUAL_TITLE'), + description: props.isEmbeddedSignup + ? t( + 'INBOX_MGMT.ACCOUNT_HEALTH.ERRORS.AUTHORIZATION.EMBEDDED_DESCRIPTION' + ) + : t( + 'INBOX_MGMT.ACCOUNT_HEALTH.ERRORS.AUTHORIZATION.MANUAL_DESCRIPTION' + ), + showConfigurationAction: true, + }; + } + + return { + title: t('INBOX_MGMT.ACCOUNT_HEALTH.ERRORS.GENERIC_TITLE'), + description: t('INBOX_MGMT.ACCOUNT_HEALTH.ERRORS.GENERIC_DESCRIPTION'), + showConfigurationAction: false, + }; }); const handleGoToSettings = () => { - const { business_id: businessId } = props.healthData || {}; + const { business_portfolio_id: businessPortfolioId } = props.healthData || {}; - if (businessId) { - // WhatsApp Business Manager URL with specific business ID and phone numbers tab - const whatsappBusinessUrl = `https://business.facebook.com/latest/whatsapp_manager/phone_numbers/?business_id=${businessId}&tab=phone-numbers`; + if (businessPortfolioId) { + const whatsappBusinessUrl = `https://business.facebook.com/latest/whatsapp_manager/phone_numbers/?business_id=${businessPortfolioId}&tab=phone-numbers`; window.open(whatsappBusinessUrl, '_blank'); } else { - // Fallback to general WhatsApp Business Manager if business_id is not available const fallbackUrl = 'https://business.facebook.com/'; window.open(fallbackUrl, '_blank'); } @@ -130,9 +331,6 @@ const getQualityRatingTextColor = rating => const formatTierDisplay = tier => t(`INBOX_MGMT.ACCOUNT_HEALTH.VALUES.TIERS.${tier}`) || tier; -const formatStatusDisplay = status => - t(`INBOX_MGMT.ACCOUNT_HEALTH.VALUES.STATUSES.${status}`) || status; - const formatModeDisplay = mode => t(`INBOX_MGMT.ACCOUNT_HEALTH.VALUES.MODES.${mode}`) || mode; @@ -163,6 +361,15 @@ const webhookUrlMismatch = computed( const handleRegisterWebhook = () => { emit('registerWebhook'); }; + +const handleGoToConfiguration = () => { + emit('goToConfiguration'); +}; + +const handleCopyWebhookUrl = async url => { + await copyTextToClipboard(url); + useAlert(t('INBOX_MGMT.ACCOUNT_HEALTH.WEBHOOK.COPY_SUCCESS')); +};