+
*/}
-
diff --git a/app/(admin)/admin/e-magazine/page.tsx b/app/(admin)/admin/e-magazine/page.tsx
deleted file mode 100644
index b2250f5..0000000
--- a/app/(admin)/admin/e-magazine/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import MagazineTable from '@/components/table/magazine/magazine-table'
-import React from 'react'
-
-const AdminMagazine = () => {
- return (
-
- )
-}
-
-export default AdminMagazine
\ No newline at end of file
diff --git a/app/(admin)/admin/e-magazine/create/page.tsx b/app/(admin)/admin/magazine/create/page.tsx
similarity index 100%
rename from app/(admin)/admin/e-magazine/create/page.tsx
rename to app/(admin)/admin/magazine/create/page.tsx
diff --git a/app/(admin)/admin/e-magazine/detail/page.tsx b/app/(admin)/admin/magazine/detail/page.tsx
similarity index 100%
rename from app/(admin)/admin/e-magazine/detail/page.tsx
rename to app/(admin)/admin/magazine/detail/page.tsx
diff --git a/app/(admin)/admin/magazine/page.tsx b/app/(admin)/admin/magazine/page.tsx
new file mode 100644
index 0000000..f2c6555
--- /dev/null
+++ b/app/(admin)/admin/magazine/page.tsx
@@ -0,0 +1,41 @@
+"use client";
+import { AddIcon } from "@/components/icons";
+import ArticleTable from "@/components/table/article-table";
+import generatedArticleIds from "@/store/generated-article-store";
+import { Button, Card } from "@nextui-org/react";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+
+export default function MagazineTable() {
+ const router = useRouter();
+ const setGeneratedArticleIdStore = generatedArticleIds(
+ (state) => state.setArticleIds
+ );
+
+ return (
+
+
+
+
+
+
+ {/*
*/}
+
+
+
+
+ );
+}
diff --git a/app/(admin)/admin/master-category/page.tsx b/app/(admin)/admin/master-category/page.tsx
index ad10faf..c3a6a51 100644
--- a/app/(admin)/admin/master-category/page.tsx
+++ b/app/(admin)/admin/master-category/page.tsx
@@ -1,4 +1,339 @@
"use client";
-export default function MasterCategory() {
- return
master category
;
+import { AddIcon, CloudUploadIcon, TimesIcon } from "@/components/icons";
+import ArticleTable from "@/components/table/article-table";
+import CategoriesTable from "@/components/table/master-categories/categories-table";
+import generatedArticleIds from "@/store/generated-article-store";
+import {
+ Button,
+ Card,
+ Input,
+ Modal,
+ ModalBody,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ Textarea,
+ useDisclosure,
+} from "@nextui-org/react";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import * as z from "zod";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { Controller, useForm } from "react-hook-form";
+import { Fragment, useEffect, useState } from "react";
+import { getArticleByCategory } from "@/service/article";
+import ReactSelect from "react-select";
+import makeAnimated from "react-select/animated";
+import { useDropzone } from "react-dropzone";
+import { close, error, loading } from "@/config/swal";
+import {
+ createCategory,
+ uploadCategoryThumbnail,
+} from "@/service/master-categories";
+import Swal from "sweetalert2";
+import withReactContent from "sweetalert2-react-content";
+
+const categorySchema = z.object({
+ id: z.number(),
+ label: z.string(),
+ value: z.number(),
+});
+
+const createArticleSchema = z.object({
+ title: z.string().min(2, {
+ message: "Judul harus diisi",
+ }),
+ description: z.string().min(2, {
+ message: "Deskripsi harus diisi",
+ }),
+ category: z.array(categorySchema).nonempty({
+ message: "Kategori harus memiliki setidaknya satu item",
+ }),
+});
+
+interface CategoryType {
+ id: number;
+ label: string;
+ value: number;
+}
+
+export default function MasterCategoryTable() {
+ const router = useRouter();
+ const MySwal = withReactContent(Swal);
+
+ const animatedComponents = makeAnimated();
+ const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
+ const [listCategory, setListCategory] = useState
([]);
+ const [files, setFiles] = useState([]);
+ const [refresh, setRefresh] = useState(false);
+
+ const formOptions = {
+ resolver: zodResolver(createArticleSchema),
+ defaultValues: { title: "", description: "", category: [], tags: [] },
+ };
+
+ const { getRootProps, getInputProps } = useDropzone({
+ onDrop: (acceptedFiles) => {
+ setFiles(acceptedFiles.map((file) => Object.assign(file)));
+ },
+ maxFiles: 1,
+ });
+ type UserSettingSchema = z.infer;
+ const {
+ control,
+ handleSubmit,
+ formState: { errors },
+ setValue,
+ getValues,
+ watch,
+ setError,
+ clearErrors,
+ } = useForm(formOptions);
+
+ useEffect(() => {
+ fetchCategory();
+ }, []);
+
+ const fetchCategory = async () => {
+ const res = await getArticleByCategory();
+ if (res?.data?.data) {
+ setupCategory(res?.data?.data);
+ }
+ };
+
+ const setupCategory = (data: any) => {
+ const temp = [];
+ for (const element of data) {
+ temp.push({
+ id: element.id,
+ label: element.title,
+ value: element.id,
+ });
+ }
+ setListCategory(temp);
+ };
+
+ const onSubmit = async (values: z.infer) => {
+ console.log("values,", values);
+ loading();
+ const formData = {
+ title: values.title,
+ statusId: 1,
+ parentId: values.category[0].id,
+ description: values.description,
+ };
+
+ const response = await createCategory(formData);
+
+ if (response?.error) {
+ error(response.message);
+ return false;
+ }
+ const categoryId = response?.data?.data?.id;
+ const formFiles = new FormData();
+
+ formFiles.append("file", files[0]);
+ const resFile = await uploadCategoryThumbnail(categoryId, formFiles);
+ if (resFile?.error) {
+ error(resFile.message);
+ return false;
+ }
+ close();
+ setRefresh(!refresh);
+ MySwal.fire({
+ title: "Sukses",
+ icon: "success",
+ confirmButtonColor: "#3085d6",
+ confirmButtonText: "OK",
+ }).then((result) => {
+ if (result.isConfirmed) {
+ }
+ });
+ };
+
+ const handleRemoveFile = (file: File) => {
+ const uploadedFiles = files;
+ const filtered = uploadedFiles.filter((i) => i.name !== file.name);
+ setFiles([...filtered]);
+ };
+
+ return (
+
+
+
+
+
+
+ {() => (
+ <>
+
+ Kategori Baru
+
+
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+ );
}
diff --git a/app/(admin)/admin/master-role/create/page.tsx b/app/(admin)/admin/master-role/create/page.tsx
index bbf1ff8..7691385 100644
--- a/app/(admin)/admin/master-role/create/page.tsx
+++ b/app/(admin)/admin/master-role/create/page.tsx
@@ -3,7 +3,7 @@ import { Card } from "@nextui-org/react";
export default function CreateMasterUserRolePage() {
return (
-
+
);
diff --git a/app/(admin)/admin/master-role/detail/[id]/page.tsx b/app/(admin)/admin/master-role/detail/[id]/page.tsx
index 0fec0be..8673227 100644
--- a/app/(admin)/admin/master-role/detail/[id]/page.tsx
+++ b/app/(admin)/admin/master-role/detail/[id]/page.tsx
@@ -1,10 +1,10 @@
-import FormDetailMasterUserRole from '@/components/form/form-detail-master-user-role'
-import { Card } from '@nextui-org/react'
+import FormDetailMasterUserRole from "@/components/form/form-detail-master-user-role";
+import { Card } from "@nextui-org/react";
export default function DetailMasterRolePage() {
- return (
-
-
-
- )
+ return (
+
+
+
+ );
}
diff --git a/app/(admin)/admin/master-role/page.tsx b/app/(admin)/admin/master-role/page.tsx
index 181e505..3c55da3 100644
--- a/app/(admin)/admin/master-role/page.tsx
+++ b/app/(admin)/admin/master-role/page.tsx
@@ -6,7 +6,7 @@ import Link from "next/link";
export default function MasterRolePage() {
return (
-
+
diff --git a/app/(admin)/admin/master-user/create/page.tsx b/app/(admin)/admin/master-user/create/page.tsx
index 788304c..d978ce2 100644
--- a/app/(admin)/admin/master-user/create/page.tsx
+++ b/app/(admin)/admin/master-user/create/page.tsx
@@ -3,7 +3,7 @@ import { Card } from "@nextui-org/react";
export default function CreateMasterUserPage() {
return (
-
+
);
diff --git a/app/(admin)/admin/master-user/page.tsx b/app/(admin)/admin/master-user/page.tsx
index 5358a1a..23531a4 100644
--- a/app/(admin)/admin/master-user/page.tsx
+++ b/app/(admin)/admin/master-user/page.tsx
@@ -6,7 +6,7 @@ import Link from "next/link";
export default function MasterUserPage() {
return (
-
+
diff --git a/app/(admin)/admin/static-page/create/page.tsx b/app/(admin)/admin/static-page/create/page.tsx
index 734515f..9898c61 100644
--- a/app/(admin)/admin/static-page/create/page.tsx
+++ b/app/(admin)/admin/static-page/create/page.tsx
@@ -3,8 +3,8 @@ import { Card } from "@nextui-org/react";
export default function StaticPageGenerator() {
return (
-
+
-
+
);
}
diff --git a/app/(admin)/admin/static-page/edit/[id]/page.tsx b/app/(admin)/admin/static-page/edit/[id]/page.tsx
index a548ba4..990418c 100644
--- a/app/(admin)/admin/static-page/edit/[id]/page.tsx
+++ b/app/(admin)/admin/static-page/edit/[id]/page.tsx
@@ -3,7 +3,7 @@ import { Card } from "@nextui-org/react";
export default function StaticPageEdit() {
return (
-
+
);
diff --git a/app/(admin)/admin/static-page/page.tsx b/app/(admin)/admin/static-page/page.tsx
index 2006165..2bbb449 100644
--- a/app/(admin)/admin/static-page/page.tsx
+++ b/app/(admin)/admin/static-page/page.tsx
@@ -5,9 +5,9 @@ import Link from "next/link";
export default function StaticPageGeneratorList() {
return (
-
+
-
+
-
diff --git a/components/form/article/create-article-form.tsx b/components/form/article/create-article-form.tsx
index fed6890..50cbcc7 100644
--- a/components/form/article/create-article-form.tsx
+++ b/components/form/article/create-article-form.tsx
@@ -13,10 +13,15 @@ import { Button } from "@nextui-org/button";
import { CloudUploadIcon, TimesIcon } from "@/components/icons";
import Image from "next/image";
import { Switch } from "@nextui-org/switch";
-import { createArticle, getArticleByCategory } from "@/service/article";
+import {
+ createArticle,
+ getArticleByCategory,
+ uploadArticleFile,
+ uploadArticleThumbnail,
+} from "@/service/article";
import ReactSelect from "react-select";
import makeAnimated from "react-select/animated";
-import { Chip } from "@nextui-org/react";
+import { Checkbox, Chip } from "@nextui-org/react";
import GenerateSingleArticleForm from "./generate-ai-single-form";
import { htmlToString } from "@/utils/global";
import { close, error, loading } from "@/config/swal";
@@ -60,7 +65,7 @@ const createArticleSchema = z.object({
}),
tags: z.array(z.string()).nonempty({
message: "Minimal 1 tag",
- }), // Array berisi string
+ }),
});
export default function CreateArticleForm() {
@@ -72,11 +77,17 @@ export default function CreateArticleForm() {
const [useAi, setUseAI] = useState(false);
const [listCategory, setListCategory] = useState
([]);
const [tag, setTag] = useState("");
+ const [thumbnailImg, setThumbnailImg] = useState([]);
+ const [selectedMainImage, setSelectedMainImage] = useState();
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
- setFiles(acceptedFiles.map((file) => Object.assign(file)));
+ setFiles((prevFiles) => [
+ ...prevFiles,
+ ...acceptedFiles.map((file) => Object.assign(file)),
+ ]);
},
+ multiple: true,
});
const formOptions = {
@@ -150,7 +161,7 @@ export default function CreateArticleForm() {
title: values.title,
typeId: 1,
slug: values.slug,
- categoryId: values.category.map((item) => item.id).join(","),
+ categoryId: values.category[0].id,
tags: values.tags.join(","),
description: htmlToString(removeImgTags(values.description)),
htmlDescription: removeImgTags(values.description),
@@ -162,6 +173,34 @@ export default function CreateArticleForm() {
error(response.message);
return false;
}
+ const articleId = response?.data?.data?.id;
+ if (files?.length > 0) {
+ const formFiles = new FormData();
+
+ for (const element of files) {
+ formFiles.append("file", element);
+ const resFile = await uploadArticleFile(articleId, formFiles);
+ }
+ }
+
+ if (thumbnailImg?.length > 0 || files?.length > 0) {
+ const formFiles = new FormData();
+
+ formFiles.append("file", thumbnailImg[0]);
+ const resFile = await uploadArticleThumbnail(articleId, formFiles);
+ } else {
+ const formFiles = new FormData();
+
+ if (selectedMainImage) {
+ formFiles.append("file", files[selectedMainImage]);
+
+ const resFile = await uploadArticleThumbnail(articleId, formFiles);
+ } else {
+ formFiles.append("file", files[0]);
+ const resFile = await uploadArticleThumbnail(articleId, formFiles);
+ }
+ }
+
close();
successSubmit("/admin/article");
};
@@ -214,7 +253,7 @@ export default function CreateArticleForm() {
setFiles([...filtered]);
};
- const fileList = files.map((file) => (
+ const fileList = files.map((file, index) => (
+
setSelectedMainImage(index)}
+ >
+ Jadikan Thumbnail
+
));
+ const handleFileChange = (event: React.ChangeEvent
) => {
+ const selectedFiles = event.target.files;
+ if (selectedFiles) {
+ setThumbnailImg(Array.from(selectedFiles));
+ }
+ };
+
return (
*/}
-
+
) : null}
@@ -369,9 +427,41 @@ export default function CreateArticleForm() {
Thubmnail
-
+
+ {thumbnailImg.length > 0 ? (
+
+
})
+
+
+ ) : (
+ <>
+
{" "}
+
+ >
+ )}
+
Kategori
([]);
const [tag, setTag] = useState("");
+ const [detailfiles, setDetailFiles] = useState([]);
+ const [mainImage, setMainImage] = useState(1);
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
- setFiles(acceptedFiles.map((file) => Object.assign(file)));
+ setFiles((prevFiles) => [
+ ...prevFiles,
+ ...acceptedFiles.map((file) => Object.assign(file)),
+ ]);
},
+ multiple: true,
});
const formOptions = {
@@ -107,24 +115,25 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
} = useForm(formOptions);
useEffect(() => {
- async function initState() {
- const res = await getArticleById(id);
- // setArticle(data);
- const data = res.data?.data;
- setValue("title", data?.title);
- // setTypeId(String(data?.typeId));
- setValue("slug", data?.slug);
- setValue("description", data?.htmlDescription);
- setValue("tags", data?.tags ? data.tags.split(",") : []);
-
- setupInitCategory([data?.categoryId]);
-
- console.log("Data Aritcle", data);
- }
-
initState();
}, [listCategory]);
+ async function initState() {
+ const res = await getArticleById(id);
+ // setArticle(data);
+ const data = res.data?.data;
+ setValue("title", data?.title);
+ // setTypeId(String(data?.typeId));
+ setValue("slug", data?.slug);
+ setValue("description", data?.htmlDescription);
+ setValue("tags", data?.tags ? data.tags.split(",") : []);
+ setDetailFiles(data?.files);
+
+ setupInitCategory([data?.categoryId]);
+
+ console.log("Data Aritcle", data);
+ }
+
const setupInitCategory = (data: number[]) => {
const temp: CategoryType[] = [];
for (let i = 0; i < data.length; i++) {
@@ -193,6 +202,16 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
error(response.message);
return false;
}
+
+ const formFiles = new FormData();
+
+ if (files?.length > 0) {
+ for (const element of files) {
+ formFiles.append("file", element);
+ const resFile = await uploadArticleFile(String(id), formFiles);
+ }
+ }
+
close();
successSubmit("/admin/article");
};
@@ -226,12 +245,10 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
const renderFilePreview = (file: FileWithPreview) => {
if (file.type.startsWith("image")) {
return (
-
);
} else {
@@ -248,7 +265,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
const fileList = files.map((file) => (
{renderFilePreview(file)}
@@ -267,6 +284,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
));
+ 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 deleteArticleFiles(id);
+
+ if (res?.error) {
+ error(res.message);
+ return false;
+ }
+ close();
+ initState();
+ MySwal.fire({
+ title: "Sukses",
+ icon: "success",
+ confirmButtonColor: "#3085d6",
+ confirmButtonText: "OK",
+ }).then((result) => {
+ if (result.isConfirmed) {
+ }
+ });
+ };
+
return (
)}
@@ -369,42 +425,125 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
)}
File Media
-
-
-
-
-
-
- Tarik file disini atau klik untuk upload.
-
-
- ( Upload file dengan format .jpg, .jpeg, atau .png. Ukuran
- maksimal 100mb.)
+ {!isDetail && (
+
+
+
+
+
+
+ Tarik file disini atau klik untuk upload.
+
+
+ ( Upload file dengan format .jpg, .jpeg, atau .png. Ukuran
+ maksimal 100mb.)
+
-
- {files.length ? (
-
- {fileList}
-
- {/*
+ {files.length ? (
+
+ {fileList}
+
+ {/*
*/}
-
+
+
+ ) : null}
+
+ )}
+
+ {isDetail ? (
+ detailfiles ? (
+ <>
+
+
-
- ) : null}
-
+
+ >
+ ) : (
+
Belum Ada File
+ )
+ ) : (
+
+ {detailfiles?.map(
+ (file: any, index: number) =>
+ index > 0 && (
+
+
+
+

+
+
+
+ {file?.file_name}
+
+
+ {Math.round(file?.size / 100) / 10 > 1000 ? (
+ <>
+ {(Math.round(file?.size / 100) / 10000).toFixed(
+ 1
+ )}
+ >
+ ) : (
+ <>
+ {(Math.round(file?.size / 100) / 10).toFixed(1)}
+ >
+ )}
+ {" kb"}
+
+
+
+
+
+
+ )
+ )}
+
+ )}
Thubmnail
-
+
Kategori
diff --git a/components/table/master-categories/categories-table.tsx b/components/table/master-categories/categories-table.tsx
new file mode 100644
index 0000000..a283650
--- /dev/null
+++ b/components/table/master-categories/categories-table.tsx
@@ -0,0 +1,659 @@
+"use client";
+import {
+ CloudUploadIcon,
+ CreateIconIon,
+ DeleteIcon,
+ DotsYIcon,
+ EyeIconMdi,
+ SearchIcon,
+ TimesIcon,
+} from "@/components/icons";
+import {
+ deleteArticle,
+ getArticleByCategory,
+ getCategoryPagination,
+ getListArticle,
+} from "@/service/article";
+import {
+ deleteCategory,
+ getCategoryById,
+ updateCategory,
+ uploadCategoryThumbnail,
+} from "@/service/master-categories";
+import { Article } from "@/types/globals";
+import { convertDateFormat } from "@/utils/global";
+import { Button } from "@nextui-org/button";
+import {
+ Chip,
+ ChipProps,
+ Dropdown,
+ DropdownItem,
+ DropdownMenu,
+ DropdownTrigger,
+ Input,
+ Modal,
+ ModalBody,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ Pagination,
+ Select,
+ SelectItem,
+ Spinner,
+ Table,
+ TableBody,
+ TableCell,
+ TableColumn,
+ TableHeader,
+ TableRow,
+ Textarea,
+ useDisclosure,
+} from "@nextui-org/react";
+import Link from "next/link";
+import { Fragment, Key, useCallback, useEffect, useState } from "react";
+import { Controller, useForm } from "react-hook-form";
+import Datepicker from "react-tailwindcss-datepicker";
+import ReactSelect from "react-select";
+import makeAnimated from "react-select/animated";
+import { useDropzone } from "react-dropzone";
+import { close, error, loading, success } from "@/config/swal";
+
+import Swal from "sweetalert2";
+import withReactContent from "sweetalert2-react-content";
+import * as z from "zod";
+import { zodResolver } from "@hookform/resolvers/zod";
+
+const columns = [
+ { name: "No", uid: "no" },
+ { name: "Kategori", uid: "title" },
+ { name: "Deskripsi", uid: "description" },
+ { name: "Kategori Terkait", uid: "parentId" },
+ { name: "Dibuat ", uid: "createdAt" },
+
+ { name: "Aksi", uid: "actions" },
+];
+
+interface CategoryType {
+ id: number;
+ label: string;
+ value: number;
+}
+type ArticleData = Article & {
+ no: number;
+ createdAt: string;
+};
+
+const categorySchema = z.object({
+ id: z.number(),
+ label: z.string(),
+ value: z.number(),
+});
+
+const createArticleSchema = z.object({
+ id: z.string().min(1, {
+ message: "Id harus valid",
+ }),
+ title: z.string().min(2, {
+ message: "Judul harus diisi",
+ }),
+ description: z.string().min(2, {
+ message: "Deskripsi harus diisi",
+ }),
+ category: z.array(categorySchema).nonempty({
+ message: "Kategori harus memiliki setidaknya satu item",
+ }),
+ file: z.string(),
+});
+
+export default function CategoriesTable(props: { triggerRefresh: boolean }) {
+ const MySwal = withReactContent(Swal);
+ const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
+ const animatedComponents = makeAnimated();
+
+ const [page, setPage] = useState(1);
+ const [totalPage, setTotalPage] = useState(1);
+ const [categories, setCategories] = useState([]);
+ const [showData, setShowData] = useState("10");
+ const [search, setSearch] = useState("");
+ const [listCategory, setListCategory] = useState([]);
+ const [files, setFiles] = useState([]);
+ const [isDetail, setIsDetail] = useState(false);
+
+ const formOptions = {
+ resolver: zodResolver(createArticleSchema),
+ defaultValues: { title: "", description: "", category: [], tags: [] },
+ };
+
+ const { getRootProps, getInputProps } = useDropzone({
+ onDrop: (acceptedFiles) => {
+ setFiles(acceptedFiles.map((file) => Object.assign(file)));
+ },
+ maxFiles: 1,
+ });
+ type UserSettingSchema = z.infer;
+ const {
+ control,
+ handleSubmit,
+ formState: { errors },
+ setValue,
+ getValues,
+ watch,
+ setError,
+ clearErrors,
+ } = useForm(formOptions);
+
+ useEffect(() => {
+ initState();
+ }, [page, showData, props.triggerRefresh]);
+
+ async function initState() {
+ const req = {
+ limit: showData,
+ page: page,
+ search: search,
+ };
+ const res = await getCategoryPagination(req);
+ getTableNumber(parseInt(showData), res.data?.data);
+ setTotalPage(res?.data?.meta?.totalPage);
+ }
+
+ const getTableNumber = (limit: number, data: Article[]) => {
+ if (data) {
+ const startIndex = limit * (page - 1);
+ let iterate = 0;
+ const newData = data.map((value: any) => {
+ iterate++;
+ value.no = startIndex + iterate;
+ return value;
+ });
+ console.log("daata", data);
+ setCategories(newData);
+ }
+ };
+
+ useEffect(() => {
+ fetchCategory();
+ }, []);
+
+ const fetchCategory = async () => {
+ const res = await getArticleByCategory();
+ if (res?.data?.data) {
+ setupCategory(res?.data?.data);
+ }
+ };
+
+ const setupCategory = (data: any) => {
+ const temp = [];
+ for (const element of data) {
+ temp.push({
+ id: element.id,
+ label: element.title,
+ value: element.id,
+ });
+ }
+ setListCategory(temp);
+ };
+
+ async function doDelete(id: number) {
+ // loading();
+ const resDelete = await deleteCategory(id);
+
+ if (resDelete?.error) {
+ error(resDelete.message);
+ return false;
+ }
+ close();
+ success("Berhasil Hapus");
+ initState();
+ }
+
+ const handleDelete = (id: number) => {
+ MySwal.fire({
+ title: "Hapus Data",
+ icon: "warning",
+ showCancelButton: true,
+ cancelButtonColor: "#3085d6",
+ confirmButtonColor: "#d33",
+ confirmButtonText: "Hapus",
+ }).then((result) => {
+ if (result.isConfirmed) {
+ doDelete(id);
+ }
+ });
+ };
+
+ const openModal = async (id: number | string, detail: boolean) => {
+ setIsDetail(detail);
+ const res = await getCategoryById(Number(id));
+ const data = res?.data?.data;
+ setValue("id", String(data?.id));
+ setValue("title", data?.title);
+ setValue("description", data?.description);
+ setValue("file", data?.thumbnailUrl);
+ setupInitCategory([data?.parentId]);
+
+ onOpen();
+ };
+
+ const setupInitCategory = (data: number[]) => {
+ const temp: CategoryType[] = [];
+ for (let i = 0; i < data.length; i++) {
+ const datas = listCategory.filter((a) => a.id == data[i]);
+
+ temp.push(datas[0]);
+ }
+ setValue("category", temp as [CategoryType, ...CategoryType[]]);
+ console.log("temp", temp);
+ };
+
+ const renderCell = useCallback(
+ (category: ArticleData, columnKey: Key) => {
+ const cellValue = category[columnKey as keyof ArticleData];
+ const statusColorMap: Record = {
+ active: "primary",
+ cancel: "danger",
+ pending: "success",
+ };
+
+ const findRelated = (parent: number | string) => {
+ const filter = listCategory?.filter((a) => a.id == parent);
+ console.log("filter", filter[0]?.label);
+ return filter[0]?.label;
+ };
+
+ switch (columnKey) {
+ case "parentId":
+ return {cellValue === 0 ? "-" : findRelated(cellValue)}
;
+ case "createdAt":
+ return {convertDateFormat(category.createdAt)}
;
+
+ case "actions":
+ return (
+
+
+
+
+
+
+ openModal(category.id, true)}>
+
+ Detail
+
+ openModal(category.id, false)}>
+
+ Edit
+
+ handleDelete(category.id)}>
+
+ Delete
+
+
+
+
+ );
+
+ default:
+ return cellValue;
+ }
+ },
+ [listCategory]
+ );
+
+ let typingTimer: NodeJS.Timeout;
+ const doneTypingInterval = 1500;
+
+ const handleKeyUp = () => {
+ clearTimeout(typingTimer);
+ typingTimer = setTimeout(doneTyping, doneTypingInterval);
+ };
+
+ const handleKeyDown = () => {
+ clearTimeout(typingTimer);
+ };
+
+ async function doneTyping() {
+ initState();
+ }
+
+ const onSubmit = async (values: z.infer) => {
+ loading();
+ const formData = {
+ id: Number(values.id),
+ title: values.title,
+ statusId: 1,
+ parentId: values.category[0].id,
+ description: values.description,
+ };
+
+ const response = await updateCategory(values.id, formData);
+
+ if (response?.error) {
+ error(response.message);
+ return false;
+ }
+ if (files?.length > 0) {
+ const formFiles = new FormData();
+
+ formFiles.append("file", files[0]);
+ const resFile = await uploadCategoryThumbnail(values.id, formFiles);
+ if (resFile?.error) {
+ error(resFile.message);
+ return false;
+ }
+ }
+ setFiles([]);
+ close();
+ initState();
+ MySwal.fire({
+ title: "Sukses",
+ icon: "success",
+ confirmButtonColor: "#3085d6",
+ confirmButtonText: "OK",
+ }).then((result) => {
+ if (result.isConfirmed) {
+ }
+ });
+ };
+
+ const handleRemoveFile = (file: File) => {
+ const uploadedFiles = files;
+ const filtered = uploadedFiles.filter((i) => i.name !== file.name);
+ setFiles([...filtered]);
+ };
+
+ return (
+ <>
+
+
+
+
+
Pencarian
+
+ }
+ type="text"
+ onChange={(e) => setSearch(e.target.value)}
+ onKeyUp={handleKeyUp}
+ onKeyDown={handleKeyDown}
+ />
+
+
+
Data
+
+
+
+
+
+ {(column) => (
+ {column.name}
+ )}
+
+ }
+ >
+ {(item) => (
+
+ {(columnKey) => (
+ {renderCell(item, columnKey)}
+ )}
+
+ )}
+
+
+
+
+
+
+
+ {() => (
+ <>
+
+ Kategori Baru
+
+
+
+
+ >
+ )}
+
+
+ >
+ );
+}
diff --git a/service/article.ts b/service/article.ts
index 6e45ca3..ca8e11b 100644
--- a/service/article.ts
+++ b/service/article.ts
@@ -55,3 +55,29 @@ export async function getArticleByCategory() {
};
return await httpGet(`/article-categories?limit=50`, headers);
}
+export async function getCategoryPagination(data: any) {
+ const headers = {
+ "content-type": "application/json",
+ };
+ return await httpGet(
+ `/article-categories?limit=${data?.limit}&page=${data?.page}&title=${data?.search}`,
+ headers
+ );
+}
+
+export async function uploadArticleFile(id: string, data: any) {
+ const headers = {
+ "content-type": "multipart/form-data",
+ };
+ return await httpPost(`/article-files/${id}`, headers, data);
+}
+export async function uploadArticleThumbnail(id: string, data: any) {
+ const headers = {
+ "content-type": "multipart/form-data",
+ };
+ return await httpPost(`/articles/thumbnail/${id}`, headers, data);
+}
+
+export async function deleteArticleFiles(id: number) {
+ return await httpDeleteInterceptor(`article-files/${id}`);
+}
diff --git a/service/master-categories.tsx b/service/master-categories.tsx
new file mode 100644
index 0000000..b73acbe
--- /dev/null
+++ b/service/master-categories.tsx
@@ -0,0 +1,44 @@
+import {
+ httpDeleteInterceptor,
+ httpGet,
+ httpPost,
+ httpPut,
+} from "./http-config/axios-base-service";
+import Cookies from "js-cookie";
+
+const token = Cookies.get("access_token");
+
+export async function createCategory(data: any) {
+ const headers = {
+ "content-type": "application/json",
+ Authorization: `Bearer ${token}`,
+ };
+ const pathUrl = `/article-categories`;
+ return await httpPost(pathUrl, headers, data);
+}
+
+export async function updateCategory(id: string, data: any) {
+ const headers = {
+ "content-type": "application/json",
+ };
+ const pathUrl = `/article-categories/${id}`;
+ return await httpPut(pathUrl, headers, data);
+}
+
+export async function getCategoryById(id: number) {
+ const headers = {
+ "content-type": "application/json",
+ };
+ return await httpGet(`/article-categories/${id}`, headers);
+}
+
+export async function deleteCategory(id: number) {
+ return await httpDeleteInterceptor(`article-categories/${id}`);
+}
+
+export async function uploadCategoryThumbnail(id: string, data: any) {
+ const headers = {
+ "content-type": "multipart/form-data",
+ };
+ return await httpPost(`/article-categories/thumbnail/${id}`, headers, data);
+}