import { Box, Button, ButtonGroup, Checkbox, Flex, FormLabel, Icon, Link, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Stack, Text, Tooltip, useToast, } from '@chakra-ui/react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { __ } from '@wordpress/i18n'; import React, { useCallback, useState } from 'react'; import { BiLinkExternal } from 'react-icons/bi'; import AsyncSelect from '../../../../assets/js/back-end/components/common/AsyncSelect'; import FormControlTwoCol from '../../../../assets/js/back-end/components/common/FormControlTwoCol'; import ToolTip from '../../../../assets/js/back-end/screens/settings/components/ToolTip'; import { WCIntegrationSchema } from '../../../../assets/js/back-end/types/course'; import API from '../../../../assets/js/back-end/utils/api'; import { deepClean } from '../../../../assets/js/back-end/utils/utils'; import { WC_COURSE_PRODUCT_TYPES } from './constants/productTypes'; import { urls } from './constants/urls'; interface MasteriyoWcIntegration { adminUrl: string; } interface WcProduct { id: number; name: string; price: string; type?: string; type_label?: string; } interface WcProductOption { value: number; label: string; price: string; type?: string; type_label?: string; } interface Props { WCIntegrationData?: WCIntegrationSchema; regularPrice?: string; } const WCIntegrationCourseSetting: React.FC = (props) => { const { WCIntegrationData, regularPrice } = props; const isFree = !regularPrice || parseFloat(regularPrice) <= 0; const toast = useToast(); const queryClient = useQueryClient(); const courseId = WCIntegrationData?.course_id || 0; const hasExistingProduct = WCIntegrationData?.product_create || false; const createProductAPI = new API(urls.create_wc_product); const createProductMutation = useMutation({ mutationFn: (data) => createProductAPI.store(data), }); const handleCreateProduct = (data: WCIntegrationSchema) => { createProductMutation.mutate(deepClean(data), { onSuccess: (data: any) => { toast({ title: data.success && data.message ? data.message : __( 'Product has been created and linked successfully.', 'learning-management-system', ), status: 'success', isClosable: true, }); queryClient.invalidateQueries({ queryKey: [`course${courseId}`] }); }, onError: (error: any) => { const message: any = error?.message ? error?.message : error?.data?.message; toast({ title: __('Failed to create product.', 'learning-management-system'), description: message ? `${message}` : undefined, status: 'error', isClosable: true, }); }, }); }; // ── Link existing product ────────────────────────────────────────────── const [selectedOption, setSelectedOption] = useState( null, ); const [isLinkModalOpen, setIsLinkModalOpen] = useState(false); const [linkConfirmed, setLinkConfirmed] = useState(false); const wcIntegrationData = ( window as { _MASTERIYO_WC_INTEGRATION_?: MasteriyoWcIntegration } )._MASTERIYO_WC_INTEGRATION_; const fetchProducts = (search: string): Promise => new API(urls.list_wc_products) .list(search ? { search } : undefined) .then((data: any) => (data?.data || []).map((p: WcProduct) => ({ value: p.id, label: p.name, price: p.price, type: p.type, type_label: p.type_label, })), ) .catch(() => []); const loadProductOptions = useCallback( (inputValue: string, callback: (options: WcProductOption[]) => void) => { fetchProducts(inputValue).then(callback); }, [], ); const linkProductMutation = useMutation({ mutationFn: () => new API(urls.link_wc_product(courseId)).store({ product_id: selectedOption!.value, }), onSuccess: () => { toast({ title: __('Product linked successfully.', 'learning-management-system'), status: 'success', isClosable: true, }); setSelectedOption(null); setIsLinkModalOpen(false); setLinkConfirmed(false); queryClient.invalidateQueries({ queryKey: [`course${courseId}`] }); }, onError: (error: any) => { toast({ title: __('Failed to link product.', 'learning-management-system'), description: error?.message || error?.data?.message, status: 'error', isClosable: true, }); }, }); const unlinkProductMutation = useMutation({ mutationFn: () => new API(urls.link_wc_product(courseId)).deleteResource(), onSuccess: () => { toast({ title: __('Product unlinked.', 'learning-management-system'), status: 'success', isClosable: true, }); queryClient.invalidateQueries({ queryKey: [`course${courseId}`] }); }, onError: (error: any) => { toast({ title: __('Failed to unlink product.', 'learning-management-system'), description: error?.message, status: 'error', isClosable: true, }); }, }); const linkedProductId = WCIntegrationData?.linked_product_id; const linkedProductName = WCIntegrationData?.linked_product_name; const linkedProductPrice = WCIntegrationData?.linked_product_price; const linkedProductEditUrl = WCIntegrationData?.linked_product_edit_url || ''; const showLinkSection = !isFree && !hasExistingProduct && !linkedProductId; const defaultProductsQuery = useQuery({ queryKey: ['wc-products-default'], queryFn: () => fetchProducts(''), enabled: showLinkSection, staleTime: 5 * 60 * 1000, }); const targetTypeLabel = __('Masteriyo Course', 'learning-management-system'); const handleLinkClick = () => { if ( selectedOption?.type && (WC_COURSE_PRODUCT_TYPES as readonly string[]).includes( selectedOption.type, ) ) { linkProductMutation.mutate(); return; } setLinkConfirmed(false); setIsLinkModalOpen(true); }; const handleLinkModalClose = () => { if (linkProductMutation.isPending) return; setIsLinkModalOpen(false); setLinkConfirmed(false); }; // ── Render helpers ───────────────────────────────────────────────────── const details = (hasExistingProduct: boolean) => ( <> {__('Create as a Product', 'learning-management-system')} ); const linkSection = () => { if (linkedProductId && linkedProductName) { return ( <> {__('Linked WC Product', 'learning-management-system')} {linkedProductName} {linkedProductEditUrl && ( )} {linkedProductPrice && ( {__('Regular price:', 'learning-management-system')}{' '} {linkedProductPrice} )} ); } const isLinkDisabled = isFree || courseId === 0; const linkDisabledTooltip = isFree ? __( 'Set a paid course price before linking a WooCommerce product.', 'learning-management-system', ) : __( 'Save the course before linking a WooCommerce product.', 'learning-management-system', ); return ( <> {__('Link Existing Product', 'learning-management-system')} setSelectedOption(option as WcProductOption | null) } loadOptions={loadProductOptions} defaultOptions={ defaultProductsQuery.isSuccess ? defaultProductsQuery.data : [] } isClearable isDisabled={isLinkDisabled} noOptionsMessage={() => __('No products found', 'learning-management-system') } loadingMessage={() => __('Searching…', 'learning-management-system') } /> {selectedOption && !isLinkDisabled && ( )} {/* Link & Convert confirmation modal */} {__('Convert & Link Product', 'learning-management-system')} {__( 'To link this product to the course, its type will be changed to Masteriyo Course.', 'learning-management-system', )} {selectedOption?.label} {selectedOption?.type_label && ( {selectedOption.type_label} {' → '} {targetTypeLabel} )} setLinkConfirmed(e.target.checked)} colorScheme="primary" size="sm" > {__( 'I understand the product type will change to ', 'learning-management-system', )} {targetTypeLabel} . ); }; return ( {details(hasExistingProduct)} {linkSection()} ); }; export default WCIntegrationCourseSetting;