Skip to content

Commit

Permalink
Virtual Assistant sample
Browse files Browse the repository at this point in the history
  • Loading branch information
epwinchell committed Mar 7, 2024
1 parent 1d19a9d commit 8fe1265
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 8 deletions.
8 changes: 5 additions & 3 deletions packages/module/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@
"dependencies": {
"@patternfly/react-core": "^5.1.2",
"@patternfly/react-icons": "^5.1.2",
"react-jss": "^10.10.0",
"clsx": "^2.1.0"
"clsx": "^2.1.0",
"react-jss": "^10.10.0"
},
"peerDependencies": {
"react": "^17 || ^18",
"react-dom": "^17 || ^18"
},
"devDependencies": {
"@patternfly/patternfly-a11y": "^4.3.1",
"@patternfly/documentation-framework": "^2.0.0-alpha.57",
"@patternfly/patternfly": "^5.1.0",
"@patternfly/patternfly-a11y": "^4.3.1",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"@types/react-router-dom": "^5.3.3",
Expand All @@ -52,6 +52,8 @@
"react-router": "^6.22.2",
"react-router-dom": "^6.22.2",
"rimraf": "^2.7.1",
"sass": "^1.71.1",
"sass-loader": "^14.1.1",
"typescript": "^5.3.3"
}
}
60 changes: 60 additions & 0 deletions packages/module/src/VirtualAssistant/AssistantMessageEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

import React from 'react';
import { Icon, Label, Split, SplitItem, TextContent } from '@patternfly/react-core';
import { createUseStyles } from 'react-jss';
import classnames from "clsx";

import RobotIcon from '@patternfly/react-icons/dist/js/icons/robot-icon';

const useStyles = createUseStyles({
chatbot: {
marginRight: "40px",
},
bubble: {
border: "1px solid var(--pf-v5-global--BackgroundColor--dark-400)",
borderRadius: "14px",
padding: "var(--pf-v5-global--spacer--sm) var(--pf-v5-global--spacer--md) var(--pf-v5-global--spacer--sm) var(--pf-v5-global--spacer--md)",
maxWidth: "100%",
wordWrap: "break-word",
},
label: {
// not working
"BorderColor": "var(--pf-v5-c-label--m-red__icon--Color)",
}
})

export const AssistantMessageEntry = () => {
const classes = useStyles();

return (
<div className="pf-v5-u-mb-md">
<Split className={classes.chatbot}>
<SplitItem>
<Icon size="lg" className="pf-v5-u-mr-sm pf-v5-u-pt-md">
<RobotIcon />
</Icon>
</SplitItem>
<SplitItem className={classnames(classes.bubble," pf-u-background-color-200")}>
<TextContent className="pf-v5-u-font-size-sm">
How many I help you today? Do you have some question for me?
</TextContent>
</SplitItem>
</Split>
{/* Options*/}
<Split>
<SplitItem className={classnames(classes.chatbot,"pf-v5-u-ml-xl pf-v5-u-mt-md")}>
<Label className={classnames(classes.label,"pf-v5-u-m-xs")} color="red">
Option #1
</Label>
<Label className={classnames(classes.label,"pf-v5-u-m-xs")} color="red">
Option #2
</Label>
<Label className={classnames(classes.label,"pf-v5-u-m-xs")} color="red">
Option #3
</Label>
</SplitItem>
</Split>
{/* Options*/}
</div>
);
};
29 changes: 29 additions & 0 deletions packages/module/src/VirtualAssistant/ConversationEndBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { Alert, TextContent } from '@patternfly/react-core';

import { createUseStyles } from 'react-jss';

const useStyles = createUseStyles({
banner: {
paddingTop: "0",
paddingBottom: "var(--pf-v5-global--spacer--md)",
},
bannerAlert: {
"& .pf-v5-c-alert__title": {
marginTop: "0",
fontSize: "var(--pf-v5-global--FontSize--sm)",
}
}
})

