Current File : /home/jeconsul/public_html/wp-content/plugins/suremails/src/screens/connections/connections.js
import { useState, useEffect, useCallback } from '@wordpress/element';
import { useLocation, useNavigate } from 'react-router-dom';
import { __ } from '@wordpress/i18n';
import { Button, toast, Table } from '@bsf/force-ui';
import { Trash, PenLine, Plus, ChevronsUpDown } from 'lucide-react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import ConnectionsSkeleton from './connections-skeleton';
import ProvidersDrawer from '@screens/connections/providers-drawer';
import TestEmailDrawer from '@screens/connections/test-email-drawer';
import NoConnection from '@screens/connections/no-connections';
import ConfirmationDialog from '@components/confirmation-dialog/confirmation-dialog';
import {
	fetchSettings,
	deleteConnection as apiDeleteConnection,
} from '@api/connections';
import { formatDate, sortData } from '@utils/utils';
import Tooltip from '@components/tooltip/tooltip';
import TruncatedTooltipText from '@components/truncated-tooltip-text';
import Title from '@components/title/title';
import useProviders from './use-dynamic-providers';

const Connections = () => {
	const [ isDrawerOpen, setIsDrawerOpen ] = useState( false );
	const [ isTestEmailDrawerOpen, setIsTestEmailDrawerOpen ] =
		useState( false );
	const [ currentConnection, setCurrentConnection ] = useState( null );
	const [ sortDirection, setSortDirection ] = useState( 'asc' );
	const [ isDialogOpen, setIsDialogOpen ] = useState( false );
	const [ dialogConfig, setDialogConfig ] = useState( {
		title: '',
		description: '',
		onConfirm: null,
	} );

	const queryClient = useQueryClient();
	const location = useLocation();
	const navigate = useNavigate();

	useEffect( () => {
		if ( location.state?.openDrawer ) {
			setIsDrawerOpen( true );

			const storedFormState =
				JSON.parse( localStorage.getItem( 'formStateValues' ) ) || {};

			const expirationTime = Date.now();
			const stored = localStorage.getItem( 'formStateValuesTimestamp' );
			const storedTime = parseInt( stored, 10 );

			if ( storedTime && storedTime < expirationTime ) {
				localStorage.removeItem( 'formStateValues' );
				localStorage.removeItem( 'formStateValuesTimestamp' );
			} else {
				setCurrentConnection( storedFormState );
				localStorage.removeItem( 'formStateValues' );
				localStorage.removeItem( 'formStateValuesTimestamp' );
			}
			navigate( location.pathname, { replace: true, state: {} } );
		}
	}, [ location.state, navigate ] );

	// Query for fetching connections
	const {
		data: settings,
		isLoading,
		error,
	} = useQuery( {
		queryKey: [ 'settings' ],
		queryFn: fetchSettings,
		select: ( data ) => data?.data || {},
		refetchInterval: 100000, // Refetch every 10 minutes
		refetchOnMount: false,
		refetchOnWindowFocus: false,
		refetchOnReconnect: true,
	} );
	const { providers: providersList, isLoading: isProvidersLoading } =
		useProviders();

	const getNewConnectionSequenceId = useCallback( () => {
		const connectionCount = Math.max(
			Object.values( settings?.connections ).length,
			0
		);

		return ( connectionCount + 1 ) * 10;
	}, [ settings?.connections ] );

	const getNewConnectionCount = useCallback( () => {
		const count = {};

		Object.values( settings?.connections ).forEach( ( connectionItem ) => {
			count[ connectionItem.type ] =
				( count[ connectionItem.type ] || 0 ) + 1;
		} );

		return count;
	}, [ settings?.connections ] );

	// Mutation for deleting connections
	const deleteMutation = useMutation( {
		mutationFn: apiDeleteConnection,
		onSuccess: ( response, connection ) => {
			if ( response.success ) {
				toast.success( __( 'Deleted!', 'suremails' ), {
					description: __(
						'Connection deleted successfully.',
						'suremails'
					),
				} );

				// Update cache by removing the deleted connection
				queryClient.setQueryData( [ 'settings' ], ( oldData ) => ( {
					...oldData,
					data: {
						...oldData.data,
						connections: Object.fromEntries(
							Object.entries( oldData.data.connections ).filter(
								( [ key ] ) => key !== connection.id
							)
						),
					},
				} ) );

				// Refetch logs and dashboard data
				queryClient.refetchQueries( {
					queryKey: [ 'logs' ],
				} );
				// Refetch dashboard data
				queryClient.refetchQueries( {
					queryKey: [ 'dashboard-data' ],
					exact: true,
				} );
				// Refetch settings
				queryClient.invalidateQueries( {
					queryKey: [ 'settings' ],
				} );
			}
		},
		onError: ( delError ) => {
			toast.error( __( 'Error deleting connection', 'suremails' ), {
				description:
					delError.message ||
					__(
						'There was an issue deleting the connection.',
						'suremails'
					),
			} );
		},
		onSettled: () => {
			setIsDialogOpen( false );
		},
	} );

	// Update connections cache when a connection is added or edited
	const updateConnections = () => {
		// Invalidate the settings query to trigger a refetch
		queryClient.invalidateQueries( {
			queryKey: [ 'settings' ],
		} );
		// Refetch dashboard data
		queryClient.refetchQueries( {
			queryKey: [ 'dashboard-data' ],
		} );
	};

	// Sort functionality
	const sortTableData = () => {
		const newDirection = sortDirection === 'asc' ? 'desc' : 'asc';
		setSortDirection( newDirection );
	};

	const renderSortedConnections = () => {
		const sortedConnections = sortData(
			Object.entries( settings?.connections || {} ).map(
				( [ key, value ] ) => ( { id: key, ...value } )
			),
			'created_at',
			sortDirection
		);

		return sortedConnections;
	};

	const handleDeleteConnection = ( connection ) => {
		setDialogConfig( {
			title: __( 'Confirm Deletion', 'suremails' ),
			description: __(
				'Are you sure you want to delete this connection? This action cannot be undone.',
				'suremails'
			),
			requireConfirmation: true,
			onConfirm: () => confirmDelete( connection ),
		} );
		setIsDialogOpen( true );
	};

	const confirmDelete = async ( connection ) => {
		await deleteMutation.mutateAsync( connection );
	};

	const handleTestEmail = ( connection ) => {
		setCurrentConnection( connection );
		setIsTestEmailDrawerOpen( true );
	};

	const handleEditConnection = ( connection ) => {
		setCurrentConnection( connection );
		setIsDrawerOpen( true );
	};

	// Show loading state
	if ( isLoading ) {
		return <ConnectionsSkeleton />;
	}

	// Show error state
	if ( error ) {
		toast.error( __( 'Error loading connections', 'suremails' ), {
			description:
				error.message ||
				__( 'There was an issue fetching connections.', 'suremails' ),
		} );
	}

	// Define table headers with sorting capability
	const headers = [
		{
			label: __( 'Connection', 'suremails' ),
		},
		{
			label: __( 'Connection Title', 'suremails' ),
		},
		{
			label: __( 'Email', 'suremails' ),
		},
		{
			label: __( 'Created On', 'suremails' ),
			sortable: true,
		},
		{
			label: __( 'Test Email', 'suremails' ),
		},
		{
			label: __( 'Action', 'suremails' ),
			srOnly: true,
		},
	];

	return (
		<div className="flex items-start justify-center h-full px-8 py-8 bg-background-secondary">
			<div className="w-full h-auto px-4 py-4 space-y-2 border-0.5 border-solid shadow-sm opacity-100 rounded-xl border-border-subtle bg-background-primary">
				{ /* Header */ }
				{ renderSortedConnections().length > 0 && (
					<div className="flex items-center justify-between w-full gap-2 px-2 py-2.25 opacity-100">
						<Title
							title={ __( 'Email Connections', 'suremails' ) }
							tag="h1"
						/>
						<Button
							variant="primary"
							size="sm"
							icon={ <Plus /> }
							iconPosition="left"
							onClick={ () => setIsDrawerOpen( true ) }
							className="font-medium"
						>
							{ __( 'Add Connection', 'suremails' ) }
						</Button>
					</div>
				) }

				{ /* Content Area */ }
				{ renderSortedConnections().length > 0 ? (
					<Table>
						<Table.Head className="bg-background-secondary">
							{ headers.map( ( header, index ) => (
								<Table.HeadCell
									key={ index }
									className="whitespace-nowrap"
								>
									{ header?.sortable ? (
										<div
											key="created-at"
											className="flex items-center cursor-pointer"
											onClick={ sortTableData }
										>
											{ header.label }
											<ChevronsUpDown className="ml-1 size-4" />
										</div>
									) : (
										<span
											className={
												header?.srOnly ? 'sr-only' : ''
											}
										>
											{ header.label }
										</span>
									) }
								</Table.HeadCell>
							) ) }
						</Table.Head>
						<Table.Body>
							{ renderSortedConnections().map( ( row ) => (
								<Table.Row
									key={ row.id } // Use unique identifier for key
								>
									<Table.Cell className="w-32">
										<div className="flex items-center">
											{
												providersList.find(
													( provider ) =>
														provider.value ===
														row?.type
												)?.icon
											}
										</div>
									</Table.Cell>
									<Table.Cell>
										<TruncatedTooltipText
											className="max-w-64"
											text={ `${ row.connection_title } - ${ row.type }` }
										/>
									</Table.Cell>
									<Table.Cell>
										<TruncatedTooltipText
											className="max-w-60"
											text={ row.from_email }
										/>
									</Table.Cell>
									<Table.Cell className="text-nowrap">
										{ formatDate( row.created_at, {
											day: true,
											month: true,
											year: true,
											hour: true,
											minute: true,
											hour12: true,
										} ) }
									</Table.Cell>
									<Table.Cell>
										{ /* Send Test Email Button */ }
										<Button
											variant="outline"
											size="xs"
											className="shadow-sm whitespace-nowrap"
											onClick={ () =>
												handleTestEmail( row )
											}
										>
											{ __(
												'Send Test Email',
												'suremails'
											) }
										</Button>
										<div className="inline-flex items-center justify-between gap-2">
											{ /* Action Buttons with Tooltips */ }
										</div>
									</Table.Cell>
									<Table.Cell>
										<div className="inline-flex justify-end w-full gap-2">
											{ /* Edit Button with Tooltip */ }
											<Tooltip
												content={ __(
													'Edit',
													'suremails'
												) }
												position="top"
												arrow
											>
												<Button
													variant="ghost"
													size="xs"
													icon={ <PenLine /> }
													iconPosition="left"
													onClick={ () =>
														handleEditConnection(
															row
														)
													}
												/>
											</Tooltip>

											<Tooltip
												content={
													<span>
														{ __(
															'Delete',
															'suremails'
														) }
													</span>
												}
												position="top"
												variant="dark"
												arrow
											>
												<Button
													variant="ghost"
													size="xs"
													icon={ <Trash /> }
													iconPosition="left"
													onClick={ () =>
														handleDeleteConnection(
															row
														)
													}
												/>
											</Tooltip>
										</div>
									</Table.Cell>
								</Table.Row>
							) ) }
						</Table.Body>
					</Table>
				) : (
					<NoConnection
						onClickAddConnection={ () => setIsDrawerOpen( true ) }
					/>
				) }

				{ /* Providers Drawer */ }
				<ProvidersDrawer
					isOpen={ isDrawerOpen }
					setIsOpen={ setIsDrawerOpen }
					currentConnection={ currentConnection } // Preselect provider with form data
					onSave={ updateConnections }
					providers={ providersList }
					isProvidersLoading={ isProvidersLoading }
					sequenceId={ getNewConnectionSequenceId() }
					connectionCount={ getNewConnectionCount() }
				/>

				{ /* Test Email Drawer */ }
				<TestEmailDrawer
					isOpen={ isTestEmailDrawerOpen }
					onClose={ ( value ) => {
						setIsTestEmailDrawerOpen( false );
						if ( ! value ) {
							setCurrentConnection( null );
						}
					} }
					connections={ settings.connections }
					currentConnection={ currentConnection }
				/>

				{ /* Confirmation Dialog */ }
				<ConfirmationDialog
					isOpen={ isDialogOpen }
					title={ dialogConfig.title }
					description={ dialogConfig.description }
					onConfirm={ dialogConfig.onConfirm }
					onCancel={ () => setIsDialogOpen( false ) }
					confirmButtonText={ __( 'Delete', 'suremails' ) } // Customize button text if needed
					cancelButtonText={ __( 'Cancel', 'suremails' ) }
					requireConfirmation={ dialogConfig.requireConfirmation }
				/>
			</div>
		</div>
	);
};

export default Connections;