upload agent image action
This commit is contained in:
10
package-lock.json
generated
10
package-lock.json
generated
@@ -23,7 +23,8 @@
|
|||||||
"sweetalert2": "^11.10.5",
|
"sweetalert2": "^11.10.5",
|
||||||
"sweetalert2-react-content": "^5.0.7",
|
"sweetalert2-react-content": "^5.0.7",
|
||||||
"tw-elements": "^1.1.0",
|
"tw-elements": "^1.1.0",
|
||||||
"tw-elements-react": "^1.0.0-alpha2"
|
"tw-elements-react": "^1.0.0-alpha2",
|
||||||
|
"uuid": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ianvs/prettier-plugin-sort-imports": "^4.1.0",
|
"@ianvs/prettier-plugin-sort-imports": "^4.1.0",
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.19",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@types/uuid": "^9.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||||
"@typescript-eslint/parser": "^6.7.3",
|
"@typescript-eslint/parser": "^6.7.3",
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
@@ -1847,6 +1849,12 @@
|
|||||||
"@types/node": "*"
|
"@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": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
"sweetalert2": "^11.10.5",
|
"sweetalert2": "^11.10.5",
|
||||||
"sweetalert2-react-content": "^5.0.7",
|
"sweetalert2-react-content": "^5.0.7",
|
||||||
"tw-elements": "^1.1.0",
|
"tw-elements": "^1.1.0",
|
||||||
"tw-elements-react": "^1.0.0-alpha2"
|
"tw-elements-react": "^1.0.0-alpha2",
|
||||||
|
"uuid": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ianvs/prettier-plugin-sort-imports": "^4.1.0",
|
"@ianvs/prettier-plugin-sort-imports": "^4.1.0",
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.19",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@types/uuid": "^9.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||||
"@typescript-eslint/parser": "^6.7.3",
|
"@typescript-eslint/parser": "^6.7.3",
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -57,11 +57,11 @@ const AgentsPage = () => {
|
|||||||
<div
|
<div
|
||||||
className={`flex flex-col ${
|
className={`flex flex-col ${
|
||||||
i % 2 == 0 ? "lg:flex-row" : "lg:flex-row-reverse"
|
i % 2 == 0 ? "lg:flex-row" : "lg:flex-row-reverse"
|
||||||
} rounded overflow-hidden h-auto min-h-40 border`}
|
} rounded overflow-hidden h-auto min-h-52 border`}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="block h-auto max-w-72 w-full lg:w-48 flex-none bg-cover"
|
className="block max-w-72 w-full lg:w-48 flex-none bg-cover"
|
||||||
src={agent.image ?? "/sailor.png"}
|
src={agent.image ?? "/agents_images/sailor.png"}
|
||||||
alt="Agent"
|
alt="Agent"
|
||||||
/>
|
/>
|
||||||
<div className="rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col leading-normal w-100">
|
<div className="rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col leading-normal w-100">
|
||||||
@@ -92,6 +92,9 @@ const AgentsPage = () => {
|
|||||||
onUpdateAgent={() => {
|
onUpdateAgent={() => {
|
||||||
refetch();
|
refetch();
|
||||||
}}
|
}}
|
||||||
|
onUploadImage={() => {
|
||||||
|
refetch();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
42
src/app/api/upload_agent_image/route.ts
Normal file
42
src/app/api/upload_agent_image/route.ts
Normal file
@@ -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 });
|
||||||
|
}
|
||||||
@@ -29,8 +29,15 @@ export default function AgentModal(props: {
|
|||||||
showModal: boolean;
|
showModal: boolean;
|
||||||
setShowModal: Function;
|
setShowModal: Function;
|
||||||
onUpdateAgent: Function;
|
onUpdateAgent: Function;
|
||||||
|
onUploadImage: Function;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { agent, showModal, setShowModal, onUpdateAgent = () => {} } = props;
|
const {
|
||||||
|
agent,
|
||||||
|
showModal,
|
||||||
|
setShowModal,
|
||||||
|
onUpdateAgent = () => {},
|
||||||
|
onUploadImage = () => {},
|
||||||
|
} = props;
|
||||||
|
|
||||||
const [isEdit, setEdit] = useState(false);
|
const [isEdit, setEdit] = useState(false);
|
||||||
|
|
||||||
@@ -40,6 +47,8 @@ export default function AgentModal(props: {
|
|||||||
setTempAgent(agent);
|
setTempAgent(agent);
|
||||||
}, [agent]);
|
}, [agent]);
|
||||||
|
|
||||||
|
const [imageFile, setImageFile] = useState<Blob>();
|
||||||
|
|
||||||
const [selectedImage, setSelectedImage] = useState<
|
const [selectedImage, setSelectedImage] = useState<
|
||||||
string | ArrayBuffer | null
|
string | ArrayBuffer | null
|
||||||
>(null);
|
>(null);
|
||||||
@@ -53,10 +62,63 @@ export default function AgentModal(props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
|
setImageFile(file);
|
||||||
reader.readAsDataURL(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 [updateAgent] = useMutation(UPDATE_AGENT);
|
||||||
const [updateAgentLoading, setUpdateAgentLoading] = useState(false);
|
const [updateAgentLoading, setUpdateAgentLoading] = useState(false);
|
||||||
|
|
||||||
@@ -228,6 +290,20 @@ export default function AgentModal(props: {
|
|||||||
accept="image/*"
|
accept="image/*"
|
||||||
onChange={handleImageChange}
|
onChange={handleImageChange}
|
||||||
/>
|
/>
|
||||||
|
<div className="text-center py-1">
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
handleUploadImage();
|
||||||
|
}}
|
||||||
|
loading={uploadLoading}
|
||||||
|
disabled={uploadLoading || !selectedImage}
|
||||||
|
color="blue"
|
||||||
|
className="mx-auto"
|
||||||
|
placeholder={undefined}
|
||||||
|
>
|
||||||
|
Upload
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
{selectedImage && (
|
{selectedImage && (
|
||||||
<img
|
<img
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -239,8 +315,8 @@ export default function AgentModal(props: {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
src={agent?.image ?? "/sailor.png"}
|
src={tempAgent?.image ?? "/agents_images/sailor.png"}
|
||||||
alt="Software Engineer"
|
alt="Agent Image"
|
||||||
className="w-7/12 mx-auto rounded-lg"
|
className="w-7/12 mx-auto rounded-lg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
TESelect,
|
TESelect,
|
||||||
TETextarea,
|
TETextarea,
|
||||||
} from "tw-elements-react";
|
} from "tw-elements-react";
|
||||||
import TWFileInput from "../inputs/file";
|
|
||||||
import { Button, Switch } from "@material-tailwind/react";
|
import { Button, Switch } from "@material-tailwind/react";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { CREATE_AGENT } from "@/utils/graphql_queries";
|
import { CREATE_AGENT } from "@/utils/graphql_queries";
|
||||||
@@ -93,7 +92,7 @@ function NewAgentModal(props: {
|
|||||||
</TEModalHeader>
|
</TEModalHeader>
|
||||||
<TEModalBody>
|
<TEModalBody>
|
||||||
<div className="sm:flex">
|
<div className="sm:flex">
|
||||||
<div className="sm:w-1/2">
|
<div className="sm:w-1/2 mx-auto">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label className="font-bold text-lg">Role:</label>
|
<label className="font-bold text-lg">Role:</label>
|
||||||
<TEInput
|
<TEInput
|
||||||
@@ -181,19 +180,6 @@ function NewAgentModal(props: {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="m-4 sm:w-1/2">
|
|
||||||
<label className="font-bold mx-2">Agent Image: </label>
|
|
||||||
<TWFileInput accept="image/*" onChange={handleImageChange} />
|
|
||||||
{selectedImage && (
|
|
||||||
<img
|
|
||||||
// @ts-ignore
|
|
||||||
src={selectedImage}
|
|
||||||
alt="Agent Image"
|
|
||||||
className="mx-auto my-3 max-w-72 h-auto"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</TEModalBody>
|
</TEModalBody>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user