/** * External Dependencies */ import { Alert, AlertIcon, Box, Button, Collapse, Container, Divider, Flex, FormControl, Grid, Heading, HStack, Icon, IconButton, Image, Input, Link, Spinner, Stack, Text, useToast, } from '@chakra-ui/react'; import { useMutation, useQueryClient, UseQueryResult, } from '@tanstack/react-query'; import apiFetch from '@wordpress/api-fetch'; import { __ } from '@wordpress/i18n'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { BiChevronDown, BiChevronUp } from 'react-icons/bi'; /** * Internal Dependencies */ import { Bulb, DocsLines, Headphones, Star, Team, Video, } from '../../components/Icon/Icon'; import applicationFormIcon from '../../images/icons/application-form.png'; import businessFormIcon from '../../images/icons/business-form.png'; import educationFormIcon from '../../images/icons/education-form.png'; import feedbackFormIcon from '../../images/icons/feedback-form.png'; import healthcareFormIcon from '../../images/icons/healthcare-form.png'; import informationFormIcon from '../../images/icons/infromation-form.png'; import SiteAssistantSkeleton from '../../skeleton/SiteAssistantSkeleton'; import { docURL, facebookGroup, featureRequestURL, submitReviewUrl, ticketUrl, } from '../../utils/constants'; interface SiteAssistantData { skipped_steps: string[]; test_email_sent: boolean; has_forms: boolean; email_sent?: boolean; last_form_email_status?: 'success' | 'failed' | ''; is_smtp_active?: boolean; is_smart_smtp_installed?: boolean; is_smart_smtp_active?: boolean; } interface ApiResponse { success: boolean; data: SiteAssistantData; } interface StepConfig { id: string; title: string; isCompleted: (data: SiteAssistantData | undefined) => boolean; } interface Props { siteAssistantQuery: UseQueryResult; } const SmartSmtpIcon = () => ( ); const ExternalLinkIcon = () => ( ); const SendEmailIcon = () => ( ); const SiteAssistant: React.FC = ({ siteAssistantQuery }) => { const dashboardData = typeof _EVF_DASHBOARD_ !== 'undefined' ? _EVF_DASHBOARD_ : {}; const { utmCampaign, evfRestApiNonce, restURL, adminEmail, adminURL, isPro, ajaxURL, smartSmtpNonce, } = dashboardData; const toast = useToast(); const queryClient = useQueryClient(); const [open, setOpen] = useState>({}); const [testEmail, setTestEmail] = useState(adminEmail || ''); const [isInstallingSmtp, setIsInstallingSmtp] = useState(false); const [smtpLoadingPhase, setSmtpLoadingPhase] = useState<'installing' | 'activating' | null>(null); const [smtpInstallError, setSmtpInstallError] = useState(null); const toggleOpen = useCallback((id: string) => { setOpen((prev) => ({ ...prev, [id]: !prev[id] })); }, []); const handleConfigureRecaptcha = () => { const settingsURL = window._EVF_DASHBOARD_?.settingsURL || `${window.location.origin}/wp-admin/admin.php?page=evf-settings`; window.open(`${settingsURL}&tab=recaptcha`, '_blank'); }; const handleOtherSpamFeatures = () => { const settingsURL = window._EVF_DASHBOARD_?.settingsURL || `${window.location.origin}/wp-admin/admin.php?page=evf-settings`; window.open(`${settingsURL}&tab=recaptcha`, '_blank'); }; const { data: siteData, isLoading, error } = siteAssistantQuery; const skipSpamProtectionMutation = useMutation({ mutationFn: async () => { const response = await apiFetch({ path: `${restURL}everest-forms/v1/site-assistant/skip-setup`, method: 'POST', headers: { 'X-WP-Nonce': evfRestApiNonce, }, data: { step: 'spam_protection', }, }); return response as ApiResponse; }, onSuccess: (data) => { queryClient.setQueryData(['siteAssistant'], data); toast({ title: __('Success', 'everest-forms'), description: __('Spam protection setup skipped.', 'everest-forms'), status: 'success', duration: 3000, isClosable: true, }); }, onError: (error: any) => { console.error('Error skipping spam protection:', error); toast({ title: __('Error', 'everest-forms'), description: error?.message || __('Failed to skip spam protection setup.', 'everest-forms'), status: 'error', duration: 3000, isClosable: true, }); }, }); const sendTestEmailMutation = useMutation({ mutationFn: async (email: string) => { const response = await apiFetch({ path: `${restURL}everest-forms/v1/site-assistant/test-email`, method: 'POST', headers: { 'X-WP-Nonce': evfRestApiNonce, }, data: { email: email, }, }); return response as ApiResponse; }, onSuccess: (data) => { queryClient.setQueryData(['siteAssistant'], (old: ApiResponse | undefined) => { const prev = old?.data; const incoming = data.data; const merged: SiteAssistantData = { ...prev, ...incoming, skipped_steps: incoming?.skipped_steps ?? prev?.skipped_steps ?? [], test_email_sent: incoming?.test_email_sent ?? prev?.test_email_sent ?? false, has_forms: incoming?.has_forms ?? prev?.has_forms ?? false, }; const next: ApiResponse = { success: data.success ?? old?.success ?? true, data: merged, }; return next; }); if (data?.data?.email_sent) { toast({ title: __('Success', 'everest-forms'), description: __( "Test email sent successfully. Didn't receive it? Please check your Spam or Junk folder.", 'everest-forms', ), status: 'success', duration: 3000, isClosable: true, }); } else { toast({ title: __('Error', 'everest-forms'), description: __( 'We could not send the test email. Please check your mail configuration.', 'everest-forms', ), status: 'error', duration: 5000, isClosable: true, }); } }, onError: (error: any) => { console.error('Error sending test email:', error); toast({ title: __('Error', 'everest-forms'), description: error?.message || __('Failed to send test email.', 'everest-forms'), status: 'error', duration: 3000, isClosable: true, }); }, }); const skipSendTestEmailMutation = useMutation({ mutationFn: async () => { const response = await apiFetch({ path: `${restURL}everest-forms/v1/site-assistant/skip-setup`, method: 'POST', headers: { 'X-WP-Nonce': evfRestApiNonce, }, data: { step: 'send_test_email', }, }); return response as ApiResponse; }, onSuccess: (data) => { queryClient.setQueryData(['siteAssistant'], data); toast({ title: __('Success', 'everest-forms'), description: __('Send test email step skipped.', 'everest-forms'), status: 'success', duration: 3000, isClosable: true, }); }, onError: (error: any) => { console.error('Error skipping send test email:', error); toast({ title: __('Error', 'everest-forms'), description: error?.message || __('Failed to skip send test email step.', 'everest-forms'), status: 'error', duration: 3000, isClosable: true, }); }, }); const assistantData = siteData?.data; const mutationData = sendTestEmailMutation.data?.data; const resolvedSmtpInstalled = mutationData?.is_smart_smtp_installed ?? assistantData?.is_smart_smtp_installed; const resolvedSmtpActive = mutationData?.is_smtp_active ?? assistantData?.is_smtp_active; const resolvedSmartSmtpPluginActive = mutationData?.is_smart_smtp_active ?? assistantData?.is_smart_smtp_active; const emailSentFromMutation = mutationData?.email_sent; const testEmailSent = emailSentFromMutation ?? assistantData?.test_email_sent ?? false; const resolvedLastFormEmailStatus = mutationData?.last_form_email_status ?? assistantData?.last_form_email_status ?? ''; const hasSuccessfulFormDelivery = resolvedLastFormEmailStatus === 'success'; // POST result is merged into the siteAssistant query cache. Mutation state can // reset (remount, minified bundle timing) before the next paint, so do not rely // on `sendTestEmailMutation.isSuccess` alone — `email_sent === false` on cached // data matches the REST failure payload and the error toast branch. const testEmailSendExplicitlyFailed = (sendTestEmailMutation.isSuccess && mutationData != null && mutationData.email_sent !== true && mutationData.test_email_sent !== true) || assistantData?.email_sent === false; const emailStatus: 'idle' | 'sent' | 'failed' = sendTestEmailMutation.isError ? 'failed' : testEmailSent || hasSuccessfulFormDelivery ? 'sent' : testEmailSendExplicitlyFailed ? 'failed' : resolvedLastFormEmailStatus === 'failed' ? 'failed' : 'idle'; const handleInstallSmtpPlugin = async () => { const isInstallFlow = !resolvedSmtpInstalled; setIsInstallingSmtp(true); setSmtpLoadingPhase(isInstallFlow ? 'installing' : 'activating'); setSmtpInstallError(null); let redirecting = false; try { const normalizedAdminUrl = (adminURL || '').endsWith('/') ? (adminURL || '').slice(0, -1) : adminURL || ''; const ajaxEndpoint = ajaxURL || `${normalizedAdminUrl}/admin-ajax.php`; const formData = new FormData(); formData.append('action', 'everest_forms_install_and_activate_smart_smtp'); formData.append('security', smartSmtpNonce || ''); const response = await fetch(ajaxEndpoint, { method: 'POST', body: formData, credentials: 'same-origin', }); const result = await response.json(); if (result.success) { // PHP installed+activated — switch to "Activating..." for the install flow // and hold it for at least 800ms so the user can read it. if (isInstallFlow) { setSmtpLoadingPhase('activating'); await new Promise((r) => window.setTimeout(r, 800)); } if (result.data?.redirection_url) { // Redirect: skip cache updates — new page loads fresh data. // Any setQueryData/invalidateQueries here triggers re-renders that // flash the idle button state before the page unloads. redirecting = true; window.location.href = result.data.redirection_url; } else { // No redirect — update cache so UI reflects installed state. queryClient.setQueryData( ['siteAssistant'], (old: ApiResponse | undefined) => { const prev = old?.data; return { success: old?.success ?? true, data: { ...prev, is_smart_smtp_installed: true, is_smart_smtp_active: true, skipped_steps: prev?.skipped_steps ?? [], test_email_sent: prev?.test_email_sent ?? false, has_forms: prev?.has_forms ?? false, } as SiteAssistantData, } as ApiResponse; }, ); void queryClient.invalidateQueries({ queryKey: ['siteAssistant'] }); } } else { setSmtpInstallError( result.data?.message || __('Installation failed. Please try manually.', 'everest-forms'), ); } } catch { setSmtpInstallError( __('Installation failed. Please try manually.', 'everest-forms'), ); } finally { if (!redirecting) { setIsInstallingSmtp(false); setSmtpLoadingPhase(null); } } }; const handleSkipSpamProtection = () => { skipSpamProtectionMutation.mutate(); }; const handleSendTestEmail = () => { if (!testEmail || !testEmail.includes('@')) { toast({ title: __('Invalid Email', 'everest-forms'), description: __('Please enter a valid email address.', 'everest-forms'), status: 'error', duration: 3000, isClosable: true, }); return; } sendTestEmailMutation.mutate(testEmail); }; const formCategories = [ { name: __('Application Form', 'everest-forms'), count: 15, icon: applicationFormIcon, slug: 'application', }, { name: __('Business Form', 'everest-forms'), count: 2, icon: businessFormIcon, slug: 'bussiness', }, { name: __('Education Form', 'everest-forms'), count: 2, icon: educationFormIcon, slug: 'education', }, { name: __('Information Form', 'everest-forms'), count: 7, icon: informationFormIcon, slug: 'information', }, { name: __('Health Care Form', 'everest-forms'), count: 1, icon: healthcareFormIcon, slug: 'healthcare', }, { name: __('Feedback Form', 'everest-forms'), count: 8, icon: feedbackFormIcon, slug: 'feedback', }, ]; const handleCreateNewForm = () => { createBlankFormMutation.mutate(__('Untitled', 'everest-forms')); }; const handleViewAllTemplates = () => { const templatesURL = `${adminURL}admin.php?page=evf-builder&create-form=1`; window.location.href = templatesURL; }; const handleCategoryClick = (categorySlug: string) => { const categoryURL = `${adminURL}admin.php?page=evf-builder&create-form=1&evf_template_category=${categorySlug}`; window.location.href = categoryURL; }; const skipCreateFormMutation = useMutation({ mutationFn: async () => { const response = await apiFetch({ path: `${restURL}everest-forms/v1/site-assistant/skip-setup`, method: 'POST', headers: { 'X-WP-Nonce': evfRestApiNonce, }, data: { step: 'create_form', }, }); return response as ApiResponse; }, onSuccess: (data) => { queryClient.setQueryData(['siteAssistant'], data); toast({ title: __('Success', 'everest-forms'), description: __('Form creation step skipped.', 'everest-forms'), status: 'success', duration: 3000, isClosable: true, }); }, onError: (error: any) => { console.error('Error skipping create form:', error); toast({ title: __('Error', 'everest-forms'), description: error?.message || __('Failed to skip create form step.', 'everest-forms'), status: 'error', duration: 3000, isClosable: true, }); }, }); const handleSkipCreateForm = () => { skipCreateFormMutation.mutate(); }; const createBlankFormMutation = useMutation({ mutationFn: async (formName: string) => { const response = await apiFetch({ path: `${restURL}everest-forms/v1/templates/create`, method: 'POST', body: JSON.stringify({ title: formName, slug: 'blank_form', }), headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': evfRestApiNonce, }, }); return response as { success: boolean; data: { redirect: string } }; }, onSuccess: (response) => { if (response.success && response.data) { window.location.href = response.data.redirect; } }, onError: (error: any) => { console.error('Error creating blank form:', error); toast({ title: __('Error', 'everest-forms'), description: error?.message || __('Failed to create blank form.', 'everest-forms'), status: 'error', duration: 3000, isClosable: true, }); }, }); const renderCreateFormContent = () => ( toggleOpen('createForm')} > {__('Start Creating Forms', 'everest-forms')} } cursor={'pointer'} fontSize={'xl'} size="sm" boxShadow="none" borderRadius="base" variant={open?.createForm ? 'solid' : 'link'} border="none" bg={open?.createForm ? 'gray.100' : 'transparent'} _hover={{ bg: open?.createForm ? 'gray.100' : 'inherit', }} pointerEvents="none" /> {__( 'To get started quickly, you can create a new form from scratch or choose from the categories.', 'everest-forms', )} {__('CATEGORIES', 'everest-forms')} {__('View All', 'everest-forms')} {formCategories.map((category, index) => ( handleCategoryClick(category.slug)} > {category.name} {category.name} {category.count}{' '} {category.count > 1 ? __('Templates', 'everest-forms') : __('Template', 'everest-forms')} ))} {skipCreateFormMutation.isLoading ? __('Skipping...', 'everest-forms') : __('Skip Setup', 'everest-forms')} ); const renderSendTestEmailContent = () => ( toggleOpen('sendTestEmail')} > {__('Send Test Email', 'everest-forms')} {' '} } cursor={'pointer'} fontSize={'xl'} size="sm" boxShadow="none" borderRadius="base" variant={open?.sendTestEmail ? 'solid' : 'link'} border="none" bg={open?.sendTestEmail ? 'gray.100' : 'transparent'} _hover={{ bg: open?.sendTestEmail ? 'gray.100' : 'inherit', }} pointerEvents="none" /> {emailStatus === 'sent' && ( {__( 'Test Email Sent Successfully - Your email delivery is working. Form notifications should reach your inbox reliably.', 'everest-forms', )} )} {emailStatus === 'failed' && (!resolvedSmartSmtpPluginActive || isInstallingSmtp) && ( {__('Having trouble sending emails?', 'everest-forms')} {__( 'SmartSMTP helps your website send emails more reliably.', 'everest-forms', )} {smtpInstallError && ( {smtpInstallError} )} )} {__( "Verify that your site can send emails. Enter your address and we'll send a quick test.", 'everest-forms', )} {__('Your Email Address', 'everest-forms')} setTestEmail(e.target.value)} isDisabled={sendTestEmailMutation.isLoading} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleSendTestEmail(); } }} sx={{ padding: '0 12px !important', paddingRight: '12px !important', boxSizing: 'border-box !important', width: '100% !important', maxWidth: '100% !important', border: '1px solid #e1e1e1 !important', fontSize: '14px !important', '&:focus, &:focus-visible': { outline: 'none !important', boxShadow: 'none !important', borderColor: '#e1e1e1 !important', }, }} outline="none" outlineOffset="0" _focus={{ boxShadow: 'none !important', outline: 'none !important', }} _focusVisible={{ boxShadow: 'none !important', outline: 'none !important', }} /> skipSendTestEmailMutation.mutate()} cursor="pointer" opacity={skipSendTestEmailMutation.isLoading ? 0.6 : 1} pointerEvents={ skipSendTestEmailMutation.isLoading ? 'none' : 'auto' } textDecor={'underline'} > {skipSendTestEmailMutation.isLoading ? __('Skipping...', 'everest-forms') : __('Skip Setup', 'everest-forms')} ); useEffect(() => { if (adminEmail) { setTestEmail(adminEmail); } }, [adminEmail]); const renderSpamProtectionContent = () => ( toggleOpen('spamProtection')} > {__('Spam Protection', 'everest-forms')} } cursor={'pointer'} fontSize={'xl'} size="sm" boxShadow="none" borderRadius="base" variant={open?.spamProtection ? 'solid' : 'link'} border="none" bg={open?.spamProtection ? 'gray.100' : 'transparent'} _hover={{ bg: open?.spamProtection ? 'gray.100' : 'inherit', }} pointerEvents="none" /> {__( 'Set up protection against spam submissions. We recommend enabling reCaptcha v2.', 'everest-forms', )} {__('reCaptcha v2', 'everest-forms')} {__('Enable Google reCaptcha protection', 'everest-forms')} {__('Configure Settings', 'everest-forms')} {__( 'You can also set up other spam protection features from ', 'everest-forms', )} {__('here', 'everest-forms')} . {skipSpamProtectionMutation.isLoading ? __('Skipping...', 'everest-forms') : __('Skip Setup', 'everest-forms')} ); const stepsConfig: StepConfig[] = useMemo(() => { const steps: StepConfig[] = []; if (!siteData?.data?.has_forms) { steps.push({ id: 'createForm', title: __('Start Creating Forms', 'everest-forms'), isCompleted: (data) => !!data?.skipped_steps?.includes('create_form') || !!data?.has_forms, }); } steps.push( { id: 'sendTestEmail', title: __('Send Test Email', 'everest-forms'), isCompleted: (data) => !!data?.test_email_sent || !!data?.skipped_steps?.includes('send_test_email') || data?.last_form_email_status === 'success', }, { id: 'spamProtection', title: __('Spam Protection', 'everest-forms'), isCompleted: (data) => !!data?.skipped_steps?.includes('spam_protection'), }, ); return steps; }, [siteData?.data?.has_forms]); const visibleSteps = useMemo(() => { return stepsConfig.filter((step) => !step.isCompleted(siteData?.data)); }, [stepsConfig, siteData]); const firstStepId = visibleSteps.length > 0 ? visibleSteps[0].id : null; useEffect(() => { if (firstStepId && open[firstStepId] === undefined) { setOpen((prev) => ({ ...prev, [firstStepId]: true })); } }, [firstStepId]); useEffect(() => { if (!isLoading && siteData && visibleSteps.length === 0) { let cleanURL = adminURL || ''; if (cleanURL.endsWith('/')) cleanURL = cleanURL.slice(0, -1); if (cleanURL.endsWith('/admin.php')) cleanURL = cleanURL.slice(0, -10); const targetPage = isPro ? 'evf-analytics' : 'evf-entries'; window.location.href = `${cleanURL}/admin.php?page=${targetPage}`; } }, [visibleSteps.length, isLoading, siteData]); if (isLoading) { return ; } return ( {visibleSteps.map((step) => ( {step.id === 'createForm' && renderCreateFormContent()} {step.id === 'sendTestEmail' && renderSendTestEmailContent()} {step.id === 'spamProtection' && renderSpamProtectionContent()} ))} {__('Everest Forms Community', 'everest-forms')} {__( 'Join our exclusive group and connect with fellow Everest Forms members. Ask questions, contribute to discussions, and share feedback!', 'everest-forms', )} {__('Join our Facebook Group', 'everest-forms')} {__('Getting Started', 'everest-forms')} {__( 'Check our documentation for detailed information on Everest Forms features and how to use them.', 'everest-forms', )} {__('View Documentation', 'everest-forms')} {__('Support', 'everest-forms')} {__( 'Submit a ticket for encountered issues and get help from our support team instantly.', 'everest-forms', )} {__('Create a Ticket', 'everest-forms')} {__('Feature Request', 'everest-forms')} {__( "Don't find a feature you're looking for? Suggest any features you think would enhance our product.", 'everest-forms', )} {__('Request a Feature', 'everest-forms')} {__('Submit a Review', 'everest-forms')} {__( "Please take a moment to give us a review. We appreciate honest feedback that'll help us improve our plugin.", 'everest-forms', )} {__('Submit a Review', 'everest-forms')} {__('Video Tutorials', 'everest-forms')} {__( "Watch our step-by-step video tutorials that'll help you get the best out of Everest Forms's features.", 'everest-forms', )} {__('Watch Videos', 'everest-forms')} ); }; export default SiteAssistant;