Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CAI-84] Chatbot History Chat Detail #1143

Merged
merged 15 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/stale-shrimps-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"nextjs-website": minor
"storybook-app": minor
---

Add chatbot chat history detail layout
11 changes: 11 additions & 0 deletions apps/nextjs-website/public/icons/chatbotChatUserBorder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Stack, Typography, useTheme } from '@mui/material';
import { defaultLocale } from '@/config';
import IconWrapper from '@/components/atoms/IconWrapper/IconWrapper';
import { parseChatMessage } from '@/helpers/chatMessageParser.helper';

type DateFormatOptions = {
locale?: string;
options?: Intl.DateTimeFormatOptions;
};

const DEFAULT_DATE_FORMAT = {
locale: defaultLocale,
options: {
timeStyle: 'short',
hourCycle: 'h23',
},
} satisfies DateFormatOptions;

type ChatMessageProps = {
text: string;
sender: string;
isQuestion: boolean;
timestamp?: string;
};

const ChatHistoryMessage = ({
text,
timestamp,
isQuestion,
sender,
}: ChatMessageProps) => {
const { palette } = useTheme();
const textColor = palette.text.primary;
const parsedChatMessage = parseChatMessage(text);
const iconSize = 28;

const timeLabel =
timestamp &&
new Intl.DateTimeFormat(
DEFAULT_DATE_FORMAT.locale,
DEFAULT_DATE_FORMAT.options
).format(new Date(timestamp));

return (
<Stack direction='column' width='100%' spacing={1}>
<Stack
direction={'row'}
margin={{ xs: '1rem 1rem 0.5rem 1rem' }}
alignItems='center'
spacing={1}
>
{isQuestion ? (
<IconWrapper
icon={'/icons/chatbotChatUserBorder.svg'}
useSrc={true}
color={palette.text.secondary}
size={iconSize}
/>
) : (
<IconWrapper
icon={'/icons/chatbotChatAvatar.svg'}
useSrc={true}
color={palette.text.secondary}
size={iconSize}
/>
)}
<Typography
color={textColor}
component='span'
minWidth='12rem'
fontWeight={600}
sx={{ fontSize: { xs: '1rem', xl: '1.125rem' } }}
>
{sender}
</Typography>
{timeLabel && (
<Typography
color={textColor}
component={'span'}
marginLeft={1}
sx={{ fontSize: { xs: '0.75rem', xl: '0.875rem' } }}
>
{timeLabel}
</Typography>
)}
</Stack>
<Typography
color={textColor}
component={'div'}
marginLeft={'1rem'}
paragraph
width={'100%'}
sx={{ fontSize: { xs: '0.875rem', xl: '1rem' } }}
>
{parsedChatMessage}
</Typography>
</Stack>
);
};

export default ChatHistoryMessage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Link, useTheme } from '@mui/material';

type ChatbotHistoryNavigationLinkProps = {
sessionId: string;
sessionTitle: string;
};

const ChatbotHistoryNavigationLink = ({
sessionId,
sessionTitle,
}: ChatbotHistoryNavigationLinkProps) => {
const { palette, typography } = useTheme();
const textColor = palette.text.secondary;

return (
<Link
maxWidth='15rem'
textOverflow='ellipsis'
color={textColor}
fontSize='1.125rem'
fontWeight='600'
fontFamily={typography.fontFamily}
component='span'
noWrap
sx={{ cursor: 'pointer', textDecoration: 'none' }}
onClick={() => {
// TODO: Implement the navigation to the chatbot history session
console.log(`Navigating to chatbot history session: ${sessionId}`);
}}
>
MarcoPonchia marked this conversation as resolved.
Show resolved Hide resolved
{sessionTitle}
</Link>
);
};

export default ChatbotHistoryNavigationLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ArrowBack, ArrowForward } from '@mui/icons-material';
import { Stack, Typography, useTheme } from '@mui/material';
import { useTranslations } from 'next-intl';
import ChatbotHistoryNavigationLink from '@/components/atoms/ChatbotHistoryNavigationLink/ChatbotHistoryNavigationLink';

export type SessionNavigationData = {
sessionId: string;
sessionTitle: string;
};

type ChatbotHistoryNavigationMenuProps = {
previousSession?: SessionNavigationData;
nextSession?: SessionNavigationData;
};

const ChatbotHistoryNavigationMenu = ({
previousSession,
nextSession,
}: ChatbotHistoryNavigationMenuProps) => {
const t = useTranslations();
const { palette } = useTheme();
const textColor = palette.text.secondary;

return (
<Stack direction='row' justifyContent='space-between'>
{previousSession && (
<Stack direction='column' spacing={1}>
<Typography component='h6' fontSize='0.875rem' color={textColor}>
{t('chatBot.previousChat')}
</Typography>
<Stack direction='row' spacing={1}>
<ArrowBack sx={{ color: textColor }} />
<ChatbotHistoryNavigationLink
sessionId={previousSession.sessionId}
sessionTitle={previousSession.sessionTitle}
/>
</Stack>
</Stack>
)}
{nextSession && (
<Stack direction='column' spacing={1}>
<Typography component='h6' fontSize='0.875rem' color={textColor}>
{t('chatBot.previousChat')}
</Typography>
<Stack direction='row' spacing={1}>
<ChatbotHistoryNavigationLink
sessionId={nextSession.sessionId}
sessionTitle={nextSession.sessionTitle}
/>
<ArrowForward sx={{ color: textColor }} />
</Stack>
</Stack>
)}
</Stack>
);
};

