import type { CallHistoryItem, Serialized } from '@rocket.chat/core-typings'; import { Pagination } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { useSort, usePagination, GenericTableLoadingRow } from '@rocket.chat/ui-client'; import { useEndpoint, useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import { MediaCallHistoryTable, isCallHistoryUnknownContact, isCallHistoryInternalContact } from '@rocket.chat/ui-voip'; import type { CallHistoryContact } from '@rocket.chat/ui-voip'; import { useQuery } from '@tanstack/react-query'; import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useCallHistoryPageFilters } from './CallHistoryPageFilters'; import CallHistoryPageLayout from './CallHistoryPageLayout'; import CallHistoryRowExternalUser from './CallHistoryRowExternalUser'; import CallHistoryRowInternalUser from './CallHistoryRowInternalUser'; import CallHistoryRowUnknownUser from './CallHistoryRowUnknownUser'; import MediaCallHistoryContextualbar from './MediaCallHistoryContextualbar'; import { getExternalContact } from './MediaCallHistoryExternal'; import GenericNoResults from '../../components/GenericNoResults'; import UserInfoWithData from '../room/contextualBar/UserInfo/UserInfoWithData'; const getSort = (sortBy: 'contact' | 'type' | 'status' | 'timestamp', sortDirection: 'asc' | 'desc') => { const sortDirectionValue = sortDirection === 'asc' ? 1 : -1; switch (sortBy) { case 'type': return { direction: sortDirectionValue, ts: -1 }; case 'status': return { state: sortDirectionValue, ts: -1 }; case 'timestamp': return { ts: sortDirectionValue }; case 'contact': return { contactName: sortDirectionValue, contactUsername: sortDirectionValue, contactExtension: sortDirectionValue, ts: -1 }; default: return { ts: -1 }; } }; const getStateFilter = (states: T): T | [...T, 'error'] | undefined => { if (states.length === 0) { return undefined; } if (states.includes('failed')) { return [...states, 'error']; } return states; }; const getContact = (item: Serialized): CallHistoryContact => { if (item.external) { return getExternalContact(item); } if (!item.contactUsername || !item.contactName) { return { unknown: true }; } return { _id: item.contactId, username: item.contactUsername, name: item.contactName }; }; type DetailsTab = { openTab: 'details'; rid: string; }; type UserInfoTab = { openTab: 'user-info'; rid: string; userId: string; }; type Tab = DetailsTab | UserInfoTab; const CallHistoryPage = () => { const { t } = useTranslation(); const [tab, setTab] = useState(null); const sortProps = useSort<'contact' | 'type' | 'status' | 'timestamp'>('timestamp', 'desc'); const getCallHistory = useEndpoint('GET', '/v1/call-history.list'); const { setItemsPerPage, setCurrent, ...paginationProps } = usePagination(); const router = useRouter(); const historyId = useRouteParameter('historyId'); const filterProps = useCallHistoryPageFilters(); const { searchText, type, states } = filterProps; const debouncedSearchText = useDebouncedValue(searchText, 400); const onClickRow = useCallback( (rid: string, _id: string) => { router.navigate(`/call-history/details/${_id}`); setTab({ openTab: 'details', rid }); }, [router], ); const openUserInfo = useCallback( (userId: string, rid: string) => { setTab({ openTab: 'user-info', rid, userId }); }, [setTab], ); const closeTab = useCallback(() => { setTab(null); router.navigate('/call-history'); }, [router]); const handleBack = useCallback(() => { if (historyId && tab?.rid) { onClickRow(tab.rid, historyId); return; } setTab(null); }, [setTab, historyId, onClickRow, tab?.rid]); const { data, isPending, error, refetch } = useQuery({ queryKey: [ 'call-history', 'list', sortProps.sortBy, sortProps.sortDirection, paginationProps.current, paginationProps.itemsPerPage, type, states, debouncedSearchText, ], queryFn: () => { const sort = getSort(sortProps.sortBy, sortProps.sortDirection); const stateFilter = getStateFilter(states); return getCallHistory({ count: paginationProps.itemsPerPage, offset: paginationProps.current, sort: JSON.stringify(sort), ...(type !== 'all' && { direction: type }), ...(stateFilter && { state: stateFilter }), ...(debouncedSearchText && { filter: debouncedSearchText }), }); }, }); const tableData = useMemo(() => { return data?.items.map((item) => { return { _id: item._id, ...('rid' in item && { rid: item.rid }), contact: getContact(item), ...('messageId' in item && { messageId: item.messageId }), type: item.direction, status: item.state, timestamp: item.ts, duration: item.duration, }; }); }, [data]); const contextualBar = (() => { if (tab?.openTab === 'user-info') { return ; } if (tab?.openTab === 'details' && historyId) { return ; } return null; })(); const pagination = ( ); if (isPending) { return ( {pagination} ); } if (error) { return ( refetch()} /> ); } return ( {!tableData || (tableData.length === 0 && )} {tableData && tableData.length > 0 && ( {tableData.map((item) => { if (isCallHistoryUnknownContact(item.contact)) { return onClickRow('', item._id)} />; } if (isCallHistoryInternalContact(item.contact)) { return ( onClickRow(item.rid ?? '', item._id)} rid={item.rid ?? ''} onClickUserInfo={item.rid ? openUserInfo : undefined} /> ); } return onClickRow('', item._id)} />; })} )} {pagination} ); }; export default CallHistoryPage;