mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: format with prettier
This commit is contained in:
parent
03fb09c3d6
commit
a147ad6610
164 changed files with 11257 additions and 8154 deletions
|
|
@ -1,327 +1,477 @@
|
|||
import { useSession } from '@components/Contexts/SessionContext'
|
||||
import { sendActivityAIChatMessage, startActivityAIChatSession } from '@services/ai/ai';
|
||||
import { AlertTriangle, BadgeInfo, NotebookTabs } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
sendActivityAIChatMessage,
|
||||
startActivityAIChatSession,
|
||||
} from '@services/ai/ai'
|
||||
import { AlertTriangle, BadgeInfo, NotebookTabs } from 'lucide-react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { FlaskConical, MessageCircle, X } from 'lucide-react'
|
||||
import Image from 'next/image';
|
||||
import learnhouseAI_icon from "public/learnhouse_ai_simple.png";
|
||||
import learnhouseAI_logo_black from "public/learnhouse_ai_black_logo.png";
|
||||
import Image from 'next/image'
|
||||
import learnhouseAI_icon from 'public/learnhouse_ai_simple.png'
|
||||
import learnhouseAI_logo_black from 'public/learnhouse_ai_black_logo.png'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { AIChatBotStateTypes, useAIChatBot, useAIChatBotDispatch } from '@components/Contexts/AI/AIChatBotContext';
|
||||
import useGetAIFeatures from '../../../AI/Hooks/useGetAIFeatures';
|
||||
import UserAvatar from '@components/Objects/UserAvatar';
|
||||
|
||||
import {
|
||||
AIChatBotStateTypes,
|
||||
useAIChatBot,
|
||||
useAIChatBotDispatch,
|
||||
} from '@components/Contexts/AI/AIChatBotContext'
|
||||
import useGetAIFeatures from '../../../AI/Hooks/useGetAIFeatures'
|
||||
import UserAvatar from '@components/Objects/UserAvatar'
|
||||
|
||||
type AIActivityAskProps = {
|
||||
activity: any;
|
||||
activity: any
|
||||
}
|
||||
|
||||
|
||||
function AIActivityAsk(props: AIActivityAskProps) {
|
||||
const is_ai_feature_enabled = useGetAIFeatures({ feature: 'activity_ask' });
|
||||
const [isButtonAvailable, setIsButtonAvailable] = React.useState(false);
|
||||
const dispatchAIChatBot = useAIChatBotDispatch() as any;
|
||||
const is_ai_feature_enabled = useGetAIFeatures({ feature: 'activity_ask' })
|
||||
const [isButtonAvailable, setIsButtonAvailable] = React.useState(false)
|
||||
const dispatchAIChatBot = useAIChatBotDispatch() as any
|
||||
|
||||
useEffect(() => {
|
||||
if (is_ai_feature_enabled) {
|
||||
setIsButtonAvailable(true);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (is_ai_feature_enabled) {
|
||||
setIsButtonAvailable(true)
|
||||
}
|
||||
, [is_ai_feature_enabled]);
|
||||
}, [is_ai_feature_enabled])
|
||||
|
||||
return (
|
||||
<>
|
||||
{isButtonAvailable && (
|
||||
<div >
|
||||
<ActivityChatMessageBox activity={props.activity} />
|
||||
<div
|
||||
onClick={() => dispatchAIChatBot({ type: 'setIsModalOpen' })}
|
||||
style={{
|
||||
background: 'conic-gradient(from 32deg at 53.75% 50%, rgb(35, 40, 93) 4deg, rgba(20, 0, 52, 0.95) 59deg, rgba(164, 45, 238, 0.88) 281deg)',
|
||||
}}
|
||||
className="rounded-full px-5 drop-shadow-md flex items-center space-x-1.5 p-2.5 text-sm text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out hover:scale-105">
|
||||
{" "}
|
||||
<i>
|
||||
<Image className='outline outline-1 outline-neutral-200/20 rounded-md' width={20} src={learnhouseAI_icon} alt="" />
|
||||
</i>{" "}
|
||||
<i className="not-italic text-xs font-bold">Ask AI</i>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
)
|
||||
return (
|
||||
<>
|
||||
{isButtonAvailable && (
|
||||
<div>
|
||||
<ActivityChatMessageBox activity={props.activity} />
|
||||
<div
|
||||
onClick={() => dispatchAIChatBot({ type: 'setIsModalOpen' })}
|
||||
style={{
|
||||
background:
|
||||
'conic-gradient(from 32deg at 53.75% 50%, rgb(35, 40, 93) 4deg, rgba(20, 0, 52, 0.95) 59deg, rgba(164, 45, 238, 0.88) 281deg)',
|
||||
}}
|
||||
className="rounded-full px-5 drop-shadow-md flex items-center space-x-1.5 p-2.5 text-sm text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out hover:scale-105"
|
||||
>
|
||||
{' '}
|
||||
<i>
|
||||
<Image
|
||||
className="outline outline-1 outline-neutral-200/20 rounded-md"
|
||||
width={20}
|
||||
src={learnhouseAI_icon}
|
||||
alt=""
|
||||
/>
|
||||
</i>{' '}
|
||||
<i className="not-italic text-xs font-bold">Ask AI</i>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export type AIMessage = {
|
||||
sender: string;
|
||||
message: any;
|
||||
type: 'ai' | 'user';
|
||||
sender: string
|
||||
message: any
|
||||
type: 'ai' | 'user'
|
||||
}
|
||||
|
||||
type ActivityChatMessageBoxProps = {
|
||||
activity: any;
|
||||
activity: any
|
||||
}
|
||||
|
||||
function ActivityChatMessageBox(props: ActivityChatMessageBoxProps) {
|
||||
const session = useSession() as any;
|
||||
const aiChatBotState = useAIChatBot() as AIChatBotStateTypes;
|
||||
const dispatchAIChatBot = useAIChatBotDispatch() as any;
|
||||
const session = useSession() as any
|
||||
const aiChatBotState = useAIChatBot() as AIChatBotStateTypes
|
||||
const dispatchAIChatBot = useAIChatBotDispatch() as any
|
||||
|
||||
// TODO : come up with a better way to handle this
|
||||
const inputClass = aiChatBotState.isWaitingForResponse
|
||||
? 'ring-1 ring-inset ring-white/10 bg-gray-950/40 w-full rounded-lg outline-none px-4 py-2 text-white text-sm placeholder:text-white/30 opacity-30 '
|
||||
: 'ring-1 ring-inset ring-white/10 bg-gray-950/40 w-full rounded-lg outline-none px-4 py-2 text-white text-sm placeholder:text-white/30';
|
||||
// TODO : come up with a better way to handle this
|
||||
const inputClass = aiChatBotState.isWaitingForResponse
|
||||
? 'ring-1 ring-inset ring-white/10 bg-gray-950/40 w-full rounded-lg outline-none px-4 py-2 text-white text-sm placeholder:text-white/30 opacity-30 '
|
||||
: 'ring-1 ring-inset ring-white/10 bg-gray-950/40 w-full rounded-lg outline-none px-4 py-2 text-white text-sm placeholder:text-white/30'
|
||||
|
||||
useEffect(() => {
|
||||
if (aiChatBotState.isModalOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = 'unset';
|
||||
}
|
||||
}, [aiChatBotState.isModalOpen]);
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (event.key === 'Enter') {
|
||||
// Perform the sending action here
|
||||
sendMessage(event.currentTarget.value);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (aiChatBotState.isModalOpen) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
} else {
|
||||
document.body.style.overflow = 'unset'
|
||||
}
|
||||
}, [aiChatBotState.isModalOpen])
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: event.currentTarget.value });
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (event.key === 'Enter') {
|
||||
// Perform the sending action here
|
||||
sendMessage(event.currentTarget.value)
|
||||
}
|
||||
}
|
||||
|
||||
const sendMessage = async (message: string) => {
|
||||
if (aiChatBotState.aichat_uuid) {
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'user', message: message, type: 'user' } });
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' });
|
||||
const response = await sendActivityAIChatMessage(message, aiChatBotState.aichat_uuid, props.activity.activity_uuid)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'setError', payload: { isError: true, status: response.status, error_message: response.data.detail } });
|
||||
return;
|
||||
}
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'ai', message: response.data.message, type: 'ai' } });
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
await dispatchAIChatBot({
|
||||
type: 'setChatInputValue',
|
||||
payload: event.currentTarget.value,
|
||||
})
|
||||
}
|
||||
|
||||
} else {
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'user', message: message, type: 'user' } });
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' });
|
||||
const response = await startActivityAIChatSession(message, props.activity.activity_uuid)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'setError', payload: { isError: true, status: response.status, error_message: response.data.detail } });
|
||||
return;
|
||||
}
|
||||
await dispatchAIChatBot({ type: 'setAichat_uuid', payload: response.data.aichat_uuid });
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'ai', message: response.data.message, type: 'ai' } });
|
||||
}
|
||||
const sendMessage = async (message: string) => {
|
||||
if (aiChatBotState.aichat_uuid) {
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'user', message: message, type: 'user' },
|
||||
})
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' })
|
||||
const response = await sendActivityAIChatMessage(
|
||||
message,
|
||||
aiChatBotState.aichat_uuid,
|
||||
props.activity.activity_uuid
|
||||
)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'setError',
|
||||
payload: {
|
||||
isError: true,
|
||||
status: response.status,
|
||||
error_message: response.data.detail,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'ai', message: response.data.message, type: 'ai' },
|
||||
})
|
||||
} else {
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'user', message: message, type: 'user' },
|
||||
})
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' })
|
||||
const response = await startActivityAIChatSession(
|
||||
message,
|
||||
props.activity.activity_uuid
|
||||
)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'setError',
|
||||
payload: {
|
||||
isError: true,
|
||||
status: response.status,
|
||||
error_message: response.data.detail,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
await dispatchAIChatBot({
|
||||
type: 'setAichat_uuid',
|
||||
payload: response.data.aichat_uuid,
|
||||
})
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'ai', message: response.data.message, type: 'ai' },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
dispatchAIChatBot({ type: 'setIsModalClose' });
|
||||
function closeModal() {
|
||||
dispatchAIChatBot({ type: 'setIsModalClose' })
|
||||
}
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (messagesEndRef.current) {
|
||||
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
}, [aiChatBotState.messages, session])
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (messagesEndRef.current) {
|
||||
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
}, [aiChatBotState.messages, session]);
|
||||
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{aiChatBotState.isModalOpen && (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0.3, filter: 'blur(5px)' }}
|
||||
animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}
|
||||
exit={{ y: 50, opacity: 0, filter: 'blur(25px)' }}
|
||||
transition={{ type: "spring", bounce: 0.35, duration: 1.7, mass: 0.2, velocity: 2 }}
|
||||
className='fixed top-0 left-0 w-full h-full z-50 flex justify-center items-center '
|
||||
style={{ pointerEvents: 'none' }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: 'auto',
|
||||
background: 'linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%), radial-gradient(105.16% 105.16% at 50% -5.16%, rgba(255, 255, 255, 0.18) 0%, rgba(0, 0, 0, 0) 100%), rgb(2 1 25 / 98%)'
|
||||
}}
|
||||
className="bg-black z-50 rounded-2xl max-w-screen-2xl w-10/12 my-10 mx-auto h-[350px] fixed bottom-0 left-1/2 transform -translate-x-1/2 shadow-lg ring-1 ring-inset ring-white/10 text-white p-4 flex-col-reverse backdrop-blur-md">
|
||||
<div className='flex flex-row-reverse pb-3 justify-between items-center'>
|
||||
<div className='flex space-x-2 items-center'>
|
||||
|
||||
<X size={20} className='text-white/50 hover:cursor-pointer bg-white/10 p-1 rounded-full items-center' onClick={closeModal} />
|
||||
|
||||
</div>
|
||||
<div className={`flex space-x-2 items-center -ml-[100px] ${aiChatBotState.isWaitingForResponse ? 'animate-pulse' : ''}`}>
|
||||
<Image className={`outline outline-1 outline-neutral-200/20 rounded-lg ${aiChatBotState.isWaitingForResponse ? 'animate-pulse' : ''}`} width={24} src={learnhouseAI_icon} alt="" />
|
||||
<span className='text-sm font-semibold text-white/70'> AI</span>
|
||||
</div>
|
||||
<div className='bg-white/5 text-white/40 py-0.5 px-3 flex space-x-1 rounded-full items-center'>
|
||||
<FlaskConical size={14} />
|
||||
<span className='text-xs font-semibold antialiased '>Experimental</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={`w-100 h-0.5 bg-white/5 rounded-full mx-auto mb-3 ${aiChatBotState.isWaitingForResponse ? 'animate-pulse' : ''}`}></div>
|
||||
{aiChatBotState.messages.length > 0 && !aiChatBotState.error.isError ? (
|
||||
<div className='flex-col h-[237px] w-full space-y-4 overflow-scroll scrollbar-w-2 scrollbar scrollbar-thumb-white/20 scrollbar-thumb-rounded-full scrollbar-track-rounded-full'>
|
||||
{aiChatBotState.messages.map((message: AIMessage, index: number) => {
|
||||
return (
|
||||
<AIMessage key={index} message={message} animated={message.sender == 'ai' ? true : false} />
|
||||
)
|
||||
})}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
) : (
|
||||
<AIMessagePlaceHolder sendMessage={sendMessage} activity_uuid={props.activity.activity_uuid} />
|
||||
)}
|
||||
{aiChatBotState.error.isError && (
|
||||
<div className='flex items-center h-[237px]'>
|
||||
<div className='flex flex-col mx-auto w-[600px] space-y-2 p-5 rounded-lg bg-red-500/20 outline outline-1 outline-red-500'>
|
||||
<AlertTriangle size={20} className='text-red-500' />
|
||||
<div className='flex flex-col'>
|
||||
<h3 className='font-semibold text-red-200'>Something wrong happened</h3>
|
||||
<span className='text-red-100 text-sm '>{aiChatBotState.error.error_message}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
<div className='flex space-x-2 items-center'>
|
||||
<div className=''>
|
||||
<UserAvatar rounded='rounded-lg' border='border-2' width={35} />
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<input onKeyDown={handleKeyDown} onChange={handleChange} disabled={aiChatBotState.isWaitingForResponse} value={aiChatBotState.chatInputValue} placeholder='Ask AI About this Lecture' type="text" className={inputClass} name="" id="" />
|
||||
|
||||
</div>
|
||||
<div className=''>
|
||||
<MessageCircle size={20} className='text-white/50 hover:cursor-pointer' onClick={() => sendMessage(aiChatBotState.chatInputValue)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{aiChatBotState.isModalOpen && (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0.3, filter: 'blur(5px)' }}
|
||||
animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}
|
||||
exit={{ y: 50, opacity: 0, filter: 'blur(25px)' }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
bounce: 0.35,
|
||||
duration: 1.7,
|
||||
mass: 0.2,
|
||||
velocity: 2,
|
||||
}}
|
||||
className="fixed top-0 left-0 w-full h-full z-50 flex justify-center items-center "
|
||||
style={{ pointerEvents: 'none' }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: 'auto',
|
||||
background:
|
||||
'linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%), radial-gradient(105.16% 105.16% at 50% -5.16%, rgba(255, 255, 255, 0.18) 0%, rgba(0, 0, 0, 0) 100%), rgb(2 1 25 / 98%)',
|
||||
}}
|
||||
className="bg-black z-50 rounded-2xl max-w-screen-2xl w-10/12 my-10 mx-auto h-[350px] fixed bottom-0 left-1/2 transform -translate-x-1/2 shadow-lg ring-1 ring-inset ring-white/10 text-white p-4 flex-col-reverse backdrop-blur-md"
|
||||
>
|
||||
<div className="flex flex-row-reverse pb-3 justify-between items-center">
|
||||
<div className="flex space-x-2 items-center">
|
||||
<X
|
||||
size={20}
|
||||
className="text-white/50 hover:cursor-pointer bg-white/10 p-1 rounded-full items-center"
|
||||
onClick={closeModal}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`flex space-x-2 items-center -ml-[100px] ${
|
||||
aiChatBotState.isWaitingForResponse ? 'animate-pulse' : ''
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
className={`outline outline-1 outline-neutral-200/20 rounded-lg ${
|
||||
aiChatBotState.isWaitingForResponse ? 'animate-pulse' : ''
|
||||
}`}
|
||||
width={24}
|
||||
src={learnhouseAI_icon}
|
||||
alt=""
|
||||
/>
|
||||
<span className="text-sm font-semibold text-white/70">
|
||||
{' '}
|
||||
AI
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-white/5 text-white/40 py-0.5 px-3 flex space-x-1 rounded-full items-center">
|
||||
<FlaskConical size={14} />
|
||||
<span className="text-xs font-semibold antialiased ">
|
||||
Experimental
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`w-100 h-0.5 bg-white/5 rounded-full mx-auto mb-3 ${
|
||||
aiChatBotState.isWaitingForResponse ? 'animate-pulse' : ''
|
||||
}`}
|
||||
></div>
|
||||
{aiChatBotState.messages.length > 0 &&
|
||||
!aiChatBotState.error.isError ? (
|
||||
<div className="flex-col h-[237px] w-full space-y-4 overflow-scroll scrollbar-w-2 scrollbar scrollbar-thumb-white/20 scrollbar-thumb-rounded-full scrollbar-track-rounded-full">
|
||||
{aiChatBotState.messages.map(
|
||||
(message: AIMessage, index: number) => {
|
||||
return (
|
||||
<AIMessage
|
||||
key={index}
|
||||
message={message}
|
||||
animated={message.sender == 'ai' ? true : false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
) : (
|
||||
<AIMessagePlaceHolder
|
||||
sendMessage={sendMessage}
|
||||
activity_uuid={props.activity.activity_uuid}
|
||||
/>
|
||||
)}
|
||||
{aiChatBotState.error.isError && (
|
||||
<div className="flex items-center h-[237px]">
|
||||
<div className="flex flex-col mx-auto w-[600px] space-y-2 p-5 rounded-lg bg-red-500/20 outline outline-1 outline-red-500">
|
||||
<AlertTriangle size={20} className="text-red-500" />
|
||||
<div className="flex flex-col">
|
||||
<h3 className="font-semibold text-red-200">
|
||||
Something wrong happened
|
||||
</h3>
|
||||
<span className="text-red-100 text-sm ">
|
||||
{aiChatBotState.error.error_message}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex space-x-2 items-center">
|
||||
<div className="">
|
||||
<UserAvatar
|
||||
rounded="rounded-lg"
|
||||
border="border-2"
|
||||
width={35}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<input
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={handleChange}
|
||||
disabled={aiChatBotState.isWaitingForResponse}
|
||||
value={aiChatBotState.chatInputValue}
|
||||
placeholder="Ask AI About this Lecture"
|
||||
type="text"
|
||||
className={inputClass}
|
||||
name=""
|
||||
id=""
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<MessageCircle
|
||||
size={20}
|
||||
className="text-white/50 hover:cursor-pointer"
|
||||
onClick={() => sendMessage(aiChatBotState.chatInputValue)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
type AIMessageProps = {
|
||||
message: AIMessage;
|
||||
animated: boolean;
|
||||
message: AIMessage
|
||||
animated: boolean
|
||||
}
|
||||
|
||||
function AIMessage(props: AIMessageProps) {
|
||||
const session = useSession() as any;
|
||||
const session = useSession() as any
|
||||
|
||||
const words = props.message.message.split(' ');
|
||||
const words = props.message.message.split(' ')
|
||||
|
||||
return (
|
||||
<div className="flex space-x-2 w-full antialiased font-medium">
|
||||
<div className="">
|
||||
{props.message.sender == 'ai' ? (
|
||||
<UserAvatar
|
||||
rounded="rounded-lg"
|
||||
border="border-2"
|
||||
predefined_avatar="ai"
|
||||
width={35}
|
||||
/>
|
||||
) : (
|
||||
<UserAvatar rounded="rounded-lg" border="border-2" width={35} />
|
||||
)}
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<p
|
||||
className="w-full rounded-lg outline-none px-2 py-1 text-white text-md placeholder:text-white/30"
|
||||
id=""
|
||||
>
|
||||
<AnimatePresence>
|
||||
{words.map((word: string, i: number) => (
|
||||
<motion.span
|
||||
key={i}
|
||||
initial={
|
||||
props.animated ? { opacity: 0, y: -10 } : { opacity: 1, y: 0 }
|
||||
}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={
|
||||
props.animated ? { opacity: 0, y: 10 } : { opacity: 1, y: 0 }
|
||||
}
|
||||
transition={props.animated ? { delay: i * 0.1 } : {}}
|
||||
>
|
||||
{word + ' '}
|
||||
</motion.span>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const AIMessagePlaceHolder = (props: {
|
||||
activity_uuid: string
|
||||
sendMessage: any
|
||||
}) => {
|
||||
const session = useSession() as any
|
||||
const [feedbackModal, setFeedbackModal] = React.useState(false)
|
||||
const aiChatBotState = useAIChatBot() as AIChatBotStateTypes
|
||||
|
||||
if (!aiChatBotState.error.isError) {
|
||||
return (
|
||||
<div className='flex space-x-2 w-full antialiased font-medium'>
|
||||
<div className=''>
|
||||
{props.message.sender == 'ai' ? (
|
||||
<UserAvatar rounded='rounded-lg' border='border-2' predefined_avatar='ai' width={35} />
|
||||
) : (
|
||||
<UserAvatar rounded='rounded-lg' border='border-2' width={35} />
|
||||
)}
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<p className='w-full rounded-lg outline-none px-2 py-1 text-white text-md placeholder:text-white/30' id="">
|
||||
<AnimatePresence>
|
||||
{words.map((word: string, i: number) => (
|
||||
<motion.span
|
||||
key={i}
|
||||
initial={props.animated ? { opacity: 0, y: -10 } : { opacity: 1, y: 0 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={props.animated ? { opacity: 0, y: 10 } : { opacity: 1, y: 0 }}
|
||||
transition={props.animated ? { delay: i * 0.1 } : {}}
|
||||
>
|
||||
{word + ' '}
|
||||
</motion.span>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-col h-[237px] w-full">
|
||||
<div className="flex flex-col text-center justify-center pt-12">
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0, filter: 'blur(5px)' }}
|
||||
animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}
|
||||
exit={{ y: 50, opacity: 0 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
bounce: 0.35,
|
||||
duration: 1.7,
|
||||
mass: 0.2,
|
||||
velocity: 2,
|
||||
delay: 0.17,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
width={100}
|
||||
className="mx-auto"
|
||||
src={learnhouseAI_logo_black}
|
||||
alt=""
|
||||
/>
|
||||
<p className="pt-3 text-2xl font-semibold text-white/70 flex justify-center space-x-2 items-center">
|
||||
<span className="items-center">Hello</span>
|
||||
<span className="capitalize flex space-x-2 items-center">
|
||||
<UserAvatar rounded="rounded-lg" border="border-2" width={35} />
|
||||
<span>{session.user.username},</span>
|
||||
</span>
|
||||
<span>how can we help today ?</span>
|
||||
</p>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0, filter: 'blur(5px)' }}
|
||||
animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}
|
||||
exit={{ y: 50, opacity: 0 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
bounce: 0.35,
|
||||
duration: 1.7,
|
||||
mass: 0.2,
|
||||
velocity: 2,
|
||||
delay: 0.27,
|
||||
}}
|
||||
className="questions flex space-x-3 mx-auto pt-6 flex-wrap justify-center"
|
||||
>
|
||||
<AIChatPredefinedQuestion
|
||||
sendMessage={props.sendMessage}
|
||||
label="about"
|
||||
/>
|
||||
<AIChatPredefinedQuestion
|
||||
sendMessage={props.sendMessage}
|
||||
label="flashcards"
|
||||
/>
|
||||
<AIChatPredefinedQuestion
|
||||
sendMessage={props.sendMessage}
|
||||
label="examples"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const AIMessagePlaceHolder = (props: { activity_uuid: string, sendMessage: any }) => {
|
||||
const session = useSession() as any;
|
||||
const [feedbackModal, setFeedbackModal] = React.useState(false);
|
||||
const aiChatBotState = useAIChatBot() as AIChatBotStateTypes;
|
||||
|
||||
if (!aiChatBotState.error.isError) {
|
||||
return <div className='flex-col h-[237px] w-full'>
|
||||
<div className='flex flex-col text-center justify-center pt-12'>
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0, filter: 'blur(5px)' }}
|
||||
animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}
|
||||
exit={{ y: 50, opacity: 0, }}
|
||||
transition={{ type: "spring", bounce: 0.35, duration: 1.7, mass: 0.2, velocity: 2, delay: 0.17 }}
|
||||
|
||||
>
|
||||
|
||||
<Image width={100} className='mx-auto' src={learnhouseAI_logo_black} alt="" />
|
||||
<p className='pt-3 text-2xl font-semibold text-white/70 flex justify-center space-x-2 items-center'>
|
||||
<span className='items-center'>Hello</span>
|
||||
<span className='capitalize flex space-x-2 items-center'>
|
||||
<UserAvatar rounded='rounded-lg' border='border-2' width={35} />
|
||||
<span>{session.user.username},</span>
|
||||
</span>
|
||||
<span>how can we help today ?</span>
|
||||
</p>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0, filter: 'blur(5px)' }}
|
||||
animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}
|
||||
exit={{ y: 50, opacity: 0, }}
|
||||
transition={{ type: "spring", bounce: 0.35, duration: 1.7, mass: 0.2, velocity: 2, delay: 0.27 }}
|
||||
|
||||
className='questions flex space-x-3 mx-auto pt-6 flex-wrap justify-center'
|
||||
>
|
||||
<AIChatPredefinedQuestion sendMessage={props.sendMessage} label='about' />
|
||||
<AIChatPredefinedQuestion sendMessage={props.sendMessage} label='flashcards' />
|
||||
<AIChatPredefinedQuestion sendMessage={props.sendMessage} label='examples' />
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
const AIChatPredefinedQuestion = (props: {
|
||||
sendMessage: any
|
||||
label: string
|
||||
}) => {
|
||||
function getQuestion(label: string) {
|
||||
if (label === 'about') {
|
||||
return `What is this Activity about ?`
|
||||
} else if (label === 'flashcards') {
|
||||
return `Generate flashcards about this Activity`
|
||||
} else if (label === 'examples') {
|
||||
return `Explain this Activity in practical examples`
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => props.sendMessage(getQuestion(props.label))}
|
||||
className="flex space-x-1.5 items-center bg-white/5 cursor-pointer px-4 py-1.5 rounded-xl outline outline-1 outline-neutral-100/10 text-xs font-semibold text-white/40 hover:text-white/60 hover:bg-white/10 hover:outline-neutral-200/40 delay-75 ease-linear transition-all"
|
||||
>
|
||||
{props.label === 'about' && <BadgeInfo size={15} />}
|
||||
{props.label === 'flashcards' && <NotebookTabs size={15} />}
|
||||
{props.label === 'examples' && <div className="text-white/50">Ex</div>}
|
||||
<span>{getQuestion(props.label)}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const AIChatPredefinedQuestion = (props: { sendMessage: any, label: string }) => {
|
||||
function getQuestion(label: string) {
|
||||
if (label === 'about') {
|
||||
return `What is this Activity about ?`
|
||||
} else if (label === 'flashcards') {
|
||||
return `Generate flashcards about this Activity`
|
||||
} else if (label === 'examples') {
|
||||
return `Explain this Activity in practical examples`
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div onClick={() => props.sendMessage(getQuestion(props.label))} className='flex space-x-1.5 items-center bg-white/5 cursor-pointer px-4 py-1.5 rounded-xl outline outline-1 outline-neutral-100/10 text-xs font-semibold text-white/40 hover:text-white/60 hover:bg-white/10 hover:outline-neutral-200/40 delay-75 ease-linear transition-all'>
|
||||
{props.label === 'about' && <BadgeInfo size={15} />}
|
||||
{props.label === 'flashcards' && <NotebookTabs size={15} />}
|
||||
{props.label === 'examples' && <div className='text-white/50'>Ex</div>}
|
||||
<span>{getQuestion(props.label)}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default AIActivityAsk
|
||||
export default AIActivityAsk
|
||||
|
|
|
|||
|
|
@ -1,24 +1,34 @@
|
|||
import { useOrg } from "@components/Contexts/OrgContext";
|
||||
import { getActivityMediaDirectory } from "@services/media/media";
|
||||
import React from "react";
|
||||
import { useOrg } from '@components/Contexts/OrgContext'
|
||||
import { getActivityMediaDirectory } from '@services/media/media'
|
||||
import React from 'react'
|
||||
|
||||
function DocumentPdfActivity({ activity, course }: { activity: any; course: any }) {
|
||||
const org = useOrg() as any;
|
||||
function DocumentPdfActivity({
|
||||
activity,
|
||||
course,
|
||||
}: {
|
||||
activity: any
|
||||
course: any
|
||||
}) {
|
||||
const org = useOrg() as any
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log(activity);
|
||||
}, [activity, org]);
|
||||
console.log(activity)
|
||||
}, [activity, org])
|
||||
|
||||
return (
|
||||
<div className="m-8 bg-zinc-900 rounded-md mt-14">
|
||||
<iframe
|
||||
className="rounded-lg w-full h-[900px]"
|
||||
src={getActivityMediaDirectory(org?.org_uuid, course?.course_uuid, activity.activity_uuid, activity.content.filename, 'documentpdf')}
|
||||
src={getActivityMediaDirectory(
|
||||
org?.org_uuid,
|
||||
course?.course_uuid,
|
||||
activity.activity_uuid,
|
||||
activity.content.filename,
|
||||
'documentpdf'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default DocumentPdfActivity;
|
||||
|
||||
|
||||
export default DocumentPdfActivity
|
||||
|
|
|
|||
|
|
@ -1,131 +1,220 @@
|
|||
import React from 'react'
|
||||
import { Editor } from '@tiptap/core';
|
||||
import learnhouseAI_icon from "public/learnhouse_ai_simple.png";
|
||||
import Image from 'next/image';
|
||||
import { BookOpen, FormInput, Languages, MoreVertical } from 'lucide-react';
|
||||
import { BubbleMenu } from '@tiptap/react';
|
||||
import ToolTip from '@components/StyledElements/Tooltip/Tooltip';
|
||||
import { AIChatBotStateTypes, useAIChatBot, useAIChatBotDispatch } from '@components/Contexts/AI/AIChatBotContext';
|
||||
import { sendActivityAIChatMessage, startActivityAIChatSession } from '@services/ai/ai';
|
||||
import useGetAIFeatures from '../../../../AI/Hooks/useGetAIFeatures';
|
||||
|
||||
|
||||
import { Editor } from '@tiptap/core'
|
||||
import learnhouseAI_icon from 'public/learnhouse_ai_simple.png'
|
||||
import Image from 'next/image'
|
||||
import { BookOpen, FormInput, Languages, MoreVertical } from 'lucide-react'
|
||||
import { BubbleMenu } from '@tiptap/react'
|
||||
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
|
||||
import {
|
||||
AIChatBotStateTypes,
|
||||
useAIChatBot,
|
||||
useAIChatBotDispatch,
|
||||
} from '@components/Contexts/AI/AIChatBotContext'
|
||||
import {
|
||||
sendActivityAIChatMessage,
|
||||
startActivityAIChatSession,
|
||||
} from '@services/ai/ai'
|
||||
import useGetAIFeatures from '../../../../AI/Hooks/useGetAIFeatures'
|
||||
|
||||
type AICanvaToolkitProps = {
|
||||
editor: Editor,
|
||||
activity: any
|
||||
editor: Editor
|
||||
activity: any
|
||||
}
|
||||
|
||||
function AICanvaToolkit(props: AICanvaToolkitProps) {
|
||||
const is_ai_feature_enabled = useGetAIFeatures({ feature: 'activity_ask' });
|
||||
const [isBubbleMenuAvailable, setIsButtonAvailable] = React.useState(false);
|
||||
const is_ai_feature_enabled = useGetAIFeatures({ feature: 'activity_ask' })
|
||||
const [isBubbleMenuAvailable, setIsButtonAvailable] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (is_ai_feature_enabled) {
|
||||
setIsButtonAvailable(true);
|
||||
}
|
||||
}, [is_ai_feature_enabled])
|
||||
React.useEffect(() => {
|
||||
if (is_ai_feature_enabled) {
|
||||
setIsButtonAvailable(true)
|
||||
}
|
||||
}, [is_ai_feature_enabled])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{isBubbleMenuAvailable && <BubbleMenu className="w-fit" tippyOptions={{ duration: 100 }} editor={props.editor}>
|
||||
<div style={{ background: 'linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%), radial-gradient(105.16% 105.16% at 50% -5.16%, rgba(255, 255, 255, 0.18) 0%, rgba(0, 0, 0, 0) 100%), rgba(2, 1, 25, 0.98)' }}
|
||||
className='py-1 h-10 px-2 w-max text-white rounded-xl shadow-md cursor-pointer flex items-center space-x-2 antialiased'
|
||||
>
|
||||
<div className='flex w-full space-x-2 font-bold text-white/80'><Image className='outline outline-1 outline-neutral-200/10 rounded-lg' width={24} src={learnhouseAI_icon} alt="" /> <div>AI</div> </div>
|
||||
<div>
|
||||
<MoreVertical className='text-white/50' size={12} />
|
||||
</div>
|
||||
<div className='flex space-x-2'>
|
||||
<AIActionButton editor={props.editor} activity={props.activity} label='Explain' />
|
||||
<AIActionButton editor={props.editor} activity={props.activity} label='Summarize' />
|
||||
<AIActionButton editor={props.editor} activity={props.activity} label='Translate' />
|
||||
<AIActionButton editor={props.editor} activity={props.activity} label='Examples' />
|
||||
</div>
|
||||
</div>
|
||||
</BubbleMenu>}
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<>
|
||||
{isBubbleMenuAvailable && (
|
||||
<BubbleMenu
|
||||
className="w-fit"
|
||||
tippyOptions={{ duration: 100 }}
|
||||
editor={props.editor}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%), radial-gradient(105.16% 105.16% at 50% -5.16%, rgba(255, 255, 255, 0.18) 0%, rgba(0, 0, 0, 0) 100%), rgba(2, 1, 25, 0.98)',
|
||||
}}
|
||||
className="py-1 h-10 px-2 w-max text-white rounded-xl shadow-md cursor-pointer flex items-center space-x-2 antialiased"
|
||||
>
|
||||
<div className="flex w-full space-x-2 font-bold text-white/80">
|
||||
<Image
|
||||
className="outline outline-1 outline-neutral-200/10 rounded-lg"
|
||||
width={24}
|
||||
src={learnhouseAI_icon}
|
||||
alt=""
|
||||
/>{' '}
|
||||
<div>AI</div>{' '}
|
||||
</div>
|
||||
<div>
|
||||
<MoreVertical className="text-white/50" size={12} />
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<AIActionButton
|
||||
editor={props.editor}
|
||||
activity={props.activity}
|
||||
label="Explain"
|
||||
/>
|
||||
<AIActionButton
|
||||
editor={props.editor}
|
||||
activity={props.activity}
|
||||
label="Summarize"
|
||||
/>
|
||||
<AIActionButton
|
||||
editor={props.editor}
|
||||
activity={props.activity}
|
||||
label="Translate"
|
||||
/>
|
||||
<AIActionButton
|
||||
editor={props.editor}
|
||||
activity={props.activity}
|
||||
label="Examples"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BubbleMenu>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function AIActionButton(props: { editor: Editor, label: string, activity: any }) {
|
||||
const dispatchAIChatBot = useAIChatBotDispatch() as any;
|
||||
const aiChatBotState = useAIChatBot() as AIChatBotStateTypes;
|
||||
function AIActionButton(props: {
|
||||
editor: Editor
|
||||
label: string
|
||||
activity: any
|
||||
}) {
|
||||
const dispatchAIChatBot = useAIChatBotDispatch() as any
|
||||
const aiChatBotState = useAIChatBot() as AIChatBotStateTypes
|
||||
|
||||
async function handleAction(label: string) {
|
||||
const selection = getTipTapEditorSelectedText();
|
||||
const prompt = getPrompt(label, selection);
|
||||
dispatchAIChatBot({ type: 'setIsModalOpen' });
|
||||
await sendMessage(prompt);
|
||||
async function handleAction(label: string) {
|
||||
const selection = getTipTapEditorSelectedText()
|
||||
const prompt = getPrompt(label, selection)
|
||||
dispatchAIChatBot({ type: 'setIsModalOpen' })
|
||||
await sendMessage(prompt)
|
||||
}
|
||||
|
||||
const getTipTapEditorSelectedText = () => {
|
||||
const selection = props.editor.state.selection
|
||||
const from = selection.from
|
||||
const to = selection.to
|
||||
const text = props.editor.state.doc.textBetween(from, to)
|
||||
return text
|
||||
}
|
||||
|
||||
const getPrompt = (label: string, selection: string) => {
|
||||
if (label === 'Explain') {
|
||||
return `Explain this part of the course "${selection}" keep this course context in mind.`
|
||||
} else if (label === 'Summarize') {
|
||||
return `Summarize this "${selection}" with the course context in mind.`
|
||||
} else if (label === 'Translate') {
|
||||
return `Translate "${selection}" to another language.`
|
||||
} else {
|
||||
return `Give examples to understand "${selection}" better, if possible give context in the course.`
|
||||
}
|
||||
}
|
||||
|
||||
const getTipTapEditorSelectedText = () => {
|
||||
const selection = props.editor.state.selection;
|
||||
const from = selection.from;
|
||||
const to = selection.to;
|
||||
const text = props.editor.state.doc.textBetween(from, to);
|
||||
return text;
|
||||
const sendMessage = async (message: string) => {
|
||||
if (aiChatBotState.aichat_uuid) {
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'user', message: message, type: 'user' },
|
||||
})
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' })
|
||||
const response = await sendActivityAIChatMessage(
|
||||
message,
|
||||
aiChatBotState.aichat_uuid,
|
||||
props.activity.activity_uuid
|
||||
)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'setError',
|
||||
payload: {
|
||||
isError: true,
|
||||
status: response.status,
|
||||
error_message: response.data.detail,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'ai', message: response.data.message, type: 'ai' },
|
||||
})
|
||||
} else {
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'user', message: message, type: 'user' },
|
||||
})
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' })
|
||||
const response = await startActivityAIChatSession(
|
||||
message,
|
||||
props.activity.activity_uuid
|
||||
)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'setError',
|
||||
payload: {
|
||||
isError: true,
|
||||
status: response.status,
|
||||
error_message: response.data.detail,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
await dispatchAIChatBot({
|
||||
type: 'setAichat_uuid',
|
||||
payload: response.data.aichat_uuid,
|
||||
})
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' })
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' })
|
||||
await dispatchAIChatBot({
|
||||
type: 'addMessage',
|
||||
payload: { sender: 'ai', message: response.data.message, type: 'ai' },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const getPrompt = (label: string, selection: string) => {
|
||||
if (label === 'Explain') {
|
||||
return `Explain this part of the course "${selection}" keep this course context in mind.`
|
||||
} else if (label === 'Summarize') {
|
||||
return `Summarize this "${selection}" with the course context in mind.`
|
||||
} else if (label === 'Translate') {
|
||||
return `Translate "${selection}" to another language.`
|
||||
} else {
|
||||
return `Give examples to understand "${selection}" better, if possible give context in the course.`
|
||||
}
|
||||
}
|
||||
|
||||
const sendMessage = async (message: string) => {
|
||||
if (aiChatBotState.aichat_uuid) {
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'user', message: message, type: 'user' } });
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' });
|
||||
const response = await sendActivityAIChatMessage(message, aiChatBotState.aichat_uuid, props.activity.activity_uuid)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'setError', payload: { isError: true, status: response.status, error_message: response.data.detail } });
|
||||
return;
|
||||
}
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'ai', message: response.data.message, type: 'ai' } });
|
||||
|
||||
} else {
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'user', message: message, type: 'user' } });
|
||||
await dispatchAIChatBot({ type: 'setIsWaitingForResponse' });
|
||||
const response = await startActivityAIChatSession(message, props.activity.activity_uuid)
|
||||
if (response.success == false) {
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'setError', payload: { isError: true, status: response.status, error_message: response.data.detail } });
|
||||
return;
|
||||
}
|
||||
await dispatchAIChatBot({ type: 'setAichat_uuid', payload: response.data.aichat_uuid });
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'ai', message: response.data.message, type: 'ai' } });
|
||||
}
|
||||
}
|
||||
|
||||
const tooltipLabel = props.label === 'Explain' ? 'Explain a word or a sentence with AI' : props.label === 'Summarize' ? 'Summarize a long paragraph or text with AI' : props.label === 'Translate' ? 'Translate to different languages with AI' : 'Give examples to understand better with AI'
|
||||
return (
|
||||
<div className='flex space-x-2' >
|
||||
<ToolTip sideOffset={10} slateBlack content={tooltipLabel}>
|
||||
<button onClick={() => handleAction(props.label)} className='flex space-x-1.5 items-center bg-white/10 px-2 py-0.5 rounded-md outline outline-1 outline-neutral-200/20 text-sm font-semibold text-white/70 hover:bg-white/20 hover:outline-neutral-200/40 delay-75 ease-linear transition-all'>
|
||||
{props.label === 'Explain' && <BookOpen size={16} />}
|
||||
{props.label === 'Summarize' && <FormInput size={16} />}
|
||||
{props.label === 'Translate' && <Languages size={16} />}
|
||||
{props.label === 'Examples' && <div className='text-white/50'>Ex</div>}
|
||||
<div>{props.label}</div>
|
||||
</button>
|
||||
</ToolTip>
|
||||
</div>
|
||||
)
|
||||
const tooltipLabel =
|
||||
props.label === 'Explain'
|
||||
? 'Explain a word or a sentence with AI'
|
||||
: props.label === 'Summarize'
|
||||
? 'Summarize a long paragraph or text with AI'
|
||||
: props.label === 'Translate'
|
||||
? 'Translate to different languages with AI'
|
||||
: 'Give examples to understand better with AI'
|
||||
return (
|
||||
<div className="flex space-x-2">
|
||||
<ToolTip sideOffset={10} slateBlack content={tooltipLabel}>
|
||||
<button
|
||||
onClick={() => handleAction(props.label)}
|
||||
className="flex space-x-1.5 items-center bg-white/10 px-2 py-0.5 rounded-md outline outline-1 outline-neutral-200/20 text-sm font-semibold text-white/70 hover:bg-white/20 hover:outline-neutral-200/40 delay-75 ease-linear transition-all"
|
||||
>
|
||||
{props.label === 'Explain' && <BookOpen size={16} />}
|
||||
{props.label === 'Summarize' && <FormInput size={16} />}
|
||||
{props.label === 'Translate' && <Languages size={16} />}
|
||||
{props.label === 'Examples' && (
|
||||
<div className="text-white/50">Ex</div>
|
||||
)}
|
||||
<div>{props.label}</div>
|
||||
</button>
|
||||
</ToolTip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AICanvaToolkit
|
||||
export default AICanvaToolkit
|
||||
|
|
|
|||
|
|
@ -1,45 +1,43 @@
|
|||
import { useEditor, EditorContent } from "@tiptap/react";
|
||||
import StarterKit from "@tiptap/starter-kit";
|
||||
import styled from "styled-components"
|
||||
import Youtube from "@tiptap/extension-youtube";
|
||||
import { useEditor, EditorContent } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import styled from 'styled-components'
|
||||
import Youtube from '@tiptap/extension-youtube'
|
||||
// Custom Extensions
|
||||
import InfoCallout from "@components/Objects/Editor/Extensions/Callout/Info/InfoCallout";
|
||||
import WarningCallout from "@components/Objects/Editor/Extensions/Callout/Warning/WarningCallout";
|
||||
import ImageBlock from "@components/Objects/Editor/Extensions/Image/ImageBlock";
|
||||
import VideoBlock from "@components/Objects/Editor/Extensions/Video/VideoBlock";
|
||||
import MathEquationBlock from "@components/Objects/Editor/Extensions/MathEquation/MathEquationBlock";
|
||||
import PDFBlock from "@components/Objects/Editor/Extensions/PDF/PDFBlock";
|
||||
import { OrderedList } from "@tiptap/extension-ordered-list";
|
||||
import QuizBlock from "@components/Objects/Editor/Extensions/Quiz/QuizBlock";
|
||||
import InfoCallout from '@components/Objects/Editor/Extensions/Callout/Info/InfoCallout'
|
||||
import WarningCallout from '@components/Objects/Editor/Extensions/Callout/Warning/WarningCallout'
|
||||
import ImageBlock from '@components/Objects/Editor/Extensions/Image/ImageBlock'
|
||||
import VideoBlock from '@components/Objects/Editor/Extensions/Video/VideoBlock'
|
||||
import MathEquationBlock from '@components/Objects/Editor/Extensions/MathEquation/MathEquationBlock'
|
||||
import PDFBlock from '@components/Objects/Editor/Extensions/PDF/PDFBlock'
|
||||
import { OrderedList } from '@tiptap/extension-ordered-list'
|
||||
import QuizBlock from '@components/Objects/Editor/Extensions/Quiz/QuizBlock'
|
||||
|
||||
// Lowlight
|
||||
import { common, createLowlight } from 'lowlight'
|
||||
const lowlight = createLowlight(common)
|
||||
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
|
||||
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
|
||||
import css from 'highlight.js/lib/languages/css'
|
||||
import js from 'highlight.js/lib/languages/javascript'
|
||||
import ts from 'highlight.js/lib/languages/typescript'
|
||||
import html from 'highlight.js/lib/languages/xml'
|
||||
import python from 'highlight.js/lib/languages/python'
|
||||
import java from 'highlight.js/lib/languages/java'
|
||||
import { NoTextInput } from "@components/Objects/Editor/Extensions/NoTextInput/NoTextInput";
|
||||
import EditorOptionsProvider from "@components/Contexts/Editor/EditorContext";
|
||||
import AICanvaToolkit from "./AI/AICanvaToolkit";
|
||||
|
||||
import { NoTextInput } from '@components/Objects/Editor/Extensions/NoTextInput/NoTextInput'
|
||||
import EditorOptionsProvider from '@components/Contexts/Editor/EditorContext'
|
||||
import AICanvaToolkit from './AI/AICanvaToolkit'
|
||||
|
||||
interface Editor {
|
||||
content: string;
|
||||
activity: any;
|
||||
content: string
|
||||
activity: any
|
||||
}
|
||||
|
||||
function Canva(props: Editor) {
|
||||
/**
|
||||
* Important Note : This is a workaround to enable user interaction features to be implemented easily, like text selection, AI features and other planned features, this is set to true but otherwise it should be set to false.
|
||||
* Another workaround is implemented below to disable the editor from being edited by the user by setting the caret-color to transparent and using a custom extension to filter out transactions that add/edit/remove text.
|
||||
* To let the various Custom Extensions know that the editor is not editable, React context (EditorOptionsProvider) will be used instead of props.extension.options.editable.
|
||||
*/
|
||||
const isEditable = true;
|
||||
|
||||
/**
|
||||
* Important Note : This is a workaround to enable user interaction features to be implemented easily, like text selection, AI features and other planned features, this is set to true but otherwise it should be set to false.
|
||||
* Another workaround is implemented below to disable the editor from being edited by the user by setting the caret-color to transparent and using a custom extension to filter out transactions that add/edit/remove text.
|
||||
* To let the various Custom Extensions know that the editor is not editable, React context (EditorOptionsProvider) will be used instead of props.extension.options.editable.
|
||||
*/
|
||||
const isEditable = true
|
||||
|
||||
// Code Block Languages for Lowlight
|
||||
lowlight.register('html', html)
|
||||
|
|
@ -49,7 +47,6 @@ function Canva(props: Editor) {
|
|||
lowlight.register('python', python)
|
||||
lowlight.register('java', java)
|
||||
|
||||
|
||||
const editor: any = useEditor({
|
||||
editable: isEditable,
|
||||
extensions: [
|
||||
|
|
@ -90,62 +87,55 @@ function Canva(props: Editor) {
|
|||
CodeBlockLowlight.configure({
|
||||
lowlight,
|
||||
}),
|
||||
|
||||
],
|
||||
|
||||
content: props.content,
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
<EditorOptionsProvider options={{ isEditable: false }}>
|
||||
<CanvaWrapper>
|
||||
<AICanvaToolkit activity={props.activity} editor={editor} />
|
||||
<EditorContent editor={editor} />
|
||||
</CanvaWrapper>
|
||||
</EditorOptionsProvider>
|
||||
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const CanvaWrapper = styled.div`
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
.bubble-menu {
|
||||
display: flex;
|
||||
background-color: #0D0D0D;
|
||||
padding: 0.2rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
background-color: #0d0d0d;
|
||||
padding: 0.2rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
button {
|
||||
border: none;
|
||||
background: none;
|
||||
color: #FFF;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
padding: 0 0.2rem;
|
||||
opacity: 0.6;
|
||||
button {
|
||||
border: none;
|
||||
background: none;
|
||||
color: #fff;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
padding: 0 0.2rem;
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover,
|
||||
&.is-active {
|
||||
opacity: 1;
|
||||
&:hover,
|
||||
&.is-active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disable chrome outline
|
||||
|
||||
.ProseMirror {
|
||||
|
||||
// Workaround to disable editor from being edited by the user.
|
||||
caret-color: transparent;
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
|
@ -176,13 +166,13 @@ const CanvaWrapper = styled.div`
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
ul,
|
||||
ol {
|
||||
padding: 0 1rem;
|
||||
padding-left: 20px;
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
|
||||
&:focus {
|
||||
outline: none !important;
|
||||
outline-style: none !important;
|
||||
|
|
@ -191,74 +181,72 @@ const CanvaWrapper = styled.div`
|
|||
|
||||
// Code Block
|
||||
pre {
|
||||
background: #0d0d0d;
|
||||
border-radius: 0.5rem;
|
||||
color: #fff;
|
||||
font-family: "JetBrainsMono", monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
background: #0d0d0d;
|
||||
border-radius: 0.5rem;
|
||||
color: #fff;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
|
||||
code {
|
||||
background: none;
|
||||
color: inherit;
|
||||
font-size: 0.8rem;
|
||||
padding: 0;
|
||||
}
|
||||
code {
|
||||
background: none;
|
||||
color: inherit;
|
||||
font-size: 0.8rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #616161;
|
||||
}
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #f98181;
|
||||
}
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #f98181;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #fbbc88;
|
||||
}
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #fbbc88;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #b9f18d;
|
||||
}
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #b9f18d;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #faf594;
|
||||
}
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #faf594;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #70cff8;
|
||||
}
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #70cff8;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: 700;
|
||||
.hljs-strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
`;
|
||||
|
||||
export default Canva;
|
||||
export default Canva
|
||||
|
|
|
|||
|
|
@ -1,68 +1,70 @@
|
|||
import React from "react";
|
||||
import YouTube from 'react-youtube';
|
||||
import { getActivityMediaDirectory } from "@services/media/media";
|
||||
import { useOrg } from "@components/Contexts/OrgContext";
|
||||
import React from 'react'
|
||||
import YouTube from 'react-youtube'
|
||||
import { getActivityMediaDirectory } from '@services/media/media'
|
||||
import { useOrg } from '@components/Contexts/OrgContext'
|
||||
|
||||
function VideoActivity({ activity, course }: { activity: any; course: any }) {
|
||||
const org = useOrg() as any;
|
||||
const [videoId, setVideoId] = React.useState('');
|
||||
const org = useOrg() as any
|
||||
const [videoId, setVideoId] = React.useState('')
|
||||
|
||||
function getYouTubeEmbed(url: any) {
|
||||
// Extract video ID from the YouTube URL
|
||||
var videoId = url.match(/(?:\?v=|\/embed\/|\/\d\/|\/vi\/|\/v\/|https?:\/\/(?:www\.)?youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^#\&\?\/]+)/)[1];
|
||||
var videoId = url.match(
|
||||
/(?:\?v=|\/embed\/|\/\d\/|\/vi\/|\/v\/|https?:\/\/(?:www\.)?youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^#\&\?\/]+)/
|
||||
)[1]
|
||||
|
||||
// Create the embed object
|
||||
var embedObject = {
|
||||
videoId: videoId,
|
||||
width: 560,
|
||||
height: 315
|
||||
};
|
||||
height: 315,
|
||||
}
|
||||
|
||||
return embedObject;
|
||||
return embedObject
|
||||
}
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log(activity);
|
||||
}, [activity, org]);
|
||||
console.log(activity)
|
||||
}, [activity, org])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{activity &&
|
||||
{activity && (
|
||||
<>
|
||||
{activity.activity_sub_type === 'SUBTYPE_VIDEO_HOSTED' && (
|
||||
<div className="m-8 bg-zinc-900 rounded-md mt-14">
|
||||
<video className="rounded-lg w-full h-[500px]" controls
|
||||
src={getActivityMediaDirectory(org?.org_uuid, course?.course_uuid, activity.activity_uuid, activity.content?.filename, 'video')}
|
||||
<video
|
||||
className="rounded-lg w-full h-[500px]"
|
||||
controls
|
||||
src={getActivityMediaDirectory(
|
||||
org?.org_uuid,
|
||||
course?.course_uuid,
|
||||
activity.activity_uuid,
|
||||
activity.content?.filename,
|
||||
'video'
|
||||
)}
|
||||
></video>
|
||||
|
||||
</div>
|
||||
)}
|
||||
{activity.activity_sub_type === 'SUBTYPE_VIDEO_YOUTUBE' && (
|
||||
<div>
|
||||
<YouTube
|
||||
className="rounded-md overflow-hidden m-8 bg-zinc-900 mt-14"
|
||||
opts={
|
||||
{
|
||||
width: '1300',
|
||||
height: '500',
|
||||
playerVars: {
|
||||
autoplay: 0,
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
videoId={videoId} />
|
||||
opts={{
|
||||
width: '1300',
|
||||
height: '500',
|
||||
playerVars: {
|
||||
autoplay: 0,
|
||||
},
|
||||
}}
|
||||
videoId={videoId}
|
||||
/>
|
||||
</div>
|
||||
)}</>}
|
||||
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export default VideoActivity;
|
||||
|
||||
|
||||
export default VideoActivity
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue