file upload and store
This commit is contained in:
@@ -4,10 +4,12 @@ const path = require("path");
|
|||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const multer = require("multer");
|
const multer = require("multer");
|
||||||
const defineAttachment = require("../../models/attachments");
|
const defineAttachment = require("../../models/attachments");
|
||||||
|
const defineCaseAttachment = require("../../models/caseAttachments");
|
||||||
const { DataTypes } = require("sequelize");
|
const { DataTypes } = require("sequelize");
|
||||||
|
|
||||||
module.exports = function (sequelize) {
|
module.exports = function (sequelize) {
|
||||||
const Attachment = defineAttachment(sequelize, DataTypes);
|
const Attachment = defineAttachment(sequelize, DataTypes);
|
||||||
|
const CaseAttachment = defineCaseAttachment(sequelize, DataTypes);
|
||||||
|
|
||||||
// Create uploads folder if it does not exist
|
// Create uploads folder if it does not exist
|
||||||
const uploadDir = path.join(__dirname, "../../public/uploads");
|
const uploadDir = path.join(__dirname, "../../public/uploads");
|
||||||
@@ -45,7 +47,9 @@ module.exports = function (sequelize) {
|
|||||||
const upload = multer({ storage });
|
const upload = multer({ storage });
|
||||||
|
|
||||||
router.post("/", upload.array("files", 10), async (req, res) => {
|
router.post("/", upload.array("files", 10), async (req, res) => {
|
||||||
|
const t = await sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
|
const caseId = req.query.parentCaseId;
|
||||||
const files = req.files;
|
const files = req.files;
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return res.status(400).json({ error: "No files uploaded" });
|
return res.status(400).json({ error: "No files uploaded" });
|
||||||
@@ -58,10 +62,19 @@ module.exports = function (sequelize) {
|
|||||||
path: `${protocol}://${host}/uploads/${file.filename}`,
|
path: `${protocol}://${host}/uploads/${file.filename}`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const newAttachments = await Attachment.bulkCreate(attachmentsData);
|
const newAttachments = await Attachment.bulkCreate(attachmentsData, {
|
||||||
|
transaction: t,
|
||||||
|
});
|
||||||
|
|
||||||
|
const caseAttachmentsData = newAttachments.map((attachment) => ({
|
||||||
|
caseId: caseId,
|
||||||
|
attachmentId: attachment.id,
|
||||||
|
}));
|
||||||
|
await CaseAttachment.bulkCreate(caseAttachmentsData, { transaction: t });
|
||||||
|
await t.commit();
|
||||||
res.json(newAttachments);
|
res.json(newAttachments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await t.rollback();
|
||||||
res.status(500).json({ error: "Internal server error" });
|
res.status(500).json({ error: "Internal server error" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,18 +11,60 @@ export default function AttachmentsEditor({
|
|||||||
attachments = [],
|
attachments = [],
|
||||||
onAttachmentDelete,
|
onAttachmentDelete,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
let images = [];
|
||||||
|
let others = [];
|
||||||
|
|
||||||
|
attachments.forEach((attachment) => {
|
||||||
|
let path = attachment.path;
|
||||||
|
let extension = path.substring(path.lastIndexOf(".") + 1).toLowerCase();
|
||||||
|
if (
|
||||||
|
extension === "png" ||
|
||||||
|
extension === "jpg" ||
|
||||||
|
extension === "jpeg" ||
|
||||||
|
extension === "gif" ||
|
||||||
|
extension === "bmp" ||
|
||||||
|
extension === "svg"
|
||||||
|
) {
|
||||||
|
images.push(attachment);
|
||||||
|
} else {
|
||||||
|
others.push(attachment);
|
||||||
|
}
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{attachments.map((attachment, index) => (
|
{images.map((image, index) => (
|
||||||
<div key={index} className="flex">
|
<div key={index} className="flex">
|
||||||
<Image alt={attachment.title} src={attachment.path} className="object-cover h-40 w-40"/>
|
<Image
|
||||||
|
alt={image.title}
|
||||||
|
src={image.path}
|
||||||
|
className="object-cover h-40 w-40"
|
||||||
|
/>
|
||||||
|
<div className="mt-3 ms-1">
|
||||||
|
<Tooltip content="Delete this image file" placement="left">
|
||||||
|
<Button
|
||||||
|
isIconOnly
|
||||||
|
size="sm"
|
||||||
|
className="bg-transparent rounded-full"
|
||||||
|
onPress={() => onAttachmentDelete(image.id)}
|
||||||
|
>
|
||||||
|
<Trash size={16} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{others.map((file, index) => (
|
||||||
|
<div key={index} className="flex">
|
||||||
|
<div>{file.title}</div>
|
||||||
|
<div>{file.path}</div>
|
||||||
<div className="mt-3 ms-1">
|
<div className="mt-3 ms-1">
|
||||||
<Tooltip content="Delete this attachment file" placement="left">
|
<Tooltip content="Delete this attachment file" placement="left">
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
size="sm"
|
size="sm"
|
||||||
className="bg-transparent rounded-full"
|
className="bg-transparent rounded-full"
|
||||||
onPress={() => onAttachmentDelete(attachment.id)}
|
onPress={() => onAttachmentDelete(file.id)}
|
||||||
>
|
>
|
||||||
<Trash size={16} />
|
<Trash size={16} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -245,15 +245,23 @@ export default function Page({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileUpload = async (event) => {
|
const handleDrop = (event) => {
|
||||||
const files = event.target.files;
|
event.preventDefault();
|
||||||
|
handleFileUpload(event.dataTransfer.files);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInput = (event) => {
|
||||||
|
handleFileUpload(event.target.files);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileUpload = async (files) => {
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
formData.append("files", files[i]);
|
formData.append("files", files[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${apiServer}/attachments`;
|
const url = `${apiServer}/attachments?parentCaseId=${params.caseId}`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
@@ -284,6 +292,21 @@ export default function Page({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-5">
|
<div className="p-5">
|
||||||
|
<div className="mt-6">
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
isLoading={isUpdating}
|
||||||
|
onPress={async () => {
|
||||||
|
setIsUpdating(true);
|
||||||
|
await updateCase(testCase);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsUpdating(false);
|
||||||
|
}, 1000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isUpdating ? "Updating..." : "Update"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<h6>Basic</h6>
|
<h6>Basic</h6>
|
||||||
<Input
|
<Input
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -458,7 +481,11 @@ export default function Page({
|
|||||||
attachments={testCase.Attachments}
|
attachments={testCase.Attachments}
|
||||||
onAttachmentDelete={(id) => console.log(id)}
|
onAttachmentDelete={(id) => console.log(id)}
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center justify-center w-96 mt-3">
|
<div
|
||||||
|
className="flex items-center justify-center w-96 mt-3"
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragOver={(event) => event.preventDefault()}
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
htmlFor="dropzone-file"
|
htmlFor="dropzone-file"
|
||||||
className="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-200 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
|
className="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-200 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
|
||||||
@@ -477,27 +504,11 @@ export default function Page({
|
|||||||
id="dropzone-file"
|
id="dropzone-file"
|
||||||
type="file"
|
type="file"
|
||||||
className="hidden"
|
className="hidden"
|
||||||
onChange={handleFileUpload}
|
onChange={handleInput}
|
||||||
multiple
|
multiple
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6">
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
isLoading={isUpdating}
|
|
||||||
onPress={async () => {
|
|
||||||
setIsUpdating(true);
|
|
||||||
await updateCase(testCase);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsUpdating(false);
|
|
||||||
}, 1000);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isUpdating ? "Updating..." : "Update"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user