mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: org wide ai features check
This commit is contained in:
parent
de93d56945
commit
077c26ce15
24 changed files with 573 additions and 163 deletions
299
apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx
Normal file
299
apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
import { useSession } from '@components/Contexts/SessionContext'
|
||||
import { sendActivityAIChatMessage, startActivityAIChatSession } from '@services/ai/ai';
|
||||
import { BadgeInfo, NotebookTabs } from 'lucide-react';
|
||||
import Avvvatars from 'avvvatars-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { FlaskConical, Keyboard, MessageCircle, MessageSquareIcon, Sparkle, Sparkles, X } from 'lucide-react'
|
||||
import Image from 'next/image';
|
||||
import { send } from 'process';
|
||||
import learnhouseAI_icon from "public/learnhouse_ai_simple.png";
|
||||
import learnhouseAI_logo_black from "public/learnhouse_ai_black_logo.png";
|
||||
import React, { use, useEffect, useRef } from 'react'
|
||||
import { AIChatBotStateTypes, useAIChatBot, useAIChatBotDispatch } from '@components/Contexts/AI/AIChatBotContext';
|
||||
import FeedbackModal from '@components/Objects/Modals/Feedback/Feedback';
|
||||
import Modal from '@components/StyledElements/Modal/Modal';
|
||||
import useGetAIFeatures from '../../../AI/Hooks/useGetAIFeatures';
|
||||
|
||||
|
||||
type AIActivityAskProps = {
|
||||
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;
|
||||
|
||||
useEffect(() => {
|
||||
if (is_ai_feature_enabled) {
|
||||
setIsButtonAvailable(true);
|
||||
}
|
||||
}
|
||||
, [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>
|
||||
)}
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export type AIMessage = {
|
||||
sender: string;
|
||||
message: any;
|
||||
type: 'ai' | 'user';
|
||||
}
|
||||
|
||||
type ActivityChatMessageBoxProps = {
|
||||
activity: any;
|
||||
}
|
||||
|
||||
function ActivityChatMessageBox(props: ActivityChatMessageBoxProps) {
|
||||
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';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: 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)
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'ai', message: response.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)
|
||||
await dispatchAIChatBot({ type: 'setAichat_uuid', payload: response.aichat_uuid });
|
||||
await dispatchAIChatBot({ type: 'setIsNoLongerWaitingForResponse' });
|
||||
await dispatchAIChatBot({ type: 'setChatInputValue', payload: '' });
|
||||
await dispatchAIChatBot({ type: 'addMessage', payload: { sender: 'ai', message: response.message, type: 'ai' } });
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
dispatchAIChatBot({ type: 'setIsModalClose' });
|
||||
}
|
||||
|
||||
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 '>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 ? (
|
||||
<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} />
|
||||
)}
|
||||
<div className='flex space-x-2 items-center'>
|
||||
<div className=''>
|
||||
<Avvvatars radius={3} border borderColor='white' borderSize={3} size={35} value={session.user.user_uuid} style="shape" />
|
||||
</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;
|
||||
}
|
||||
|
||||
function AIMessage(props: AIMessageProps) {
|
||||
const session = useSession() as any;
|
||||
|
||||
const words = props.message.message.split(' ');
|
||||
|
||||
return (
|
||||
<div className='flex space-x-2 w-full antialiased font-medium'>
|
||||
<div className=''>
|
||||
<Avvvatars radius={3} border borderColor='white' borderSize={3} size={35} value={props.message.type == 'ai' ? 'ai' : session.user.user_uuid} style="shape" />
|
||||
</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);
|
||||
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'> <Avvvatars radius={3} border borderColor='white' borderSize={3} size={25} value={session.user.user_uuid} style="shape" />
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default AIActivityAsk
|
||||
|
|
@ -7,6 +7,7 @@ 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';
|
||||
|
||||
|
||||
|
||||
|
|
@ -16,23 +17,35 @@ type AICanvaToolkitProps = {
|
|||
}
|
||||
|
||||
function AICanvaToolkit(props: AICanvaToolkitProps) {
|
||||
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])
|
||||
|
||||
|
||||
return (
|
||||
<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} />
|
||||
<>
|
||||
{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>
|
||||
<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>
|
||||
</BubbleMenu>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +58,7 @@ function AIActionButton(props: { editor: Editor, label: string, activity: any })
|
|||
const prompt = getPrompt(label, selection);
|
||||
dispatchAIChatBot({ type: 'setIsModalOpen' });
|
||||
await sendMessage(prompt);
|
||||
|
||||
|
||||
}
|
||||
|
||||
const getTipTapEditorSelectedText = () => {
|
||||
|
|
@ -24,7 +24,7 @@ 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 "./Elements/AICanvaToolkit";
|
||||
import AICanvaToolkit from "./AI/AICanvaToolkit";
|
||||
|
||||
|
||||
interface Editor {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { Editor } from '@tiptap/react';
|
|||
import { AIChatBotStateTypes, useAIChatBot, useAIChatBotDispatch } from '@components/Contexts/AI/AIChatBotContext';
|
||||
import { AIEditorStateTypes, useAIEditor, useAIEditorDispatch } from '@components/Contexts/AI/AIEditorContext';
|
||||
import { sendActivityAIChatMessage, startActivityAIChatSession } from '@services/ai/ai';
|
||||
import useGetAIFeatures from '@components/AI/Hooks/useGetAIFeatures';
|
||||
|
||||
type AIEditorToolkitProps = {
|
||||
editor: Editor,
|
||||
|
|
@ -22,48 +23,61 @@ type AIPromptsLabels = {
|
|||
function AIEditorToolkit(props: AIEditorToolkitProps) {
|
||||
const dispatchAIEditor = useAIEditorDispatch() as any;
|
||||
const aiEditorState = useAIEditor() as AIEditorStateTypes;
|
||||
const is_ai_feature_enabled = useGetAIFeatures({ feature: 'editor' });
|
||||
const [isToolkitAvailable, setIsToolkitAvailable] = React.useState(true);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (is_ai_feature_enabled) {
|
||||
setIsToolkitAvailable(true);
|
||||
}
|
||||
}, [is_ai_feature_enabled])
|
||||
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{aiEditorState.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(3px)' }}
|
||||
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' }}
|
||||
>
|
||||
<>
|
||||
{aiEditorState.isFeedbackModalOpen && <UserFeedbackModal activity={props.activity} editor={props.editor} />}
|
||||
<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="z-50 rounded-2xl max-w-screen-2xl my-10 mx-auto w-fit fixed bottom-0 left-1/2 transform -translate-x-1/2 shadow-xl ring-1 ring-inset ring-white/10 text-white p-3 flex-col-reverse backdrop-blur-md">
|
||||
<div className='flex space-x-2'>
|
||||
<div className='pr-1'>
|
||||
<div className='flex w-full space-x-2 font-bold text-white/80 items-center'>
|
||||
<Image className='outline outline-1 outline-neutral-200/20 rounded-lg' width={24} src={learnhouseAI_icon} alt="" />
|
||||
<div >AI Editor</div>
|
||||
<MoreVertical className='text-white/50' size={12} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='tools flex space-x-2'>
|
||||
<AiEditorToolButton label='Writer' />
|
||||
<AiEditorToolButton label='ContinueWriting' />
|
||||
<AiEditorToolButton label='MakeLonger' />
|
||||
<>
|
||||
{isToolkitAvailable && <div className='flex space-x-2'>
|
||||
<AnimatePresence>
|
||||
{aiEditorState.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(3px)' }}
|
||||
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' }}
|
||||
>
|
||||
<>
|
||||
{aiEditorState.isFeedbackModalOpen && <UserFeedbackModal activity={props.activity} editor={props.editor} />}
|
||||
<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="z-50 rounded-2xl max-w-screen-2xl my-10 mx-auto w-fit fixed bottom-0 left-1/2 transform -translate-x-1/2 shadow-xl ring-1 ring-inset ring-white/10 text-white p-3 flex-col-reverse backdrop-blur-md">
|
||||
<div className='flex space-x-2'>
|
||||
<div className='pr-1'>
|
||||
<div className='flex w-full space-x-2 font-bold text-white/80 items-center'>
|
||||
<Image className='outline outline-1 outline-neutral-200/20 rounded-lg' width={24} src={learnhouseAI_icon} alt="" />
|
||||
<div >AI Editor</div>
|
||||
<MoreVertical className='text-white/50' size={12} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='tools flex space-x-2'>
|
||||
<AiEditorToolButton label='Writer' />
|
||||
<AiEditorToolButton label='ContinueWriting' />
|
||||
<AiEditorToolButton label='MakeLonger' />
|
||||
|
||||
<AiEditorToolButton label='Translate' />
|
||||
</div>
|
||||
<div className='flex space-x-2 items-center'>
|
||||
<X onClick={() => Promise.all([dispatchAIEditor({ type: 'setIsModalClose' }), dispatchAIEditor({ type: 'setIsFeedbackModalClose' })])} size={20} className='text-white/50 hover:cursor-pointer bg-white/10 p-1 rounded-full items-center' />
|
||||
</div>
|
||||
</div>
|
||||
</div></>
|
||||
</motion.div>}
|
||||
</AnimatePresence>
|
||||
</div>}
|
||||
</>
|
||||
|
||||
<AiEditorToolButton label='Translate' />
|
||||
</div>
|
||||
<div className='flex space-x-2 items-center'>
|
||||
<X onClick={() => Promise.all([dispatchAIEditor({ type: 'setIsModalClose' }), dispatchAIEditor({ type: 'setIsFeedbackModalClose' })])} size={20} className='text-white/50 hover:cursor-pointer bg-white/10 p-1 rounded-full items-center' />
|
||||
</div>
|
||||
</div>
|
||||
</div></>
|
||||
</motion.div>}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,9 @@ 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 { CourseProvider } from "@components/Contexts/CourseContext";
|
||||
import { OrgProvider } from "@components/Contexts/OrgContext";
|
||||
import { useSession } from "@components/Contexts/SessionContext";
|
||||
import AIEditorTools from "./AI/AIEditorToolkit";
|
||||
import AIEditorToolkit from "./AI/AIEditorToolkit";
|
||||
import useGetAIFeatures from "@components/AI/Hooks/useGetAIFeatures";
|
||||
|
||||
|
||||
interface Editor {
|
||||
|
|
@ -59,6 +58,14 @@ function Editor(props: Editor) {
|
|||
const session = useSession() as any;
|
||||
const dispatchAIEditor = useAIEditorDispatch() as any;
|
||||
const aiEditorState = useAIEditor() as AIEditorStateTypes;
|
||||
const is_ai_feature_enabled = useGetAIFeatures({ feature: 'editor' });
|
||||
const [isButtonAvailable, setIsButtonAvailable] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (is_ai_feature_enabled) {
|
||||
setIsButtonAvailable(true);
|
||||
}
|
||||
}, [is_ai_feature_enabled])
|
||||
|
||||
// remove course_ from course_uuid
|
||||
const course_uuid = props.course.course_uuid.substring(7);
|
||||
|
|
@ -137,7 +144,6 @@ function Editor(props: Editor) {
|
|||
|
||||
return (
|
||||
<Page>
|
||||
<OrgProvider orgslug={props.org?.slug}>
|
||||
<CourseProvider courseuuid={props.course.course_uuid}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.98 }}
|
||||
|
|
@ -172,7 +178,7 @@ function Editor(props: Editor) {
|
|||
<EditorUsersSection className="space-x-2">
|
||||
<div>
|
||||
<div className="transition-all ease-linear text-teal-100 rounded-md hover:cursor-pointer" >
|
||||
<div
|
||||
{isButtonAvailable && <div
|
||||
onClick={() => dispatchAIEditor({ type: aiEditorState.isModalOpen ? 'setIsModalClose' : '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)',
|
||||
|
|
@ -183,7 +189,7 @@ function Editor(props: Editor) {
|
|||
<Image className='' width={20} src={learnhouseAI_icon} alt="" />
|
||||
</i>{" "}
|
||||
<i className="not-italic text-xs font-bold">AI Editor</i>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
<DividerVerticalIcon style={{ marginTop: "auto", marginBottom: "auto", color: "grey", opacity: '0.5' }} />
|
||||
|
|
@ -224,7 +230,6 @@ function Editor(props: Editor) {
|
|||
</EditorContentWrapper>
|
||||
</motion.div>
|
||||
</CourseProvider>
|
||||
</OrgProvider>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import Editor from "./Editor";
|
|||
import { updateActivity } from "@services/courses/activities";
|
||||
import { toast } from "react-hot-toast";
|
||||
import Toast from "@components/StyledElements/Toast/Toast";
|
||||
import { OrgProvider } from "@components/Contexts/OrgContext";
|
||||
|
||||
interface EditorWrapperProps {
|
||||
content: string;
|
||||
|
|
@ -26,7 +27,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
|||
// setProviderState(provider);
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -50,8 +51,9 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
|||
} else {
|
||||
return <>
|
||||
<Toast></Toast>
|
||||
<Editor org={props.org} course={props.course} activity={props.activity} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
|
||||
|
||||
<OrgProvider orgslug={props.org.slug}>
|
||||
<Editor org={props.org} course={props.course} activity={props.activity} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
|
||||
</OrgProvider>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue