packages/blueprints/gen-ai-chatbot/static-assets/chatbot-genai-components/frontend/src/pages/AdminSharedBotAnalyticsPage.tsx (172 lines of code) (raw):

import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import Help from '../components/Help'; import usePublicBotsForAdmin from '../hooks/usePublicBotsForAdmin'; import ListItemBot from '../components/ListItemBot'; import { addDate, formatDate } from '../utils/DateUtils'; import InputText from '../components/InputText'; import Button from '../components/Button'; import { PiArrowDown } from 'react-icons/pi'; import Skeleton from '../components/Skeleton'; import { twMerge } from 'tailwind-merge'; import { useNavigate } from 'react-router-dom'; const DATA_FORMAT = 'YYYYMMDD'; const AdminSharedBotAnalyticsPage: React.FC = () => { const { t } = useTranslation(); const [searchDateFrom, setSearchDateFrom] = useState<null | string>( formatDate(addDate(new Date(), -1, 'month'), DATA_FORMAT) ); const [searchDateTo, setSearchDateTo] = useState<null | string>( formatDate(new Date(), DATA_FORMAT) ); const [isDescCost, setIsDescCost] = useState(true); const { publicBots, isLoading: isLoadingPublicBots } = usePublicBotsForAdmin({ start: searchDateFrom ? searchDateFrom + '00' : undefined, end: searchDateTo ? searchDateTo + '23' : undefined, }); const sortedBots = useMemo(() => { const tmp = isDescCost ? -1 : 1; return publicBots?.sort((a, b) => a.totalPrice > b.totalPrice ? tmp : tmp * -1 ); }, [isDescCost, publicBots]); const validationErrorMessage = useMemo(() => { return !!searchDateFrom === !!searchDateTo ? null : t('admin.validationError.period'); }, [searchDateFrom, searchDateTo, t]); const navigate = useNavigate(); const onClickViewBot = useCallback( (botId: string) => { navigate(`/admin/bot/${botId}`); }, [navigate] ); return ( <> <div className="flex h-full justify-center"> <div className="w-2/3"> <div className="h-full w-full pt-8"> <div className="flex items-end justify-between"> <div className="flex items-center gap-2"> <div className="text-xl font-bold"> {t('admin.sharedBotAnalytics.label.pageTitle')} </div> <Help direction="right" message={t('admin.sharedBotAnalytics.help.overview')} /> </div> </div> <div className="my-2 rounded border p-2"> <div className="flex items-center gap-1 text-sm font-bold"> {t('admin.sharedBotAnalytics.label.SearchCondition.title')} <Help message={t('admin.sharedBotAnalytics.help.calculationPeriod')} /> </div> <div className="flex gap-2 sm:w-full md:w-3/4"> <InputText className="w-full" type="date" label={t( 'admin.sharedBotAnalytics.label.SearchCondition.from' )} value={formatDate(searchDateFrom, 'YYYY-MM-DD')} onChange={(val) => { if (val === '') { setSearchDateFrom(null); return; } setSearchDateFrom(formatDate(val, DATA_FORMAT)); }} errorMessage={ searchDateFrom ? undefined : validationErrorMessage ?? undefined } /> <InputText className="w-full" type="date" label={t('admin.sharedBotAnalytics.label.SearchCondition.to')} value={formatDate(searchDateTo, 'YYYY-MM-DD')} onChange={(val) => { if (val === '') { setSearchDateTo(null); return; } setSearchDateTo(formatDate(val, DATA_FORMAT)); }} errorMessage={ searchDateTo ? undefined : validationErrorMessage ?? undefined } /> </div> </div> <div className="my-2 flex justify-end"> <Button outlined rightIcon={ <PiArrowDown className={twMerge( 'transition', isDescCost ? 'rotate-0' : 'rotate-180' )} /> } onClick={() => { setIsDescCost(!isDescCost); }}> {t('admin.sharedBotAnalytics.label.sortByCost')} </Button> </div> <div className="mt-2 border-b border-gray"></div> <div className="h-4/5 overflow-x-hidden overflow-y-scroll border-b border-gray pr-1 scrollbar-thin scrollbar-thumb-aws-font-color/20 "> {isLoadingPublicBots && ( <div className="flex flex-col gap-2"> {new Array(15).fill('').map((_, idx) => { return <Skeleton key={idx} className="h-12 w-full" />; })} </div> )} {publicBots?.length === 0 && ( <div className="flex h-full w-full items-center justify-center italic text-dark-gray"> {t('admin.sharedBotAnalytics.label.noPublicBotUsages')} </div> )} {sortedBots?.map((bot, idx) => ( <ListItemBot key={idx} bot={{ ...bot, available: true, }} onClick={() => { onClickViewBot(bot.id); }}> <div className="relative flex h-full items-center"> <div className="text-lg font-bold"> {(Math.floor(bot.totalPrice * 100) / 100).toFixed(2)} USD </div> <div className="absolute bottom-0 right-0 flex origin-bottom-right whitespace-nowrap text-xs font-light"> {bot.isPublished ? ( <> {bot.isPublished ? t('admin.sharedBotAnalytics.label.published') : null} </> ) : ( <div></div> )} </div> </div> </ListItemBot> ))} </div> </div> </div> </div> </> ); }; export default AdminSharedBotAnalyticsPage;