UNPKG

strapi-supergpt

Version:

A plugin that gives you the ability to integrate ChatGPT into strapi

354 lines (331 loc) 10.7 kB
import React, { useState, useEffect, useRef } from "react"; import { useIntl } from "react-intl"; import { Helmet } from "react-helmet"; import axios from "axios"; import { auth } from "@strapi/helper-plugin"; import { Button, TextInput, SingleSelect, SingleSelectOption, Layout, HeaderLayout, ContentLayout, Main, Box, Card, CardBody, CardContent, Grid, GridItem, ActionLayout, Tab, Stack, Tabs, TabGroup, TabPanels, TabPanel, Typography } from "@strapi/design-system"; import { PaperPlane, Command, Cog, Picture, PlusCircle } from "@strapi/icons"; import CustomTab from "./tab"; import Response from "./response"; import Help from "../Help"; import LoadingOverlay from "../Loading"; import Integration from "../Integration"; const Home = () => { const { formatMessage } = useIntl(); const imageFormats = [ formatMessage({ id: "strapi-supergpt.homePage.imageFormat" }), "1024x1024", "1024x1792", "1792x1024", ]; const [prompt, setPrompt] = useState(""); const [highlightedId, setHighlighted] = useState(0); const [convos, setConvos] = useState([]); const [error, setError] = useState(""); const [format, setFormat] = useState(imageFormats[0]); const [loading, setLoading] = useState(false); const messagesEndRef = useRef(null); const [isModalVisible, setIsModalVisible] = useState(false); const [isApiIntegrationModalVisible, setIsApiIntegrationModalVisible] = useState(false); const instance = axios.create({ baseURL: process.env.STRAPI_ADMIN_BACKEND_URL, headers: { Authorization: `Bearer ${auth.get("jwtToken")}`, "Content-Type": "application/json", }, }); const handlePromptChange = (e) => { setError(""); setPrompt(e.target.value); }; const handleImageSizeChange = (e) => { setFormat(e); }; const setSelectedResponse = (index) => { if (index >= convos.length) { setError(formatMessage({id: "strapi-supergpt.homePage.error.unselectableTab"})); return; } const selectedConvo = convos[index]; if (!selectedConvo.content.length) { instance.get(`/strapi-supergpt/convo/${selectedConvo.id}`) .then(content => { const updatedConvos = [...convos]; updatedConvos[index] = { ...selectedConvo, content: content.data }; setConvos(updatedConvos); }); } setHighlighted(index); }; const handleCreateTab = async () => { const defaultConvoName = formatMessage({id: "strapi-supergpt.homePage.convo.new.name"}) const { data: newConvo } = await instance.post(`/strapi-supergpt/convo`, { name: `${defaultConvoName} ${convos.length + 1}` }); setConvos(prevConvos => [...prevConvos, newConvo]); setHighlighted(newConvo.id); }; const handleSaveTab = async (e) => { const id = convos[highlightedId] await instance.put(`/strapi-supergpt/convo`, { name: id, content: convos[highlightedId], }) .then(convo => setConvos([...convos, convo.data])); }; const handleDeleteTab = async (id) => { if (convos.length > 1) { await instance.delete(`/strapi-supergpt/convo/${id}`); setConvos(prevConvos => { const updatedConvos = prevConvos.filter(convo => convo.id !== id); const newHighlightedId = updatedConvos.length > 0 ? updatedConvos[0].id : null; setHighlighted(newHighlightedId); return updatedConvos; }); } else { await instance.put(`/strapi-supergpt/convo/${id}`, { name: formatMessage({ id: "strapi-supergpt.homePage.convo.default.name" }), content: [], }); setConvos([{ id, name: formatMessage({ id: "strapi-supergpt.homePage.convo.default.name" }), content: [], }]); setHighlighted(id); } }; const handleSubmit = async (e) => { e.preventDefault(); setError(""); if (!prompt) { setError(formatMessage({id: "strapi-supergpt.homePage.error.promptRequired"})); return; } let response; try { if (e.target.name === "picture") { if (format === imageFormats[0]) { setError(formatMessage({id: "strapi-supergpt.homePage.error.imageSizeRequired"})); return; } setLoading(true); const data = await instance.post("/strapi-supergpt/generateImage", { prompt: prompt, size: format, }) setLoading(false); response = data.data; } else { setLoading(true); const format = formatMessage({ id: "strapi-supergpt.homePage.prompt.format"}) const data = await instance.post("/strapi-supergpt/prompt", { prompt: `${prompt} ${format}?`, }) setLoading(false); response = data.data; } } catch (e) { const errorMessage = e.message || e.response?.data?.error || "An unknown error occurred"; setError(errorMessage); setLoading(false); } let highlightedConvo = convos[highlightedId]; highlightedConvo.content = [ ...highlightedConvo.content, { name: "you", message: prompt }, { name: "chatgpt", message: response.response, } ]; await instance.put(`/strapi-supergpt/convo/${highlightedConvo.id}`, { name: highlightedConvo.name, content: highlightedConvo.content }); setPrompt(""); }; useEffect(() => { if (convos.length === 0) { instance.get('/strapi-supergpt/convos') .then(conversations => { if (conversations.data.length > 0) { setConvos(conversations.data) } else { handleCreateTab(); } }); } }, [setConvos]); useEffect(() => { if (messagesEndRef.current) { messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, []); return ( <Layout> <Helmet title={"strapi-supergpt"} /> <Main aria-busy={false}> <HeaderLayout title={ <Box display="flex" alignItems="center"> <Typography variant="alpha" as="h1"> SuperGPT </Typography> </Box> } subtitle={formatMessage({ id: "strapi-supergpt.homePage.header", })} /> <ActionLayout startActions={ <SingleSelect onChange={handleImageSizeChange} value={format}> {imageFormats.map((format, idx) => ( <SingleSelectOption key={idx} value={format}> {format} </SingleSelectOption> ))} </SingleSelect> } endActions={ <Stack horizontal gap={2}> <Button variant="secondary" startIcon={<Cog />} onClick={() => setIsApiIntegrationModalVisible(true)} > {formatMessage({ id: "strapi-supergpt.homePage.API_Integration.button" })} </Button> <Button variant="secondary" startIcon={<Command />} onClick={() => setIsModalVisible(true)} > {formatMessage({ id: "strapi-supergpt.homePage.help.button" })} </Button> </Stack> } /> <ContentLayout> <TabGroup onTabChange={setSelectedResponse}> <Tabs> {convos.length > 0 && convos.map(convo => ( <CustomTab key={convo.id} value={convo.id} onRename={handleSaveTab} onDelete={() => handleDeleteTab(convo.id)} > {convo.name} </CustomTab> ))} <Tab onClick={handleCreateTab}><PlusCircle /></Tab> </Tabs> <TabPanels> {convos.length > 0 && convos.map((convo) => ( <TabPanel key={convo.id}> <Card> <CardBody style={{ height: "64vh", overflowY: "scroll", width: "100%" }} > <CardContent> <LoadingOverlay isLoading={loading} /> {convo.content.map((response, index) => ( <Response key={`${index}`}> {response} </Response> ))} <div ref={messagesEndRef} /> </CardContent> </CardBody> </Card> </TabPanel> ))} </TabPanels> </TabGroup> <Box> <form> <Grid spacing={1} gap={2} paddingTop={4}> <GridItem col={11}> <TextInput id="chatInput" placeholder={formatMessage({ id: "strapi-supergpt.homePage.prompt.placeholder" })} aria-label="Content" name="prompt" error={error} onChange={handlePromptChange} value={prompt} disabled={loading} onPaste={handlePromptChange} /> </GridItem> <GridItem style={{ display: 'flex', justifyContent: 'space-between', gap: '0.5rem' }}> <Button size="L" name="prompt" startIcon={<PaperPlane />} value="prompt" loading={loading} onClick={handleSubmit} > {formatMessage({ id: "strapi-supergpt.homePage.prompt.button" })} </Button> <Button size="L" name="picture" value="picture" onClick={handleSubmit} startIcon={<Picture />} > {formatMessage({ id: "strapi-supergpt.homePage.image.button" })} </Button> </GridItem> </Grid> </form> </Box> </ContentLayout> <Help isOpen={isModalVisible} onClose={() => setIsModalVisible(false)} /> <Integration isOpen={isApiIntegrationModalVisible} onClose={() => setIsApiIntegrationModalVisible(false)} /> </Main> </Layout> ); }; export default Home;