Skip to content

Commit

Permalink
Full width slide down syllabus chat for the resource drawer (#2063)
Browse files Browse the repository at this point in the history
* Remove columned chatbot. Refactor

* Full width / slide down syllabus bot

* Update Posthog replay vars

* Refactor chat with entry screen for reuse

* Update test and clean up

* Fix enum import

* Test updates

* Prevent tab to masked content when the chat is open (accessibility)

* Limit scroll to content area when chat enabled

* Remove scrollbar-gutter not needed

* Rewind limit scroll to content area when chat enabled

This reverts commit 823d955.

* avoid layout shift, show full scrollbar

* Make chat inert when closed. Fix pointer events stopped at opener container

---------

Co-authored-by: Chris Chudzicki <[email protected]>
  • Loading branch information
jonkafton and ChristopherChudzicki authored Feb 21, 2025
1 parent b4626b9 commit 1f7a800
Show file tree
Hide file tree
Showing 25 changed files with 1,276 additions and 1,186 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
POSTHOG_API_HOST: https://app.posthog.com
POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID_PROD }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_PROJECT_API_KEY_PROD }}
POSTHOG_ENABLE_SESSION_RECORDING: ${{ secrets.POSTHOG_PROJECT_ENABLE_SESSION_RECORDING_PROD }}
POSTHOG_ENABLE_SESSION_RECORDING: ${{ secrets.POSTHOG_ENABLE_SESSION_RECORDING_PROD }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN_PROD }}
SENTRY_ENV: ${{ secrets.MITOL_ENVIRONMENT_PROD }}
SENTRY_PROFILES_SAMPLE_RATE: ${{ secrets.SENTRY_PROFILES_SAMPLE_RATE_PROD }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
POSTHOG_API_HOST: https://app.posthog.com
POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID_RC }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_PROJECT_API_KEY_RC }}
POSTHOG_ENABLE_SESSION_RECORDING: ${{ secrets.POSTHOG_PROJECT_ENABLE_SESSION_RECORDING_RC }}
POSTHOG_ENABLE_SESSION_RECORDING: ${{ secrets.POSTHOG_ENABLE_SESSION_RECORDING_RC }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN_RC }}
SENTRY_ENV: ${{ secrets.MITOL_ENVIRONMENT_RC }}
SENTRY_PROFILES_SAMPLE_RATE: ${{ secrets.SENTRY_PROFILES_SAMPLE_RATE_RC }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,33 @@
import React, { useState, useRef, useEffect } from "react"
import { Typography, styled, Drawer, AdornmentButton } from "ol-components"
import {
RiSparkling2Line,
RiSendPlaneFill,
RiCloseLine,
} from "@remixicon/react"
import { Input, ActionButton } from "@mitodl/smoot-design"
import type { AiChatMessage } from "@mitodl/smoot-design/ai"
import AiRecommendationBot, { STARTERS } from "./AiRecommendationBot"
import React, { useEffect, useRef, useState } from "react"
import { styled, Typography, AdornmentButton } from "ol-components"
import { AiChat } from "@mitodl/smoot-design/ai"
import type { AiChatMessage, AiChatProps } from "@mitodl/smoot-design/ai"
import { RiSparkling2Line, RiSendPlaneFill } from "@remixicon/react"
import { Input } from "@mitodl/smoot-design"
import Image from "next/image"
import timLogo from "@/public/images/icons/tim.svg"

const Container = styled.div(({ theme }) => ({
width: "900px",
height: "100%",
[theme.breakpoints.down("md")]: {
width: "100%",
},
}))

const EntryScreen = styled.div(({ theme }) => ({
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: "16px",
padding: "136px 40px 24px 40px",
width: "900px",
[theme.breakpoints.down("md")]: {
padding: "136px 24px 24px 24px",
width: "100%",
},
}))

const CloseButton = styled(ActionButton)(({ theme }) => ({
position: "absolute",
top: "24px",
right: "40px",
backgroundColor: theme.custom.colors.lightGray2,
"&&:hover": {
backgroundColor: theme.custom.colors.red,
color: theme.custom.colors.white,
},
[theme.breakpoints.down("md")]: {
right: "24px",
},
}))

const TimLogoBox = styled.div(({ theme }) => ({
position: "relative",
padding: "16px",
Expand Down Expand Up @@ -86,6 +75,7 @@ const SendIcon = styled(RiSendPlaneFill)(({ theme }) => ({
const Starters = styled.div(({ theme }) => ({
display: "flex",
gap: "16px",
width: "100%",
[theme.breakpoints.down("sm")]: {
flexDirection: "column",
},
Expand Down Expand Up @@ -113,12 +103,33 @@ const Starter = styled.button(({ theme }) => ({
},
}))

const AiRecommendationBotDrawer = ({
open,
setOpen,
const ChatScreen = styled.div(({ theme }) => ({
padding: "16px 40px 24px",
height: "100%",
[theme.breakpoints.down("md")]: {
padding: "16px 24px 16px",
width: "100%",
},
}))

const AiChatWithEntryScreen = ({
entryTitle,
starters,
initialMessages,
askTimTitle,
requestOpts,
onClose,
chatScreenClassName,
className,
}: {
open: boolean
setOpen: (open: boolean) => void
entryTitle: string
starters: AiChatProps["conversationStarters"]
initialMessages: AiChatProps["initialMessages"]
askTimTitle?: string
requestOpts: AiChatProps["requestOpts"]
onClose?: () => void
className?: string
chatScreenClassName?: string
}) => {
const [initialPrompt, setInitialPrompt] = useState("")
const [showEntryScreen, setShowEntryScreen] = useState(true)
Expand Down Expand Up @@ -156,41 +167,15 @@ const AiRecommendationBotDrawer = ({
setShowEntryScreen(false)
}

const closeDrawer = () => {
setOpen(false)
setShowEntryScreen(true)
}

return (
<Drawer
open={open}
anchor="right"
onClose={closeDrawer}
PaperProps={{
sx: {
minWidth: (theme) => ({
[theme.breakpoints.down("md")]: {
width: "100%",
},
}),
},
}}
>
<CloseButton
variant="text"
size="medium"
onClick={closeDrawer}
aria-label="Close"
>
<RiCloseLine />
</CloseButton>
<Container className={className}>
{showEntryScreen ? (
<EntryScreen>
<TimLogoBox>
<RiSparkling2Line />
<TimLogo src={timLogo.src} alt="" width={40} height={40} />
</TimLogoBox>
<Title variant="h4">What do you want to learn from MIT?</Title>
<Title variant="h4">{entryTitle}</Title>
<StyledInput
fullWidth
size="chat"
Expand All @@ -208,7 +193,7 @@ const AiRecommendationBotDrawer = ({
responsive
/>
<Starters>
{STARTERS.map(({ content }, index) => (
{starters?.map(({ content }, index) => (
<Starter
key={index}
onClick={() => onStarterClick(content)}
Expand All @@ -225,10 +210,22 @@ const AiRecommendationBotDrawer = ({
</Starters>
</EntryScreen>
) : (
<AiRecommendationBot ref={aiChatRef} />
<ChatScreen
className={chatScreenClassName}
data-testid="ai-chat-screen"
>
<AiChat
askTimTitle={askTimTitle}
conversationStarters={starters}
initialMessages={initialMessages}
onClose={onClose}
requestOpts={requestOpts}
ref={aiChatRef}
/>
</ChatScreen>
)}
</Drawer>
</Container>
)
}

export default AiRecommendationBotDrawer
export default AiChatWithEntryScreen
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React from "react"
import { styled, Drawer } from "ol-components"
import { RiCloseLine } from "@remixicon/react"
import { ActionButton } from "@mitodl/smoot-design"
import type { AiChatProps } from "@mitodl/smoot-design/ai"
import AiChatWithEntryScreen from "./AiChatWithEntryScreen"
import { getCsrfToken } from "@/common/utils"

const CloseButton = styled(ActionButton)(({ theme }) => ({
position: "absolute",
top: "24px",
right: "40px",
backgroundColor: theme.custom.colors.lightGray2,
"&&:hover": {
backgroundColor: theme.custom.colors.red,
color: theme.custom.colors.white,
},
[theme.breakpoints.down("md")]: {
right: "24px",
},
}))

const StyledAiChatWithEntryScreen = styled(AiChatWithEntryScreen)(
({ theme }) => ({
width: "900px",
[theme.breakpoints.down("md")]: {
width: "100%",
},
}),
)

const INITIAL_MESSAGES: AiChatProps["initialMessages"] = [
{
content: "What do you want to learn about today?",
role: "assistant",
},
]

const STARTERS = [
{
content:
"I'm interested in courses on quantum computing that offer certificates.",
},
{
content:
"I want to learn about global warming, can you recommend any videos?",
},
{
content:
"I would like to learn about linear regression, preferably at no cost.",
},
]

const AiRecommendationBotDrawer = ({
open,
setOpen,
}: {
open: boolean
setOpen: (open: boolean) => void
}) => {
const closeDrawer = () => {
setOpen(false)
// setShowEntryScreen(true)
}

return (
<Drawer
open={open}
anchor="right"
onClose={closeDrawer}
PaperProps={{
sx: {
minWidth: (theme) => ({
[theme.breakpoints.down("md")]: {
width: "100%",
},
}),
},
}}
>
<CloseButton
variant="text"
size="medium"
onClick={closeDrawer}
aria-label="Close"
>
<RiCloseLine />
</CloseButton>
<StyledAiChatWithEntryScreen
entryTitle="What do you want to learn from MIT?"
starters={STARTERS}
askTimTitle="to recommend a course"
initialMessages={INITIAL_MESSAGES}
requestOpts={{
apiUrl: process.env.NEXT_PUBLIC_LEARN_AI_RECOMMENDATION_ENDPOINT!,
fetchOpts: {
headers: {
"X-CSRFToken": getCsrfToken(),
},
},
transformBody: (messages) => ({
message: messages[messages.length - 1].content,
}),
}}
/>
</Drawer>
)
}

export default AiRecommendationBotDrawer

This file was deleted.

Loading

0 comments on commit 1f7a800

Please sign in to comment.