export const ConversationEndBanner: React.FunctionComponent = () => {
const classes = useStyles();

return (
<>
<TextContent className={classes.banner}>
<Alert className={classes.bannerAlert} variant="info" isInline title="You can start a new conversation at anytime by typing below." component="h6" />
</TextContent>
</>
);
};
80 changes: 80 additions & 0 deletions packages/module/src/VirtualAssistant/LoadingMessageEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { FunctionComponent } from 'react';
import { Icon, Split, SplitItem } from '@patternfly/react-core';
import { createUseStyles } from 'react-jss';
import classnames from "clsx";

import RobotIcon from '@patternfly/react-icons/dist/js/icons/robot-icon';

const useStyles = createUseStyles({
chatbot: {
marginBottom: "var(--pf-v5-global--spacer--md)",
marginRight: "40px",
},
bubble: {
border: "1px solid var(--pf-v5-global--BackgroundColor--dark-400)",
borderRadius: "14px",
padding: "var(--pf-v5-global--spacer--sm) var(--pf-v5-global--spacer--md) var(--pf-v5-global--spacer--sm) var(--pf-v5-global--spacer--md)",
maxWidth: "100%",
wordWrap: "break-word",
},
"@keyframes mercuryTypingAnimation": {
"0%": {
transform: "translateY(0px)",
backgroundColor: "var(--pf-v5-global--palette--black-600)",
},
"28%": {
transform: "translateY(-7px)",
backgroundColor: "var(--pf-v5-global--palette--black-400)",
},
"44%": {
transform: "translateY(0px)",
backgroundColor: "var(--pf-v5-global--palette--black-200)",
}
},
dot: {},
typing: {
height: "17px",
"& $dot": {
animation: "$mercuryTypingAnimation 1.8s infinite ease-in-out",
borderRadius: "50%",
display: "inline-block",
height: "7px",
marginRight: "4px",
marginTop: "6px",
verticalAlign: "middle",
width: "7px",
"&:nth-child(1)": {
animationDelay: "200ms",
},
"&:nth-child(2)": {
animationDelay: "300ms",
},
"&:nth-child(3)": {
animationDelay: "400ms",
},
"&:last-child": {
marginRight: "0",
},
}
}
})

export const LoadingMessageEntry: FunctionComponent<unknown> = () => {
const classes = useStyles();
return (
<Split className={classes.chatbot}>
<SplitItem>
<Icon size="lg" className="pf-v5-u-mr-sm pf-v5-u-pt-md">
<RobotIcon />
</Icon>
</SplitItem>
<SplitItem className={classnames(classes.bubble," pf-u-background-color-200")} >
<div className={classnames(classes.typing,"pf-v5-u-display-flex pf-u-align-items-center")}>
<div className={classes.dot}></div>
<div className={classes.dot}></div>
<div className={classes.dot}></div>
</div>
</SplitItem>
</Split>
);
};
22 changes: 22 additions & 0 deletions packages/module/src/VirtualAssistant/SystemMessageEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Text, TextContent, TextVariants } from '@patternfly/react-core';
import { createUseStyles } from 'react-jss';
import classnames from "clsx";

Check failure on line 4 in packages/module/src/VirtualAssistant/SystemMessageEntry.tsx

View workflow job for this annotation

GitHub Actions / call-build-lint-test-workflow / lint

'classnames' is defined but never used

const useStyles = createUseStyles({
systemMessageText: {
paddingBottom: "var(--pf-v5-global--spacer--md)",
textAlign: "center",
}
})

export const SystemMessageEntry = () => {
const classes = useStyles();
return (
<TextContent>
<Text component={TextVariants.small} className={classes.systemMessageText}>
End of conversation
</Text>
</TextContent>
);
};
41 changes: 41 additions & 0 deletions packages/module/src/VirtualAssistant/UserMessageEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { Icon, Split, SplitItem, TextContent } from '@patternfly/react-core';
import OutlinedUserIcon from '@patternfly/react-icons/dist/js/icons/outlined-user-icon';
import { createUseStyles } from 'react-jss';
import classnames from "clsx";

const useStyles = createUseStyles({
user: {
margin: "0 0 12px 40px",
},
bubbleUser: {
border: "1px solid var(--pf-v5-global--BackgroundColor--dark-400)",
borderRadius: "14px",
padding: "var(--pf-v5-global--spacer--sm) var(--pf-v5-global--spacer--md) var(--pf-v5-global--spacer--sm) var(--pf-v5-global--spacer--md)",
maxWidth: "100%",
wordWrap: "break-word",
}
})