export default ChatbotHistoryNavigationMenu;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import ChatHistoryMessage from '@/components/atoms/ChatHistoryMessage/ChatHistoryMessage';
import { Query } from '@/lib/chatbot/queries';
import { Stack } from '@mui/material';
import { useTranslations } from 'next-intl';

type ChatbotHistoryMessagesProps = {
queries: Query[];
userName: string;
};

const ChatbotHistoryMessages = ({
queries,
userName,
}: ChatbotHistoryMessagesProps) => {
const t = useTranslations();

return (
<Stack direction='column' width='100%' spacing={2}>
{queries.map((query) => (
<Stack key={query.id} direction='column' width='100%' spacing={2}>
<ChatHistoryMessage
text={query.question}
timestamp={query.queriedAt}
isQuestion={true}
sender={userName}
/>
{query.answer && query.createdAt && (
<ChatHistoryMessage
text={query.answer}
timestamp={query.createdAt}
isQuestion={false}
sender={t('chatBot.title')}
/>
)}
</Stack>
))}
</Stack>
);
};

export default ChatbotHistoryMessages;
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import ChatbotHistoryNavigationMenu, {
SessionNavigationData,
} from '@/components/atoms/ChatbotHistoryNavigationMenu/ChatbotHistoryNavigationMenu';
import ChatbotHistoryMessages from '@/components/molecules/ChatbotHistoryMessages/ChatbotHistoryMessages';
import { defaultLocale } from '@/config';
import { Query } from '@/lib/chatbot/queries';
import { Delete } from '@mui/icons-material';
import { Box, Button, Stack, Typography, useTheme } from '@mui/material';
import { useTranslations } from 'next-intl';

type DateFormatOptions = {
locale?: string;
options?: Intl.DateTimeFormatOptions;
};

const DEFAULT_DATE_FORMAT = {
locale: defaultLocale,
options: {
day: '2-digit',
month: 'long',
year: 'numeric',
},
} satisfies DateFormatOptions;

type ChatbotHistoryDetailLayoutProps = {
queries: Query[];
userName: string;
previousSession?: SessionNavigationData;
nextSession?: SessionNavigationData;
onDeleteChatSession: (sessionId: string) => null;
};

const ChatbotHistoryDetailLayout = ({
queries,
userName,
previousSession,
nextSession,
onDeleteChatSession,
}: ChatbotHistoryDetailLayoutProps) => {
const t = useTranslations();
const { palette } = useTheme();

const date = new Intl.DateTimeFormat(
DEFAULT_DATE_FORMAT.locale,
DEFAULT_DATE_FORMAT.options
).format(new Date(queries[0].queriedAt));

return (
<Stack direction='column' spacing={2}>
<Typography variant='h2'>{queries[0].question}</Typography>
<Stack direction='row' justifyContent='space-between'>
<Typography
component='span'
color={palette.text.secondary}
sx={{ fontSize: { xs: '0.75rem', xl: '1rem' } }}
>
{date}
</Typography>
<Button
variant='outlined'
startIcon={<Delete />}
color='error'
sx={{ display: { xs: 'none', xl: 'flex' } }}
>
{t('chatBot.deleteChat')}
</Button>
</Stack>
<ChatbotHistoryMessages queries={queries} userName={userName} />
<Box
sx={{
display: { xs: 'flex', md: 'none' },
justifyContent: 'center',
paddingTop: '2rem',
width: '100%',
}}
>
<Button
variant='outlined'
startIcon={<Delete />}
color='error'
onClick={() => onDeleteChatSession(queries[0].sessionId)}
MarcoPonchia marked this conversation as resolved.
Show resolved Hide resolved
>
{t('chatBot.deleteChat')}
</Button>
</Box>
<Box
paddingTop='3rem'
sx={{ display: { xs: 'none', md: 'block' }, width: '100%' }}
>
<ChatbotHistoryNavigationMenu
previousSession={previousSession}
nextSession={nextSession}
/>
</Box>
</Stack>
);
};

export default ChatbotHistoryDetailLayout;
3 changes: 3 additions & 0 deletions apps/nextjs-website/src/messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,9 @@
"copied": "Copiato",
"welcomeMessage": "**Benvenuto nel Dev Portal!**\n\nSono _Discovery_, il tuo assistente virtuale per la documentazione tecnica. Posso aiutarti con informazioni su API, guide, e altro.\n\n_Nota_: Discovery è in versione beta, quindi alcune risposte potrebbero non essere accurate. Verifica sempre le informazioni importanti con la documentazione ufficiale.\n\nCome posso aiutarti oggi?",
"guestMessage": "Ciao sono _Discovery_ , il chatbot di DevPortal!\n\nPosso aiutarti a trovare in modo semplice e rapido le informazioni presenti nella documentazione del Portale.\n\nPer poter accedere al servizio, ti invito a [iscriverti a PagoPA DevPortal]({host}/auth/login)",
"deleteChat": "Elimina Chat",
"previousChat": "Chat Precedente",
"nextChat": "Chat Successiva",
"errors": {
"title": "Errore",
"serviceDown": "Il chatbot al momento non è disponibile. Riprovare più tardi.",
Expand Down
11 changes: 11 additions & 0 deletions apps/storybook-app/public/icons/chatbotChatUserBorder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading