feat:magazine edit

This commit is contained in:
Rama Priyanto 2025-01-21 18:20:58 +07:00
parent d64ce7fc14
commit 471539804c
4 changed files with 281 additions and 47 deletions

View File

@ -33,7 +33,7 @@ import {
PptIcon, PptIcon,
WordIcon, WordIcon,
} from "@/components/icons/globals"; } from "@/components/icons/globals";
import { createMagazine } from "@/service/magazine"; import { createMagazine, uploadMagazineFile } from "@/service/magazine";
// const CustomEditor = dynamic( // const CustomEditor = dynamic(
// () => { // () => {
@ -85,11 +85,6 @@ export default function NewCreateMagazineForm() {
const router = useRouter(); const router = useRouter();
const editor = useRef(null); const editor = useRef(null);
const [files, setFiles] = useState<FileWithPreview[]>([]); const [files, setFiles] = useState<FileWithPreview[]>([]);
const [useAi, setUseAI] = useState(false);
const [listCategory, setListCategory] = useState<CategoryType[]>([]);
const [tag, setTag] = useState("");
const [thumbnailImg, setThumbnailImg] = useState<File[]>([]);
const [selectedMainImage, setSelectedMainImage] = useState<number>();
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => { onDrop: (acceptedFiles) => {
@ -160,28 +155,23 @@ export default function NewCreateMagazineForm() {
// rows: values.rows, // rows: values.rows,
}; };
console.log("formd", formData); console.log("formd", formData);
// const response = await createMagazine(formData); const response = await createMagazine(formData);
// if (response?.error) { if (response?.error) {
// error(response.message); error(response.message);
// return false; return false;
// } }
// const magazineId = response?.data?.data?.id; const magazineId = response?.data?.data?.id;
// if (files?.length > 0) { if (files?.length > 0) {
// const formFiles = new FormData(); const formFiles = new FormData();
// for (const element of files) { for (let i = 0; i < files.length; i++) {
// formFiles.append("file", element); formFiles.append("files", files[i]);
// const resFile = await uploadArticleFile(magazineId, formFiles); formFiles.append("title", values.rows[i].title);
// } formFiles.append("file", values.rows[i].description);
// } const resFile = await uploadMagazineFile(magazineId, formFiles);
}
// if (thumbnailImg?.length > 0) { }
// const formFiles = new FormData();
// formFiles.append("file", thumbnailImg[0]);
// const resFile = await uploadArticleThumbnail(magazineId, formFiles);
// }
close(); close();
successSubmit("/admin/magazine"); successSubmit("/admin/magazine");
@ -310,6 +300,7 @@ export default function NewCreateMagazineForm() {
<Button <Button
className=" border-none rounded-full" className=" border-none rounded-full"
variant="bordered" variant="bordered"
color="danger"
onClick={() => handleRemoveFile(file)} onClick={() => handleRemoveFile(file)}
> >
<TimesIcon /> <TimesIcon />

View File

@ -29,12 +29,14 @@ import Link from "next/link";
import { import {
CsvIcon, CsvIcon,
ExcelIcon, ExcelIcon,
FileIcon,
PdfIcon, PdfIcon,
PptIcon, PptIcon,
WordIcon, WordIcon,
} from "@/components/icons/globals"; } from "@/components/icons/globals";
import { import {
createMagazine, createMagazine,
deleteMagazineFiles,
getMagazineById, getMagazineById,
updateMagazine, updateMagazine,
uploadMagazineFile, uploadMagazineFile,
@ -93,6 +95,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
const router = useRouter(); const router = useRouter();
const editor = useRef(null); const editor = useRef(null);
const [files, setFiles] = useState<FileWithPreview[]>([]); const [files, setFiles] = useState<FileWithPreview[]>([]);
const [detailfiles, setDetailFiles] = useState<any>([]);
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => { onDrop: (acceptedFiles) => {
@ -144,6 +147,8 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
const data = res?.data?.data; const data = res?.data?.data;
setValue("title", data?.title); setValue("title", data?.title);
setValue("description", data?.description); setValue("description", data?.description);
setDetailFiles(data?.files);
console.log("datasss", data); console.log("datasss", data);
}; };
@ -222,31 +227,38 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
setValue("slug", generateSlug(watchTitle)); setValue("slug", generateSlug(watchTitle));
}, [watchTitle]); }, [watchTitle]);
const renderPreview = (file: File) => { const renderPreview = (file: File, fileName?: string) => {
if (file.type === "application/pdf") { const fileType = fileName?.split(".")[fileName?.split(".").length - 1];
if (file.type === "application/pdf" || fileType == "pdf") {
return <PdfIcon size={60} />; return <PdfIcon size={60} />;
} else if (file.type === "text/csv") { } else if (file.type === "text/csv" || fileType == "csv") {
return <CsvIcon size={60} />; return <CsvIcon size={60} />;
} else if ( } else if (
file.type === file.type ===
"application/vnd.openxmlformats-officedocument.wordprocessingml.document" || "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ||
file.type === "application/msword" file.type === "application/msword" ||
fileType == "doc" ||
fileType == "docx"
) { ) {
return <WordIcon size={60} />; return <WordIcon size={60} />;
} else if ( } else if (
file.type === file.type ===
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
file.type === "application/vnd.ms-excel" file.type === "application/vnd.ms-excel" ||
fileType == "xls" ||
fileType == "xlsx"
) { ) {
return <ExcelIcon size={60} />; return <ExcelIcon size={60} />;
} else if ( } else if (
file.type === file.type ===
"application/vnd.openxmlformats-officedocument.presentationml.presentation" || "application/vnd.openxmlformats-officedocument.presentationml.presentation" ||
file.type === "application/vnd.ms-powerpoint" file.type === "application/vnd.ms-powerpoint" ||
fileType == "ppt" ||
fileType == "pptx"
) { ) {
return <PptIcon size={60} />; return <PptIcon size={60} />;
} else { } else {
return "unknown"; return <FileIcon size={60} />;
} }
}; };
@ -319,6 +331,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
<Button <Button
className=" border-none rounded-full" className=" border-none rounded-full"
variant="bordered" variant="bordered"
color="danger"
onClick={() => handleRemoveFile(file)} onClick={() => handleRemoveFile(file)}
> >
<TimesIcon /> <TimesIcon />
@ -326,6 +339,43 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
</div> </div>
)); ));
const handleDeleteFile = (id: number) => {
MySwal.fire({
title: "Hapus File",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
deleteFile(id);
}
});
};
const deleteFile = async (id: number) => {
loading();
const res = await deleteMagazineFiles(id);
if (res?.error) {
error(res.message);
return false;
}
close();
initFetch();
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
}
});
};
return ( return (
<form <form
className="flex flex-row gap-8 text-black" className="flex flex-row gap-8 text-black"
@ -411,8 +461,8 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
)} )}
<p className="text-sm mt-3">File Media</p> <p className="text-sm mt-3">File Media</p>
<Fragment>
{!isDetail && ( {!isDetail && (
<Fragment>
<div {...getRootProps({ className: "dropzone" })} className="mb-2"> <div {...getRootProps({ className: "dropzone" })} className="mb-2">
<input {...getInputProps()} /> <input {...getInputProps()} />
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col"> <div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
@ -426,21 +476,190 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
</div> </div>
</div> </div>
</div> </div>
)} <div className="grid grid-cols-2 gap-2">
{files.length ? ( {files.length ? (
<Fragment> <Fragment>
<div className="grid grid-cols-2 gap-2">{fileList}</div> {fileList}
{files.length > 1 && (
{/* {files.length > 1 && (
<div className=" flex justify-between gap-2"> <div className=" flex justify-between gap-2">
<Button onPress={() => setFiles([])} size="sm"> <Button onPress={() => setFiles([])} size="sm">
Hapus Semua Hapus Semua
</Button> </Button>
</div> </div>
)} )} */}
</Fragment> </Fragment>
) : null} ) : null}
{detailfiles?.map((file: any, index: number) => (
<div
key={file.fileName + index}
className=" flex justify-between border p-3 rounded-md"
>
<div className="flex gap-3 grow">
<div className="file-preview">
{renderPreview(file, file.fileName)}
</div>
<div className="flex flex-col gap-1 grow">
<p className="text-sm font-semibold">Nama File</p>
<div className="flex flex-row gap-2 items-center">
<p className=" text-sm text-card-foreground">
{file.fileName}
</p>
<p className=" text-xs font-light text-muted-foreground">
{Math.round(file.size / 100) / 10 > 1000 ? (
<>
{(Math.round(file.size / 100) / 10000).toFixed(1)}
</>
) : (
<>{(Math.round(file.size / 100) / 10).toFixed(1)}</>
)}
{" kb"}
</p>
</div>
<p className="text-sm font-semibold">Judul</p>
<Input
type="text"
id="title"
placeholder=""
label=""
isReadOnly
value={file.title}
onValueChange={(e) =>
setValue(`rows.${index}.title`, e)
}
labelPlacement="outside"
className="w-full "
classNames={{
inputWrapper: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
variant="bordered"
/>
<p className="text-sm font-semibold">Deskripsi</p>
<Textarea
type="text"
id="title"
placeholder=""
label=""
value={file.description}
onValueChange={(e) =>
setValue(`rows.${index}.description`, e)
}
isReadOnly
labelPlacement="outside"
className="w-full "
classNames={{
inputWrapper: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
variant="bordered"
/>
</div>
</div>
<Button
className=" border-none rounded-full"
variant="bordered"
color="danger"
onClick={() => handleDeleteFile(file?.id)}
>
<TimesIcon />
</Button>
</div>
))}
</div>
</Fragment> </Fragment>
)}
{isDetail && (
<div className="grid grid-cols-2 gap-2">
{detailfiles?.map((file: any, index: number) => (
<div
key={file.fileName + index}
className=" flex justify-between border p-3 rounded-md"
>
<div className="flex gap-3 grow">
<div className="file-preview">
{renderPreview(file, file.fileName)}
</div>
<div className="flex flex-col gap-1 grow">
<p className="text-sm font-semibold">Nama File</p>
<div className="flex flex-row gap-2 items-center">
<p className=" text-sm text-card-foreground">
{file.fileName}
</p>
<p className=" text-xs font-light text-muted-foreground">
{Math.round(file.size / 100) / 10 > 1000 ? (
<>
{(Math.round(file.size / 100) / 10000).toFixed(1)}
</>
) : (
<>{(Math.round(file.size / 100) / 10).toFixed(1)}</>
)}
{" kb"}
</p>
</div>
<p className="text-sm font-semibold">Judul</p>
<Input
type="text"
id="title"
placeholder=""
label=""
isReadOnly
value={file.title}
onValueChange={(e) => setValue(`rows.${index}.title`, e)}
labelPlacement="outside"
className="w-full "
classNames={{
inputWrapper: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
variant="bordered"
/>
<p className="text-sm font-semibold">Deskripsi</p>
<Textarea
type="text"
id="title"
placeholder=""
label=""
value={file.description}
onValueChange={(e) =>
setValue(`rows.${index}.description`, e)
}
isReadOnly
labelPlacement="outside"
className="w-full "
classNames={{
inputWrapper: [
"border-1 rounded-lg",
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
],
}}
variant="bordered"
/>
</div>
</div>
{/* <Button
className=" border-none rounded-full"
variant="bordered"
color="danger"
onClick={() => handleRemoveFile(file)}
>
<TimesIcon />
</Button> */}
</div>
))}
</div>
)}
<div className="flex flex-row gap-3 mt-3"> <div className="flex flex-row gap-3 mt-3">
{!isDetail && ( {!isDetail && (
<Button color="primary" type="submit"> <Button color="primary" type="submit">

View File

@ -128,3 +128,23 @@ export const PptIcon = ({
/> />
</svg> </svg>
); );
export const FileIcon = ({
size,
height = 24,
width = 24,
fill = "currentColor",
...props
}: IconSvgProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || width}
height={size || height}
{...props}
viewBox="0 0 15 15"
>
<path
fill="currentColor"
d="m10.5.5l.354-.354L10.707 0H10.5zm3 3h.5v-.207l-.146-.147zm-1 10.5h-10v1h10zM2 13.5v-12H1v12zM2.5 1h8V0h-8zM13 3.5v10h1v-10zM10.146.854l3 3l.708-.708l-3-3zM2.5 14a.5.5 0 0 1-.5-.5H1A1.5 1.5 0 0 0 2.5 15zm10 1a1.5 1.5 0 0 0 1.5-1.5h-1a.5.5 0 0 1-.5.5zM2 1.5a.5.5 0 0 1 .5-.5V0A1.5 1.5 0 0 0 1 1.5z"
/>
</svg>
);

View File

@ -56,3 +56,7 @@ export async function uploadMagazineFile(id: string, data: any) {
}; };
return await httpPost(`/magazine-files/${id}`, headers, data); return await httpPost(`/magazine-files/${id}`, headers, data);
} }
export async function deleteMagazineFiles(id: number) {
return await httpDeleteInterceptor(`magazine-files/${id}`);
}