const UserMessageEntry: React.FunctionComponent = () => {
const classes = useStyles();

return (
<>
<Split className={classnames(classes.user,"pf-v5-u-mb-md pf-v5-u-align-items-flex-start pf-v5-u-justify-content-flex-end")}>
<SplitItem className={classes.bubbleUser}>
<TextContent className="pf-v5-u-color-300 pf-v5-u-font-size-sm">
Hello, Can you help me?
</TextContent>
</SplitItem>
<SplitItem>
<Icon size="lg" className="pf-v5-u-ml-sm pf-v5-u-pt-xs">
<OutlinedUserIcon />
</Icon>
</SplitItem>
</Split>
</>
);
};

export default UserMessageEntry;
87 changes: 82 additions & 5 deletions packages/module/src/VirtualAssistant/VirtualAssistant.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,91 @@
import React from 'react';
import { Text } from '@patternfly/react-core'
import { Button, Card, CardBody, CardFooter, CardHeader, CardTitle, InputGroup, InputGroupText, TextArea } from '@patternfly/react-core'
import PlaneIcon from '@patternfly/react-icons/dist/esm/icons/paper-plane-icon';

import { LoadingMessageEntry } from './LoadingMessageEntry';
import { AssistantMessageEntry } from './AssistantMessageEntry';
import UserMessageEntry from './UserMessageEntry';
import { ConversationEndBanner } from './ConversationEndBanner';
import { SystemMessageEntry } from './SystemMessageEntry';

import { createUseStyles } from 'react-jss';
import classnames from "clsx";

export interface VirtualAssistantProps {
/** Content text */
text?: string;
title?: string;
};

const VirtualAssistant: React.FunctionComponent<VirtualAssistantProps> = ({ text, ...props }: VirtualAssistantProps) => (
<Text {...props}>{text ?? 'Virtual assistant content'}</Text>
);
const useStyles = createUseStyles({
card: {
width: "350px",
height: "550px",
overflow: "hidden",
"@media screen and (max-width: 768px)": {
height: "420px",
width: "100%",
},
},
cardHeader: {
background: "var(--pf-v5-global--BackgroundColor--dark-400)",
},
cardTitle: {
color: "var(--pf-v5-global--Color--light-100)",
},
cardBody: {
backgroundColor: "var(--pf-v5-global--BackgroundColor--100)",
paddingLeft: "var(--pf-v5-global--spacer--md)",
paddingRight: "var(--pf-v5-global--spacer--md)",
paddingTop: "var(--pf-v5-global--spacer--lg)",
overflowY: "scroll",
"&::-webkit-scrollbar": "display: none",
},
cardFooter: {
padding: "0",
},
inputGroup: {
height: "60px",
},
textArea: {
resize: "none",
}
})

const VirtualAssistant: React.FunctionComponent<VirtualAssistantProps> = ({ title, ...props }: VirtualAssistantProps) => {
const classes = useStyles();

return (
<Card className={classes.card}>
<CardHeader className={classes.cardHeader}>
<CardTitle className={classnames(classes.cardTitle,"pf-v5-u-font-size-xl")} {...props}>
{title ?? 'Virtual Assistant'}
</CardTitle>
</CardHeader>
<CardBody className={classes.cardBody}>
<AssistantMessageEntry />
<UserMessageEntry />
<LoadingMessageEntry />
<ConversationEndBanner />
<SystemMessageEntry />
</CardBody>
<CardFooter className={classes.cardFooter}>
<InputGroup className={classes.inputGroup}>
<TextArea
className={classes.textArea}
placeholder="Type a message..."
name="user-query"
type="text"
aria-label="User question"
/>
<InputGroupText id="username">
<Button variant="plain" className="pf-v5-u-px-sm">
<PlaneIcon />
</Button>
</InputGroupText>
</InputGroup>
</CardFooter>
</Card>
);
};

export default VirtualAssistant;

0 comments on commit 8fe1265

Please sign in to comment.