From 0b164fd5109daccb224996c89900204fdc92b848 Mon Sep 17 00:00:00 2001 From: "Eng. Elias" Date: Sat, 2 Mar 2024 01:47:33 +0400 Subject: [PATCH] upload agent image action --- package-lock.json | 10 ++- package.json | 4 +- public/{ => agents_images}/sailor.png | Bin src/app/agents/page.tsx | 9 ++- src/app/api/upload_agent_image/route.ts | 42 +++++++++++ src/components/modals/agent_modal.tsx | 82 +++++++++++++++++++++- src/components/modals/new_agent_modal.tsx | 16 +---- 7 files changed, 140 insertions(+), 23 deletions(-) rename public/{ => agents_images}/sailor.png (100%) create mode 100644 src/app/api/upload_agent_image/route.ts diff --git a/package-lock.json b/package-lock.json index e9896a3..dd9e91e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,8 @@ "sweetalert2": "^11.10.5", "sweetalert2-react-content": "^5.0.7", "tw-elements": "^1.1.0", - "tw-elements-react": "^1.0.0-alpha2" + "tw-elements-react": "^1.0.0-alpha2", + "uuid": "^9.0.1" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.1.0", @@ -31,6 +32,7 @@ "@types/node": "^20.11.19", "@types/react": "^18", "@types/react-dom": "^18", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", "autoprefixer": "^10.0.1", @@ -1847,6 +1849,12 @@ "@types/node": "*" } }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", diff --git a/package.json b/package.json index 596c593..cb25f19 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "sweetalert2": "^11.10.5", "sweetalert2-react-content": "^5.0.7", "tw-elements": "^1.1.0", - "tw-elements-react": "^1.0.0-alpha2" + "tw-elements-react": "^1.0.0-alpha2", + "uuid": "^9.0.1" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.1.0", @@ -33,6 +34,7 @@ "@types/node": "^20.11.19", "@types/react": "^18", "@types/react-dom": "^18", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", "autoprefixer": "^10.0.1", diff --git a/public/sailor.png b/public/agents_images/sailor.png similarity index 100% rename from public/sailor.png rename to public/agents_images/sailor.png diff --git a/src/app/agents/page.tsx b/src/app/agents/page.tsx index 36ecf9e..5674692 100644 --- a/src/app/agents/page.tsx +++ b/src/app/agents/page.tsx @@ -57,11 +57,11 @@ const AgentsPage = () => {
Agent
@@ -92,6 +92,9 @@ const AgentsPage = () => { onUpdateAgent={() => { refetch(); }} + onUploadImage={() => { + refetch(); + }} />
diff --git a/src/app/api/upload_agent_image/route.ts b/src/app/api/upload_agent_image/route.ts new file mode 100644 index 0000000..05842b5 --- /dev/null +++ b/src/app/api/upload_agent_image/route.ts @@ -0,0 +1,42 @@ +import path from "path"; +import fs from "fs"; +import { v4 as uuidv4 } from "uuid"; +import { NextRequest, NextResponse } from "next/server"; +import prisma from "@/utils/prisma"; + +export async function POST(request: NextRequest) { + const data = await request.formData(); + const file: File | null = data.get("image") as unknown as File; + + if (!file) { + return NextResponse.json({ + success: false, + error: "There is no file or the file is corrupted", + }); + } + + // With the file data in the buffer, you can do whatever you want with it. + // For this, we'll just write it to the filesystem in a new location + const bytes = await file.arrayBuffer(); + const buffer = Buffer.from(bytes); + + const filename = `${uuidv4()}-${new Date().getTime()}-${file.name}`; + const storagePath = path.join( + process.cwd(), + "public", + "agents_images", + filename + ); + fs.writeFileSync(storagePath, buffer, { flag: "w" }); + + const imageURL = `/agents_images/${filename}`; + await prisma.agent.update({ + // @ts-ignore + where: { id: Number.parseInt(data.get("agent_id")) }, + data: { + image: imageURL, + }, + }); + + return NextResponse.json({ success: true, url: imageURL }); +} diff --git a/src/components/modals/agent_modal.tsx b/src/components/modals/agent_modal.tsx index a1ba197..69c26ad 100644 --- a/src/components/modals/agent_modal.tsx +++ b/src/components/modals/agent_modal.tsx @@ -29,8 +29,15 @@ export default function AgentModal(props: { showModal: boolean; setShowModal: Function; onUpdateAgent: Function; + onUploadImage: Function; }): JSX.Element { - const { agent, showModal, setShowModal, onUpdateAgent = () => {} } = props; + const { + agent, + showModal, + setShowModal, + onUpdateAgent = () => {}, + onUploadImage = () => {}, + } = props; const [isEdit, setEdit] = useState(false); @@ -40,6 +47,8 @@ export default function AgentModal(props: { setTempAgent(agent); }, [agent]); + const [imageFile, setImageFile] = useState(); + const [selectedImage, setSelectedImage] = useState< string | ArrayBuffer | null >(null); @@ -53,10 +62,63 @@ export default function AgentModal(props: { }; if (file) { + setImageFile(file); reader.readAsDataURL(file); } }; + const [uploadLoading, setUploadLoading] = useState(false); + const handleUploadImage = async () => { + setUploadLoading(true); + if (imageFile) { + const body = new FormData(); + body.append("agent_id", agent.id as string); + body.append("image", imageFile); + const response = await fetch("/api/upload_agent_image", { + method: "POST", + body, + }); + if (response.ok) { + response + .json() + .then((data) => { + if (data.success) { + setTempAgent({ ...tempAgent, image: data.url }); + ReactSwal.fire({ + title: "Finished", + text: "Agent image updated successfully", + icon: "success", + }); + onUploadImage(); + } else { + ReactSwal.fire({ + title: "Error", + text: data.error, + icon: "error", + }); + } + }) + .catch((error) => { + ReactSwal.fire({ + title: "Error", + text: error, + icon: "error", + }); + }) + .finally(() => { + setUploadLoading(false); + }); + } else { + ReactSwal.fire({ + title: "Error", + text: "An error occurred, try again.", + icon: "error", + }); + setUploadLoading(false); + } + } + }; + const [updateAgent] = useMutation(UPDATE_AGENT); const [updateAgentLoading, setUpdateAgentLoading] = useState(false); @@ -228,6 +290,20 @@ export default function AgentModal(props: { accept="image/*" onChange={handleImageChange} /> +
+ +
{selectedImage && ( ) : ( Software Engineer )} diff --git a/src/components/modals/new_agent_modal.tsx b/src/components/modals/new_agent_modal.tsx index 6fd2343..0a4325f 100644 --- a/src/components/modals/new_agent_modal.tsx +++ b/src/components/modals/new_agent_modal.tsx @@ -15,7 +15,6 @@ import { TESelect, TETextarea, } from "tw-elements-react"; -import TWFileInput from "../inputs/file"; import { Button, Switch } from "@material-tailwind/react"; import { useMutation } from "@apollo/client"; import { CREATE_AGENT } from "@/utils/graphql_queries"; @@ -93,7 +92,7 @@ function NewAgentModal(props: {
-
+
- -
- - - {selectedImage && ( - Agent Image - )} -