little update

This commit is contained in:
Rama Priyanto 2025-01-21 14:31:30 +07:00
parent eda6921ae2
commit 91f34586cf
4 changed files with 357 additions and 241 deletions

View File

@ -1,12 +1,13 @@
"use client";
import { AddIcon } from "@/components/icons";
import ArticleTable from "@/components/table/article-table";
import MagazineTable from "@/components/table/magazine/magazine-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() {
export default function MagazineTablePage() {
const router = useRouter();
const setGeneratedArticleIdStore = generatedArticleIds(
(state) => state.setArticleIds
@ -33,7 +34,7 @@ export default function MagazineTable() {
</Button> */}
</div>
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-2">
<ArticleTable />
<MagazineTable />
</div>
</div>
</div>

View File

@ -132,29 +132,6 @@ export default function NewCreateMagazineForm() {
clearErrors,
} = useForm<UserSettingSchema>(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<typeof createArticleSchema>) => {
MySwal.fire({
title: "Simpan Data",
@ -171,15 +148,6 @@ export default function NewCreateMagazineForm() {
});
};
function removeImgTags(htmlString: string) {
const parser = new DOMParser();
const doc = parser.parseFromString(String(htmlString), "text/html");
const images = doc.querySelectorAll("img");
images.forEach((img) => img.remove());
return doc.body.innerHTML;
}
const save = async (values: z.infer<typeof createArticleSchema>) => {
loading();
const formData = {
@ -189,15 +157,15 @@ export default function NewCreateMagazineForm() {
statusId: 1,
// description: htmlToString(removeImgTags(values.description)),
description: values.description,
rows: values.rows,
// rows: values.rows,
};
console.log("formd", formData);
// const response = await createMagazine(formData);
const response = await createMagazine(formData);
// if (response?.error) {
// error(response.message);
// return false;
// }
if (response?.error) {
error(response.message);
return false;
}
// const magazineId = response?.data?.data?.id;
// if (files?.length > 0) {
// const formFiles = new FormData();

View File

@ -1,100 +1,149 @@
"use client";
import {
TableCell,
TableRow,
Table,
TableHeader,
TableColumn,
TableBody,
Pagination,
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
Input,
User,
Card,
Divider,
Chip,
ChipProps,
} from "@nextui-org/react";
import { Button } from "@nextui-org/button";
import React, { Key, useCallback, useMemo, useState } from "react";
import {
AddIcon,
CreateIconIon,
DeleteIcon,
DotsYIcon,
EyeFilledIcon,
EyeIconMdi,
SearchIcon,
} from "@/components/icons";
import { error, success } from "@/config/swal";
import {
deleteArticle,
getArticleByCategory,
getListArticle,
} from "@/service/article";
import { getListMagazine } from "@/service/magazine";
import { Article } from "@/types/globals";
import { convertDateFormat } from "@/utils/global";
import { Button } from "@nextui-org/button";
import {
Chip,
ChipProps,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownTrigger,
Input,
Pagination,
Select,
SelectItem,
Spinner,
Table,
TableBody,
TableCell,
TableColumn,
TableHeader,
TableRow,
} from "@nextui-org/react";
import Link from "next/link";
type UserObject = {
id: number;
title: string;
status: string;
description: string;
avatar: string;
};
const statusColorMap = {
active: "success",
paused: "danger",
vacation: "warning",
};
export default function MagazineTable() {
type TableRow = (typeof magazineTable)[0];
import { Key, useCallback, useEffect, useState } from "react";
import Datepicker from "react-tailwindcss-datepicker";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
const columns = [
{ name: "No", uid: "no" },
{ name: "Judul", uid: "title" },
{ name: "Kategori", uid: "categoryName" },
{ name: "Tanggal Unggah", uid: "createdAt" },
{ name: "Kreator", uid: "createdByName" },
{ name: "Title", uid: "title" },
{ name: "Description", uid: "description" },
{ name: "Action", uid: "actions" },
{ name: "Aksi", uid: "actions" },
];
const magazineTable = [
{
id: 1,
title: "Proses pembuatan website humas ",
status: "active",
description: "Pembuatan website Humas adalah sebuah proses yang strategis untuk membangun identitas digital sebuah organisasi atau entitas, yang bertujuan untuk menyebarkan informasi kepada publik, memperkuat citra merek, serta menjaga keterbukaan dan transparansi. Proses ini melibatkan beberapa tahapan yang terstruktur dan terkoordinasi dengan baik",
avatar: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSa8Luglga9J2R3Bxt_PsWZISUHQWODD6_ZTAJ5mIQgxYCAE-YbkY81faTqp-hSA_jVPTs&usqp=CAU",
},
{
id: 2,
title: "Proses pembuatan website humas ",
status: "active",
description: "Pembuatan website Humas adalah sebuah proses yang strategis untuk membangun identitas digital sebuah organisasi atau entitas, yang bertujuan untuk menyebarkan informasi kepada publik, memperkuat citra merek, serta menjaga keterbukaan dan transparansi. Proses ini melibatkan beberapa tahapan yang terstruktur dan terkoordinasi dengan baik",
avatar: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSa8Luglga9J2R3Bxt_PsWZISUHQWODD6_ZTAJ5mIQgxYCAE-YbkY81faTqp-hSA_jVPTs&usqp=CAU",
},
{
id: 3,
title: "Proses pembuatan website humas ",
status: "active",
description: "Pembuatan website Humas adalah sebuah proses yang strategis untuk membangun identitas digital sebuah organisasi atau entitas, yang bertujuan untuk menyebarkan informasi kepada publik, memperkuat citra merek, serta menjaga keterbukaan dan transparansi. Proses ini melibatkan beberapa tahapan yang terstruktur dan terkoordinasi dengan baik",
avatar: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSa8Luglga9J2R3Bxt_PsWZISUHQWODD6_ZTAJ5mIQgxYCAE-YbkY81faTqp-hSA_jVPTs&usqp=CAU",
},
{
id: 4,
title: "Proses pembuatan website humas ",
status: "active",
description: "Pembuatan website Humas adalah sebuah proses yang strategis untuk membangun identitas digital sebuah organisasi atau entitas, yang bertujuan untuk menyebarkan informasi kepada publik, memperkuat citra merek, serta menjaga keterbukaan dan transparansi. Proses ini melibatkan beberapa tahapan yang terstruktur dan terkoordinasi dengan baik",
avatar: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSa8Luglga9J2R3Bxt_PsWZISUHQWODD6_ZTAJ5mIQgxYCAE-YbkY81faTqp-hSA_jVPTs&usqp=CAU",
},
{
id: 5,
title: "Proses pembuatan website humas ",
status: "active",
description: "Pembuatan website Humas adalah sebuah proses yang strategis untuk membangun identitas digital sebuah organisasi atau entitas, yang bertujuan untuk menyebarkan informasi kepada publik, memperkuat citra merek, serta menjaga keterbukaan dan transparansi. Proses ini melibatkan beberapa tahapan yang terstruktur dan terkoordinasi dengan baik",
avatar: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSa8Luglga9J2R3Bxt_PsWZISUHQWODD6_ZTAJ5mIQgxYCAE-YbkY81faTqp-hSA_jVPTs&usqp=CAU",
},
];
type ArticleData = Article & {
no: number;
createdAt: string;
};
const renderCell = useCallback((magazine: TableRow, columnKey: Key) => {
const cellValue = magazine[columnKey as keyof UserObject];
export default function MagazineTable() {
const MySwal = withReactContent(Swal);
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
const [article, setArticle] = useState<ArticleData[]>([]);
const [showData, setShowData] = useState("10");
const [search, setSearch] = useState("");
const [categories, setCategoies] = useState<any>([]);
const [selectedCategories, setSelectedCategories] = useState<any>([]);
const [startDateValue, setStartDateValue] = useState({
startDate: null,
endDate: null,
});
useEffect(() => {
initState();
}, [page, showData, startDateValue]);
useEffect(() => {
getCategories();
}, []);
async function getCategories() {
const res = await getArticleByCategory();
const data = res?.data?.data;
console.log("datass", res?.data?.data);
setCategoies(data);
}
async function initState() {
const req = {
limit: showData,
page: page,
search: search,
startDate:
startDateValue.startDate === null ? "" : startDateValue.startDate,
endDate: startDateValue.endDate === null ? "" : startDateValue.endDate,
};
const res = await getListMagazine(req);
getTableNumber(parseInt(showData), res.data?.data);
console.log("res.data?.data magz", res.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);
setArticle(newData);
}
};
async function doDelete(id: any) {
// loading();
const resDelete = await deleteArticle(id);
if (resDelete?.error) {
error(resDelete.message);
return false;
}
close();
success("Berhasil Hapus");
initState();
}
const handleDelete = (id: any) => {
MySwal.fire({
title: "Hapus Data",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
const renderCell = useCallback((article: ArticleData, columnKey: Key) => {
const cellValue = article[columnKey as keyof ArticleData];
const statusColorMap: Record<string, ChipProps["color"]> = {
active: "primary",
cancel: "danger",
@ -102,26 +151,11 @@ export default function MagazineTable() {
};
switch (columnKey) {
case "no":
return (
<div>{magazine.id}</div>
)
case "title":
return (
<div className="w-[350px]">{magazine.title}</div>
)
case "description":
return (
<div className="">{magazine.description}</div>
)
case "status":
return (
<Chip
className="capitalize "
color={statusColorMap[magazine.status]}
color={statusColorMap[article.status]}
size="lg"
variant="flat"
>
@ -130,6 +164,8 @@ export default function MagazineTable() {
</div>
</Chip>
);
case "createdAt":
return <p>{convertDateFormat(article.createdAt)}</p>;
case "actions":
return (
@ -141,36 +177,26 @@ export default function MagazineTable() {
</Button>
</DropdownTrigger>
<DropdownMenu>
<DropdownItem
>
<Link
href={`/admin/magazine/detail`}
>
<DropdownItem>
<Link href={`/admin/article/detail/${article.id}`}>
<EyeIconMdi className="inline mr-2 mb-1" />
Detail
</Link>
</DropdownItem>
<DropdownItem
>
<Link
href={`#`}
>
<DropdownItem>
<Link href={`/admin/article/edit/${article.id}`}>
<CreateIconIon className="inline mr-2 mb-1" />
Edit
</Link>
</DropdownItem>
<DropdownItem
>
<Link
href={`#`}
>
<DropdownItem onClick={() => handleDelete(article.id)}>
<DeleteIcon
color="red"
width={20}
height={16}
className="inline mr-2 mb-1"
/>
Delete
</Link>
</DropdownItem>
</DropdownMenu>
</Dropdown>
@ -182,21 +208,110 @@ export default function MagazineTable() {
}
}, []);
let typingTimer: NodeJS.Timeout;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
initState();
}
return (
<>
<div className="mx-3 my-5">
<Link href="/admin/magazine/create" >
<Button className="my-3 bg-blue-600 text-white" ><CreateIconIon />Create New Magazine</Button>
</Link>
<div className="flex flex-col items-center rounded-2xl">
<div className="p-3">
<div className="flex flex-col items-start rounded-2xl gap-3">
<div className="flex flex-col md:flex-row gap-3 w-full">
<div className="flex flex-col gap-1 w-1/3">
<p className="font-semibold text-sm">Pencarian</p>
<Input
aria-label="Search"
classNames={{
inputWrapper: "bg-default-100",
input: "text-sm",
}}
labelPlacement="outside"
startContent={
<SearchIcon className="text-base text-default-400 pointer-events-none flex-shrink-0" />
}
type="text"
onChange={(e) => setSearch(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
/>
</div>
<div className="flex flex-col gap-1 w-[72px]">
<p className="font-semibold text-sm">Data</p>
<Select
label=""
variant="bordered"
labelPlacement="outside"
placeholder="Select"
selectedKeys={[showData]}
className="w-full"
classNames={{ trigger: "border-1" }}
onChange={(e) =>
e.target.value === "" ? "" : setShowData(e.target.value)
}
>
<SelectItem key="5" value="5">
5
</SelectItem>
<SelectItem key="10" value="10">
10
</SelectItem>
</Select>
</div>
<div className="flex flex-col gap-1 w-[230px]">
<p className="font-semibold text-sm">Kategori</p>
<Select
label=""
variant="bordered"
labelPlacement="outside"
placeholder="Select"
selectionMode="multiple"
selectedKeys={[selectedCategories]}
className="w-full"
classNames={{ trigger: "border-1" }}
onChange={(e) => {
e.target.value === ""
? ""
: setSelectedCategories(e.target.value);
console.log("eeess", e.target.value);
}}
>
{categories?.map((category: any) => (
<SelectItem key={category?.id} value={category?.id}>
{category?.title}
</SelectItem>
))}
</Select>
</div>
<div className="flex flex-col gap-1 w-full md:w-[240px]">
<p className="font-semibold text-sm">Tanggal</p>
<Datepicker
value={startDateValue}
displayFormat="DD/MM/YYYY"
onChange={(e: any) => setStartDateValue(e)}
inputClassName="z-50 w-full text-sm bg-transparent border-1 border-gray-200 px-2 py-[6px] rounded-xl h-[40px] text-gray-600 dark:text-gray-300"
/>
</div>
</div>
<Table
// selectionMode="multiple"
aria-label="micro issue table"
className="rounded-xl"
className="rounded-3xl"
classNames={{
th: "bg-white dark:bg-black text-black dark:text-white border-b-1 text-md",
base: "bg-white dark:bg-black border",
wrapper: "min-h-[50px] bg-transpararent text-black dark:text-white ",
wrapper:
"min-h-[50px] bg-transpararent text-black dark:text-white ",
}}
>
<TableHeader columns={columns}>
@ -204,7 +319,11 @@ export default function MagazineTable() {
<TableColumn key={column.uid}>{column.name}</TableColumn>
)}
</TableHeader>
<TableBody items={magazineTable} emptyContent={"No data to display."}>
<TableBody
items={article}
emptyContent={"No data to display."}
loadingContent={<Spinner label="Loading..." />}
>
{(item) => (
<TableRow key={item.id}>
{(columnKey) => (
@ -214,9 +333,23 @@ export default function MagazineTable() {
)}
</TableBody>
</Table>
<div className="my-2 w-full flex justify-center">
<Pagination
isCompact
showControls
showShadow
color="primary"
classNames={{
base: "bg-transparent",
wrapper: "bg-transparent",
}}
page={page}
total={totalPage}
onChange={(page) => setPage(page)}
/>
</div>
</div>
</div>
</>
);
}

View File

@ -1,3 +1,4 @@
import { PaginationRequest } from "@/types/globals";
import {
httpDeleteInterceptor,
httpGet,
@ -16,3 +17,16 @@ export async function createMagazine(data: any) {
const pathUrl = `/magazines`;
return await httpPost(pathUrl, headers, data);
}
export async function getListMagazine(props: PaginationRequest) {
const { page, limit, search, startDate, endDate } = props;
const headers = {
"content-type": "application/json",
};
return await httpGet(
`/magazines?limit=${limit}&page=${page}&title=${search}&startDate=${
startDate || ""
}&endDate=${endDate || ""}`,
headers
);
}