strapi-supergpt
Version:
A plugin that gives you the ability to integrate ChatGPT into strapi
354 lines (331 loc) • 10.7 kB
JSX
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;