HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/agentcloud/webapp/src/components/PreviewSessionList.tsx
import * as API from '@api';
import { Menu, Transition } from '@headlessui/react';
import {
	EllipsisHorizontalIcon,
} from '@heroicons/react/20/solid';
import ButtonSpinner from 'components/ButtonSpinner';
import { useAccountContext } from 'context/account';
import { useSocketContext } from 'context/socket';
import Head from 'next/head';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { Fragment,useEffect, useState } from 'react';
import { toast } from 'react-toastify';
// import ContentLoader from 'react-content-loader';
function classNames(...classes) {
	return classes.filter(Boolean).join(' ');
}

export default function PreviewSessionList(props) {

	const [accountContext]: any = useAccountContext();
	const [_, __, sessionTrigger]: any = useSocketContext();
	const { account, teamName, csrf } = accountContext as any;
	const router = useRouter();
	const resourceSlug = router?.query?.resourceSlug || account?.currentTeam;
	const [state, dispatch] = useState(props);
	const [loading, setLoading] = useState(false);
	const [error, setError] = useState();
	const { sessions } = state;

	async function fetchSessions(noLoading: boolean = false) {
		!noLoading && setLoading(true);
		const start = Date.now();
		try {
			await API.getSessions({ resourceSlug }, dispatch, setError, router);
		} finally {
			!noLoading && setTimeout(() => {
				setLoading(false);
			}, 500-(Date.now()-start));
		}
	}

	async function deleteSession(sessionId) {
		API.deleteSession({
			_csrf: csrf,
			resourceSlug,
			sessionId,
		}, () => {
			fetchSessions(true);
			toast('Deleted session');
			if (router.asPath.includes(`/session/${sessionId}`)) {
				return router.push(`/${resourceSlug}/apps`);
			}
		}, () => {
			toast.error('Error deleting session');
		}, router);
	}

	useEffect(() => {
		fetchSessions();
	}, []);
	
	useEffect(() => {
		fetchSessions(true);
	}, [sessionTrigger]);
	
	useEffect(() => {
		const interval = setInterval(() => {
			fetchSessions(true);
		}, 30000);
		return () => {
			clearInterval(interval);
		};
	}, []);

	if (!resourceSlug) {
		return null;
	}

	// if (!sessions || loading) {
	// 	// return new Array(5).fill(0).map((x, xi) => (<div key={`contentLoader_${xi}`} className='ps-4 py-2 flex items-center justify-center'>
	// 	// 	<ContentLoader 
	// 	// 		speed={2}
	// 	// 		width={'100%'}
	// 	// 		height={10}
	// 	// 		viewBox='0 0 100% 10'
	// 	// 		backgroundColor='rgb(136, 143, 155)'
	// 	// 		foregroundColor='#5a5a5a'
	// 	// 	>
	// 	// 		<rect x='0' y='0' rx='5' width='100%' height='7' />
	// 	// 	</ContentLoader>
	// 	// </div>));
	// }

	//TODO: sesion infinite scrolling/loading

	return <li className='overflow-y-scroll pb-[220px]'>
		{sessions?.length > 0 && <div className='text-xs font-semibold leading-6 text-indigo-200'>Session History</div>}	
		{(!sessions || loading)
			? <div className='py-2 flex items-center justify-center'>
				<ButtonSpinner />
			</div>
			: <ul>
				{sessions.map(s => (<li key={s._id} className='flex justify-between'>
					<Link
						suppressHydrationWarning
						className={`text-gray-400 hover:text-white hover:bg-gray-700 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold w-full ${router.asPath.includes(`/session/${s._id}`) ? 'bg-gray-800 text-white' : ''}`}
						href={`/${resourceSlug}/session/${s._id}`}
					>
						<p className='overflow-hidden truncate text-ellipsis'>{s.previewLabel || s.name || <span className='text-xs italic'>New Session</span>}</p>
						
					</Link>
					<Menu as='div' className=''>
						<Menu.Button className='block p-2.5 text-gray-400 hover:text-gray-500 hover:text-white hover:bg-gray-700 rounded'>
							<span className='sr-only'>Open options</span>
							<EllipsisHorizontalIcon className='h-5 w-5' aria-hidden='true' />
						</Menu.Button>
						<Transition
							as={Fragment}
							enter='transition ease-out duration-100'
							enterFrom='transform opacity-0 scale-95'
							enterTo='transform opacity-100 scale-100'
							leave='transition ease-in duration-75'
							leaveFrom='transform opacity-100 scale-100'
							leaveTo='transform opacity-0 scale-95'
						>
							<Menu.Items className='absolute right-0 z-10 mt-0.5 w-32 origin-top-right rounded-md bg-white dark:bg-slate-800 py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none'>
								<Menu.Item>
									{({ active }) => (
										<a
											href={`/${resourceSlug}/session/${s._id}`}
											className={classNames(
												active ? 'bg-gray-50 dark:bg-slate-700' : '',
												'block px-3 py-1 text-sm leading-6 text-gray-900 dark:text-white'
											)}
										>
																View
										</a>
									)}
								</Menu.Item>
								<Menu.Item>
									{({ active }) => (
										<button
											onClick={(e) => {
												e.stopPropagation();
												deleteSession(s._id);
											}}
											className={classNames(
												active ? 'bg-gray-50 dark:bg-slate-700' : '',
												'block px-3 py-1 text-sm leading-6 text-red-600 w-full text-left'
											)}
										>
											Delete
										</button>
									)}
								</Menu.Item>
							</Menu.Items>
						</Transition>
					</Menu>
				</li>))}
			</ul>}
	</li>;

};

export async function getServerSideProps({ req, res, query, resolvedUrl, locale, locales, defaultLocale }) {
	return JSON.parse(JSON.stringify({ props: res?.locals?.data || {} }));
};